Browse Source

wallet: txi/txo small clean-up

dependabot/pip/contrib/deterministic-build/ecdsa-0.13.3
SomberNight 5 years ago
parent
commit
b138fff9a5
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 19
      electrum/address_synchronizer.py
  2. 27
      electrum/json_db.py
  3. 7
      electrum/transaction.py
  4. 12
      electrum/wallet.py

19
electrum/address_synchronizer.py

@ -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

27
electrum/json_db.py

@ -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

7
electrum/transaction.py

@ -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():

12
electrum/wallet.py

@ -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…
Cancel
Save