From 0df6786f508a97830c9688cd42807907467a7bf3 Mon Sep 17 00:00:00 2001 From: svatantrya Date: Mon, 9 Feb 2026 11:58:07 -0400 Subject: [PATCH] dust bug fix --- heirs.py | 53 ++++++++++++++++++++++++++++------------ qt.py | 32 ++++++++++++++++++------ will.py | 63 +++++++++++++++++++++++++++++++++--------------- willexecutors.py | 4 +-- 4 files changed, 108 insertions(+), 44 deletions(-) diff --git a/heirs.py b/heirs.py index 1408a36..e2a8da6 100644 --- a/heirs.py +++ b/heirs.py @@ -29,7 +29,8 @@ from electrum.util import ( from .util import Util from .willexecutors import Willexecutors - +from electrum.util import BitcoinException +from electrum import constants if TYPE_CHECKING: from .simple_config import SimpleConfig from .wallet_db import WalletDB @@ -41,6 +42,7 @@ HEIR_ADDRESS = 0 HEIR_AMOUNT = 1 HEIR_LOCKTIME = 2 HEIR_REAL_AMOUNT = 3 +HEIR_DUST_AMOUNT = 4 TRANSACTION_LABEL = "inheritance transaction" @@ -88,6 +90,7 @@ def prepare_transactions(locktimes, available_utxos, fees, wallet): txsout = {} locktime, _ = Util.get_lowest_locktimes(locktimes) if not locktime: + _logger.info("prepare transactions, no locktime") return locktime = locktime[0] @@ -101,22 +104,25 @@ def prepare_transactions(locktimes, available_utxos, fees, wallet): outputs = [] paid_heirs = {} for name, heir in heirs.items(): - - try: - if len(heir) > HEIR_REAL_AMOUNT: + if len(heir) > HEIR_REAL_AMOUNT and not "DUST" in str(heir[HEIR_REAL_AMOUNT]): + try: real_amount = heir[HEIR_REAL_AMOUNT] - out_amount += real_amount - description += f"{name}\n" - paid_heirs[name] = heir outputs.append( PartialTxOutput.from_address_and_value( heir[HEIR_ADDRESS], real_amount ) ) - else: + out_amount += real_amount + description += f"{name}\n" + except BitcoinException as e: + _logger.info("exception decoding output{} - {}".format(type(e),e)) + heir[HEIR_REAL_AMOUNT]=e + + except Exception as e: + heir[HEIR_REAL_AMOUNT]=e + _logger.info(f"error preparing transactions {e}") pass - except Exception as e: - pass + paid_heirs[name] = heir in_amount = 0.0 used_utxos = [] @@ -125,12 +131,14 @@ def prepare_transactions(locktimes, available_utxos, fees, wallet): value = utxo.value_sats() in_amount += value used_utxos.append(utxo) - if in_amount > out_amount: + if in_amount >= out_amount: break except IndexError as e: + _logger.info(f"error preparing transactions index error {e} {in_amount}, {out_amount}") pass if int(in_amount) < int(out_amount): + _logger.info("error preparing transactions in_amount < out_amount ({} < {}) ") break heirsvalue = out_amount change = get_change_output(wallet, in_amount, out_amount, fee) @@ -263,9 +271,10 @@ def get_change_output(wallet, in_amount, out_amount, fee): class Heirs(dict, Logger): - def __init__(self, db: "WalletDB"): + def __init__(self, wallet): Logger.__init__(self) - self.db = db + self.db = wallet.db + self.wallet=wallet d = self.db.get("heirs", {}) try: self.update(d) @@ -327,6 +336,10 @@ class Heirs(dict, Logger): if value > wallet.dust_threshold(): heir_list[key].insert(HEIR_REAL_AMOUNT, value) amount += value + else: + heir_list[key].insert(HEIR_REAL_AMOUNT,f"DUST: {value}") + heir_list[key].insert(HEIR_DUST_AMOUNT,value) + _logger.info(f"{key}, {value} is dust will be ignored") except Exception as e: raise e @@ -449,6 +462,7 @@ class Heirs(dict, Logger): ): Heirs._validate(self) if len(self) <= 0: + _logger.info("while building transactions there was no heirs") return balance = 0.0 len_utxo_set = 0 @@ -464,6 +478,7 @@ class Heirs(dict, Logger): len_utxo_set += 1 available_utxos.append(utxo) if len_utxo_set == 0: + _logger.info("no usable utxos") return j = -2 willexecutorsitems = list(willexecutors.items()) @@ -505,6 +520,7 @@ class Heirs(dict, Logger): if not txs: return {} except Exception as e: + _logger.info(f"error preparing transactions{e}") try: if "w!ll3x3c" in e.heirname: Willexecutors.is_selected(willexecutors[w], False) @@ -632,7 +648,7 @@ class Heirs(dict, Logger): return None def validate_address(address): - if not bitcoin.is_address(address): + if not bitcoin.is_address(address,net=constants.net): raise NotAnAddress(f"not an address,{address}") return address @@ -661,12 +677,14 @@ class Heirs(dict, Logger): return (address, amount, locktime) def _validate(data, timestamp_to_check=False): + for k, v in list(data.items()): if k == "heirs": - return Heirs._validate(v) + return Heirs._validate(v,timestamp_to_check) try: - Heirs.validate_heir(k, v) + Heirs.validate_heir(k, v, timestamp_to_check) except Exception as e: + _logger.info(f"exception heir removed {e}") data.pop(k) return data @@ -685,3 +703,6 @@ class LocktimeNotValid(ValueError): class HeirExpiredException(LocktimeNotValid): pass + +class HeirAmountIsDustException(Exception): + pass diff --git a/qt.py b/qt.py index 13bb0bf..acfeb03 100644 --- a/qt.py +++ b/qt.py @@ -145,7 +145,7 @@ from electrum.util import ( from .bal import BalPlugin from .bal_resources import DEFAULT_ICON, icon_path -from .heirs import Heirs +from .heirs import (Heirs,HEIR_REAL_AMOUNT,HEIR_DUST_AMOUNT) from .util import Util from .will import ( AmountException, @@ -473,7 +473,7 @@ class BalWindow(Logger): self.bal_plugin, update=False, bal_window=self ) if not self.heirs: - self.heirs = Heirs._validate(Heirs(self.wallet.db)) + self.heirs = Heirs._validate(Heirs(self.wallet)) if not self.will: self.will = self.wallet.db.get_dict("will") Util.fix_will_tx_fees(self.will) @@ -655,6 +655,7 @@ class BalWindow(Logger): Will.normalize_will(self.willitems, self.wallet) def build_will(self, ignore_duplicate=True, keep_original=True): + _logger.debug("building will...") will = {} willtodelete = [] willtoappend = {} @@ -670,6 +671,9 @@ class BalWindow(Logger): if Willexecutors.is_selected(w): f = True if not f: + _logger.error( + "No Will-Executor or backup transaction selected" + ) raise NoWillExecutorNotPresent( "No Will-Executor or backup transaction selected" ) @@ -680,7 +684,8 @@ class BalWindow(Logger): None, self.date_to_check, ) - self.logger.info(txs) + + self.logger.info(f"txs built: {txs}") creation_time = time.time() if txs: for txid in txs: @@ -699,7 +704,13 @@ class BalWindow(Logger): tx["txchildren"] = [] will[txid] = WillItem(tx, _id=txid, wallet=self.wallet) self.update_will(will) + else: + self.logger.info("No transactions was built") + self.logger.info(f"will-settings: {self.will_settings}") + self.logger.info(f"date_to_check:{self.date_to_check}") + self.logger.info(f"heirs: {self.heirs}") except Exception as e: + self.logger.info(f"Exception build_will: {e}") raise e pass return self.willitems @@ -2110,7 +2121,7 @@ class BalBuildWillDialog(BalDialog): _logger.debug("no heirs") self.msg_set_checking("No Heirs") except NotCompleteWillException as e: - _logger.debug("not complete", e) + _logger.debug(f"not complete {e} true") message = False have_to_build = True if isinstance(e, HeirChangeException): @@ -2124,7 +2135,7 @@ class BalBuildWillDialog(BalDialog): elif isinstance(e, HeirNotFoundException): message = "Heir not found" if message: - _logger.debug("message") + _logger.debug(f"message: {message}") self.msg_set_checking(message) else: self.msg_set_checking("New") @@ -2140,6 +2151,15 @@ class BalBuildWillDialog(BalDialog): except Exception as e: self.msg_set_building(self.msg_error(e)) return False, None + + + excluded_heirs=[] + for wid in Will.only_valid(self.bal_window.willitems): + heirs=self.bal_window.willitems[wid].heirs + for hid,heir in heirs.items(): + if "DUST" in str(heir[HEIR_REAL_AMOUNT]): + self.msg_set_status(f"{hid},{heir[HEIR_DUST_AMOUNT]} is DUST",None,f"Excluded from will {wid}") + have_to_sign = False for wid in Will.only_valid(self.bal_window.willitems): if not self.bal_window.willitems[wid].get_status("COMPLETE"): @@ -2399,8 +2419,6 @@ class BalBuildWillDialog(BalDialog): self.password = self.bal_window.get_wallet_password(msg, parent=self) def msg_edit_row(self, line, row=None): - _logger.debug(f"{row},{line}") - try: self.labels[row] = line except Exception: diff --git a/will.py b/will.py index 3e6584b..af52fdf 100644 --- a/will.py +++ b/will.py @@ -28,7 +28,6 @@ _logger = get_logger(__name__) class Will: - # return an array with the list of children def get_children(will, willid): out = [] for _id in will: @@ -153,6 +152,15 @@ class Will: inp._TxInput__value_sats = change.value return inp + """ + in questa situazione sono presenti due transazioni con id differente(quindi transazioni differenti) + per prima cosa controllo il locktime + se il locktime della nuova transazione e' maggiore del locktime della vecchia transazione, allora + confronto gli eredi, per locktime se corrispondono controllo i willexecutor + se hanno la stessa url ma le fee vecchie sono superiori alle fee nuove, allora anticipare. + + + """ def check_anticipate(ow: "WillItem", nw: "WillItem"): anticipate = Util.anticipate_locktime(ow.tx.locktime, days=1) if int(nw.tx.locktime) >= int(anticipate): @@ -256,7 +264,7 @@ class Will: to_append = {} new_inputs = Will.get_all_inputs(will, only_valid=True) for nid, nwi in will.items(): - if nwi.search_anticipate(new_inputs) or nwi.search_anticipate(old_inputs): + if nwi.search_anticipate(new_inputs): if nid != nwi.tx.txid(): redo = True to_delete.append(nid) @@ -266,6 +274,17 @@ class Will: Will.change_input( will, nid, i, outputs[i], new_inputs, to_delete, to_append ) + if nwi.search_anticipate(old_inputs): + if nid != nwi.tx.txid(): + redo = True + + to_delete.append(nid) + to_append[nwi.tx.txid()] = nwi + outputs = nwi.tx.outputs() + for i in range(0, len(outputs)): + Will.change_input( + will, nid, i, outputs[i], new_inputs, to_delete, to_append + ) for w in to_delete: try: @@ -275,8 +294,10 @@ class Will: for k, w in to_append.items(): will[k] = w if redo: + Will.search_anticipate_rec(will, old_inputs) + def update_will(old_will, new_will): all_old_inputs = Will.get_all_inputs(old_will, only_valid=True) all_inputs_min_locktime = Will.get_all_inputs_min_locktime(all_old_inputs) @@ -284,7 +305,6 @@ class Will: # check if the new input is already spent by other transaction # if it is use the same locktime, or anticipate. Will.search_anticipate_rec(new_will, all_old_inputs) - other_inputs = Will.get_all_inputs(old_will, {}) try: Will.normalize_will(new_will, others_inputs=other_inputs) @@ -419,20 +439,18 @@ class Will: # check if transactions are stil valid tecnically valid def check_invalidated(willtree, utxos_list, wallet): for wid, w in willtree.items(): - #if not w.father: - for inp in w.tx.inputs(): - inp_str = Util.utxo_to_str(inp) - if not inp_str in utxos_list: - if wallet: - height = Will.check_tx_height(w.tx, wallet) - if height < 0: - Will.set_invalidate(wid, willtree) - elif height == 0: - w.set_status("PENDING", True) - else: - w.set_status("CONFIRMED", True) - #else: - # print("father",w.father) + if not w.father or willtree[w.father].get_status("CONFIRMED") or willtree[w.father].get_status("PENDING"): + for inp in w.tx.inputs(): + inp_str = Util.utxo_to_str(inp) + if not inp_str in utxos_list: + if wallet: + height = Will.check_tx_height(w.tx, wallet) + if height < 0: + Will.set_invalidate(wid, willtree) + elif height == 0: + w.set_status("PENDING", True) + else: + w.set_status("CONFIRMED", True) def reflect_to_children(treeitem): if not treeitem.get_status("VALID"): @@ -632,7 +650,6 @@ class Will: class WillItem(Logger): - STATUS_DEFAULT = { "ANTICIPATED": ["Anticipated", False], "BROADCASTED": ["Broadcasted", False], @@ -662,7 +679,7 @@ class WillItem(Logger): if self.STATUS[status][1] == bool(value): return None - self.status += "." + ("NOT " if not value else "" + _(self.STATUS[status][0])) + self.status += "." + (("NOT " if not value else "") + _(self.STATUS[status][0])) self.STATUS[status][1] = bool(value) if value: if status in ["INVALIDATED", "REPLACED", "CONFIRMED", "PENDING"]: @@ -867,3 +884,11 @@ class PercAmountException(AmountException): class FixedAmountException(AmountException): pass + + + + + + +def test_check_invalidated(): + Will.check_invalidated(will, utxos_list, wallet) diff --git a/willexecutors.py b/willexecutors.py index 3a3686b..c785600 100644 --- a/willexecutors.py +++ b/willexecutors.py @@ -119,7 +119,7 @@ class Willexecutors: def send_request(method, url, data=None, *, timeout=10): network = Network.get_instance() if not network: - raise ErrorConnectingServer("You are offline.") + raise Exception("You are offline.") _logger.debug(f"<-- {method} {url} {data}") headers = {} headers["user-agent"] = f"BalPlugin v:{BalPlugin.version()}" @@ -171,7 +171,7 @@ class Willexecutors: out = True try: - _logger.debug(f"willexecutor['txs']") + _logger.debug(f"{willexecutor[url]}: {willexecutor['txs']}") if w := Willexecutors.send_request( "post", willexecutor["url"] + "/" + constants.net.NET_NAME + "/pushtxs",