fixed refresh and some minor bug about dust amounts and empty wallet

This commit is contained in:
2026-03-05 10:46:38 -04:00
parent c5ad5a61bb
commit ef0ab56de4
4 changed files with 127 additions and 76 deletions

126
heirs.py
View File

@@ -96,9 +96,9 @@ def prepare_transactions(locktimes, available_utxos, fees, wallet):
locktime = locktime[0] locktime = locktime[0]
heirs = locktimes[locktime] heirs = locktimes[locktime]
vero = True true = True
while vero: while true:
vero = False true = False
fee = fees.get(locktime, 0) fee = fees.get(locktime, 0)
out_amount = fee out_amount = fee
description = "" description = ""
@@ -118,12 +118,12 @@ def prepare_transactions(locktimes, available_utxos, fees, wallet):
out_amount += real_amount out_amount += real_amount
description += f"{name}\n" description += f"{name}\n"
except BitcoinException as e: except BitcoinException as e:
_logger.info("exception decoding output{} - {}".format(type(e), e)) _logger.info("exception decoding output {} - {}".format(type(e), e))
heir[HEIR_REAL_AMOUNT] = e heir[HEIR_REAL_AMOUNT] = e
except Exception as e: except Exception as e:
heir[HEIR_REAL_AMOUNT] = e heir[HEIR_REAL_AMOUNT] = e
_logger.info(f"error preparing transactions {e}") _logger.error(f"error preparing transactions: {e}")
pass pass
paid_heirs[name] = heir paid_heirs[name] = heir
@@ -138,15 +138,15 @@ def prepare_transactions(locktimes, available_utxos, fees, wallet):
break break
except IndexError as e: except IndexError as e:
_logger.info( _logger.error(
f"error preparing transactions index error {e} {in_amount}, {out_amount}" f"error preparing transactions index error {e} {in_amount}, {out_amount}"
) )
pass pass
if int(in_amount) < int(out_amount): if int(in_amount) < int(out_amount):
_logger.info( _logger.error(
"error preparing transactions in_amount < out_amount ({} < {}) " "error preparing transactions in_amount < out_amount ({} < {}) "
) )
break continue
heirsvalue = out_amount heirsvalue = out_amount
change = get_change_output(wallet, in_amount, out_amount, fee) change = get_change_output(wallet, in_amount, out_amount, fee)
if change: if change:
@@ -319,7 +319,7 @@ class Heirs(dict, Logger):
locktime = Util.parse_locktime_string(self[key][HEIR_LOCKTIME]) locktime = Util.parse_locktime_string(self[key][HEIR_LOCKTIME])
if locktime > from_locktime and not a or locktime <= from_locktime and a: if locktime > from_locktime and not a or locktime <= from_locktime and a:
locktimes[int(locktime)] = None locktimes[int(locktime)] = None
return locktimes.keys() return list(locktimes.keys())
def check_locktime(self): def check_locktime(self):
return False return False
@@ -333,6 +333,8 @@ class Heirs(dict, Logger):
column = HEIR_AMOUNT column = HEIR_AMOUNT
if real: if real:
column = HEIR_REAL_AMOUNT column = HEIR_REAL_AMOUNT
if "DUST" in str(v[column]):
column = HEIR_DUST_AMOUNT
value = int( value = int(
math.floor( math.floor(
total_balance total_balance
@@ -366,27 +368,32 @@ class Heirs(dict, Logger):
fixed_amount = 0.0 fixed_amount = 0.0
percent_heirs = {} percent_heirs = {}
percent_amount = 0.0 percent_amount = 0.0
fixed_amount_with_dust =0.0
for key in self.keys(): for key in self.keys():
try: try:
cmp = ( cmp = (
Util.parse_locktime_string(self[key][HEIR_LOCKTIME]) - from_locktime Util.parse_locktime_string(self[key][HEIR_LOCKTIME]) - from_locktime
) )
if cmp <= 0: if cmp <= 0:
_logger.debug("cmp < 0 {} {} {} ".format(cmp, key, self[key][HEIR_LOCKTIME], from_locktime))
continue continue
if Util.is_perc(self[key][HEIR_AMOUNT]): if Util.is_perc(self[key][HEIR_AMOUNT]):
percent_amount += float(self[key][HEIR_AMOUNT][:-1]) percent_amount += float(self[key][HEIR_AMOUNT][:-1])
percent_heirs[key] = list(self[key]) percent_heirs[key] = list(self[key])
else: else:
heir_amount = int(math.floor(float(self[key][HEIR_AMOUNT]))) heir_amount = int(math.floor(float(self[key][HEIR_AMOUNT])))
fixed_amount_with_dust += heir_amount
fixed_heirs[key] = list(self[key])
if heir_amount > dust_threshold: if heir_amount > dust_threshold:
fixed_amount += heir_amount fixed_amount += heir_amount
fixed_heirs[key] = list(self[key])
fixed_heirs[key].insert(HEIR_REAL_AMOUNT, heir_amount) fixed_heirs[key].insert(HEIR_REAL_AMOUNT, heir_amount)
else: else:
pass fixed_heirs[key] = list(self[key])
fixed_heirs[key].insert(HEIR_REAL_AMOUNT, f"DUST: {heir_amount}")
fixed_heirs[key].insert(HEIR_DUST_AMOUNT, heir_amount)
except Exception as e: except Exception as e:
_logger.error(e) _logger.error(e)
return fixed_heirs, fixed_amount, percent_heirs, percent_amount return fixed_heirs, fixed_amount, percent_heirs, percent_amount, fixed_amount_with_dust
def prepare_lists( def prepare_lists(
self, balance, total_fees, wallet, willexecutor=False, from_locktime=0 self, balance, total_fees, wallet, willexecutor=False, from_locktime=0
@@ -419,9 +426,13 @@ class Heirs(dict, Logger):
), ),
heir_list.update(willexecutors) heir_list.update(willexecutors)
newbalance -= willexecutors_amount newbalance -= willexecutors_amount
fixed_heirs, fixed_amount, percent_heirs, percent_amount = ( if newbalance<0:
raise WillExecutorFeeException(willexecutor)
a=list(self.fixed_percent_lists_amount(from_locktime, wallet.dust_threshold()))
fixed_heirs, fixed_amount, percent_heirs, percent_amount,fixed_amount_with_dust = (
self.fixed_percent_lists_amount(from_locktime, wallet.dust_threshold()) self.fixed_percent_lists_amount(from_locktime, wallet.dust_threshold())
) )
if fixed_amount > newbalance: if fixed_amount > newbalance:
fixed_amount = self.normalize_perc( fixed_amount = self.normalize_perc(
fixed_heirs, newbalance, fixed_amount, wallet fixed_heirs, newbalance, fixed_amount, wallet
@@ -442,7 +453,7 @@ class Heirs(dict, Logger):
if newbalance > 0: if newbalance > 0:
newbalance += fixed_amount newbalance += fixed_amount
fixed_amount = self.normalize_perc( fixed_amount = self.normalize_perc(
fixed_heirs, newbalance, fixed_amount, wallet, real=True fixed_heirs, newbalance, fixed_amount_with_dust, wallet, real=True
) )
newbalance -= fixed_amount newbalance -= fixed_amount
heir_list.update(fixed_heirs) heir_list.update(fixed_heirs)
@@ -509,7 +520,7 @@ class Heirs(dict, Logger):
break break
fees = {} fees = {}
i = 0 i = 0
while True: while i<10:
txs = {} txs = {}
redo = False redo = False
i += 1 i += 1
@@ -517,47 +528,55 @@ class Heirs(dict, Logger):
for fee in fees: for fee in fees:
total_fees += int(fees[fee]) total_fees += int(fees[fee])
newbalance = balance newbalance = balance
locktimes, onlyfixed = self.prepare_lists(
balance, total_fees, wallet, willexecutor, from_locktime
)
try: try:
txs = prepare_transactions( locktimes, onlyfixed = self.prepare_lists(
locktimes, available_utxos[:], fees, wallet balance, total_fees, wallet, willexecutor, from_locktime
) )
if not txs: except WillExecutorFeeException as e:
return {} i=10
except Exception as e: continue
_logger.info(f"error preparing transactions{e}") if locktimes:
try: try:
if "w!ll3x3c" in e.heirname: txs = prepare_transactions(
Willexecutors.is_selected(willexecutors[w], False) locktimes, available_utxos[:], fees, wallet
break )
except: if not txs:
raise e return {}
total_fees = 0 except Exception as e:
total_fees_real = 0 _logger.error(f"build transactions: error preparing transactions: {e}")
total_in = 0 try:
for txid, tx in txs.items(): if "w!ll3x3c" in e.heirname:
tx.willexecutor = willexecutor Willexecutors.is_selected(willexecutors[w], False)
fee = tx.estimated_size() * tx_fees break
txs[txid].tx_fees = tx_fees except:
total_fees += fee raise e
total_fees_real += tx.get_fee() total_fees = 0
total_in += tx.input_value() total_fees_real = 0
rfee = tx.input_value() - tx.output_value() total_in = 0
if rfee < fee or rfee > fee + wallet.dust_threshold(): for txid, tx in txs.items():
redo = True tx.willexecutor = willexecutor
oldfees = fees.get(tx.my_locktime, 0) fee = tx.estimated_size() * tx_fees
fees[tx.my_locktime] = fee txs[txid].tx_fees = tx_fees
total_fees += fee
total_fees_real += tx.get_fee()
total_in += tx.input_value()
rfee = tx.input_value() - tx.output_value()
if rfee < fee or rfee > fee + wallet.dust_threshold():
redo = True
oldfees = fees.get(tx.my_locktime, 0)
fees[tx.my_locktime] = fee
if balance - total_in > wallet.dust_threshold(): if balance - total_in > wallet.dust_threshold():
redo = True redo = True
if not redo: if not redo:
break break
if i >= 10: if i >= 10:
break
else:
_logger.info(f"no locktimes for willexecutor {willexecutor} skipped")
break break
alltxs.update(txs) alltxs.update(txs)
return alltxs return alltxs
def get_transactions( def get_transactions(
@@ -714,3 +733,10 @@ class HeirExpiredException(LocktimeNotValid):
class HeirAmountIsDustException(Exception): class HeirAmountIsDustException(Exception):
pass pass
class NoHeirsException(Exception):
pass
class WillExecutorFeeException(Exception):
def __init__(self,willexecutor):
self.willexecutor=willexecutor
def __str__(self):
return "WillExecutorFeeException: {} fee:{}".format(self.willexecutor['url'],self.willexecutor['base_fee'])

61
qt.py
View File

@@ -500,6 +500,7 @@ class BalWindow(Logger):
self.logger.debug("not_will_settings {}".format(self.will_settings)) self.logger.debug("not_will_settings {}".format(self.will_settings))
self.bal_plugin.validate_will_settings(self.will_settings) self.bal_plugin.validate_will_settings(self.will_settings)
self.heir_list.update_will_settings() self.heir_list.update_will_settings()
self.heir_list.update()
def init_wizard(self): def init_wizard(self):
wizard_dialog = BalWizardDialog(self) wizard_dialog = BalWizardDialog(self)
@@ -710,6 +711,7 @@ class BalWindow(Logger):
self.logger.info(f"will-settings: {self.will_settings}") self.logger.info(f"will-settings: {self.will_settings}")
self.logger.info(f"date_to_check:{self.date_to_check}") self.logger.info(f"date_to_check:{self.date_to_check}")
self.logger.info(f"heirs: {self.heirs}") self.logger.info(f"heirs: {self.heirs}")
return {}
except Exception as e: except Exception as e:
self.logger.info(f"Exception build_will: {e}") self.logger.info(f"Exception build_will: {e}")
raise e raise e
@@ -2036,6 +2038,9 @@ class bal_checkbox(QCheckBox):
class BalBuildWillDialog(BalDialog): class BalBuildWillDialog(BalDialog):
updatemessage = pyqtSignal() updatemessage = pyqtSignal()
COLOR_WARNING='#cfa808'
COLOR_ERROR='#ff0000'
COLOR_OK='#05ad05'
def __init__(self, bal_window, parent=None): def __init__(self, bal_window, parent=None):
if not parent: if not parent:
@@ -2092,25 +2097,24 @@ class BalBuildWillDialog(BalDialog):
self.bal_window.window.wallet.dust_threshold(), self.bal_window.window.wallet.dust_threshold(),
) )
_logger.debug("variables ok") _logger.debug("variables ok")
self.msg_set_status("checking variables:", varrow, "Ok") self.msg_set_status("checking variables:", varrow, "Ok",self.COLOR_OK)
except AmountException: except AmountException:
self.msg_set_status( self.msg_set_status(
"checking variables", "checking variables",
varrow, varrow,
'<font color="#ff0000">' _(
+ _(
"In the inheritance process, " "In the inheritance process, "
+ "the entire wallet will always be fully emptied. \n" + "the entire wallet will always be fully emptied. \n"
+ "Your settings require an adjustment of the amounts" + "Your settings require an adjustment of the amounts"
) ),
+ "</font>", self.COLOR_WARNING
) )
self.msg_set_checking() self.msg_set_checking()
have_to_build = False have_to_build = False
try: try:
self.bal_window.check_will() self.bal_window.check_will()
self.msg_set_checking("Ok") self.msg_set_checking(self.msg_ok())
except WillExpiredException: except WillExpiredException:
_logger.debug("expired") _logger.debug("expired")
self.msg_set_checking("Expired") self.msg_set_checking("Expired")
@@ -2144,11 +2148,23 @@ class BalBuildWillDialog(BalDialog):
if have_to_build: if have_to_build:
self.msg_set_building() self.msg_set_building()
try: try:
self.bal_window.build_will() if not self.bal_window.build_will():
self.msg_set_status(
_("Balance is too low. No transaction was built"),None,_("Skipped"),self.COLOR_ERROR
)
self.bal_window.check_will() self.bal_window.check_will()
for wid in Will.only_valid(self.bal_window.willitems): for wid in Will.only_valid(self.bal_window.willitems):
self.bal_window.wallet.set_label(wid, "BAL Transaction") self.bal_window.wallet.set_label(wid, "BAL Transaction")
self.msg_set_building("Ok") self.msg_set_building(self.msg_ok())
except WillExecutorNotPresent as e:
self.msg_set_status(
_("Will-Executor excluded"),
None,
_("Skipped"),
self.COLOR_ERROR
)
except Exception as e: except Exception as e:
self.msg_set_building(self.msg_error(e)) self.msg_set_building(self.msg_error(e))
return False, None return False, None
@@ -2159,9 +2175,10 @@ class BalBuildWillDialog(BalDialog):
for hid, heir in heirs.items(): for hid, heir in heirs.items():
if "DUST" in str(heir[HEIR_REAL_AMOUNT]): if "DUST" in str(heir[HEIR_REAL_AMOUNT]):
self.msg_set_status( self.msg_set_status(
f'<font color="red">{hid},{heir[HEIR_DUST_AMOUNT]} is DUST', f'{hid},{heir[HEIR_DUST_AMOUNT]} is DUST',
None, None,
f"Excluded from will {wid}</font>", f"Excluded from will {wid}",
self.COLOR_WARNING
) )
have_to_sign = False have_to_sign = False
@@ -2197,7 +2214,7 @@ class BalBuildWillDialog(BalDialog):
txid = self.network.run_from_another_thread( txid = self.network.run_from_another_thread(
self.network.broadcast_transaction(tx, timeout=120), timeout=120 self.network.broadcast_transaction(tx, timeout=120), timeout=120
) )
self.msg_set_invalidating("Ok") self.msg_set_invalidating(self.msg_ok())
if not txid: if not txid:
_logger.debug(f"should not be none txid: {txid}") _logger.debug(f"should not be none txid: {txid}")
@@ -2354,7 +2371,7 @@ class BalBuildWillDialog(BalDialog):
for txid, tx in txs.items(): for txid, tx in txs.items():
self.bal_window.willitems[txid].tx = copy.deepcopy(tx) self.bal_window.willitems[txid].tx = copy.deepcopy(tx)
self.bal_window.save_willitems() self.bal_window.save_willitems()
self.msg_set_signing("Ok") self.msg_set_signing(self.msg_ok())
except Exception as e: except Exception as e:
self.msg_set_signing(self.msg_error(e)) self.msg_set_signing(self.msg_error(e))
@@ -2369,11 +2386,11 @@ class BalBuildWillDialog(BalDialog):
else: else:
try: try:
self.loop_push() self.loop_push()
self.msg_set_pushing("Ok") self.msg_set_pushing(self.msg_ok())
except Exception as e: except Exception as e:
self.msg_set_pushing(self.msg_error(e)) self.msg_set_pushing(self.msg_error(e))
self.msg_edit_row("Ok") self.msg_edit_row(self.msg_ok())
self.wait(5) self.wait(5)
def on_error_phase1(self, error): def on_error_phase1(self, error):
@@ -2413,13 +2430,20 @@ class BalBuildWillDialog(BalDialog):
self.wait_row = self.msg_edit_row(f"Please wait {status}secs", self.wait_row) self.wait_row = self.msg_edit_row(f"Please wait {status}secs", self.wait_row)
def msg_error(self, e): def msg_error(self, e):
return "Error: {}".format(e) return "<font color='{}'>{}</font>".format(self.COLOR_ERROR,e)
def msg_set_status(self, msg, row=None, status=None): def msg_ok(self, e="Ok"):
return "<font color='{}'>{}</font>".format(self.COLOR_OK,e)
def msg_warning(self, e):
return "<font color='{}'>{}</font".format(self.COLOR_WARNING,e)
def msg_set_status(self, msg, row=None, status=None,color="#000000"):
status = "Wait" if status is None else status status = "Wait" if status is None else status
line = "{}:\t{}".format(_(msg), status) line = "<font color={}>{}:\t{}</font>".format(color,_(msg), status)
return self.msg_edit_row(line, row) return self.msg_edit_row(line, row)
def ask_password(self, msg=None): def ask_password(self, msg=None):
self.password = self.bal_window.get_wallet_password(msg, parent=self) self.password = self.bal_window.get_wallet_password(msg, parent=self)
@@ -2616,7 +2640,6 @@ class HeirList(MyTreeView, MessageBoxMixin):
set_current = QPersistentModelIndex(idx) set_current = QPersistentModelIndex(idx)
self.set_current_idx(set_current) self.set_current_idx(set_current)
# FIXME refresh loses sort order; so set "default" here: # FIXME refresh loses sort order; so set "default" here:
self.repaint()
self.filter() self.filter()
run_hook("update_heirs_tab", self) run_hook("update_heirs_tab", self)
self.update_will_settings() self.update_will_settings()
@@ -2949,7 +2972,7 @@ class PreviewList(MyTreeView):
display = QPushButton(_("Display")) display = QPushButton(_("Display"))
display.clicked.connect(self.bal_window.preview_modal_dialog) display.clicked.connect(self.bal_window.preview_modal_dialog)
display = QPushButton(_("refresh")) display = QPushButton(_("Refresh"))
display.clicked.connect(self.check) display.clicked.connect(self.check)
widget = QWidget() widget = QWidget()

View File

@@ -84,9 +84,11 @@ class Util:
if Util.is_perc(amount): if Util.is_perc(amount):
return amount return amount
else: else:
num = 8 - decimal_point basestr = "{{:0.{}f}}".format(decimal_point)
basestr = "{{:0{}.{}f}}".format(num, num) try:
return "{:08.8f}".format(float(amount) / pow(10, decimal_point)) return basestr.format(float(amount) / pow(10, decimal_point))
except:
return str(amount)
def is_perc(value): def is_perc(value):
try: try:

View File

@@ -470,7 +470,7 @@ class Will:
Will.reflect_to_children(wc) Will.reflect_to_children(wc)
def check_amounts(heirs, willexecutors, all_utxos, timestamp_to_check, dust): def check_amounts(heirs, willexecutors, all_utxos, timestamp_to_check, dust):
fixed_heirs, fixed_amount, perc_heirs, perc_amount = ( fixed_heirs, fixed_amount, perc_heirs, perc_amount,fixed_amount_with_dust = (
heirs.fixed_percent_lists_amount(timestamp_to_check, dust, reverse=True) heirs.fixed_percent_lists_amount(timestamp_to_check, dust, reverse=True)
) )
wallet_balance = 0 wallet_balance = 0
@@ -620,9 +620,9 @@ class Will:
heirs_found[wheir] = count + 1 heirs_found[wheir] = count + 1
else: else:
_logger.debug( _logger.debug(
"heir not present transaction is not valid:", wid, w f"heir not present transaction is not valid:{wheir} {wid}, {w}"
) )
continue
if willexecutor := w.we: if willexecutor := w.we:
count = willexecutors_found.get(willexecutor["url"], 0) count = willexecutors_found.get(willexecutor["url"], 0)
if Util.cmp_willexecutor( if Util.cmp_willexecutor(
@@ -634,6 +634,7 @@ class Will:
no_willexecutor += 1 no_willexecutor += 1
count_heirs = 0 count_heirs = 0
for h in heirs: for h in heirs:
if Util.parse_locktime_string(heirs[h][2]) >= check_date: if Util.parse_locktime_string(heirs[h][2]) >= check_date:
count_heirs += 1 count_heirs += 1
if not h in heirs_found: if not h in heirs_found:
@@ -643,7 +644,6 @@ class Will:
raise NoHeirsException("there are not valid heirs") raise NoHeirsException("there are not valid heirs")
if self_willexecutor and no_willexecutor == 0: if self_willexecutor and no_willexecutor == 0:
raise NoWillExecutorNotPresent("Backup tx") raise NoWillExecutorNotPresent("Backup tx")
for url, we in willexecutors.items(): for url, we in willexecutors.items():
if Willexecutors.is_selected(we): if Willexecutors.is_selected(we):
if not url in willexecutors_found: if not url in willexecutors_found: