Move the verified and unverified txs to the wallet.

This commit is contained in:
Neil Booth 2015-05-07 14:05:10 +09:00
parent 656560be72
commit a47881d72b
2 changed files with 57 additions and 58 deletions

View File

@ -25,41 +25,23 @@ import util
from bitcoin import * from bitcoin import *
class SPV(util.DaemonThread): class SPV(util.DaemonThread):
""" Simple Payment Verification """ """ Simple Payment Verification """
def __init__(self, network, storage): def __init__(self, network, wallet):
util.DaemonThread.__init__(self) util.DaemonThread.__init__(self)
self.storage = storage self.wallet = wallet
self.network = network self.network = network
self.transactions = {} # requested verifications (with height sent by the requestor)
self.verified_tx = storage.get('verified_tx3',{}) # height, timestamp of verified transactions
self.merkle_roots = {} # hashed by me self.merkle_roots = {} # hashed by me
self.lock = threading.Lock()
self.queue = Queue.Queue() self.queue = Queue.Queue()
def get_height(self, tx_hash):
with self.lock:
v = self.verified_tx.get(tx_hash)
height = v[0] if v else None
return height
def add(self, tx_hash, tx_height):
""" add a transaction to the list of monitored transactions. """
assert tx_height > 0
with self.lock:
if tx_hash not in self.transactions.keys():
self.transactions[tx_hash] = tx_height
def run(self): def run(self):
requested_merkle = [] requested_merkle = []
while self.is_running(): while self.is_running():
verified_tx, unverified_tx = self.wallet.get_transactions()
# request missing tx # request missing tx
for tx_hash, tx_height in self.transactions.items(): for tx_hash, tx_height in unverified_tx.items():
if tx_hash not in self.verified_tx: if tx_hash not in verified_tx:
# do not request merkle branch before headers are available # do not request merkle branch before headers are available
if tx_height > self.network.get_local_height(): if tx_height > self.network.get_local_height():
continue continue
@ -102,12 +84,8 @@ class SPV(util.DaemonThread):
# we passed all the tests # we passed all the tests
self.merkle_roots[tx_hash] = merkle_root self.merkle_roots[tx_hash] = merkle_root
timestamp = header.get('timestamp') self.print_error("verified %s" % tx_hash)
with self.lock: self.wallet.add_verified_tx(tx_hash, (tx_height, header.get('timestamp'), pos))
self.verified_tx[tx_hash] = (tx_height, timestamp, pos)
self.print_error("verified %s"%tx_hash)
self.storage.put('verified_tx3', self.verified_tx, True)
self.network.trigger_callback('updated')
def hash_merkle_root(self, merkle_s, target_hash, pos): def hash_merkle_root(self, merkle_s, target_hash, pos):
@ -118,15 +96,13 @@ class SPV(util.DaemonThread):
return hash_encode(h) return hash_encode(h)
def undo_verifications(self, height): def undo_verifications(self, height):
with self.lock: verified_tx, unverified_tx = self.wallet.get_transactions()
items = self.verified_tx.items()[:] txs = []
for tx_hash, item in items: for tx_hash, item in verified_tx:
tx_height, timestamp, pos = item tx_height, timestamp, pos = item
if tx_height >= height: if tx_height >= height:
self.print_error("redoing", tx_hash) self.print_error("redoing", tx_hash)
with self.lock: txs.append(tx_hash)
self.verified_tx.pop(tx_hash) self.merkle_roots.pop(tx_hash, None)
if tx_hash in self.merkle_roots: self.wallet.unverify_txs(txs)
self.merkle_roots.pop(tx_hash)

View File

