wallet: introduce namedtuples TxMinedStatus and VerifiedTxInfo
This commit is contained in:
@@ -27,7 +27,7 @@ from collections import defaultdict
|
||||
|
||||
from . import bitcoin
|
||||
from .bitcoin import COINBASE_MATURITY, TYPE_ADDRESS, TYPE_PUBKEY
|
||||
from .util import PrintError, profiler, bfh
|
||||
from .util import PrintError, profiler, bfh, VerifiedTxInfo, TxMinedStatus
|
||||
from .transaction import Transaction
|
||||
from .synchronizer import Synchronizer
|
||||
from .verifier import SPV
|
||||
@@ -63,8 +63,11 @@ class AddressSynchronizer(PrintError):
|
||||
self.transaction_lock = threading.RLock()
|
||||
# address -> list(txid, height)
|
||||
self.history = storage.get('addr_history',{})
|
||||
# Verified transactions. txid -> (height, timestamp, block_pos, block_hash). Access with self.lock.
|
||||
self.verified_tx = storage.get('verified_tx3', {})
|
||||
# Verified transactions. txid -> VerifiedTxInfo. Access with self.lock.
|
||||
verified_tx = storage.get('verified_tx3', {})
|
||||
self.verified_tx = {}
|
||||
for txid, (height, timestamp, txpos, header_hash) in verified_tx.items():
|
||||
self.verified_tx[txid] = VerifiedTxInfo(height, timestamp, txpos, header_hash)
|
||||
# Transactions pending verification. txid -> tx_height. Access with self.lock.
|
||||
self.unverified_tx = defaultdict(int)
|
||||
# true when synchronized
|
||||
@@ -92,7 +95,7 @@ class AddressSynchronizer(PrintError):
|
||||
with self.lock, self.transaction_lock:
|
||||
related_txns = self._history_local.get(addr, set())
|
||||
for tx_hash in related_txns:
|
||||
tx_height = self.get_tx_height(tx_hash)[0]
|
||||
tx_height = self.get_tx_height(tx_hash).height
|
||||
h.append((tx_hash, tx_height))
|
||||
return h
|
||||
|
||||
@@ -195,7 +198,7 @@ class AddressSynchronizer(PrintError):
|
||||
# of add_transaction tx, we might learn of more-and-more inputs of
|
||||
# being is_mine, as we roll the gap_limit forward
|
||||
is_coinbase = tx.inputs()[0]['type'] == 'coinbase'
|
||||
tx_height = self.get_tx_height(tx_hash)[0]
|
||||
tx_height = self.get_tx_height(tx_hash).height
|
||||
if not allow_unrelated:
|
||||
# note that during sync, if the transactions are not properly sorted,
|
||||
# it could happen that we think tx is unrelated but actually one of the inputs is is_mine.
|
||||
@@ -214,10 +217,10 @@ class AddressSynchronizer(PrintError):
|
||||
conflicting_txns = self.get_conflicting_transactions(tx)
|
||||
if conflicting_txns:
|
||||
existing_mempool_txn = any(
|
||||
self.get_tx_height(tx_hash2)[0] in (TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_UNCONF_PARENT)
|
||||
self.get_tx_height(tx_hash2).height in (TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_UNCONF_PARENT)
|
||||
for tx_hash2 in conflicting_txns)
|
||||
existing_confirmed_txn = any(
|
||||
self.get_tx_height(tx_hash2)[0] > 0
|
||||
self.get_tx_height(tx_hash2).height > 0
|
||||
for tx_hash2 in conflicting_txns)
|
||||
if existing_confirmed_txn and tx_height <= 0:
|
||||
# this is a non-confirmed tx that conflicts with confirmed txns; drop.
|
||||
@@ -395,7 +398,7 @@ class AddressSynchronizer(PrintError):
|
||||
def remove_local_transactions_we_dont_have(self):
|
||||
txid_set = set(self.txi) | set(self.txo)
|
||||
for txid in txid_set:
|
||||
tx_height = self.get_tx_height(txid)[0]
|
||||
tx_height = self.get_tx_height(txid).height
|
||||
if tx_height == TX_HEIGHT_LOCAL and txid not in self.transactions:
|
||||
self.remove_transaction(txid)
|
||||
|
||||
@@ -433,11 +436,11 @@ class AddressSynchronizer(PrintError):
|
||||
self.save_transactions()
|
||||
|
||||
def get_txpos(self, tx_hash):
|
||||
"return position, even if the tx is unverified"
|
||||
"""Returns (height, txpos) tuple, even if the tx is unverified."""
|
||||
with self.lock:
|
||||
if tx_hash in self.verified_tx:
|
||||
height, timestamp, pos, header_hash = self.verified_tx[tx_hash]
|
||||
return height, pos
|
||||
info = self.verified_tx[tx_hash]
|
||||
return info.height, info.txpos
|
||||
elif tx_hash in self.unverified_tx:
|
||||
height = self.unverified_tx[tx_hash]
|
||||
return (height, 0) if height > 0 else ((1e9 - height), 0)
|
||||
@@ -464,16 +467,16 @@ class AddressSynchronizer(PrintError):
|
||||
history = []
|
||||
for tx_hash in tx_deltas:
|
||||
delta = tx_deltas[tx_hash]
|
||||
height, conf, timestamp, header_hash = self.get_tx_height(tx_hash)
|
||||
history.append((tx_hash, height, conf, timestamp, delta))
|
||||
tx_mined_status = self.get_tx_height(tx_hash)
|
||||
history.append((tx_hash, tx_mined_status, delta))
|
||||
history.sort(key = lambda x: self.get_txpos(x[0]))
|
||||
history.reverse()
|
||||
# 3. add balance
|
||||
c, u, x = self.get_balance(domain)
|
||||
balance = c + u + x
|
||||
h2 = []
|
||||
for tx_hash, height, conf, timestamp, delta in history:
|
||||
h2.append((tx_hash, height, conf, timestamp, delta, balance))
|
||||
for tx_hash, tx_mined_status, delta in history:
|
||||
h2.append((tx_hash, tx_mined_status, delta, balance))
|
||||
if balance is None or delta is None:
|
||||
balance = None
|
||||
else:
|
||||
@@ -519,13 +522,13 @@ class AddressSynchronizer(PrintError):
|
||||
if self.verifier:
|
||||
self.verifier.remove_spv_proof_for_tx(tx_hash)
|
||||
|
||||
def add_verified_tx(self, tx_hash, info):
|
||||
def add_verified_tx(self, tx_hash: str, info: VerifiedTxInfo):
|
||||
# Remove from the unverified map and add to the verified map
|
||||
with self.lock:
|
||||
self.unverified_tx.pop(tx_hash, None)
|
||||
self.verified_tx[tx_hash] = info # (tx_height, timestamp, pos, header_hash)
|
||||
height, conf, timestamp, header_hash = self.get_tx_height(tx_hash)
|
||||
self.network.trigger_callback('verified', tx_hash, height, conf, timestamp)
|
||||
self.verified_tx[tx_hash] = info
|
||||
tx_mined_status = self.get_tx_height(tx_hash)
|
||||
self.network.trigger_callback('verified', tx_hash, tx_mined_status)
|
||||
|
||||
def get_unverified_txs(self):
|
||||
'''Returns a map from tx hash to transaction height'''
|
||||
@@ -536,11 +539,11 @@ class AddressSynchronizer(PrintError):
|
||||
'''Used by the verifier when a reorg has happened'''
|
||||
txs = set()
|
||||
with self.lock:
|
||||
for tx_hash, item in list(self.verified_tx.items()):
|
||||
tx_height, timestamp, pos, header_hash = item
|
||||
for tx_hash, info in list(self.verified_tx.items()):
|
||||
tx_height = info.height
|
||||
if tx_height >= height:
|
||||
header = blockchain.read_header(tx_height)
|
||||
if not header or hash_header(header) != header_hash:
|
||||
if not header or hash_header(header) != info.header_hash:
|
||||
self.verified_tx.pop(tx_hash, None)
|
||||
# NOTE: we should add these txns to self.unverified_tx,
|
||||
# but with what height?
|
||||
@@ -559,19 +562,19 @@ class AddressSynchronizer(PrintError):
|
||||
""" return last known height if we are offline """
|
||||
return self.network.get_local_height() if self.network else self.storage.get('stored_height', 0)
|
||||
|
||||
def get_tx_height(self, tx_hash):
|
||||
def get_tx_height(self, tx_hash: str) -> TxMinedStatus:
|
||||
""" Given a transaction, returns (height, conf, timestamp, header_hash) """
|
||||
with self.lock:
|
||||
if tx_hash in self.verified_tx:
|
||||
height, timestamp, pos, header_hash = self.verified_tx[tx_hash]
|
||||
conf = max(self.get_local_height() - height + 1, 0)
|
||||
return height, conf, timestamp, header_hash
|
||||
info = self.verified_tx[tx_hash]
|
||||
conf = max(self.get_local_height() - info.height + 1, 0)
|
||||
return TxMinedStatus(info.height, conf, info.timestamp, info.header_hash)
|
||||
elif tx_hash in self.unverified_tx:
|
||||
height = self.unverified_tx[tx_hash]
|
||||
return height, 0, None, None
|
||||
return TxMinedStatus(height, 0, None, None)
|
||||
else:
|
||||
# local transaction
|
||||
return TX_HEIGHT_LOCAL, 0, None, None
|
||||
return TxMinedStatus(TX_HEIGHT_LOCAL, 0, None, None)
|
||||
|
||||
def set_up_to_date(self, up_to_date):
|
||||
with self.lock:
|
||||
|
||||
Reference in New Issue
Block a user