commit 73e2b09ba82fcc34f2762500b7ab3d7b839b5162
parent 2484c52611defa96ed060af4152707b3d1bfdea5
Author: SomberNight <somber.night@protonmail.com>
Date: Fri, 30 Nov 2018 16:36:37 +0100
blockchain: check best chain on disk is consistent with checkpoints
had a corrupted mainnet datadir that had testnet blockchain_headers file
(I had probably corrupted it myself but electrum could not recover from it)
Diffstat:
1 file changed, 18 insertions(+), 9 deletions(-)
diff --git a/electrum/blockchain.py b/electrum/blockchain.py
@@ -86,11 +86,20 @@ blockchains_lock = threading.RLock()
def read_blockchains(config: 'SimpleConfig'):
- blockchains[constants.net.GENESIS] = Blockchain(config=config,
- forkpoint=0,
- parent=None,
- forkpoint_hash=constants.net.GENESIS,
- prev_hash=None)
+ best_chain = Blockchain(config=config,
+ forkpoint=0,
+ parent=None,
+ forkpoint_hash=constants.net.GENESIS,
+ prev_hash=None)
+ blockchains[constants.net.GENESIS] = best_chain
+ # consistency checks
+ if best_chain.height() > constants.net.max_checkpoint():
+ header_after_cp = best_chain.read_header(constants.net.max_checkpoint()+1)
+ if not header_after_cp or not best_chain.can_connect(header_after_cp, check_height=False):
+ util.print_error("[blockchain] deleting best chain. cannot connect header after last cp to last cp.")
+ os.unlink(best_chain.path())
+ best_chain.update_size()
+ # forks
fdir = os.path.join(util.get_headers_dir(config), 'forks')
util.make_dir(fdir)
# files are named as: fork2_{forkpoint}_{prev_hash}_{first_hash}
@@ -98,7 +107,7 @@ def read_blockchains(config: 'SimpleConfig'):
l = sorted(l, key=lambda x: int(x.split('_')[1])) # sort by forkpoint
def delete_chain(filename, reason):
- util.print_error("[blockchain]", reason, filename)
+ util.print_error(f"[blockchain] deleting chain {filename}: {reason}")
os.unlink(os.path.join(fdir, filename))
def instantiate_chain(filename):
@@ -222,10 +231,10 @@ class Blockchain(util.PrintError):
prev_hash=parent.get_hash(forkpoint-1))
open(self.path(), 'w+').close()
self.save_header(header)
- # put into global dict
+ # put into global dict. note that in some cases
+ # save_header might have already put it there but that's OK
chain_id = self.get_id()
with blockchains_lock:
- assert chain_id not in blockchains, (chain_id, list(blockchains))
blockchains[chain_id] = self
return self
@@ -392,7 +401,7 @@ class Blockchain(util.PrintError):
delta = header.get('block_height') - self.forkpoint
data = bfh(serialize_header(header))
# headers are only _appended_ to the end:
- assert delta == self.size()
+ assert delta == self.size(), (delta, self.size())
assert len(data) == HEADER_SIZE
self.write(data, delta*HEADER_SIZE)
self.swap_with_parent()