1
0

tests: test_network: add more header chain resolution test cases

This commit is contained in:
SomberNight
2025-06-09 20:20:47 +00:00
parent eb69b6b516
commit c2e8188568
2 changed files with 106 additions and 10 deletions

View File

@@ -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(

View File

@@ -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__":