Compare commits
1 commit
master
...
proof_chec
Author | SHA1 | Date | |
---|---|---|---|
|
7885f34221 |
6 changed files with 62 additions and 7 deletions
|
@ -214,7 +214,9 @@ class CommandTestCase(IntegrationTestCase):
|
|||
self.assertEqual(claim['outputs'][0]['name'], name)
|
||||
if confirm:
|
||||
await self.on_transaction_dict(claim)
|
||||
height = self.ledger.headers.height
|
||||
await self.generate(1)
|
||||
await self.on_header(height + 1)
|
||||
await self.on_transaction_dict(claim)
|
||||
return claim
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ __node_daemon__ = 'lbrycrdd'
|
|||
__node_cli__ = 'lbrycrd-cli'
|
||||
__node_bin__ = ''
|
||||
__node_url__ = (
|
||||
'https://github.com/lbryio/lbrycrd/releases/download/v0.12.4.1/lbrycrd-linux.zip'
|
||||
'https://github.com/lbryio/lbrycrd/releases/download/v0.17.2.0/lbrycrd-linux.zip'
|
||||
)
|
||||
__spvserver__ = 'lbry.wallet.server.coin.LBCRegTest'
|
||||
|
||||
|
|
|
@ -3,10 +3,11 @@ import logging
|
|||
from binascii import unhexlify
|
||||
from typing import Tuple, List, Dict
|
||||
|
||||
from lbry.wallet.claim_proofs import verify_proof, InvalidProofError
|
||||
from torba.client.baseledger import BaseLedger
|
||||
from torba.client.baseaccount import SingleKey
|
||||
from lbry.schema.result import Outputs
|
||||
from lbry.schema.url import URL
|
||||
from lbry.schema.url import URL, normalize_name
|
||||
from lbry.wallet.dewies import dewies_to_lbc
|
||||
from lbry.wallet.resolve import Resolver
|
||||
from lbry.wallet.account import Account
|
||||
|
@ -72,7 +73,50 @@ class MainNetLedger(BaseLedger):
|
|||
result[url] = txo
|
||||
else:
|
||||
result[url] = {'error': f'{url} did not resolve to a claim'}
|
||||
return result
|
||||
return await self.validate_resolutions(result)
|
||||
|
||||
async def validate_resolutions(self, resolutions: dict):
|
||||
provables = {}
|
||||
for url, txo in resolutions.items():
|
||||
if isinstance(txo, dict) and 'error' in txo:
|
||||
continue
|
||||
parsed_url = URL.parse(url)
|
||||
# cases we check: names without sequence, claim id or any modifier
|
||||
# except: @channel/name as the signature is enough, instead we check '@channel'
|
||||
if parsed_url.has_channel:
|
||||
if not parsed_url.has_stream_in_channel and parsed_url.channel.to_dict().keys() == {'name'}:
|
||||
provables.setdefault(normalize_name(parsed_url.channel.name), []).append((url, txo.id))
|
||||
elif parsed_url.has_stream and parsed_url.stream.to_dict().keys() == {'name'}:
|
||||
provables.setdefault(normalize_name(parsed_url.stream.name), []).append((url, txo.id))
|
||||
if not provables:
|
||||
return resolutions
|
||||
root_hash = self.headers.claim_trie_root.decode()
|
||||
names = list(provables.keys())
|
||||
proofs = await self.network.get_name_proofs(self.headers.hash().decode(), *names)
|
||||
for proof, name in zip(proofs, names):
|
||||
all_failed = True
|
||||
for url, txid in provables[name]:
|
||||
if not proof.get('txhash'):
|
||||
resolutions[url] = {'error': f"Proof mismatch on {url}: no claims under {name} but we got {txid}"}
|
||||
continue
|
||||
expected_txid = f"{proof['txhash']}:{proof['nOut']}"
|
||||
if txid != expected_txid:
|
||||
resolutions[url] = {
|
||||
'error': f"Proof mismatch: {url} resolved to {txid} instead of {expected_txid} on {name}"
|
||||
}
|
||||
else:
|
||||
all_failed = False
|
||||
if not all_failed:
|
||||
try:
|
||||
verify_proof(proof, root_hash, name)
|
||||
continue
|
||||
except InvalidProofError as error:
|
||||
for url, _ in provables[name]:
|
||||
resolution = resolutions[url]
|
||||
if isinstance(resolution, dict) and 'error' in resolution:
|
||||
continue
|
||||
resolutions[url] = {'error': f'Invalid proof for {url}: {str(error)}'}
|
||||
return resolutions
|
||||
|
||||
async def claim_search(self, **kwargs) -> Tuple[List, int, int]:
|
||||
return await self._inflate_outputs(self.network.claim_search(**kwargs))
|
||||
|
|
|
@ -9,5 +9,8 @@ class Network(BaseNetwork):
|
|||
def resolve(self, urls):
|
||||
return self.rpc('blockchain.claimtrie.resolve', urls)
|
||||
|
||||
def get_name_proofs(self, block_hash, *names):
|
||||
return self.rpc('blockchain.claimtrie.getnameproofs', (block_hash, *names))
|
||||
|
||||
def claim_search(self, **kwargs):
|
||||
return self.rpc('blockchain.claimtrie.search', kwargs)
|
||||
|
|
|
@ -104,6 +104,7 @@ class LBRYElectrumX(ElectrumX):
|
|||
'blockchain.transaction.get_height': self.transaction_get_height,
|
||||
'blockchain.claimtrie.search': self.claimtrie_search,
|
||||
'blockchain.claimtrie.resolve': self.claimtrie_resolve,
|
||||
'blockchain.claimtrie.getnameproofs': self.claimtrie_getnameproofs,
|
||||
'blockchain.claimtrie.getclaimsbyids': self.claimtrie_getclaimsbyids,
|
||||
'blockchain.block.get_server_height': self.get_server_height,
|
||||
}
|
||||
|
@ -165,6 +166,9 @@ class LBRYElectrumX(ElectrumX):
|
|||
async def get_server_height(self):
|
||||
return self.bp.height
|
||||
|
||||
def claimtrie_getnameproofs(self, block_hash, *names):
|
||||
return self.daemon._send_vector('getnameproof', iter((name, block_hash) for name in names))
|
||||
|
||||
async def transaction_get_height(self, tx_hash):
|
||||
self.assert_tx_hash(tx_hash)
|
||||
transaction_info = await self.daemon.getrawtransaction(tx_hash, True)
|
||||
|
|
|
@ -266,6 +266,7 @@ class BlockchainNode:
|
|||
self.data_path = None
|
||||
self.protocol = None
|
||||
self.transport = None
|
||||
self.blockchain_address = None
|
||||
self._block_expected = 0
|
||||
self.hostname = 'localhost'
|
||||
self.peerport = 9246 + 2 # avoid conflict with default peer port
|
||||
|
@ -329,7 +330,7 @@ class BlockchainNode:
|
|||
self.daemon_bin,
|
||||
f'-datadir={self.data_path}', '-printtoconsole', '-regtest', '-server', '-txindex',
|
||||
f'-rpcuser={self.rpcuser}', f'-rpcpassword={self.rpcpassword}', f'-rpcport={self.rpcport}',
|
||||
f'-port={self.peerport}'
|
||||
f'-port={self.peerport}', '-addresstype=legacy', '-vbparams=segwit:0:999999999999'
|
||||
)
|
||||
self.log.info(' '.join(command))
|
||||
self.transport, self.protocol = await loop.subprocess_exec(
|
||||
|
@ -364,9 +365,10 @@ class BlockchainNode:
|
|||
self.log.info(out.decode().strip())
|
||||
return out.decode().strip()
|
||||
|
||||
def generate(self, blocks):
|
||||
async def generate(self, blocks, address=None):
|
||||
self._block_expected += blocks
|
||||
return self._cli_cmnd('generate', str(blocks))
|
||||
address = await self.get_raw_change_address()
|
||||
return await self._cli_cmnd('generatetoaddress', str(blocks), str(address))
|
||||
|
||||
def invalidate_block(self, blockhash):
|
||||
return self._cli_cmnd('invalidateblock', blockhash)
|
||||
|
@ -375,7 +377,7 @@ class BlockchainNode:
|
|||
return self._cli_cmnd('getblockhash', str(block))
|
||||
|
||||
def get_raw_change_address(self):
|
||||
return self._cli_cmnd('getrawchangeaddress')
|
||||
return self._cli_cmnd('getrawchangeaddress', 'legacy')
|
||||
|
||||
async def get_balance(self):
|
||||
return float(await self._cli_cmnd('getbalance'))
|
||||
|
|
Loading…
Reference in a new issue