Browse Source

interface: split up 'step'; binary search of headers

master
SomberNight 7 years ago
parent
commit
1d711eeadc
No known key found for this signature in database
GPG Key ID: B33B5F232C6271E9
  1. 134
      electrum/interface.py

134
electrum/interface.py

@ -427,7 +427,7 @@ class Interface(PrintError):
chain = blockchain.check_header(header) if 'mock' not in header else header['mock']['check'](header) chain = blockchain.check_header(header) if 'mock' not in header else header['mock']['check'](header)
if chain: if chain:
self.blockchain = chain self.blockchain = chain if isinstance(chain, Blockchain) else self.blockchain
return 'catchup', height+1 return 'catchup', height+1
can_connect = blockchain.can_connect(header) if 'mock' not in header else header['mock']['connect'](height) can_connect = blockchain.can_connect(header) if 'mock' not in header else header['mock']['connect'](height)
@ -445,80 +445,84 @@ class Interface(PrintError):
self.blockchain.save_header(header) self.blockchain.save_header(header)
return 'catchup', height return 'catchup', height
# binary good, height, bad, bad_header = await self._search_headers_binary(height, bad, bad_header, chain)
if isinstance(chain, Blockchain): # not when mocking return await self._resolve_potential_chain_fork_given_forkpoint(good, height, bad, bad_header)
self.blockchain = chain
async def _search_headers_binary(self, height, bad, bad_header, chain):
self.blockchain = chain if isinstance(chain, Blockchain) else self.blockchain
good = height good = height
height = (bad + good) // 2
header = await self.get_block_header(height, 'binary')
while True: while True:
self.print_error("binary step") assert good < bad, (good, bad)
height = (good + bad) // 2
self.print_error("binary step. good {}, bad {}, height {}".format(good, bad, height))
header = await self.get_block_header(height, 'binary')
chain = blockchain.check_header(header) if 'mock' not in header else header['mock']['check'](header) chain = blockchain.check_header(header) if 'mock' not in header else header['mock']['check'](header)
if chain: if chain:
self.blockchain = chain if isinstance(chain, Blockchain) else self.blockchain
good = height good = height
self.blockchain = self.blockchain if type(chain) in [bool, int] else chain
else: else:
bad = height bad = height
bad_header = header bad_header = header
assert good < bad, (good, bad) if good + 1 == bad:
if bad != good + 1: break
height = (bad + good) // 2
header = await self.get_block_header(height, 'binary') mock = bad_header and 'mock' in bad_header and bad_header['mock']['connect'](height)
continue real = not mock and self.blockchain.can_connect(bad_header, check_height=False)
mock = bad_header and 'mock' in bad_header and bad_header['mock']['connect'](height) if not real and not mock:
real = not mock and self.blockchain.can_connect(bad_header, check_height=False) raise Exception('unexpected bad header during binary: {}'.format(bad_header))
if not real and not mock: self.print_error("binary search exited. good {}, bad {}".format(good, bad))
raise Exception('unexpected bad header during binary' + str(bad_header)) # line 948 in 8e69174374aee87d73cd2f8005fbbe87c93eee9c's network.py return good, height, bad, bad_header
branch = blockchain.blockchains.get(bad)
self.print_error("binary search exited. good {}, bad {}".format(good, bad)) async def _resolve_potential_chain_fork_given_forkpoint(self, good, height, bad, bad_header):
if branch is not None: branch = blockchain.blockchains.get(bad)
self.print_error("existing fork found at bad height {}".format(bad)) if branch is not None:
ismocking = type(branch) is dict self.print_error("existing fork found at bad height {}".format(bad))
# FIXME: it does not seem sufficient to check that the branch ismocking = type(branch) is dict
# contains the bad_header. what if self.blockchain doesn't? # FIXME: it does not seem sufficient to check that the branch
# the chains shouldn't be joined then. observe the incorrect # contains the bad_header. what if self.blockchain doesn't?
# joining on regtest with a server that has a fork of height # the chains shouldn't be joined then. observe the incorrect
# one. the problem is observed only if forking is not during # joining on regtest with a server that has a fork of height
# electrum runtime # one. the problem is observed only if forking is not during
if not ismocking and branch.check_header(bad_header) \ # electrum runtime
or ismocking and branch['check'](bad_header): if not ismocking and branch.check_header(bad_header) \
self.print_error('joining chain', bad) or ismocking and branch['check'](bad_header):
height += 1 self.print_error('joining chain', bad)
return 'join', height height += 1
else: return 'join', height
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: else:
bh = self.blockchain.height() height = bad + 1
self.print_error("no existing fork yet at bad height {}. local chain height: {}".format(bad, bh)) if ismocking:
if bh > good: self.print_error("TODO replace blockchain")
forkfun = self.blockchain.fork return 'conflict', height
if 'mock' in bad_header: self.print_error('forkpoint conflicts with existing fork', branch.path())
chain = bad_header['mock']['check'](bad_header) branch.write(b'', 0)
forkfun = bad_header['mock']['fork'] if 'fork' in bad_header['mock'] else forkfun branch.save_header(bad_header)
else: self.blockchain = branch
chain = self.blockchain.check_header(bad_header) return 'conflict', height
if not chain: else:
b = forkfun(bad_header) bh = self.blockchain.height()
assert bad not in blockchain.blockchains, (bad, list(blockchain.blockchains.keys())) self.print_error("no existing fork yet at bad height {}. local chain height: {}".format(bad, bh))
blockchain.blockchains[bad] = b if bh > good:
self.blockchain = b forkfun = self.blockchain.fork
height = b.forkpoint + 1 if 'mock' in bad_header:
assert b.forkpoint == bad chain = bad_header['mock']['check'](bad_header)
return 'fork', height forkfun = bad_header['mock']['fork'] if 'fork' in bad_header['mock'] else forkfun
else: else:
assert bh == good chain = self.blockchain.check_header(bad_header)
if bh < self.tip: if not chain:
self.print_error("catching up from %d"% (bh + 1)) b = forkfun(bad_header)
height = bh + 1 assert bad not in blockchain.blockchains, (bad, list(blockchain.blockchains.keys()))
return 'no_fork', height blockchain.blockchains[bad] = b
self.blockchain = b
height = b.forkpoint + 1
assert b.forkpoint == bad
return 'fork', height
else:
assert bh == good
if bh < self.tip:
self.print_error("catching up from %d" % (bh + 1))
height = bh + 1
return 'no_fork', height
async def _search_headers_backwards(self, height, header): async def _search_headers_backwards(self, height, header):
async def iterate(): async def iterate():

Loading…
Cancel
Save