Browse Source

wallet: simplify get_wallet_delta

patch-4
SomberNight 4 years ago
parent
commit
f125a06453
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 87
      electrum/address_synchronizer.py
  2. 4
      electrum/gui/kivy/uix/dialogs/tx_dialog.py
  3. 12
      electrum/gui/qt/main_window.py
  4. 26
      electrum/wallet.py

87
electrum/address_synchronizer.py

@ -57,6 +57,14 @@ class HistoryItem(NamedTuple):
balance: Optional[int] balance: Optional[int]
class TxWalletDelta(NamedTuple):
is_relevant: bool # "related to wallet?"
is_any_input_ismine: bool
is_all_input_ismine: bool
delta: int
fee: Optional[int]
class AddressSynchronizer(Logger): class AddressSynchronizer(Logger):
""" """
inherited by wallet inherited by wallet
@ -652,7 +660,7 @@ class AddressSynchronizer(Logger):
def get_tx_delta(self, tx_hash, address): def get_tx_delta(self, tx_hash, address):
"""effect of tx on address""" """effect of tx on address"""
delta = 0 delta = 0
# substract the value of coins sent from address # subtract the value of coins sent from address
d = self.db.get_txi_addr(tx_hash, address) d = self.db.get_txi_addr(tx_hash, address)
for n, v in d: for n, v in d:
delta -= v delta -= v
@ -662,65 +670,43 @@ class AddressSynchronizer(Logger):
delta += v delta += v
return delta return delta
@with_transaction_lock def get_wallet_delta(self, tx: Transaction) -> TxWalletDelta:
def get_tx_value(self, txid):
"""effect of tx on the entire domain"""
delta = 0
for addr in self.db.get_txi_addresses(txid):
d = self.db.get_txi_addr(txid, addr)
for n, v in d:
delta -= v
for addr in self.db.get_txo_addresses(txid):
d = self.db.get_txo_addr(txid, addr)
for n, (v, cb) in d.items():
delta += v
return delta
def get_wallet_delta(self, tx: Transaction):
"""effect of tx on wallet""" """effect of tx on wallet"""
is_relevant = False # "related to wallet?" is_relevant = False # "related to wallet?"
is_mine = False # "is any input mine?" num_input_ismine = 0
is_pruned = False v_in = v_in_mine = v_out = v_out_mine = 0
is_partial = False with self.lock, self.transaction_lock:
v_in = v_out = v_out_mine = 0
for txin in tx.inputs(): for txin in tx.inputs():
addr = self.get_txin_address(txin) addr = self.get_txin_address(txin)
value = self.get_txin_value(txin, address=addr)
if self.is_mine(addr): if self.is_mine(addr):
is_mine = True num_input_ismine += 1
is_relevant = True is_relevant = True
value = self.get_txin_value(txin, address=addr) assert value is not None
v_in_mine += value
if value is None: if value is None:
is_pruned = True v_in = None
else: elif v_in is not None:
v_in += value v_in += value
else: for txout in tx.outputs():
is_partial = True v_out += txout.value
if not is_mine: if self.is_mine(txout.address):
is_partial = False v_out_mine += txout.value
for o in tx.outputs():
v_out += o.value
if self.is_mine(o.address):
v_out_mine += o.value
is_relevant = True is_relevant = True
if is_pruned: delta = v_out_mine - v_in_mine
# some inputs are mine: if v_in is not None:
fee = None
if is_mine:
v = v_out_mine - v_out
else:
# no input is mine
v = v_out_mine
else:
v = v_out_mine - v_in
if is_partial:
# some inputs are mine, but not all
fee = None
else:
# all inputs are mine
fee = v_in - v_out fee = v_in - v_out
if not is_mine: else:
fee = None fee = None
return is_relevant, is_mine, v, fee if fee is None and isinstance(tx, PartialTransaction):
fee = tx.get_fee()
return TxWalletDelta(
is_relevant=is_relevant,
is_any_input_ismine=num_input_ismine > 0,
is_all_input_ismine=num_input_ismine == len(tx.inputs()),
delta=delta,
fee=fee,
)
def get_tx_fee(self, txid: str) -> Optional[int]: def get_tx_fee(self, txid: str) -> Optional[int]:
""" Returns tx_fee or None. Use server fee only if tx is unconfirmed and not mine""" """ Returns tx_fee or None. Use server fee only if tx is unconfirmed and not mine"""
@ -747,8 +733,7 @@ class AddressSynchronizer(Logger):
tx = self.db.get_transaction(txid) tx = self.db.get_transaction(txid)
if not tx: if not tx:
return None return None
with self.lock, self.transaction_lock: fee = self.get_wallet_delta(tx).fee
is_relevant, is_mine, v, fee = self.get_wallet_delta(tx)
# save result # save result
self.db.add_tx_fee_we_calculated(txid, fee) self.db.add_tx_fee_we_calculated(txid, fee)
self.db.add_num_inputs_to_tx(txid, len(tx.inputs())) self.db.add_num_inputs_to_tx(txid, len(tx.inputs()))

4
electrum/gui/kivy/uix/dialogs/tx_dialog.py

