commit da23e71db1f270c26ffbb217ca29f3df6649a315
parent ab94a47b8e5a84fc9735e840903f728f30d15d14
Author: SomberNight <somber.night@protonmail.com>
Date: Sun, 16 Sep 2018 07:34:05 +0200
interface: block header search simplifications
Diffstat:
2 files changed, 23 insertions(+), 27 deletions(-)
diff --git a/electrum/interface.py b/electrum/interface.py
@@ -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)
@@ -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:
@@ -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?
@@ -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:
diff --git a/electrum/tests/test_network.py b/electrum/tests/test_network.py
@@ -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):
@@ -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):
@@ -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):
@@ -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):
@@ -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}})
@@ -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()