Browse Source

interface: block header search simplifications

3.3.3.1
SomberNight 6 years ago
parent
commit
da23e71db1
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 35
      electrum/interface.py
  2. 15
      electrum/tests/test_network.py

35
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:

15
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()

Loading…
Cancel
Save