tests: test_network: add more header chain resolution test cases
This commit is contained in:
@@ -1111,7 +1111,7 @@ class Interface(Logger):
|
||||
raise Exception('unexpected bad header during binary: {}'.format(bad_header))
|
||||
_assert_header_does_not_check_against_any_chain(bad_header)
|
||||
|
||||
self.logger.info(f"binary search exited. good {good}, bad {bad}")
|
||||
self.logger.info(f"binary search exited. good {good}, bad {bad}. {chain=}")
|
||||
return good, bad, bad_header
|
||||
|
||||
async def _resolve_potential_chain_fork_given_forkpoint(
|
||||
|
||||
@@ -71,7 +71,7 @@ class MockInterface(Interface):
|
||||
async def get_block_header(self, height: int, *, mode: ChainResolutionMode) -> dict:
|
||||
assert self.q.qsize() > 0, (height, mode)
|
||||
item = await self.q.get()
|
||||
self.logger.debug(f"step with {height=}. {item=}")
|
||||
self.logger.debug(f"step with {height=}. {mode=}. will get {item=}")
|
||||
assert item['block_height'] == height, (item['block_height'], height)
|
||||
assert mode in item['mock'], (mode, item)
|
||||
return item
|
||||
@@ -83,7 +83,7 @@ class MockInterface(Interface):
|
||||
return
|
||||
|
||||
|
||||
class TestNetwork(ElectrumTestCase):
|
||||
class TestHeaderChainResolution(ElectrumTestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
@@ -104,10 +104,75 @@ class TestNetwork(ElectrumTestCase):
|
||||
self.config = SimpleConfig({'electrum_path': self.electrum_path})
|
||||
self.interface = MockInterface(self.config)
|
||||
|
||||
# finds forkpoint during binary, new fork
|
||||
async def test_catchup_one_block_behind(self):
|
||||
"""Single chain, but client is behind. The client's height is 5, server is on block 6.
|
||||
- first missing block found during *catchup* phase
|
||||
"""
|
||||
ifa = self.interface
|
||||
ifa.tip = 6
|
||||
ifa.blockchain = MockBlockchain(["00a", "01a", "02a", "03a", "04a", "05a"])
|
||||
blockchain.blockchains = {
|
||||
"00a": ifa.blockchain,
|
||||
}
|
||||
ifa.q.put_nowait({'block_height': 6, 'mock': {CRM.CATCHUP:1, 'id': '06a', 'prev_id': '05a'}})
|
||||
res = await ifa.sync_until(ifa.tip)
|
||||
self.assertEqual((CRM.CATCHUP, 7), res)
|
||||
self.assertEqual(ifa.q.qsize(), 0)
|
||||
self.assertEqual(len(blockchain.blockchains), 1)
|
||||
|
||||
async def test_catchup_already_up_to_date(self):
|
||||
"""Single chain, local chain tip already matches server tip."""
|
||||
ifa = self.interface
|
||||
ifa.tip = 5
|
||||
ifa.blockchain = MockBlockchain(["00a", "01a", "02a", "03a", "04a", "05a"])
|
||||
blockchain.blockchains = {
|
||||
"00a": ifa.blockchain,
|
||||
}
|
||||
ifa.q.put_nowait({'block_height': 5, 'mock': {CRM.CATCHUP:1, 'id': '05a', 'prev_id': '04a'}})
|
||||
res = await ifa.sync_until(ifa.tip)
|
||||
self.assertEqual((CRM.CATCHUP, 6), res)
|
||||
self.assertEqual(ifa.q.qsize(), 0)
|
||||
self.assertEqual(len(blockchain.blockchains), 1)
|
||||
|
||||
async def test_catchup_client_ahead_of_lagging_server(self):
|
||||
"""Single chain, server is lagging."""
|
||||
ifa = self.interface
|
||||
ifa.tip = 3
|
||||
ifa.blockchain = MockBlockchain(["00a", "01a", "02a", "03a", "04a", "05a"])
|
||||
blockchain.blockchains = {
|
||||
"00a": ifa.blockchain,
|
||||
}
|
||||
ifa.q.put_nowait({'block_height': 3, 'mock': {CRM.CATCHUP:1, 'id': '03a', 'prev_id': '02a'}})
|
||||
res = await ifa.sync_until(ifa.tip)
|
||||
self.assertEqual((CRM.CATCHUP, 4), res)
|
||||
self.assertEqual(ifa.q.qsize(), 0)
|
||||
self.assertEqual(len(blockchain.blockchains), 1)
|
||||
|
||||
async def test_catchup_fast_forward(self):
|
||||
"""Single chain, but client is behind. The client's height is 5, server is already on block 12.
|
||||
- first missing block found during *backward* phase
|
||||
"""
|
||||
ifa = self.interface
|
||||
ifa.tip = 12
|
||||
ifa.blockchain = MockBlockchain(["00a", "01a", "02a", "03a", "04a", "05a"])
|
||||
blockchain.blockchains = {
|
||||
"00a": ifa.blockchain,
|
||||
}
|
||||
ifa.q.put_nowait({'block_height': 12, 'mock': {CRM.CATCHUP:1, 'id': '12a', 'prev_id': '11a'}})
|
||||
ifa.q.put_nowait({'block_height': 6, 'mock': {CRM.BACKWARD:1, 'id': '06a', 'prev_id': '05a'}})
|
||||
ifa.q.put_nowait({'block_height': 7, 'mock': {CRM.CATCHUP: 1, 'id': '07a', 'prev_id': '06a'}})
|
||||
ifa.q.put_nowait({'block_height': 8, 'mock': {CRM.CATCHUP: 1, 'id': '08a', 'prev_id': '07a'}})
|
||||
ifa.q.put_nowait({'block_height': 9, 'mock': {CRM.CATCHUP: 1, 'id': '09a', 'prev_id': '08a'}})
|
||||
res = await ifa.sync_until(ifa.tip, next_height=9)
|
||||
self.assertEqual((CRM.CATCHUP, 10), res)
|
||||
self.assertEqual(ifa.q.qsize(), 0)
|
||||
self.assertEqual(len(blockchain.blockchains), 1)
|
||||
|
||||
async def test_fork(self):
|
||||
"""client starts on main chain, has no knowledge of any fork.
|
||||
server is on other side of chain split, the last common block is height 6.
|
||||
- first missing block found during *binary* phase
|
||||
- is *new* fork
|
||||
"""
|
||||
ifa = self.interface
|
||||
ifa.tip = 8
|
||||
@@ -119,15 +184,16 @@ class TestNetwork(ElectrumTestCase):
|
||||
ifa.q.put_nowait({'block_height': 7, 'mock': {CRM.BACKWARD:1, 'id': '07b', 'prev_id': '06a'}})
|
||||
ifa.q.put_nowait({'block_height': 5, 'mock': {CRM.BACKWARD:1, 'id': '05a', 'prev_id': '04a'}})
|
||||
ifa.q.put_nowait({'block_height': 6, 'mock': {CRM.BINARY:1, 'id': '06a', 'prev_id': '05a'}})
|
||||
res = await ifa.sync_until(8, next_height=7)
|
||||
res = await ifa.sync_until(ifa.tip, next_height=7)
|
||||
self.assertEqual((CRM.FORK, 8), res)
|
||||
self.assertEqual(ifa.q.qsize(), 0)
|
||||
self.assertEqual(len(blockchain.blockchains), 2)
|
||||
|
||||
# finds forkpoint during backwards, existing fork
|
||||
async def test_can_connect_during_backward(self):
|
||||
"""client starts on main chain. client already knows about another fork, which has local height 4.
|
||||
server is on that fork but has more blocks.
|
||||
client happens to ask for header at height 5 during backward search (which directly builds on top the existing fork).
|
||||
- first missing block found during *backward* phase
|
||||
- is *existing* fork
|
||||
"""
|
||||
ifa = self.interface
|
||||
ifa.tip = 8
|
||||
@@ -140,14 +206,16 @@ class TestNetwork(ElectrumTestCase):
|
||||
ifa.q.put_nowait({'block_height': 7, 'mock': {CRM.BACKWARD:1, 'id': '07b', 'prev_id': '06b'}})
|
||||
ifa.q.put_nowait({'block_height': 5, 'mock': {CRM.BACKWARD:1, 'id': '05b', 'prev_id': '04b'}})
|
||||
ifa.q.put_nowait({'block_height': 6, 'mock': {CRM.CATCHUP:1, 'id': '06b', 'prev_id': '05b'}})
|
||||
res = await ifa.sync_until(8, next_height=6)
|
||||
res = await ifa.sync_until(ifa.tip, next_height=6)
|
||||
self.assertEqual((CRM.CATCHUP, 7), res)
|
||||
self.assertEqual(ifa.q.qsize(), 0)
|
||||
self.assertEqual(len(blockchain.blockchains), 2)
|
||||
|
||||
# finds forkpoint during binary, new fork
|
||||
async def test_chain_false_during_binary(self):
|
||||
"""client starts on main chain, has no knowledge of any fork.
|
||||
server is on other side of chain split, the last common block is height 3.
|
||||
- first missing block found during *binary* phase
|
||||
- is *new* fork
|
||||
"""
|
||||
ifa = self.interface
|
||||
ifa.tip = 8
|
||||
@@ -163,9 +231,37 @@ class TestNetwork(ElectrumTestCase):
|
||||
ifa.q.put_nowait({'block_height': 4, 'mock': {CRM.BINARY:1, 'id': '04b', 'prev_id': '03a'}})
|
||||
ifa.q.put_nowait({'block_height': 5, 'mock': {CRM.CATCHUP:1, 'id': '05b', 'prev_id': '04b'}})
|
||||
ifa.q.put_nowait({'block_height': 6, 'mock': {CRM.CATCHUP:1, 'id': '06b', 'prev_id': '05b'}})
|
||||
res = await ifa.sync_until(8, next_height=6)
|
||||
res = await ifa.sync_until(ifa.tip, next_height=6)
|
||||
self.assertEqual((CRM.CATCHUP, 7), res)
|
||||
self.assertEqual(ifa.q.qsize(), 0)
|
||||
self.assertEqual(len(blockchain.blockchains), 2)
|
||||
|
||||
async def test_chain_true_during_binary(self):
|
||||
"""client starts on main chain. client already knows about another fork, which has local height 10.
|
||||
server is on that fork but has more blocks.
|
||||
- first missing block found during *binary* phase
|
||||
- is *existing* fork
|
||||
"""
|
||||
ifa = self.interface
|
||||
ifa.tip = 20
|
||||
ifa.blockchain = MockBlockchain(["00a", "01a", "02a", "03a", "04a", "05a", "06a", "07a", "08a", "09a", "10a", "11a", "12a", "13a", "14a"])
|
||||
blockchain.blockchains = {
|
||||
"00a": ifa.blockchain,
|
||||
"07b": MockBlockchain(["00a", "01a", "02a", "03a", "04a", "05a", "06a", "07b", "08b", "09b", "10b"]),
|
||||
}
|
||||
ifa.q.put_nowait({'block_height': 20, 'mock': {CRM.CATCHUP:1, 'id': '20b', 'prev_id': '19b'}})
|
||||
ifa.q.put_nowait({'block_height': 15, 'mock': {CRM.BACKWARD:1, 'id': '15b', 'prev_id': '14b'}})
|
||||
ifa.q.put_nowait({'block_height': 13, 'mock': {CRM.BACKWARD:1, 'id': '13b', 'prev_id': '12b'}})
|
||||
ifa.q.put_nowait({'block_height': 9, 'mock': {CRM.BACKWARD:1, 'id': '09b', 'prev_id': '08b'}})
|
||||
ifa.q.put_nowait({'block_height': 11, 'mock': {CRM.BINARY:1, 'id': '11b', 'prev_id': '10b'}})
|
||||
ifa.q.put_nowait({'block_height': 10, 'mock': {CRM.BINARY:1, 'id': '10b', 'prev_id': '09b'}})
|
||||
ifa.q.put_nowait({'block_height': 11, 'mock': {CRM.CATCHUP:1, 'id': '11b', 'prev_id': '10b'}})
|
||||
ifa.q.put_nowait({'block_height': 12, 'mock': {CRM.CATCHUP:1, 'id': '12b', 'prev_id': '11b'}})
|
||||
ifa.q.put_nowait({'block_height': 13, 'mock': {CRM.CATCHUP:1, 'id': '13b', 'prev_id': '12b'}})
|
||||
res = await ifa.sync_until(ifa.tip, next_height=13)
|
||||
self.assertEqual((CRM.CATCHUP, 14), res)
|
||||
self.assertEqual(ifa.q.qsize(), 0)
|
||||
self.assertEqual(len(blockchain.blockchains), 2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user