electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit c4e9afa019e74a46585ca9af3eef433f19f338b9
parent 4f3a28b87e244a158d6a038fdf791c480f583418
Author: SomberNight <somber.night@protonmail.com>
Date:   Thu, 21 Jan 2021 18:05:48 +0100

wallet.remove_transaction: also rm dependent/child txs

Main motivation is that I often use wallet.remove_transaction
from the Qt console, and would find this behaviour more intuitive.
Note that previously if one were to call this on a tx with children,
the crash reporter would appear with "wallet.get_history() failed balance sanity-check".

related: https://github.com/spesmilo/electrum/issues/6960#issuecomment-764716533

Diffstat:
Melectrum/address_synchronizer.py | 19++++++++++++++-----
Melectrum/commands.py | 5+----
Melectrum/gui/kivy/uix/dialogs/tx_dialog.py | 10++++------
Melectrum/gui/qt/history_list.py | 10++++------
4 files changed, 23 insertions(+), 21 deletions(-)

diff --git a/electrum/address_synchronizer.py b/electrum/address_synchronizer.py @@ -292,11 +292,7 @@ class AddressSynchronizer(Logger): # this is a local tx that conflicts with non-local txns; drop. return False # keep this txn and remove all conflicting - to_remove = set() - to_remove |= conflicting_txns - for conflicting_tx_hash in conflicting_txns: - to_remove |= self.get_depending_transactions(conflicting_tx_hash) - for tx_hash2 in to_remove: + for tx_hash2 in conflicting_txns: self.remove_transaction(tx_hash2) # add inputs def add_value_from_prev_output(): @@ -342,6 +338,19 @@ class AddressSynchronizer(Logger): return True def remove_transaction(self, tx_hash: str) -> None: + """Removes a transaction AND all its dependents/children + from the wallet history. + """ + with self.lock, self.transaction_lock: + to_remove = {tx_hash} + to_remove |= self.get_depending_transactions(tx_hash) + for txid in to_remove: + self._remove_transaction(txid) + + def _remove_transaction(self, tx_hash: str) -> None: + """Removes a single transaction from the wallet history, and attempts + to undo all effects of the tx (spending inputs, creating outputs, etc). + """ def remove_from_spent_outpoints(): # undo spends in spent_outpoints if tx is not None: diff --git a/electrum/commands.py b/electrum/commands.py @@ -933,13 +933,10 @@ class Commands: if not is_hash256_str(txid): raise Exception(f"{repr(txid)} is not a txid") height = wallet.get_tx_height(txid).height - to_delete = {txid} if height != TX_HEIGHT_LOCAL: raise Exception(f'Only local transactions can be removed. ' f'This tx has height: {height} != {TX_HEIGHT_LOCAL}') - to_delete |= wallet.get_depending_transactions(txid) - for tx_hash in to_delete: - wallet.remove_transaction(tx_hash) + wallet.remove_transaction(txid) wallet.save_db() @command('wn') diff --git a/electrum/gui/kivy/uix/dialogs/tx_dialog.py b/electrum/gui/kivy/uix/dialogs/tx_dialog.py @@ -348,17 +348,15 @@ class TxDialog(Factory.Popup): def remove_local_tx(self): txid = self.tx.txid() - to_delete = {txid} - to_delete |= self.wallet.get_depending_transactions(txid) + num_child_txs = len(self.wallet.get_depending_transactions(txid)) question = _("Are you sure you want to remove this transaction?") - if len(to_delete) > 1: + if num_child_txs > 0: question = (_("Are you sure you want to remove this transaction and {} child transactions?") - .format(len(to_delete) - 1)) + .format(num_child_txs)) def on_prompt(b): if b: - for tx in to_delete: - self.wallet.remove_transaction(tx) + self.wallet.remove_transaction(txid) self.wallet.save_db() self.app._trigger_update_wallet() # FIXME private... self.dismiss() diff --git a/electrum/gui/qt/history_list.py b/electrum/gui/qt/history_list.py @@ -710,17 +710,15 @@ class HistoryList(MyTreeView, AcceptFileDragDrop): menu.exec_(self.viewport().mapToGlobal(position)) def remove_local_tx(self, tx_hash: str): - to_delete = {tx_hash} - to_delete |= self.wallet.get_depending_transactions(tx_hash) + num_child_txs = len(self.wallet.get_depending_transactions(tx_hash)) question = _("Are you sure you want to remove this transaction?") - if len(to_delete) > 1: + if num_child_txs > 0: question = (_("Are you sure you want to remove this transaction and {} child transactions?") - .format(len(to_delete) - 1)) + .format(num_child_txs)) if not self.parent.question(msg=question, title=_("Please confirm")): return - for tx in to_delete: - self.wallet.remove_transaction(tx) + self.wallet.remove_transaction(tx_hash) self.wallet.save_db() # need to update at least: history_list, utxo_list, address_list self.parent.need_update.set()