electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 060404e17c806e363a6f512e2f544f5c12821dd3
parent 4159322db06009a2da975ac419facd7282b3fafe
Author: SomberNight <somber.night@protonmail.com>
Date:   Wed, 30 May 2018 18:21:29 +0200

wallet/verifier: persist wallet.verified_tx when verifier finishes

Previously verified_tx was only persisted in wallet.stop_threads(), hence only on clean shutdowns.

Diffstat:
Mlib/verifier.py | 29+++++++++++++++++++++++------
Mlib/wallet.py | 22++++++++++++++++------
2 files changed, 39 insertions(+), 12 deletions(-)

diff --git a/lib/verifier.py b/lib/verifier.py @@ -31,9 +31,8 @@ class SPV(ThreadJob): self.wallet = wallet self.network = network self.blockchain = network.blockchain() - # Keyed by tx hash. Value is None if the merkle branch was - # requested, and the merkle root once it has been verified - self.merkle_roots = {} + self.merkle_roots = {} # txid -> merkle root (once it has been verified) + self.requested_merkle = set() # txid set of pending requests def run(self): interface = self.network.interface @@ -53,12 +52,13 @@ class SPV(ThreadJob): if index < len(blockchain.checkpoints): self.network.request_chunk(interface, index) else: - if tx_hash not in self.merkle_roots: + if (tx_hash not in self.requested_merkle + and tx_hash not in self.merkle_roots): request = ('blockchain.transaction.get_merkle', [tx_hash, tx_height]) self.network.send([request], self.verify_merkle) self.print_error('requested merkle', tx_hash) - self.merkle_roots[tx_hash] = None + self.requested_merkle.add(tx_hash) if self.network.blockchain() != self.blockchain: self.blockchain = self.network.blockchain() @@ -94,8 +94,15 @@ class SPV(ThreadJob): return # we passed all the tests self.merkle_roots[tx_hash] = merkle_root + try: + # note: we could pop in the beginning, but then we would request + # this proof again in case of verification failure from the same server + self.requested_merkle.remove(tx_hash) + except KeyError: pass self.print_error("verified %s" % tx_hash) self.wallet.add_verified_tx(tx_hash, (tx_height, header.get('timestamp'), pos)) + if self.is_up_to_date() and self.wallet.is_up_to_date(): + self.wallet.save_verified_tx(write=True) @classmethod def hash_merkle_root(cls, merkle_s, target_hash, pos): @@ -110,4 +117,14 @@ class SPV(ThreadJob): tx_hashes = self.wallet.undo_verifications(self.blockchain, height) for tx_hash in tx_hashes: self.print_error("redoing", tx_hash) - self.merkle_roots.pop(tx_hash, None) + self.remove_spv_proof_for_tx(tx_hash) + + def remove_spv_proof_for_tx(self, tx_hash): + self.merkle_roots.pop(tx_hash, None) + try: + self.requested_merkle.remove(tx_hash) + except KeyError: + pass + + def is_up_to_date(self): + return not self.requested_merkle diff --git a/lib/wallet.py b/lib/wallet.py @@ -222,9 +222,10 @@ class Abstract_Wallet(PrintError): self.load_unverified_transactions() self.remove_local_transactions_we_dont_have() - # there is a difference between wallet.up_to_date and interface.is_up_to_date() - # interface.is_up_to_date() returns true when all requests have been answered and processed + # There is a difference between wallet.up_to_date and network.is_up_to_date(). + # network.is_up_to_date() returns true when all requests have been answered and processed # wallet.up_to_date is true when the wallet is synchronized (stronger requirement) + # Neither of them considers the verifier. self.up_to_date = False # save wallet type the first time @@ -291,6 +292,12 @@ class Abstract_Wallet(PrintError): if write: self.storage.write() + def save_verified_tx(self, write=False): + with self.lock: + self.storage.put('verified_tx3', self.verified_tx) + if write: + self.storage.write() + def clear_history(self): with self.lock: with self.transaction_lock: @@ -365,6 +372,10 @@ class Abstract_Wallet(PrintError): self.up_to_date = up_to_date if up_to_date: self.save_transactions(write=True) + # if the verifier is also up to date, persist that too; + # otherwise it will persist its results when it finishes + if self.verifier and self.verifier.is_up_to_date(): + self.save_verified_tx(write=True) def is_up_to_date(self): with self.lock: return self.up_to_date @@ -445,7 +456,7 @@ class Abstract_Wallet(PrintError): with self.lock: self.verified_tx.pop(tx_hash) if self.verifier: - self.verifier.merkle_roots.pop(tx_hash, None) + self.verifier.remove_spv_proof_for_tx(tx_hash) # tx will be verified only if height > 0 if tx_hash not in self.verified_tx: @@ -965,7 +976,7 @@ class Abstract_Wallet(PrintError): self.unverified_tx.pop(tx_hash, None) self.verified_tx.pop(tx_hash, None) if self.verifier: - self.verifier.merkle_roots.pop(tx_hash, None) + self.verifier.remove_spv_proof_for_tx(tx_hash) # but remove completely if not is_mine if self.txi[tx_hash] == {}: # FIXME the test here should be for "not all is_mine"; cannot detect conflict in some cases @@ -1322,8 +1333,7 @@ class Abstract_Wallet(PrintError): # remain so they will be GC-ed self.storage.put('stored_height', self.get_local_height()) self.save_transactions() - with self.lock: - self.storage.put('verified_tx3', self.verified_tx) + self.save_verified_tx() self.storage.write() def wait_until_synchronized(self, callback=None):