tinterface: block header search simplifications - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit da23e71db1f270c26ffbb217ca29f3df6649a315 DIR parent ab94a47b8e5a84fc9735e840903f728f30d15d14 HTML Author: SomberNight <somber.night@protonmail.com> Date: Sun, 16 Sep 2018 07:34:05 +0200 interface: block header search simplifications Diffstat: M electrum/interface.py | 35 ++++++++++++++----------------- M electrum/tests/test_network.py | 15 +++++++-------- 2 files changed, 23 insertions(+), 27 deletions(-) --- DIR diff --git a/electrum/interface.py b/electrum/interface.py t@@ -314,6 +314,7 @@ class Interface(PrintError): async def get_block_header(self, height, assert_mode): # use lower timeout as we usually have network.bhi_lock here + self.print_error('requesting block header {} in mode {}'.format(height, assert_mode)) timeout = 5 if not self.proxy else 10 res = await self.session.send_request('blockchain.block.header', [height], timeout=timeout) return blockchain.deserialize_header(bytes.fromhex(res), height) t@@ -385,20 +386,20 @@ class Interface(PrintError): raise GracefulDisconnect('server tip below max checkpoint') self.mark_ready() async with self.network.bhi_lock: - if self.blockchain.height() < height - 1: - _, height = await self.sync_until(height, None) if self.blockchain.height() >= height and self.blockchain.check_header(header): # another interface amended the blockchain self.print_error("skipping header", height) continue - height = min(height, self.tip) _, height = await self.step(height, header) + # in the simple case, height == self.tip+1 + if height <= self.tip: + await self.sync_until(height) async def sync_until(self, height, next_height=None): if next_height is None: next_height = self.tip last = None - while last is None or height < next_height: + while last is None or height <= next_height: if next_height > height + 10: could_connect, num_headers = await self.request_chunk(height, next_height) if not could_connect: t@@ -463,7 +464,9 @@ class Interface(PrintError): if not real and not mock: raise Exception('unexpected bad header during binary' + str(bad_header)) # line 948 in 8e69174374aee87d73cd2f8005fbbe87c93eee9c's network.py branch = blockchain.blockchains.get(bad) + self.print_error("binary search exited. good {}, bad {}".format(good, bad)) if branch is not None: + self.print_error("existing fork found at bad height {}".format(bad)) ismocking = type(branch) is dict # FIXME: it does not seem sufficient to check that the branch # contains the bad_header. what if self.blockchain doesn't? t@@ -477,24 +480,18 @@ class Interface(PrintError): height += 1 return 'join', height else: - if not ismocking and branch.parent().check_header(header) \ - or ismocking and branch['parent']['check'](header): - self.print_error('reorg', bad, self.tip) - self.blockchain = branch.parent() if not ismocking else branch['parent'] - height = bad - header = await self.get_block_header(height, 'binary') - else: - height = bad + 1 - if ismocking: - self.print_error("TODO replace blockchain") - return 'conflict', height - self.print_error('forkpoint conflicts with existing fork', branch.path()) - branch.write(b'', 0) - branch.save_header(bad_header) - self.blockchain = branch + height = bad + 1 + if ismocking: + self.print_error("TODO replace blockchain") return 'conflict', height + self.print_error('forkpoint conflicts with existing fork', branch.path()) + branch.write(b'', 0) + branch.save_header(bad_header) + self.blockchain = branch + return 'conflict', height else: bh = self.blockchain.height() + self.print_error("no existing fork yet at bad height {}. local chain height: {}".format(bad, bh)) if bh > good: forkfun = self.blockchain.fork if 'mock' in bad_header: DIR diff --git a/electrum/tests/test_network.py b/electrum/tests/test_network.py t@@ -49,7 +49,7 @@ class TestNetwork(unittest.TestCase): self.interface.q.put_nowait({'block_height': 5, 'mock': {'binary':1,'check':lambda x: True, 'connect': lambda x: True}}) self.interface.q.put_nowait({'block_height': 6, 'mock': {'binary':1,'check':lambda x: True, 'connect': lambda x: True}}) ifa = self.interface - self.assertEqual(('fork', 8), asyncio.get_event_loop().run_until_complete(ifa.sync_until(8, next_height=8))) + self.assertEqual(('fork', 8), asyncio.get_event_loop().run_until_complete(ifa.sync_until(8, next_height=7))) self.assertEqual(self.interface.q.qsize(), 0) def test_new_can_connect_during_backward(self): t@@ -62,7 +62,7 @@ class TestNetwork(unittest.TestCase): self.interface.q.put_nowait({'block_height': 3, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: True}}) self.interface.q.put_nowait({'block_height': 4, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: True}}) ifa = self.interface - self.assertEqual(('catchup', 5), asyncio.get_event_loop().run_until_complete(ifa.sync_until(8, next_height=5))) + self.assertEqual(('catchup', 5), asyncio.get_event_loop().run_until_complete(ifa.sync_until(8, next_height=4))) self.assertEqual(self.interface.q.qsize(), 0) def mock_fork(self, bad_header): t@@ -79,7 +79,7 @@ class TestNetwork(unittest.TestCase): self.interface.q.put_nowait({'block_height': 5, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: True}}) self.interface.q.put_nowait({'block_height': 6, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: True}}) ifa = self.interface - self.assertEqual(('catchup', 7), asyncio.get_event_loop().run_until_complete(ifa.sync_until(8, next_height=7))) + self.assertEqual(('catchup', 7), asyncio.get_event_loop().run_until_complete(ifa.sync_until(8, next_height=6))) self.assertEqual(self.interface.q.qsize(), 0) def test_new_join(self): t@@ -91,7 +91,7 @@ class TestNetwork(unittest.TestCase): self.interface.q.put_nowait({'block_height': 5, 'mock': {'binary':1, 'check': lambda x: True, 'connect': lambda x: False}}) self.interface.q.put_nowait({'block_height': 6, 'mock': {'binary':1, 'check': lambda x: True, 'connect': lambda x: True}}) ifa = self.interface - self.assertEqual(('join', 7), asyncio.get_event_loop().run_until_complete(ifa.sync_until(8, next_height=7))) + self.assertEqual(('join', 7), asyncio.get_event_loop().run_until_complete(ifa.sync_until(8, next_height=6))) self.assertEqual(self.interface.q.qsize(), 0) def test_new_reorg(self): t@@ -100,7 +100,7 @@ class TestNetwork(unittest.TestCase): nonlocal times self.assertEqual(header['block_height'], 7) times += 1 - return times != 1 + return False blockchain.blockchains = {7: {'check': check, 'parent': {'check': lambda x: True}}} self.interface.q.put_nowait({'block_height': 8, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: False}}) self.interface.q.put_nowait({'block_height': 7, 'mock': {'backward':1, 'check': lambda x: False, 'connect': lambda height: height == 6}}) t@@ -108,11 +108,10 @@ class TestNetwork(unittest.TestCase): self.interface.q.put_nowait({'block_height': 4, 'mock': {'binary':1, 'check': lambda x: 1, 'connect': lambda x: False}}) self.interface.q.put_nowait({'block_height': 5, 'mock': {'binary':1, 'check': lambda x: 1, 'connect': lambda x: False}}) self.interface.q.put_nowait({'block_height': 6, 'mock': {'binary':1, 'check': lambda x: 1, 'connect': lambda x: True}}) - self.interface.q.put_nowait({'block_height': 7, 'mock': {'binary':1, 'check': lambda x: False, 'connect': lambda x: True}}) ifa = self.interface - self.assertEqual(('join', 8), asyncio.get_event_loop().run_until_complete(ifa.sync_until(8, next_height=8))) + self.assertEqual(('conflict', 8), asyncio.get_event_loop().run_until_complete(ifa.sync_until(8, next_height=7))) self.assertEqual(self.interface.q.qsize(), 0) - self.assertEqual(times, 2) + self.assertEqual(times, 1) if __name__=="__main__": constants.set_regtest()