@ -232,7 +232,7 @@ class TxDialog(Factory.Popup):
def do_rbf(self): def do_rbf(self):
from .bump_fee_dialog import BumpFeeDialog from .bump_fee_dialog import BumpFeeDialog
is_relevant, is_mine, v, fee = self.wallet.get_wallet_delta(self.tx) fee = self.wallet.get_wallet_delta(self.tx).fee
if fee is None: if fee is None:
self.app.show_error(_("Can't bump fee: unknown fee for original transaction.")) self.app.show_error(_("Can't bump fee: unknown fee for original transaction."))
return return
@ -257,7 +257,7 @@ class TxDialog(Factory.Popup):
def do_dscancel(self): def do_dscancel(self):
from .dscancel_dialog import DSCancelDialog from .dscancel_dialog import DSCancelDialog
is_relevant, is_mine, v, fee = self.wallet.get_wallet_delta(self.tx) fee = self.wallet.get_wallet_delta(self.tx).fee
if fee is None: if fee is None:
self.app.show_error(_('Cannot cancel transaction') + ': ' + _('unknown fee for original transaction')) self.app.show_error(_('Cannot cancel transaction') + ': ' + _('unknown fee for original transaction'))
return return

12
electrum/gui/qt/main_window.py

@ -812,18 +812,18 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
if len(txns) >= 3: if len(txns) >= 3:
total_amount = 0 total_amount = 0
for tx in txns: for tx in txns:
is_relevant, is_mine, v, fee = self.wallet.get_wallet_delta(tx) tx_wallet_delta = self.wallet.get_wallet_delta(tx)
if not is_relevant: if not tx_wallet_delta.is_relevant:
continue continue
total_amount += v total_amount += tx_wallet_delta.delta
self.notify(_("{} new transactions: Total amount received in the new transactions {}") self.notify(_("{} new transactions: Total amount received in the new transactions {}")
.format(len(txns), self.format_amount_and_units(total_amount))) .format(len(txns), self.format_amount_and_units(total_amount)))
else: else:
for tx in txns: for tx in txns:
is_relevant, is_mine, v, fee = self.wallet.get_wallet_delta(tx) tx_wallet_delta = self.wallet.get_wallet_delta(tx)
if not is_relevant: if not tx_wallet_delta.is_relevant:
continue continue
self.notify(_("New transaction: {}").format(self.format_amount_and_units(v))) self.notify(_("New transaction: {}").format(self.format_amount_and_units(tx_wallet_delta.delta)))
def notify(self, message): def notify(self, message):
if self.tray: if self.tray:

26
electrum/wallet.py

@ -557,10 +557,11 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
"""Returns a map: pubkey -> (keystore, derivation_suffix)""" """Returns a map: pubkey -> (keystore, derivation_suffix)"""
return {} return {}
def get_tx_info(self, tx) -> TxWalletDetails: def get_tx_info(self, tx: Transaction) -> TxWalletDetails:
is_relevant, is_mine, v, fee = self.get_wallet_delta(tx) tx_wallet_delta = self.get_wallet_delta(tx)
if fee is None and isinstance(tx, PartialTransaction): is_relevant = tx_wallet_delta.is_relevant
fee = tx.get_fee() is_any_input_ismine = tx_wallet_delta.is_any_input_ismine
fee = tx_wallet_delta.fee
exp_n = None exp_n = None
can_broadcast = False can_broadcast = False
can_bump = False can_bump = False
@ -596,28 +597,27 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
size = tx.estimated_size() size = tx.estimated_size()
fee_per_byte = fee / size fee_per_byte = fee / size
exp_n = self.config.fee_to_depth(fee_per_byte) exp_n = self.config.fee_to_depth(fee_per_byte)
can_bump = is_mine and not tx.is_final() can_bump = is_any_input_ismine and not tx.is_final()
can_dscancel = (is_mine and not tx.is_final() can_dscancel = (is_any_input_ismine and not tx.is_final()
and not all([self.is_mine(txout.address) for txout in tx.outputs()])) and not all([self.is_mine(txout.address) for txout in tx.outputs()]))
else: else:
status = _('Local') status = _('Local')
can_broadcast = self.network is not None can_broadcast = self.network is not None
can_bump = is_mine and not tx.is_final() can_bump = is_any_input_ismine and not tx.is_final()
else: else:
status = _("Signed") status = _("Signed")
can_broadcast = self.network is not None can_broadcast = self.network is not None
else: else:
assert isinstance(tx, PartialTransaction)
s, r = tx.signature_count() s, r = tx.signature_count()
status = _("Unsigned") if s == 0 else _('Partially signed') + ' (%d/%d)'%(s,r) status = _("Unsigned") if s == 0 else _('Partially signed') + ' (%d/%d)'%(s,r)
if is_relevant: if is_relevant:
if is_mine: if tx_wallet_delta.is_all_input_ismine:
if fee is not None: assert fee is not None
amount = v + fee amount = tx_wallet_delta.delta + fee
else:
amount = v
else: else:
amount = v amount = tx_wallet_delta.delta
else: else:
amount = None amount = None

Loading…
Cancel
Save