URI: 
       twallet/verifier: persist wallet.verified_tx when verifier finishes - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 060404e17c806e363a6f512e2f544f5c12821dd3
   DIR parent 4159322db06009a2da975ac419facd7282b3fafe
  HTML 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:
         M lib/verifier.py                     |      29 +++++++++++++++++++++++------
         M lib/wallet.py                       |      22 ++++++++++++++++------
       
       2 files changed, 39 insertions(+), 12 deletions(-)
       ---
   DIR diff --git a/lib/verifier.py b/lib/verifier.py
       t@@ -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
       t@@ -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()
       t@@ -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):
       t@@ -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
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -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
       t@@ -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:
       t@@ -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
       t@@ -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:
       t@@ -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
       t@@ -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):