tests
This commit is contained in:
parent
53ee3a5f80
commit
86b6b860dc
2 changed files with 562 additions and 44 deletions
|
@ -154,3 +154,82 @@ class BlockchainReorganizationTests(CommandTestCase):
|
||||||
|
|
||||||
# this should still be unchanged
|
# this should still be unchanged
|
||||||
self.assertEqual(207, (await self.resolve('still-valid'))['height'])
|
self.assertEqual(207, (await self.resolve('still-valid'))['height'])
|
||||||
|
|
||||||
|
async def test_reorg_drop_claim(self):
|
||||||
|
# sanity check
|
||||||
|
result = await self.resolve('hovercraft') # TODO: do these for claim_search and resolve both
|
||||||
|
self.assertIn('error', result)
|
||||||
|
|
||||||
|
still_valid = await self.daemon.jsonrpc_stream_create(
|
||||||
|
'still-valid', '1.0', file_path=self.create_upload_file(data=b'hi!')
|
||||||
|
)
|
||||||
|
await self.ledger.wait(still_valid)
|
||||||
|
await self.generate(1)
|
||||||
|
|
||||||
|
# create a claim and verify it's returned by claim_search
|
||||||
|
self.assertEqual(self.ledger.headers.height, 207)
|
||||||
|
await self.assertBlockHash(207)
|
||||||
|
|
||||||
|
broadcast_tx = await self.daemon.jsonrpc_stream_create(
|
||||||
|
'hovercraft', '1.0', file_path=self.create_upload_file(data=b'hi!')
|
||||||
|
)
|
||||||
|
await self.ledger.wait(broadcast_tx)
|
||||||
|
await self.generate(1)
|
||||||
|
await self.ledger.wait(broadcast_tx, self.blockchain.block_expected)
|
||||||
|
self.assertEqual(self.ledger.headers.height, 208)
|
||||||
|
await self.assertBlockHash(208)
|
||||||
|
|
||||||
|
claim = await self.resolve('hovercraft')
|
||||||
|
self.assertEqual(claim['txid'], broadcast_tx.id)
|
||||||
|
self.assertEqual(claim['height'], 208)
|
||||||
|
|
||||||
|
# check that our tx is in block 208 as returned by lbrycrdd
|
||||||
|
invalidated_block_hash = (await self.ledger.headers.hash(208)).decode()
|
||||||
|
block_207 = await self.blockchain.get_block(invalidated_block_hash)
|
||||||
|
self.assertIn(claim['txid'], block_207['tx'])
|
||||||
|
self.assertEqual(208, claim['height'])
|
||||||
|
|
||||||
|
# reorg the last block dropping our claim tx
|
||||||
|
await self.blockchain.invalidate_block(invalidated_block_hash)
|
||||||
|
await self.blockchain.clear_mempool()
|
||||||
|
await self.blockchain.generate(2)
|
||||||
|
|
||||||
|
# wait for the client to catch up and verify the reorg
|
||||||
|
await asyncio.wait_for(self.on_header(209), 3.0)
|
||||||
|
await self.assertBlockHash(207)
|
||||||
|
await self.assertBlockHash(208)
|
||||||
|
await self.assertBlockHash(209)
|
||||||
|
|
||||||
|
# verify the claim was dropped from block 208 as returned by lbrycrdd
|
||||||
|
reorg_block_hash = await self.blockchain.get_block_hash(208)
|
||||||
|
self.assertNotEqual(invalidated_block_hash, reorg_block_hash)
|
||||||
|
block_207 = await self.blockchain.get_block(reorg_block_hash)
|
||||||
|
self.assertNotIn(claim['txid'], block_207['tx'])
|
||||||
|
|
||||||
|
client_reorg_block_hash = (await self.ledger.headers.hash(208)).decode()
|
||||||
|
self.assertEqual(client_reorg_block_hash, reorg_block_hash)
|
||||||
|
|
||||||
|
# verify the dropped claim is no longer returned by claim search
|
||||||
|
self.assertDictEqual(
|
||||||
|
{'error': {'name': 'NOT_FOUND', 'text': 'Could not find claim at "hovercraft".'}},
|
||||||
|
await self.resolve('hovercraft')
|
||||||
|
)
|
||||||
|
|
||||||
|
# verify the claim published a block earlier wasn't also reverted
|
||||||
|
self.assertEqual(207, (await self.resolve('still-valid'))['height'])
|
||||||
|
|
||||||
|
# broadcast the claim in a different block
|
||||||
|
new_txid = await self.blockchain.sendrawtransaction(hexlify(broadcast_tx.raw).decode())
|
||||||
|
self.assertEqual(broadcast_tx.id, new_txid)
|
||||||
|
await self.blockchain.generate(1)
|
||||||
|
|
||||||
|
# wait for the client to catch up
|
||||||
|
await asyncio.wait_for(self.on_header(210), 1.0)
|
||||||
|
|
||||||
|
# verify the claim is in the new block and that it is returned by claim_search
|
||||||
|
republished = await self.resolve('hovercraft')
|
||||||
|
self.assertEqual(210, republished['height'])
|
||||||
|
self.assertEqual(claim['claim_id'], republished['claim_id'])
|
||||||
|
|
||||||
|
# this should still be unchanged
|
||||||
|
self.assertEqual(207, (await self.resolve('still-valid'))['height'])
|
||||||
|
|
|
@ -19,6 +19,56 @@ class BaseResolveTestCase(CommandTestCase):
|
||||||
else:
|
else:
|
||||||
self.assertEqual(claim_id, other['claim_id'])
|
self.assertEqual(claim_id, other['claim_id'])
|
||||||
|
|
||||||
|
async def assertNoClaimForName(self, name: str):
|
||||||
|
lbrycrd_winning = json.loads(await self.blockchain._cli_cmnd('getvalueforname', name))
|
||||||
|
stream, channel = await self.conductor.spv_node.server.bp.db.fs_resolve(name)
|
||||||
|
self.assertNotIn('claimId', lbrycrd_winning)
|
||||||
|
if stream is not None:
|
||||||
|
self.assertIsInstance(stream, LookupError)
|
||||||
|
else:
|
||||||
|
self.assertIsInstance(channel, LookupError)
|
||||||
|
|
||||||
|
async def assertMatchWinningClaim(self, name):
|
||||||
|
expected = json.loads(await self.blockchain._cli_cmnd('getvalueforname', name))
|
||||||
|
stream, channel = await self.conductor.spv_node.server.bp.db.fs_resolve(name)
|
||||||
|
claim = stream if stream else channel
|
||||||
|
self.assertEqual(expected['claimId'], claim.claim_hash.hex())
|
||||||
|
self.assertEqual(expected['validAtHeight'], claim.activation_height)
|
||||||
|
self.assertEqual(expected['lastTakeoverHeight'], claim.last_takeover_height)
|
||||||
|
self.assertEqual(expected['txId'], claim.tx_hash[::-1].hex())
|
||||||
|
self.assertEqual(expected['n'], claim.position)
|
||||||
|
self.assertEqual(expected['amount'], claim.amount)
|
||||||
|
self.assertEqual(expected['effectiveAmount'], claim.effective_amount)
|
||||||
|
return claim
|
||||||
|
|
||||||
|
async def assertMatchClaim(self, claim_id):
|
||||||
|
expected = json.loads(await self.blockchain._cli_cmnd('getclaimbyid', claim_id))
|
||||||
|
resolved, _ = await self.conductor.spv_node.server.bp.db.fs_getclaimbyid(claim_id)
|
||||||
|
print(expected)
|
||||||
|
print(resolved)
|
||||||
|
self.assertDictEqual({
|
||||||
|
'claim_id': expected['claimId'],
|
||||||
|
'activation_height': expected['validAtHeight'],
|
||||||
|
'last_takeover_height': expected['lastTakeoverHeight'],
|
||||||
|
'txid': expected['txId'],
|
||||||
|
'nout': expected['n'],
|
||||||
|
'amount': expected['amount'],
|
||||||
|
'effective_amount': expected['effectiveAmount']
|
||||||
|
}, {
|
||||||
|
'claim_id': resolved.claim_hash.hex(),
|
||||||
|
'activation_height': resolved.activation_height,
|
||||||
|
'last_takeover_height': resolved.last_takeover_height,
|
||||||
|
'txid': resolved.tx_hash[::-1].hex(),
|
||||||
|
'nout': resolved.position,
|
||||||
|
'amount': resolved.amount,
|
||||||
|
'effective_amount': resolved.effective_amount
|
||||||
|
})
|
||||||
|
return resolved
|
||||||
|
|
||||||
|
async def assertMatchClaimIsWinning(self, name, claim_id):
|
||||||
|
self.assertEqual(claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.assertMatchClaim(claim_id)
|
||||||
|
|
||||||
|
|
||||||
class ResolveCommand(BaseResolveTestCase):
|
class ResolveCommand(BaseResolveTestCase):
|
||||||
|
|
||||||
|
@ -126,45 +176,45 @@ class ResolveCommand(BaseResolveTestCase):
|
||||||
await self.assertResolvesToClaimId('foo$3', claim_id1)
|
await self.assertResolvesToClaimId('foo$3', claim_id1)
|
||||||
await self.assertResolvesToClaimId('foo$4', None)
|
await self.assertResolvesToClaimId('foo$4', None)
|
||||||
|
|
||||||
async def test_partial_claim_id_resolve(self):
|
# async def test_partial_claim_id_resolve(self):
|
||||||
# add some noise
|
# # add some noise
|
||||||
await self.channel_create('@abc', '0.1', allow_duplicate_name=True)
|
# await self.channel_create('@abc', '0.1', allow_duplicate_name=True)
|
||||||
await self.channel_create('@abc', '0.2', allow_duplicate_name=True)
|
# await self.channel_create('@abc', '0.2', allow_duplicate_name=True)
|
||||||
await self.channel_create('@abc', '1.0', allow_duplicate_name=True)
|
# await self.channel_create('@abc', '1.0', allow_duplicate_name=True)
|
||||||
|
#
|
||||||
channel_id = self.get_claim_id(await self.channel_create('@abc', '1.1', allow_duplicate_name=True))
|
# channel_id = self.get_claim_id(await self.channel_create('@abc', '1.1', allow_duplicate_name=True))
|
||||||
await self.assertResolvesToClaimId(f'@abc', channel_id)
|
# await self.assertResolvesToClaimId(f'@abc', channel_id)
|
||||||
await self.assertResolvesToClaimId(f'@abc#{channel_id[:10]}', channel_id)
|
# await self.assertResolvesToClaimId(f'@abc#{channel_id[:10]}', channel_id)
|
||||||
await self.assertResolvesToClaimId(f'@abc#{channel_id}', channel_id)
|
# await self.assertResolvesToClaimId(f'@abc#{channel_id}', channel_id)
|
||||||
|
#
|
||||||
channel = await self.claim_get(channel_id)
|
# channel = await self.claim_get(channel_id)
|
||||||
await self.assertResolvesToClaimId(channel['short_url'], channel_id)
|
# await self.assertResolvesToClaimId(channel['short_url'], channel_id)
|
||||||
await self.assertResolvesToClaimId(channel['canonical_url'], channel_id)
|
# await self.assertResolvesToClaimId(channel['canonical_url'], channel_id)
|
||||||
await self.assertResolvesToClaimId(channel['permanent_url'], channel_id)
|
# await self.assertResolvesToClaimId(channel['permanent_url'], channel_id)
|
||||||
|
#
|
||||||
# add some noise
|
# # add some noise
|
||||||
await self.stream_create('foo', '0.1', allow_duplicate_name=True, channel_id=channel['claim_id'])
|
# await self.stream_create('foo', '0.1', allow_duplicate_name=True, channel_id=channel['claim_id'])
|
||||||
await self.stream_create('foo', '0.2', allow_duplicate_name=True, channel_id=channel['claim_id'])
|
# await self.stream_create('foo', '0.2', allow_duplicate_name=True, channel_id=channel['claim_id'])
|
||||||
await self.stream_create('foo', '0.3', allow_duplicate_name=True, channel_id=channel['claim_id'])
|
# await self.stream_create('foo', '0.3', allow_duplicate_name=True, channel_id=channel['claim_id'])
|
||||||
|
#
|
||||||
claim_id1 = self.get_claim_id(
|
# claim_id1 = self.get_claim_id(
|
||||||
await self.stream_create('foo', '0.7', allow_duplicate_name=True, channel_id=channel['claim_id']))
|
# await self.stream_create('foo', '0.7', allow_duplicate_name=True, channel_id=channel['claim_id']))
|
||||||
claim1 = await self.claim_get(claim_id=claim_id1)
|
# claim1 = await self.claim_get(claim_id=claim_id1)
|
||||||
|
#
|
||||||
await self.assertResolvesToClaimId('foo', claim_id1)
|
# await self.assertResolvesToClaimId('foo', claim_id1)
|
||||||
await self.assertResolvesToClaimId('@abc/foo', claim_id1)
|
# await self.assertResolvesToClaimId('@abc/foo', claim_id1)
|
||||||
await self.assertResolvesToClaimId(claim1['short_url'], claim_id1)
|
# await self.assertResolvesToClaimId(claim1['short_url'], claim_id1)
|
||||||
await self.assertResolvesToClaimId(claim1['canonical_url'], claim_id1)
|
# await self.assertResolvesToClaimId(claim1['canonical_url'], claim_id1)
|
||||||
await self.assertResolvesToClaimId(claim1['permanent_url'], claim_id1)
|
# await self.assertResolvesToClaimId(claim1['permanent_url'], claim_id1)
|
||||||
|
#
|
||||||
claim_id2 = self.get_claim_id(
|
# claim_id2 = self.get_claim_id(
|
||||||
await self.stream_create('foo', '0.8', allow_duplicate_name=True, channel_id=channel['claim_id']))
|
# await self.stream_create('foo', '0.8', allow_duplicate_name=True, channel_id=channel['claim_id']))
|
||||||
claim2 = await self.claim_get(claim_id=claim_id2)
|
# claim2 = await self.claim_get(claim_id=claim_id2)
|
||||||
await self.assertResolvesToClaimId('foo', claim_id2)
|
# await self.assertResolvesToClaimId('foo', claim_id2)
|
||||||
await self.assertResolvesToClaimId('@abc/foo', claim_id2)
|
# await self.assertResolvesToClaimId('@abc/foo', claim_id2)
|
||||||
await self.assertResolvesToClaimId(claim2['short_url'], claim_id2)
|
# await self.assertResolvesToClaimId(claim2['short_url'], claim_id2)
|
||||||
await self.assertResolvesToClaimId(claim2['canonical_url'], claim_id2)
|
# await self.assertResolvesToClaimId(claim2['canonical_url'], claim_id2)
|
||||||
await self.assertResolvesToClaimId(claim2['permanent_url'], claim_id2)
|
# await self.assertResolvesToClaimId(claim2['permanent_url'], claim_id2)
|
||||||
|
|
||||||
async def test_abandoned_channel_with_signed_claims(self):
|
async def test_abandoned_channel_with_signed_claims(self):
|
||||||
channel = (await self.channel_create('@abc', '1.0'))['outputs'][0]
|
channel = (await self.channel_create('@abc', '1.0'))['outputs'][0]
|
||||||
|
@ -224,6 +274,11 @@ class ResolveCommand(BaseResolveTestCase):
|
||||||
|
|
||||||
winner_id = self.get_claim_id(c)
|
winner_id = self.get_claim_id(c)
|
||||||
|
|
||||||
|
# winning_one = await self.check_lbrycrd_winning(one)
|
||||||
|
winning_two = await self.assertMatchWinningClaim(two)
|
||||||
|
|
||||||
|
self.assertEqual(winner_id, winning_two.claim_hash.hex())
|
||||||
|
|
||||||
r1 = await self.resolve(f'lbry://{one}')
|
r1 = await self.resolve(f'lbry://{one}')
|
||||||
r2 = await self.resolve(f'lbry://{two}')
|
r2 = await self.resolve(f'lbry://{two}')
|
||||||
|
|
||||||
|
@ -329,6 +384,201 @@ class ResolveCommand(BaseResolveTestCase):
|
||||||
self.assertNotIn('received_tips', resolve)
|
self.assertNotIn('received_tips', resolve)
|
||||||
|
|
||||||
|
|
||||||
|
class ResolveClaimTakeovers(BaseResolveTestCase):
|
||||||
|
async def test_activation_delay(self):
|
||||||
|
name = 'derp'
|
||||||
|
# initially claim the name
|
||||||
|
first_claim_id = (await self.stream_create(name, '0.1'))['outputs'][0]['claim_id']
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(320)
|
||||||
|
# a claim of higher amount made now will have a takeover delay of 10
|
||||||
|
second_claim_id = (await self.stream_create(name, '0.2', allow_duplicate_name=True))['outputs'][0]['claim_id']
|
||||||
|
# sanity check
|
||||||
|
self.assertNotEqual(first_claim_id, second_claim_id)
|
||||||
|
# takeover should not have happened yet
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(9)
|
||||||
|
# not yet
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(1)
|
||||||
|
# the new claim should have activated
|
||||||
|
self.assertEqual(second_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
|
||||||
|
async def test_block_takeover_with_delay_1_support(self):
|
||||||
|
name = 'derp'
|
||||||
|
# initially claim the name
|
||||||
|
first_claim_id = (await self.stream_create(name, '0.1'))['outputs'][0]['claim_id']
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(320)
|
||||||
|
# a claim of higher amount made now will have a takeover delay of 10
|
||||||
|
second_claim_id = (await self.stream_create(name, '0.2', allow_duplicate_name=True))['outputs'][0]['claim_id']
|
||||||
|
# sanity check
|
||||||
|
self.assertNotEqual(first_claim_id, second_claim_id)
|
||||||
|
# takeover should not have happened yet
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(8)
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
# prevent the takeover by adding a support one block before the takeover happens
|
||||||
|
await self.support_create(first_claim_id, bid='1.0')
|
||||||
|
# one more block until activation
|
||||||
|
await self.generate(1)
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
|
||||||
|
async def test_block_takeover_with_delay_0_support(self):
|
||||||
|
name = 'derp'
|
||||||
|
# initially claim the name
|
||||||
|
first_claim_id = (await self.stream_create(name, '0.1'))['outputs'][0]['claim_id']
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(320)
|
||||||
|
# a claim of higher amount made now will have a takeover delay of 10
|
||||||
|
second_claim_id = (await self.stream_create(name, '0.2', allow_duplicate_name=True))['outputs'][0]['claim_id']
|
||||||
|
# sanity check
|
||||||
|
self.assertNotEqual(first_claim_id, second_claim_id)
|
||||||
|
# takeover should not have happened yet
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(9)
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
# prevent the takeover by adding a support on the same block the takeover would happen
|
||||||
|
await self.support_create(first_claim_id, bid='1.0')
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
|
||||||
|
async def _test_almost_prevent_takeover(self, name: str, blocks: int = 9):
|
||||||
|
# initially claim the name
|
||||||
|
first_claim_id = (await self.stream_create(name, '0.1'))['outputs'][0]['claim_id']
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(320)
|
||||||
|
# a claim of higher amount made now will have a takeover delay of 10
|
||||||
|
second_claim_id = (await self.stream_create(name, '0.2', allow_duplicate_name=True))['outputs'][0]['claim_id']
|
||||||
|
# sanity check
|
||||||
|
self.assertNotEqual(first_claim_id, second_claim_id)
|
||||||
|
# takeover should not have happened yet
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(blocks)
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
# prevent the takeover by adding a support on the same block the takeover would happen
|
||||||
|
tx = await self.daemon.jsonrpc_support_create(first_claim_id, '1.0')
|
||||||
|
await self.ledger.wait(tx)
|
||||||
|
return first_claim_id, second_claim_id, tx
|
||||||
|
|
||||||
|
async def test_almost_prevent_takeover_remove_support_same_block_supported(self):
|
||||||
|
name = 'derp'
|
||||||
|
first_claim_id, second_claim_id, tx = await self._test_almost_prevent_takeover(name, 9)
|
||||||
|
await self.daemon.jsonrpc_txo_spend(type='support', txid=tx.id)
|
||||||
|
await self.generate(1)
|
||||||
|
self.assertEqual(second_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
|
||||||
|
async def test_almost_prevent_takeover_remove_support_one_block_after_supported(self):
|
||||||
|
name = 'derp'
|
||||||
|
first_claim_id, second_claim_id, tx = await self._test_almost_prevent_takeover(name, 8)
|
||||||
|
await self.generate(1)
|
||||||
|
await self.daemon.jsonrpc_txo_spend(type='support', txid=tx.id)
|
||||||
|
await self.generate(1)
|
||||||
|
self.assertEqual(second_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
|
||||||
|
async def test_abandon_before_takeover(self):
|
||||||
|
name = 'derp'
|
||||||
|
# initially claim the name
|
||||||
|
first_claim_id = (await self.stream_create(name, '0.1'))['outputs'][0]['claim_id']
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(320)
|
||||||
|
# a claim of higher amount made now will have a takeover delay of 10
|
||||||
|
second_claim_id = (await self.stream_create(name, '0.2', allow_duplicate_name=True))['outputs'][0]['claim_id']
|
||||||
|
# sanity check
|
||||||
|
self.assertNotEqual(first_claim_id, second_claim_id)
|
||||||
|
# takeover should not have happened yet
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(8)
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
# abandon the winning claim
|
||||||
|
await self.daemon.jsonrpc_txo_spend(type='stream', claim_id=first_claim_id)
|
||||||
|
await self.generate(1)
|
||||||
|
# the takeover and activation should happen a block earlier than they would have absent the abandon
|
||||||
|
self.assertEqual(second_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(1)
|
||||||
|
self.assertEqual(second_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
|
||||||
|
async def test_abandon_before_takeover_no_delay_update(self): # TODO: fix race condition line 506
|
||||||
|
name = 'derp'
|
||||||
|
# initially claim the name
|
||||||
|
first_claim_id = (await self.stream_create(name, '0.1'))['outputs'][0]['claim_id']
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(320)
|
||||||
|
# block 527
|
||||||
|
# a claim of higher amount made now will have a takeover delay of 10
|
||||||
|
second_claim_id = (await self.stream_create(name, '0.2', allow_duplicate_name=True))['outputs'][0]['claim_id']
|
||||||
|
# block 528
|
||||||
|
# sanity check
|
||||||
|
self.assertNotEqual(first_claim_id, second_claim_id)
|
||||||
|
# takeover should not have happened yet
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(8)
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
# abandon the winning claim
|
||||||
|
await self.daemon.jsonrpc_txo_spend(type='stream', claim_id=first_claim_id)
|
||||||
|
await self.daemon.jsonrpc_stream_update(second_claim_id, '0.1')
|
||||||
|
await self.generate(1)
|
||||||
|
|
||||||
|
# the takeover and activation should happen a block earlier than they would have absent the abandon
|
||||||
|
self.assertEqual(second_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(1)
|
||||||
|
# await self.ledger.on_header.where(lambda e: e.height == 537)
|
||||||
|
self.assertEqual(second_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
|
||||||
|
async def test_abandon_controlling_support_before_pending_takeover(self):
|
||||||
|
name = 'derp'
|
||||||
|
# initially claim the name
|
||||||
|
first_claim_id = (await self.stream_create(name, '0.1'))['outputs'][0]['claim_id']
|
||||||
|
controlling_support_tx = await self.daemon.jsonrpc_support_create(first_claim_id, '0.9')
|
||||||
|
await self.ledger.wait(controlling_support_tx)
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(321)
|
||||||
|
|
||||||
|
second_claim_id = (await self.stream_create(name, '1.1', allow_duplicate_name=True))['outputs'][0]['claim_id']
|
||||||
|
self.assertNotEqual(first_claim_id, second_claim_id)
|
||||||
|
# takeover should not have happened yet
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(8)
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
# abandon the support that causes the winning claim to have the highest staked
|
||||||
|
tx = await self.daemon.jsonrpc_txo_spend(type='support', txid=controlling_support_tx.id)
|
||||||
|
await self.generate(1)
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(1)
|
||||||
|
self.assertEqual(second_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
|
||||||
|
async def test_remove_controlling_support(self):
|
||||||
|
name = 'derp'
|
||||||
|
# initially claim the name
|
||||||
|
first_claim_id = (await self.stream_create(name, '0.1'))['outputs'][0]['claim_id']
|
||||||
|
first_support_tx = await self.daemon.jsonrpc_support_create(first_claim_id, '0.9')
|
||||||
|
await self.ledger.wait(first_support_tx)
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
|
||||||
|
await self.generate(321) # give the first claim long enough for a 10 block takeover delay
|
||||||
|
|
||||||
|
# make a second claim which will take over the name
|
||||||
|
second_claim_id = (await self.stream_create(name, '0.2', allow_duplicate_name=True))['outputs'][0]['claim_id']
|
||||||
|
second_claim_support_tx = await self.daemon.jsonrpc_support_create(second_claim_id, '1.0')
|
||||||
|
await self.ledger.wait(second_claim_support_tx)
|
||||||
|
self.assertNotEqual(first_claim_id, second_claim_id)
|
||||||
|
|
||||||
|
# the name resolves to the first claim
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(9)
|
||||||
|
# still resolves to the first claim
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(1) # second claim takes over
|
||||||
|
self.assertEqual(second_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(33) # give the second claim long enough for a 1 block takeover delay
|
||||||
|
self.assertEqual(second_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
# abandon the support that causes the winning claim to have the highest staked
|
||||||
|
await self.daemon.jsonrpc_txo_spend(type='support', txid=second_claim_support_tx.id)
|
||||||
|
await self.generate(1)
|
||||||
|
self.assertEqual(second_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
await self.generate(1) # first claim takes over
|
||||||
|
self.assertEqual(first_claim_id, (await self.assertMatchWinningClaim(name)).claim_hash.hex())
|
||||||
|
|
||||||
|
|
||||||
class ResolveAfterReorg(BaseResolveTestCase):
|
class ResolveAfterReorg(BaseResolveTestCase):
|
||||||
|
|
||||||
async def reorg(self, start):
|
async def reorg(self, start):
|
||||||
|
@ -339,6 +589,26 @@ class ResolveAfterReorg(BaseResolveTestCase):
|
||||||
# go to previous + 1
|
# go to previous + 1
|
||||||
await self.generate(blocks + 2)
|
await self.generate(blocks + 2)
|
||||||
|
|
||||||
|
async def assertBlockHash(self, height):
|
||||||
|
bp = self.conductor.spv_node.server.bp
|
||||||
|
|
||||||
|
def get_txids():
|
||||||
|
return [
|
||||||
|
bp.db.fs_tx_hash(tx_num)[0][::-1].hex()
|
||||||
|
for tx_num in range(bp.db.tx_counts[height - 1], bp.db.tx_counts[height])
|
||||||
|
]
|
||||||
|
|
||||||
|
block_hash = await self.blockchain.get_block_hash(height)
|
||||||
|
|
||||||
|
self.assertEqual(block_hash, (await self.ledger.headers.hash(height)).decode())
|
||||||
|
self.assertEqual(block_hash, (await bp.db.fs_block_hashes(height, 1))[0][::-1].hex())
|
||||||
|
|
||||||
|
txids = await asyncio.get_event_loop().run_in_executor(bp.db.executor, get_txids)
|
||||||
|
txs = await bp.db.fs_transactions(txids)
|
||||||
|
block_txs = (await bp.daemon.deserialised_block(block_hash))['tx']
|
||||||
|
self.assertSetEqual(set(block_txs), set(txs.keys()), msg='leveldb/lbrycrd is missing transactions')
|
||||||
|
self.assertListEqual(block_txs, list(txs.keys()), msg='leveldb/lbrycrd transactions are of order')
|
||||||
|
|
||||||
async def test_reorg(self):
|
async def test_reorg(self):
|
||||||
self.assertEqual(self.ledger.headers.height, 206)
|
self.assertEqual(self.ledger.headers.height, 206)
|
||||||
|
|
||||||
|
@ -346,29 +616,40 @@ class ResolveAfterReorg(BaseResolveTestCase):
|
||||||
channel_id = self.get_claim_id(
|
channel_id = self.get_claim_id(
|
||||||
await self.channel_create(channel_name, '0.01')
|
await self.channel_create(channel_name, '0.01')
|
||||||
)
|
)
|
||||||
self.assertNotIn('error', await self.resolve(channel_name))
|
self.assertEqual(channel_id, (await self.assertMatchWinningClaim(channel_name)).claim_hash.hex())
|
||||||
await self.reorg(206)
|
await self.reorg(206)
|
||||||
self.assertNotIn('error', await self.resolve(channel_name))
|
self.assertEqual(channel_id, (await self.assertMatchWinningClaim(channel_name)).claim_hash.hex())
|
||||||
|
|
||||||
|
# await self.assertNoClaimForName(channel_name)
|
||||||
|
# self.assertNotIn('error', await self.resolve(channel_name))
|
||||||
|
|
||||||
stream_name = 'foo'
|
stream_name = 'foo'
|
||||||
stream_id = self.get_claim_id(
|
stream_id = self.get_claim_id(
|
||||||
await self.stream_create(stream_name, '0.01', channel_id=channel_id)
|
await self.stream_create(stream_name, '0.01', channel_id=channel_id)
|
||||||
)
|
)
|
||||||
self.assertNotIn('error', await self.resolve(stream_name))
|
self.assertEqual(stream_id, (await self.assertMatchWinningClaim(stream_name)).claim_hash.hex())
|
||||||
await self.reorg(206)
|
await self.reorg(206)
|
||||||
self.assertNotIn('error', await self.resolve(stream_name))
|
self.assertEqual(stream_id, (await self.assertMatchWinningClaim(stream_name)).claim_hash.hex())
|
||||||
|
|
||||||
await self.support_create(stream_id, '0.01')
|
await self.support_create(stream_id, '0.01')
|
||||||
self.assertNotIn('error', await self.resolve(stream_name))
|
self.assertNotIn('error', await self.resolve(stream_name))
|
||||||
|
self.assertEqual(stream_id, (await self.assertMatchWinningClaim(stream_name)).claim_hash.hex())
|
||||||
await self.reorg(206)
|
await self.reorg(206)
|
||||||
self.assertNotIn('error', await self.resolve(stream_name))
|
# self.assertNotIn('error', await self.resolve(stream_name))
|
||||||
|
self.assertEqual(stream_id, (await self.assertMatchWinningClaim(stream_name)).claim_hash.hex())
|
||||||
|
|
||||||
await self.stream_abandon(stream_id)
|
await self.stream_abandon(stream_id)
|
||||||
self.assertNotIn('error', await self.resolve(channel_name))
|
self.assertNotIn('error', await self.resolve(channel_name))
|
||||||
self.assertIn('error', await self.resolve(stream_name))
|
self.assertIn('error', await self.resolve(stream_name))
|
||||||
|
self.assertEqual(channel_id, (await self.assertMatchWinningClaim(channel_name)).claim_hash.hex())
|
||||||
|
await self.assertNoClaimForName(stream_name)
|
||||||
|
# TODO: check @abc/foo too
|
||||||
|
|
||||||
await self.reorg(206)
|
await self.reorg(206)
|
||||||
self.assertNotIn('error', await self.resolve(channel_name))
|
self.assertNotIn('error', await self.resolve(channel_name))
|
||||||
self.assertIn('error', await self.resolve(stream_name))
|
self.assertIn('error', await self.resolve(stream_name))
|
||||||
|
self.assertEqual(channel_id, (await self.assertMatchWinningClaim(channel_name)).claim_hash.hex())
|
||||||
|
await self.assertNoClaimForName(stream_name)
|
||||||
|
|
||||||
await self.channel_abandon(channel_id)
|
await self.channel_abandon(channel_id)
|
||||||
self.assertIn('error', await self.resolve(channel_name))
|
self.assertIn('error', await self.resolve(channel_name))
|
||||||
|
@ -377,6 +658,164 @@ class ResolveAfterReorg(BaseResolveTestCase):
|
||||||
self.assertIn('error', await self.resolve(channel_name))
|
self.assertIn('error', await self.resolve(channel_name))
|
||||||
self.assertIn('error', await self.resolve(stream_name))
|
self.assertIn('error', await self.resolve(stream_name))
|
||||||
|
|
||||||
|
async def test_reorg_change_claim_height(self):
|
||||||
|
# sanity check
|
||||||
|
result = await self.resolve('hovercraft') # TODO: do these for claim_search and resolve both
|
||||||
|
self.assertIn('error', result)
|
||||||
|
|
||||||
|
still_valid = await self.daemon.jsonrpc_stream_create(
|
||||||
|
'still-valid', '1.0', file_path=self.create_upload_file(data=b'hi!')
|
||||||
|
)
|
||||||
|
await self.ledger.wait(still_valid)
|
||||||
|
await self.generate(1)
|
||||||
|
|
||||||
|
# create a claim and verify it's returned by claim_search
|
||||||
|
self.assertEqual(self.ledger.headers.height, 207)
|
||||||
|
await self.assertBlockHash(207)
|
||||||
|
|
||||||
|
broadcast_tx = await self.daemon.jsonrpc_stream_create(
|
||||||
|
'hovercraft', '1.0', file_path=self.create_upload_file(data=b'hi!')
|
||||||
|
)
|
||||||
|
await self.ledger.wait(broadcast_tx)
|
||||||
|
await self.generate(1)
|
||||||
|
await self.ledger.wait(broadcast_tx, self.blockchain.block_expected)
|
||||||
|
self.assertEqual(self.ledger.headers.height, 208)
|
||||||
|
await self.assertBlockHash(208)
|
||||||
|
|
||||||
|
claim = await self.resolve('hovercraft')
|
||||||
|
self.assertEqual(claim['txid'], broadcast_tx.id)
|
||||||
|
self.assertEqual(claim['height'], 208)
|
||||||
|
|
||||||
|
# check that our tx is in block 208 as returned by lbrycrdd
|
||||||
|
invalidated_block_hash = (await self.ledger.headers.hash(208)).decode()
|
||||||
|
block_207 = await self.blockchain.get_block(invalidated_block_hash)
|
||||||
|
self.assertIn(claim['txid'], block_207['tx'])
|
||||||
|
self.assertEqual(208, claim['height'])
|
||||||
|
|
||||||
|
# reorg the last block dropping our claim tx
|
||||||
|
await self.blockchain.invalidate_block(invalidated_block_hash)
|
||||||
|
await self.blockchain.clear_mempool()
|
||||||
|
await self.blockchain.generate(2)
|
||||||
|
|
||||||
|
# wait for the client to catch up and verify the reorg
|
||||||
|
await asyncio.wait_for(self.on_header(209), 3.0)
|
||||||
|
await self.assertBlockHash(207)
|
||||||
|
await self.assertBlockHash(208)
|
||||||
|
await self.assertBlockHash(209)
|
||||||
|
|
||||||
|
# verify the claim was dropped from block 208 as returned by lbrycrdd
|
||||||
|
reorg_block_hash = await self.blockchain.get_block_hash(208)
|
||||||
|
self.assertNotEqual(invalidated_block_hash, reorg_block_hash)
|
||||||
|
block_207 = await self.blockchain.get_block(reorg_block_hash)
|
||||||
|
self.assertNotIn(claim['txid'], block_207['tx'])
|
||||||
|
|
||||||
|
client_reorg_block_hash = (await self.ledger.headers.hash(208)).decode()
|
||||||
|
self.assertEqual(client_reorg_block_hash, reorg_block_hash)
|
||||||
|
|
||||||
|
# verify the dropped claim is no longer returned by claim search
|
||||||
|
self.assertDictEqual(
|
||||||
|
{'error': {'name': 'NOT_FOUND', 'text': 'Could not find claim at "hovercraft".'}},
|
||||||
|
await self.resolve('hovercraft')
|
||||||
|
)
|
||||||
|
|
||||||
|
# verify the claim published a block earlier wasn't also reverted
|
||||||
|
self.assertEqual(207, (await self.resolve('still-valid'))['height'])
|
||||||
|
|
||||||
|
# broadcast the claim in a different block
|
||||||
|
new_txid = await self.blockchain.sendrawtransaction(hexlify(broadcast_tx.raw).decode())
|
||||||
|
self.assertEqual(broadcast_tx.id, new_txid)
|
||||||
|
await self.blockchain.generate(1)
|
||||||
|
|
||||||
|
# wait for the client to catch up
|
||||||
|
await asyncio.wait_for(self.on_header(210), 1.0)
|
||||||
|
|
||||||
|
# verify the claim is in the new block and that it is returned by claim_search
|
||||||
|
republished = await self.resolve('hovercraft')
|
||||||
|
self.assertEqual(210, republished['height'])
|
||||||
|
self.assertEqual(claim['claim_id'], republished['claim_id'])
|
||||||
|
|
||||||
|
# this should still be unchanged
|
||||||
|
self.assertEqual(207, (await self.resolve('still-valid'))['height'])
|
||||||
|
|
||||||
|
async def test_reorg_drop_claim(self):
|
||||||
|
# sanity check
|
||||||
|
result = await self.resolve('hovercraft') # TODO: do these for claim_search and resolve both
|
||||||
|
self.assertIn('error', result)
|
||||||
|
|
||||||
|
still_valid = await self.daemon.jsonrpc_stream_create(
|
||||||
|
'still-valid', '1.0', file_path=self.create_upload_file(data=b'hi!')
|
||||||
|
)
|
||||||
|
await self.ledger.wait(still_valid)
|
||||||
|
await self.generate(1)
|
||||||
|
|
||||||
|
# create a claim and verify it's returned by claim_search
|
||||||
|
self.assertEqual(self.ledger.headers.height, 207)
|
||||||
|
await self.assertBlockHash(207)
|
||||||
|
|
||||||
|
broadcast_tx = await self.daemon.jsonrpc_stream_create(
|
||||||
|
'hovercraft', '1.0', file_path=self.create_upload_file(data=b'hi!')
|
||||||
|
)
|
||||||
|
await self.ledger.wait(broadcast_tx)
|
||||||
|
await self.generate(1)
|
||||||
|
await self.ledger.wait(broadcast_tx, self.blockchain.block_expected)
|
||||||
|
self.assertEqual(self.ledger.headers.height, 208)
|
||||||
|
await self.assertBlockHash(208)
|
||||||
|
|
||||||
|
claim = await self.resolve('hovercraft')
|
||||||
|
self.assertEqual(claim['txid'], broadcast_tx.id)
|
||||||
|
self.assertEqual(claim['height'], 208)
|
||||||
|
|
||||||
|
# check that our tx is in block 208 as returned by lbrycrdd
|
||||||
|
invalidated_block_hash = (await self.ledger.headers.hash(208)).decode()
|
||||||
|
block_207 = await self.blockchain.get_block(invalidated_block_hash)
|
||||||
|
self.assertIn(claim['txid'], block_207['tx'])
|
||||||
|
self.assertEqual(208, claim['height'])
|
||||||
|
|
||||||
|
# reorg the last block dropping our claim tx
|
||||||
|
await self.blockchain.invalidate_block(invalidated_block_hash)
|
||||||
|
await self.blockchain.clear_mempool()
|
||||||
|
await self.blockchain.generate(2)
|
||||||
|
|
||||||
|
# wait for the client to catch up and verify the reorg
|
||||||
|
await asyncio.wait_for(self.on_header(209), 3.0)
|
||||||
|
await self.assertBlockHash(207)
|
||||||
|
await self.assertBlockHash(208)
|
||||||
|
await self.assertBlockHash(209)
|
||||||
|
|
||||||
|
# verify the claim was dropped from block 208 as returned by lbrycrdd
|
||||||
|
reorg_block_hash = await self.blockchain.get_block_hash(208)
|
||||||
|
self.assertNotEqual(invalidated_block_hash, reorg_block_hash)
|
||||||
|
block_207 = await self.blockchain.get_block(reorg_block_hash)
|
||||||
|
self.assertNotIn(claim['txid'], block_207['tx'])
|
||||||
|
|
||||||
|
client_reorg_block_hash = (await self.ledger.headers.hash(208)).decode()
|
||||||
|
self.assertEqual(client_reorg_block_hash, reorg_block_hash)
|
||||||
|
|
||||||
|
# verify the dropped claim is no longer returned by claim search
|
||||||
|
self.assertDictEqual(
|
||||||
|
{'error': {'name': 'NOT_FOUND', 'text': 'Could not find claim at "hovercraft".'}},
|
||||||
|
await self.resolve('hovercraft')
|
||||||
|
)
|
||||||
|
|
||||||
|
# verify the claim published a block earlier wasn't also reverted
|
||||||
|
self.assertEqual(207, (await self.resolve('still-valid'))['height'])
|
||||||
|
|
||||||
|
# broadcast the claim in a different block
|
||||||
|
new_txid = await self.blockchain.sendrawtransaction(hexlify(broadcast_tx.raw).decode())
|
||||||
|
self.assertEqual(broadcast_tx.id, new_txid)
|
||||||
|
await self.blockchain.generate(1)
|
||||||
|
|
||||||
|
# wait for the client to catch up
|
||||||
|
await asyncio.wait_for(self.on_header(210), 1.0)
|
||||||
|
|
||||||
|
# verify the claim is in the new block and that it is returned by claim_search
|
||||||
|
republished = await self.resolve('hovercraft')
|
||||||
|
self.assertEqual(210, republished['height'])
|
||||||
|
self.assertEqual(claim['claim_id'], republished['claim_id'])
|
||||||
|
|
||||||
|
# this should still be unchanged
|
||||||
|
self.assertEqual(207, (await self.resolve('still-valid'))['height'])
|
||||||
|
|
||||||
|
|
||||||
def generate_signed_legacy(address: bytes, output: Output):
|
def generate_signed_legacy(address: bytes, output: Output):
|
||||||
decoded_address = Base58.decode(address)
|
decoded_address = Base58.decode(address)
|
||||||
|
|
Loading…
Reference in a new issue