From 351272f0b6f8f253483ea6913fcc8c5932a16378 Mon Sep 17 00:00:00 2001 From: Neil Booth Date: Fri, 28 Aug 2015 12:39:19 +0900 Subject: [PATCH] Small optimization for large wallets Previously the verifier job would scan all transactions in unverified_tx each time it ran. Nothing was ever removed from this map; it would essentially be the full set of transactions. As the job runs about 10 times a second, for a wallet with 500 txs this would be 5,000 useless loops a second. This patch makes unverified_tx be simply the set of confirmed transactions that haven't yet been verified. txs are added once confirmed, and removed once verified. Hence it will almost always be empty. --- lib/verifier.py | 8 ++++++-- lib/wallet.py | 15 ++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/verifier.py b/lib/verifier.py index 558e1d97..7cccbc31 100644 --- a/lib/verifier.py +++ b/lib/verifier.py @@ -33,9 +33,11 @@ class SPV(ThreadJob): self.merkle_roots = {} def run(self): + lh = self.wallet.get_local_height() unverified = self.wallet.get_unverified_txs() - for (tx_hash, tx_height) in unverified: - if tx_hash not in self.merkle_roots: + for tx_hash, tx_height in unverified.items(): + # do not request merkle branch before headers are available + if tx_hash not in self.merkle_roots and tx_height <= lh: request = ('blockchain.transaction.get_merkle', [tx_hash, tx_height]) if self.network.send([request], self.merkle_response): @@ -64,6 +66,8 @@ class SPV(ThreadJob): merkle_root = self.hash_merkle_root(merkle['merkle'], tx_hash, pos) header = header.get('result') if not header or header.get('merkle_root') != merkle_root: + # FIXME: we should make a fresh connection to a server to + # recover from this, as this TX will now never verify self.print_error("merkle verification failed for", tx_hash) return diff --git a/lib/wallet.py b/lib/wallet.py index a78acae0..22466c0a 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -417,10 +417,13 @@ class Abstract_Wallet(object): return decrypted def add_unverified_tx(self, tx_hash, tx_height): - if tx_height > 0: + # Only add if confirmed and not verified + if tx_height > 0 and tx_hash not in self.verified_tx: self.unverified_tx[tx_hash] = tx_height def add_verified_tx(self, tx_hash, info): + # Remove from the unverified map and add to the verified map and + self.unverified_tx.pop(tx_hash, None) with self.lock: self.verified_tx[tx_hash] = info # (tx_height, timestamp, pos) self.storage.put('verified_tx3', self.verified_tx, True) @@ -429,14 +432,8 @@ class Abstract_Wallet(object): self.network.trigger_callback('verified', (tx_hash, conf, timestamp)) def get_unverified_txs(self): - '''Returns a list of tuples (tx_hash, height) that are unverified - and not beyond local height''' - txs = [] - for tx_hash, tx_height in self.unverified_tx.items(): - # do not request merkle branch before headers are available - if tx_hash not in self.verified_tx and tx_height <= self.get_local_height(): - txs.append((tx_hash, tx_height)) - return txs + '''Returns a map from tx hash to transaction height''' + return self.unverified_tx def undo_verifications(self, height): '''Used by the verifier when a reorg has happened'''