@ -182,6 +182,11 @@ class Abstract_Wallet(object):
# spv # spv
self.verifier = None self.verifier = None
# Transactions pending verification. Each value is the transaction height. Access with self.lock.
self.unverified_tx = {}
# Verified transactions. Each value is a (height, timestamp, block_pos) tuple. Access with self.lock.
self.verified_tx = storage.get('verified_tx3',{})
# there is a difference between wallet.up_to_date and interface.is_up_to_date() # 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 # interface.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) # wallet.up_to_date is true when the wallet is synchronized (stronger requirement)
@ -388,32 +393,48 @@ class Abstract_Wallet(object):
return decrypted return decrypted
def add_unverified_tx(self, tx_hash, tx_height): def add_unverified_tx(self, tx_hash, tx_height):
if self.verifier and tx_height > 0: if tx_height > 0:
self.verifier.add(tx_hash, tx_height) with self.lock:
self.unverified_tx[tx_hash] = tx_height
def add_verified_tx(self, tx_hash, info):
with self.lock:
self.verified_tx[tx_hash] = info # (tx_height, timestamp, pos)
self.storage.put('verified_tx3', self.verified_tx, True)
self.network.trigger_callback('updated')
def unverify_txs(self, txs):
'''Used by the verifier when a reorg has happened'''
with self.lock:
for tx_hash in txs:
self.verified_tx.pop(tx_hash, None)
def get_transactions(self):
'''Return the verified and unverified tx dicts'''
with self.lock:
return self.verified_tx, self.unverified_tx
def get_confirmations(self, tx): def get_confirmations(self, tx):
""" return the number of confirmations of a monitored transaction. """ """ return the number of confirmations of a monitored transaction. """
if not self.verifier: verified_tx, unverified_tx = self.get_transactions()
return (None, None) if tx in verified_tx:
with self.verifier.lock: height, timestamp, pos = verified_tx[tx]
if tx in self.verifier.verified_tx: conf = (self.network.get_local_height() - height + 1)
height, timestamp, pos = self.verifier.verified_tx[tx] if conf <= 0: timestamp = None
conf = (self.network.get_local_height() - height + 1) elif tx in unverified_tx:
if conf <= 0: timestamp = None conf = -1
elif tx in self.verifier.transactions: timestamp = None
conf = -1 else:
timestamp = None conf = 0
else: timestamp = None
conf = 0
timestamp = None
return conf, timestamp return conf, timestamp
def get_txpos(self, tx_hash): def get_txpos(self, tx_hash):
"return position, even if the tx is unverified" "return position, even if the tx is unverified"
with self.verifier.lock: verified_tx, unverified_tx = self.get_transactions()
x = self.verifier.verified_tx.get(tx_hash) x = verified_tx.get(tx_hash)
y = self.verifier.transactions.get(tx_hash) y = unverified_tx.get(tx_hash)
if x: if x:
height, timestamp, pos = x height, timestamp, pos = x
return height, pos return height, pos
@ -1000,7 +1021,8 @@ class Abstract_Wallet(object):
self.add_unverified_tx (tx_hash, tx_height) self.add_unverified_tx (tx_hash, tx_height)
# if we are on a pruning server, remove unverified transactions # if we are on a pruning server, remove unverified transactions
vr = self.verifier.transactions.keys() + self.verifier.verified_tx.keys() verified_tx, unverified_tx = self.get_transactions()
vr = verified_tx.keys() + unverified_tx.keys()
for tx_hash in self.transactions.keys(): for tx_hash in self.transactions.keys():
if tx_hash not in vr: if tx_hash not in vr:
print_error("removing transaction", tx_hash) print_error("removing transaction", tx_hash)
@ -1016,6 +1038,7 @@ class Abstract_Wallet(object):
return False return False
# check that we are not "orphaning" a transaction # check that we are not "orphaning" a transaction
verified_tx, unverified_tx = self.get_transactions()
old_hist = self.history.get(addr,[]) old_hist = self.history.get(addr,[])
for tx_hash, height in old_hist: for tx_hash, height in old_hist:
if tx_hash in map(lambda x:x[0], hist): if tx_hash in map(lambda x:x[0], hist):
@ -1035,7 +1058,7 @@ class Abstract_Wallet(object):
if not tx: continue if not tx: continue
# already verified? # already verified?
if self.verifier.get_height(tx_hash): if tx_hash in verified_tx:
continue continue
# unconfirmed tx # unconfirmed tx
print_error("new history is orphaning transaction:", tx_hash) print_error("new history is orphaning transaction:", tx_hash)
@ -1065,7 +1088,7 @@ class Abstract_Wallet(object):
from verifier import SPV from verifier import SPV
self.network = network self.network = network
if self.network is not None: if self.network is not None:
self.verifier = SPV(self.network, self.storage) self.verifier = SPV(self.network, self)
self.verifier.start() self.verifier.start()
self.set_verifier(self.verifier) self.set_verifier(self.verifier)
self.synchronizer = WalletSynchronizer(self, network) self.synchronizer = WalletSynchronizer(self, network)