mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-31 09:21:39 +00:00
wallet: txi/txo small clean-up
This commit is contained in:
parent
241873f0a4
commit
b138fff9a5
4 changed files with 38 additions and 27 deletions
|
@ -125,13 +125,13 @@ class AddressSynchronizer(Logger):
|
||||||
"""Return number of transactions where address is involved."""
|
"""Return number of transactions where address is involved."""
|
||||||
return len(self._history_local.get(addr, ()))
|
return len(self._history_local.get(addr, ()))
|
||||||
|
|
||||||
def get_txin_address(self, txi):
|
def get_txin_address(self, txi) -> Optional[str]:
|
||||||
addr = txi.get('address')
|
addr = txi.get('address')
|
||||||
if addr and addr != "(pubkey)":
|
if addr and addr != "(pubkey)":
|
||||||
return addr
|
return addr
|
||||||
prevout_hash = txi.get('prevout_hash')
|
prevout_hash = txi.get('prevout_hash')
|
||||||
prevout_n = txi.get('prevout_n')
|
prevout_n = txi.get('prevout_n')
|
||||||
for addr in self.db.get_txo(prevout_hash):
|
for addr in self.db.get_txo_addresses(prevout_hash):
|
||||||
l = self.db.get_txo_addr(prevout_hash, addr)
|
l = self.db.get_txo_addr(prevout_hash, addr)
|
||||||
for n, v, is_cb in l:
|
for n, v, is_cb in l:
|
||||||
if n == prevout_n:
|
if n == prevout_n:
|
||||||
|
@ -266,7 +266,7 @@ class AddressSynchronizer(Logger):
|
||||||
# add inputs
|
# add inputs
|
||||||
def add_value_from_prev_output():
|
def add_value_from_prev_output():
|
||||||
# note: this nested loop takes linear time in num is_mine outputs of prev_tx
|
# note: this nested loop takes linear time in num is_mine outputs of prev_tx
|
||||||
for addr in self.db.get_txo(prevout_hash):
|
for addr in self.db.get_txo_addresses(prevout_hash):
|
||||||
outputs = self.db.get_txo_addr(prevout_hash, addr)
|
outputs = self.db.get_txo_addr(prevout_hash, addr)
|
||||||
# note: instead of [(n, v, is_cb), ...]; we could store: {n -> (v, is_cb)}
|
# note: instead of [(n, v, is_cb), ...]; we could store: {n -> (v, is_cb)}
|
||||||
for n, v, is_cb in outputs:
|
for n, v, is_cb in outputs:
|
||||||
|
@ -325,7 +325,7 @@ class AddressSynchronizer(Logger):
|
||||||
tx = self.db.remove_transaction(tx_hash)
|
tx = self.db.remove_transaction(tx_hash)
|
||||||
remove_from_spent_outpoints()
|
remove_from_spent_outpoints()
|
||||||
self._remove_tx_from_local_history(tx_hash)
|
self._remove_tx_from_local_history(tx_hash)
|
||||||
for addr in itertools.chain(self.db.get_txi(tx_hash), self.db.get_txo(tx_hash)):
|
for addr in itertools.chain(self.db.get_txi_addresses(tx_hash), self.db.get_txo_addresses(tx_hash)):
|
||||||
self._get_addr_balance_cache.pop(addr, None) # invalidate cache
|
self._get_addr_balance_cache.pop(addr, None) # invalidate cache
|
||||||
self.db.remove_txi(tx_hash)
|
self.db.remove_txi(tx_hash)
|
||||||
self.db.remove_txo(tx_hash)
|
self.db.remove_txo(tx_hash)
|
||||||
|
@ -384,7 +384,7 @@ class AddressSynchronizer(Logger):
|
||||||
for addr in hist_addrs_mine:
|
for addr in hist_addrs_mine:
|
||||||
hist = self.db.get_addr_history(addr)
|
hist = self.db.get_addr_history(addr)
|
||||||
for tx_hash, tx_height in hist:
|
for tx_hash, tx_height in hist:
|
||||||
if self.db.get_txi(tx_hash) or self.db.get_txo(tx_hash):
|
if self.db.get_txi_addresses(tx_hash) or self.db.get_txo_addresses(tx_hash):
|
||||||
continue
|
continue
|
||||||
tx = self.db.get_transaction(tx_hash)
|
tx = self.db.get_transaction(tx_hash)
|
||||||
if tx is not None:
|
if tx is not None:
|
||||||
|
@ -475,7 +475,7 @@ class AddressSynchronizer(Logger):
|
||||||
|
|
||||||
def _add_tx_to_local_history(self, txid):
|
def _add_tx_to_local_history(self, txid):
|
||||||
with self.transaction_lock:
|
with self.transaction_lock:
|
||||||
for addr in itertools.chain(self.db.get_txi(txid), self.db.get_txo(txid)):
|
for addr in itertools.chain(self.db.get_txi_addresses(txid), self.db.get_txo_addresses(txid)):
|
||||||
cur_hist = self._history_local.get(addr, set())
|
cur_hist = self._history_local.get(addr, set())
|
||||||
cur_hist.add(txid)
|
cur_hist.add(txid)
|
||||||
self._history_local[addr] = cur_hist
|
self._history_local[addr] = cur_hist
|
||||||
|
@ -483,7 +483,7 @@ class AddressSynchronizer(Logger):
|
||||||
|
|
||||||
def _remove_tx_from_local_history(self, txid):
|
def _remove_tx_from_local_history(self, txid):
|
||||||
with self.transaction_lock:
|
with self.transaction_lock:
|
||||||
for addr in itertools.chain(self.db.get_txi(txid), self.db.get_txo(txid)):
|
for addr in itertools.chain(self.db.get_txi_addresses(txid), self.db.get_txo_addresses(txid)):
|
||||||
cur_hist = self._history_local.get(addr, set())
|
cur_hist = self._history_local.get(addr, set())
|
||||||
try:
|
try:
|
||||||
cur_hist.remove(txid)
|
cur_hist.remove(txid)
|
||||||
|
@ -584,6 +584,7 @@ class AddressSynchronizer(Logger):
|
||||||
height = self.unverified_tx[tx_hash]
|
height = self.unverified_tx[tx_hash]
|
||||||
return TxMinedInfo(height=height, conf=0)
|
return TxMinedInfo(height=height, conf=0)
|
||||||
elif tx_hash in self.future_tx:
|
elif tx_hash in self.future_tx:
|
||||||
|
# FIXME this is ugly
|
||||||
conf = self.future_tx[tx_hash]
|
conf = self.future_tx[tx_hash]
|
||||||
return TxMinedInfo(height=TX_HEIGHT_FUTURE, conf=conf)
|
return TxMinedInfo(height=TX_HEIGHT_FUTURE, conf=conf)
|
||||||
else:
|
else:
|
||||||
|
@ -623,11 +624,11 @@ class AddressSynchronizer(Logger):
|
||||||
def get_tx_value(self, txid):
|
def get_tx_value(self, txid):
|
||||||
"""effect of tx on the entire domain"""
|
"""effect of tx on the entire domain"""
|
||||||
delta = 0
|
delta = 0
|
||||||
for addr in self.db.get_txi(txid):
|
for addr in self.db.get_txi_addresses(txid):
|
||||||
d = self.db.get_txi_addr(txid, addr)
|
d = self.db.get_txi_addr(txid, addr)
|
||||||
for n, v in d:
|
for n, v in d:
|
||||||
delta -= v
|
delta -= v
|
||||||
for addr in self.db.get_txo(txid):
|
for addr in self.db.get_txo_addresses(txid):
|
||||||
d = self.db.get_txo_addr(txid, addr)
|
d = self.db.get_txo_addr(txid, addr)
|
||||||
for n, v, cb in d:
|
for n, v, cb in d:
|
||||||
delta += v
|
delta += v
|
||||||
|
|
|
@ -28,7 +28,7 @@ import json
|
||||||
import copy
|
import copy
|
||||||
import threading
|
import threading
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional, List, Tuple, Set, Iterable
|
||||||
|
|
||||||
from . import util, bitcoin
|
from . import util, bitcoin
|
||||||
from .util import profiler, WalletFileException, multisig_type, TxMinedInfo
|
from .util import profiler, WalletFileException, multisig_type, TxMinedInfo
|
||||||
|
@ -518,20 +518,24 @@ class JsonDB(Logger):
|
||||||
raise WalletFileException(msg)
|
raise WalletFileException(msg)
|
||||||
|
|
||||||
@locked
|
@locked
|
||||||
def get_txi(self, tx_hash):
|
def get_txi_addresses(self, tx_hash) -> List[str]:
|
||||||
|
"""Returns list of is_mine addresses that appear as inputs in tx."""
|
||||||
return list(self.txi.get(tx_hash, {}).keys())
|
return list(self.txi.get(tx_hash, {}).keys())
|
||||||
|
|
||||||
@locked
|
@locked
|
||||||
def get_txo(self, tx_hash):
|
def get_txo_addresses(self, tx_hash) -> List[str]:
|
||||||
|
"""Returns list of is_mine addresses that appear as outputs in tx."""
|
||||||
return list(self.txo.get(tx_hash, {}).keys())
|
return list(self.txo.get(tx_hash, {}).keys())
|
||||||
|
|
||||||
@locked
|
@locked
|
||||||
def get_txi_addr(self, tx_hash, address):
|
def get_txi_addr(self, tx_hash, address) -> Iterable[Tuple[str, int]]:
|
||||||
return self.txi.get(tx_hash, {}).get(address, [])
|
"""Returns an iterable of (prev_outpoint, value)."""
|
||||||
|
return self.txi.get(tx_hash, {}).get(address, []).copy()
|
||||||
|
|
||||||
@locked
|
@locked
|
||||||
def get_txo_addr(self, tx_hash, address):
|
def get_txo_addr(self, tx_hash, address) -> Iterable[Tuple[int, int, bool]]:
|
||||||
return self.txo.get(tx_hash, {}).get(address, [])
|
"""Returns an iterable of (output_index, value, is_coinbase)."""
|
||||||
|
return self.txo.get(tx_hash, {}).get(address, []).copy()
|
||||||
|
|
||||||
@modifier
|
@modifier
|
||||||
def add_txi_addr(self, tx_hash, addr, ser, v):
|
def add_txi_addr(self, tx_hash, addr, ser, v):
|
||||||
|
@ -754,8 +758,11 @@ class JsonDB(Logger):
|
||||||
@profiler
|
@profiler
|
||||||
def _load_transactions(self):
|
def _load_transactions(self):
|
||||||
# references in self.data
|
# references in self.data
|
||||||
self.txi = self.get_data_ref('txi') # txid -> address -> list of (prev_outpoint, value)
|
# TODO make all these private
|
||||||
self.txo = self.get_data_ref('txo') # txid -> address -> list of (output_index, value, is_coinbase)
|
# txid -> address -> set of (prev_outpoint, value)
|
||||||
|
self.txi = self.get_data_ref('txi') # type: Dict[str, Dict[str, Set[Tuple[str, int]]]]
|
||||||
|
# txid -> address -> set of (output_index, value, is_coinbase)
|
||||||
|
self.txo = self.get_data_ref('txo') # type: Dict[str, Dict[str, Set[Tuple[int, int, bool]]]]
|
||||||
self.transactions = self.get_data_ref('transactions') # type: Dict[str, Transaction]
|
self.transactions = self.get_data_ref('transactions') # type: Dict[str, Transaction]
|
||||||
self.spent_outpoints = self.get_data_ref('spent_outpoints')
|
self.spent_outpoints = self.get_data_ref('spent_outpoints')
|
||||||
self.history = self.get_data_ref('addr_history') # address -> list of (txid, height)
|
self.history = self.get_data_ref('addr_history') # address -> list of (txid, height)
|
||||||
|
@ -771,7 +778,7 @@ class JsonDB(Logger):
|
||||||
d[addr] = set([tuple(x) for x in lst])
|
d[addr] = set([tuple(x) for x in lst])
|
||||||
# remove unreferenced tx
|
# remove unreferenced tx
|
||||||
for tx_hash in list(self.transactions.keys()):
|
for tx_hash in list(self.transactions.keys()):
|
||||||
if not self.get_txi(tx_hash) and not self.get_txo(tx_hash):
|
if not self.get_txi_addresses(tx_hash) and not self.get_txo_addresses(tx_hash):
|
||||||
self.logger.info(f"removing unreferenced tx: {tx_hash}")
|
self.logger.info(f"removing unreferenced tx: {tx_hash}")
|
||||||
self.transactions.pop(tx_hash)
|
self.transactions.pop(tx_hash)
|
||||||
# remove unreferenced outpoints
|
# remove unreferenced outpoints
|
||||||
|
|
|
@ -31,7 +31,7 @@ import struct
|
||||||
import traceback
|
import traceback
|
||||||
import sys
|
import sys
|
||||||
from typing import (Sequence, Union, NamedTuple, Tuple, Optional, Iterable,
|
from typing import (Sequence, Union, NamedTuple, Tuple, Optional, Iterable,
|
||||||
Callable, List, Dict, Set)
|
Callable, List, Dict, Set, TYPE_CHECKING)
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
from . import ecc, bitcoin, constants, segwit_addr
|
from . import ecc, bitcoin, constants, segwit_addr
|
||||||
|
@ -45,6 +45,9 @@ from .crypto import sha256d
|
||||||
from .keystore import xpubkey_to_address, xpubkey_to_pubkey
|
from .keystore import xpubkey_to_address, xpubkey_to_pubkey
|
||||||
from .logging import get_logger
|
from .logging import get_logger
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .wallet import Abstract_Wallet
|
||||||
|
|
||||||
|
|
||||||
_logger = get_logger(__name__)
|
_logger = get_logger(__name__)
|
||||||
|
|
||||||
|
@ -687,7 +690,7 @@ class Transaction:
|
||||||
txin['witness'] = None # force re-serialization
|
txin['witness'] = None # force re-serialization
|
||||||
self.raw = None
|
self.raw = None
|
||||||
|
|
||||||
def add_inputs_info(self, wallet):
|
def add_inputs_info(self, wallet: 'Abstract_Wallet') -> None:
|
||||||
if self.is_complete():
|
if self.is_complete():
|
||||||
return
|
return
|
||||||
for txin in self.inputs():
|
for txin in self.inputs():
|
||||||
|
|
|
@ -699,9 +699,9 @@ class Abstract_Wallet(AddressSynchronizer):
|
||||||
return self.labels.get(tx_hash, '') or self.get_default_label(tx_hash)
|
return self.labels.get(tx_hash, '') or self.get_default_label(tx_hash)
|
||||||
|
|
||||||
def get_default_label(self, tx_hash):
|
def get_default_label(self, tx_hash):
|
||||||
if not self.db.get_txi(tx_hash):
|
if not self.db.get_txi_addresses(tx_hash):
|
||||||
labels = []
|
labels = []
|
||||||
for addr in self.db.get_txo(tx_hash):
|
for addr in self.db.get_txo_addresses(tx_hash):
|
||||||
label = self.labels.get(addr)
|
label = self.labels.get(addr)
|
||||||
if label:
|
if label:
|
||||||
labels.append(label)
|
labels.append(label)
|
||||||
|
@ -1150,7 +1150,7 @@ class Abstract_Wallet(AddressSynchronizer):
|
||||||
txin['value'] = item[1]
|
txin['value'] = item[1]
|
||||||
self.add_input_sig_info(txin, address)
|
self.add_input_sig_info(txin, address)
|
||||||
|
|
||||||
def can_sign(self, tx):
|
def can_sign(self, tx: Transaction) -> bool:
|
||||||
if tx.is_complete():
|
if tx.is_complete():
|
||||||
return False
|
return False
|
||||||
# add info to inputs if we can; otherwise we might return a false negative:
|
# add info to inputs if we can; otherwise we might return a false negative:
|
||||||
|
@ -1505,7 +1505,7 @@ class Abstract_Wallet(AddressSynchronizer):
|
||||||
def txin_value(self, txin):
|
def txin_value(self, txin):
|
||||||
txid = txin['prevout_hash']
|
txid = txin['prevout_hash']
|
||||||
prev_n = txin['prevout_n']
|
prev_n = txin['prevout_n']
|
||||||
for addr in self.db.get_txo(txid):
|
for addr in self.db.get_txo_addresses(txid):
|
||||||
d = self.db.get_txo_addr(txid, addr)
|
d = self.db.get_txo_addr(txid, addr)
|
||||||
for n, v, cb in d:
|
for n, v, cb in d:
|
||||||
if n == prev_n:
|
if n == prev_n:
|
||||||
|
@ -1530,7 +1530,7 @@ class Abstract_Wallet(AddressSynchronizer):
|
||||||
""" Average acquisition price of the inputs of a transaction """
|
""" Average acquisition price of the inputs of a transaction """
|
||||||
input_value = 0
|
input_value = 0
|
||||||
total_price = 0
|
total_price = 0
|
||||||
for addr in self.db.get_txi(txid):
|
for addr in self.db.get_txi_addresses(txid):
|
||||||
d = self.db.get_txi_addr(txid, addr)
|
d = self.db.get_txi_addr(txid, addr)
|
||||||
for ser, v in d:
|
for ser, v in d:
|
||||||
input_value += v
|
input_value += v
|
||||||
|
@ -1551,7 +1551,7 @@ class Abstract_Wallet(AddressSynchronizer):
|
||||||
result = self._coin_price_cache.get(cache_key, None)
|
result = self._coin_price_cache.get(cache_key, None)
|
||||||
if result is not None:
|
if result is not None:
|
||||||
return result
|
return result
|
||||||
if self.db.get_txi(txid):
|
if self.db.get_txi_addresses(txid):
|
||||||
result = self.average_price(txid, price_func, ccy) * txin_value/Decimal(COIN)
|
result = self.average_price(txid, price_func, ccy) * txin_value/Decimal(COIN)
|
||||||
self._coin_price_cache[cache_key] = result
|
self._coin_price_cache[cache_key] = result
|
||||||
return result
|
return result
|
||||||
|
|
Loading…
Add table
Reference in a new issue