commit 1d711eeadc754024bfba9b12822635ec4a013984
parent 58a5346d724be74de3ca84217ff3a79fdd278129
Author: SomberNight <somber.night@protonmail.com>
Date: Sun, 16 Sep 2018 08:29:01 +0200
interface: split up 'step'; binary search of headers
Diffstat:
M | electrum/interface.py | | | 134 | +++++++++++++++++++++++++++++++++++++++++-------------------------------------- |
1 file changed, 69 insertions(+), 65 deletions(-)
diff --git a/electrum/interface.py b/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)
if chain:
- self.blockchain = chain
+ self.blockchain = chain if isinstance(chain, Blockchain) else self.blockchain
return 'catchup', height+1
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)
return 'catchup', height
- # binary
- if isinstance(chain, Blockchain): # not when mocking
- self.blockchain = chain
+ good, height, bad, bad_header = await self._search_headers_binary(height, bad, bad_header, chain)
+ return await self._resolve_potential_chain_fork_given_forkpoint(good, height, bad, bad_header)
+
+ async def _search_headers_binary(self, height, bad, bad_header, chain):
+ self.blockchain = chain if isinstance(chain, Blockchain) else self.blockchain
good = height
- height = (bad + good) // 2
- header = await self.get_block_header(height, 'binary')
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)
if chain:
+ self.blockchain = chain if isinstance(chain, Blockchain) else self.blockchain
good = height
- self.blockchain = self.blockchain if type(chain) in [bool, int] else chain
else:
bad = height
bad_header = header
- assert good < bad, (good, bad)
- if bad != good + 1:
- height = (bad + good) // 2
- header = await self.get_block_header(height, 'binary')
- continue
- mock = bad_header and 'mock' in bad_header and bad_header['mock']['connect'](height)
- real = not mock and self.blockchain.can_connect(bad_header, check_height=False)
- 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?
- # the chains shouldn't be joined then. observe the incorrect
- # joining on regtest with a server that has a fork of height
- # one. the problem is observed only if forking is not during
- # electrum runtime
- if not ismocking and branch.check_header(bad_header) \
- or ismocking and branch['check'](bad_header):
- self.print_error('joining chain', bad)
- height += 1
- return 'join', height
- 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
- return 'conflict', height
+ if good + 1 == bad:
+ break
+
+ mock = bad_header and 'mock' in bad_header and bad_header['mock']['connect'](height)
+ real = not mock and self.blockchain.can_connect(bad_header, check_height=False)
+ if not real and not mock:
+ raise Exception('unexpected bad header during binary: {}'.format(bad_header))
+ self.print_error("binary search exited. good {}, bad {}".format(good, bad))
+ return good, height, bad, bad_header
+
+ async def _resolve_potential_chain_fork_given_forkpoint(self, good, height, bad, bad_header):
+ branch = blockchain.blockchains.get(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?
+ # the chains shouldn't be joined then. observe the incorrect
+ # joining on regtest with a server that has a fork of height
+ # one. the problem is observed only if forking is not during
+ # electrum runtime
+ if not ismocking and branch.check_header(bad_header) \
+ or ismocking and branch['check'](bad_header):
+ self.print_error('joining chain', bad)
+ height += 1
+ return 'join', 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:
- chain = bad_header['mock']['check'](bad_header)
- forkfun = bad_header['mock']['fork'] if 'fork' in bad_header['mock'] else forkfun
- else:
- chain = self.blockchain.check_header(bad_header)
- if not chain:
- b = forkfun(bad_header)
- assert bad not in blockchain.blockchains, (bad, list(blockchain.blockchains.keys()))
- blockchain.blockchains[bad] = b
- self.blockchain = b
- height = b.forkpoint + 1
- assert b.forkpoint == bad
- return 'fork', 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:
+ 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:
+ chain = bad_header['mock']['check'](bad_header)
+ forkfun = bad_header['mock']['fork'] if 'fork' in bad_header['mock'] else forkfun
else:
- assert bh == good
- if bh < self.tip:
- self.print_error("catching up from %d"% (bh + 1))
- height = bh + 1
- return 'no_fork', height
+ chain = self.blockchain.check_header(bad_header)
+ if not chain:
+ b = forkfun(bad_header)
+ assert bad not in blockchain.blockchains, (bad, list(blockchain.blockchains.keys()))
+ 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 iterate():