This commit is contained in:
2026-02-09 12:01:06 -04:00
parent 0df6786f50
commit 609db44c1a
5 changed files with 129 additions and 83 deletions

3
bal.py
View File

@@ -1,4 +1,5 @@
import os import os
# import random # import random
# import zipfile as zipfile_lib # import zipfile as zipfile_lib
@@ -18,7 +19,6 @@ json_db.register_dict("will", dict, None)
json_db.register_dict("will_settings", lambda x: x, None) json_db.register_dict("will_settings", lambda x: x, None)
def get_will(x): def get_will(x):
try: try:
x["tx"] = tx_from_any(x["tx"]) x["tx"] = tx_from_any(x["tx"])
@@ -149,4 +149,3 @@ class BalPlugin(BasePlugin):
def default_will_settings(self): def default_will_settings(self):
return {"baltx_fees": 100, "threshold": "180d", "locktime": "1y"} return {"baltx_fees": 100, "threshold": "180d", "locktime": "1y"}

View File

@@ -31,6 +31,7 @@ from .util import Util
from .willexecutors import Willexecutors from .willexecutors import Willexecutors
from electrum.util import BitcoinException from electrum.util import BitcoinException
from electrum import constants from electrum import constants
if TYPE_CHECKING: if TYPE_CHECKING:
from .simple_config import SimpleConfig from .simple_config import SimpleConfig
from .wallet_db import WalletDB from .wallet_db import WalletDB
@@ -104,7 +105,9 @@ def prepare_transactions(locktimes, available_utxos, fees, wallet):
outputs = [] outputs = []
paid_heirs = {} paid_heirs = {}
for name, heir in heirs.items(): for name, heir in heirs.items():
if len(heir) > HEIR_REAL_AMOUNT and not "DUST" in str(heir[HEIR_REAL_AMOUNT]): if len(heir) > HEIR_REAL_AMOUNT and not "DUST" in str(
heir[HEIR_REAL_AMOUNT]
):
try: try:
real_amount = heir[HEIR_REAL_AMOUNT] real_amount = heir[HEIR_REAL_AMOUNT]
outputs.append( outputs.append(
@@ -135,10 +138,14 @@ def prepare_transactions(locktimes, available_utxos, fees, wallet):
break break
except IndexError as e: except IndexError as e:
_logger.info(f"error preparing transactions index error {e} {in_amount}, {out_amount}") _logger.info(
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("error preparing transactions in_amount < out_amount ({} < {}) ") _logger.info(
"error preparing transactions in_amount < out_amount ({} < {}) "
)
break break
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)
@@ -704,5 +711,6 @@ class LocktimeNotValid(ValueError):
class HeirExpiredException(LocktimeNotValid): class HeirExpiredException(LocktimeNotValid):
pass pass
class HeirAmountIsDustException(Exception): class HeirAmountIsDustException(Exception):
pass pass

89
qt.py
View File

@@ -145,7 +145,7 @@ from electrum.util import (
from .bal import BalPlugin from .bal import BalPlugin
from .bal_resources import DEFAULT_ICON, icon_path from .bal_resources import DEFAULT_ICON, icon_path
from .heirs import (Heirs,HEIR_REAL_AMOUNT,HEIR_DUST_AMOUNT) from .heirs import Heirs, HEIR_REAL_AMOUNT, HEIR_DUST_AMOUNT
from .util import Util from .util import Util
from .will import ( from .will import (
AmountException, AmountException,
@@ -522,7 +522,6 @@ class BalWindow(Logger):
tab.is_shown_cv = shown_cv(True) tab.is_shown_cv = shown_cv(True)
return tab return tab
def new_heir_dialog(self, heir_key=None): def new_heir_dialog(self, heir_key=None):
heir = self.heirs.get(heir_key) heir = self.heirs.get(heir_key)
title = "New heir" title = "New heir"
@@ -671,9 +670,7 @@ class BalWindow(Logger):
if Willexecutors.is_selected(w): if Willexecutors.is_selected(w):
f = True f = True
if not f: if not f:
_logger.error( _logger.error("No Will-Executor or backup transaction selected")
"No Will-Executor or backup transaction selected"
)
raise NoWillExecutorNotPresent( raise NoWillExecutorNotPresent(
"No Will-Executor or backup transaction selected" "No Will-Executor or backup transaction selected"
) )
@@ -1768,7 +1765,9 @@ class BalWizardWEDownloadWidget(BalWizardWidget):
def on_success(willexecutors): def on_success(willexecutors):
self.bal_window.willexecutors.update(willexecutors) self.bal_window.willexecutors.update(willexecutors)
self.bal_window.ping_willexecutors(self.bal_window.willexecutors,False) self.bal_window.ping_willexecutors(
self.bal_window.willexecutors, False
)
if index < 1: if index < 1:
for we in self.bal_window.willexecutors: for we in self.bal_window.willexecutors:
if self.bal_window.willexecutors[we]["status"] == 200: if self.bal_window.willexecutors[we]["status"] == 200:
@@ -1839,7 +1838,6 @@ class BalWizardLocktimeAndFeeWidget(BalWizardWidget):
self.heir_locktime.get_locktime() self.heir_locktime.get_locktime()
if self.heir_locktime.get_locktime() if self.heir_locktime.get_locktime()
else "1y" else "1y"
) )
self.bal_window.bal_plugin.WILL_SETTINGS.set(self.bal_window.will_settings) self.bal_window.bal_plugin.WILL_SETTINGS.set(self.bal_window.will_settings)
@@ -1857,7 +1855,6 @@ class BalWizardLocktimeAndFeeWidget(BalWizardWidget):
) )
self.bal_window.bal_plugin.WILL_SETTINGS.set(self.bal_window.will_settings) self.bal_window.bal_plugin.WILL_SETTINGS.set(self.bal_window.will_settings)
self.heir_threshold.valueEdited.connect(on_heir_threshold) self.heir_threshold.valueEdited.connect(on_heir_threshold)
self.heir_tx_fees = QSpinBox(widget) self.heir_tx_fees = QSpinBox(widget)
@@ -2152,13 +2149,16 @@ class BalBuildWillDialog(BalDialog):
self.msg_set_building(self.msg_error(e)) self.msg_set_building(self.msg_error(e))
return False, None return False, None
excluded_heirs = [] excluded_heirs = []
for wid in Will.only_valid(self.bal_window.willitems): for wid in Will.only_valid(self.bal_window.willitems):
heirs = self.bal_window.willitems[wid].heirs heirs = self.bal_window.willitems[wid].heirs
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(f"<font color=\"red\">{hid},{heir[HEIR_DUST_AMOUNT]} is DUST",None,f"Excluded from will {wid}</font>") self.msg_set_status(
f'<font color="red">{hid},{heir[HEIR_DUST_AMOUNT]} is DUST',
None,
f"Excluded from will {wid}</font>",
)
have_to_sign = False have_to_sign = False
for wid in Will.only_valid(self.bal_window.willitems): for wid in Will.only_valid(self.bal_window.willitems):
@@ -2289,6 +2289,7 @@ class BalBuildWillDialog(BalDialog):
def on_error(self, error): def on_error(self, error):
_logger.error(error) _logger.error(error)
pass pass
def on_success_phase1(self, result): def on_success_phase1(self, result):
self.have_to_sign, tx = list(result) self.have_to_sign, tx = list(result)
_logger.debug("have to sign {}".format(self.have_to_sign)) _logger.debug("have to sign {}".format(self.have_to_sign))
@@ -2591,9 +2592,15 @@ class HeirList(MyTreeView, MessageBoxMixin):
items[self.Columns.NAME].setEditable(True) items[self.Columns.NAME].setEditable(True)
items[self.Columns.ADDRESS].setEditable(True) items[self.Columns.ADDRESS].setEditable(True)
items[self.Columns.AMOUNT].setEditable(True) items[self.Columns.AMOUNT].setEditable(True)
items[self.Columns.NAME].setData(key, self.ROLE_HEIR_KEY + self.Columns.NAME) items[self.Columns.NAME].setData(
items[self.Columns.ADDRESS].setData(key, self.ROLE_HEIR_KEY + self.Columns.ADDRESS) key, self.ROLE_HEIR_KEY + self.Columns.NAME
items[self.Columns.AMOUNT].setData(key, self.ROLE_HEIR_KEY + self.Columns.AMOUNT) )
items[self.Columns.ADDRESS].setData(
key, self.ROLE_HEIR_KEY + self.Columns.ADDRESS
)
items[self.Columns.AMOUNT].setData(
key, self.ROLE_HEIR_KEY + self.Columns.AMOUNT
)
row_count = self.model().rowCount() row_count = self.model().rowCount()
self.model().insertRow(row_count, items) self.model().insertRow(row_count, items)
@@ -2612,10 +2619,9 @@ class HeirList(MyTreeView, MessageBoxMixin):
pass pass
def get_edit_key_from_coordinate(self, row, col): def get_edit_key_from_coordinate(self, row, col):
a= self.get_role_data_from_coordinate( a = self.get_role_data_from_coordinate(row, col, role=self.ROLE_HEIR_KEY + col)
row, col, role=self.ROLE_HEIR_KEY + col
)
return a return a
def create_toolbar(self, config): def create_toolbar(self, config):
toolbar, menu = self.create_toolbar_with_menu("") toolbar, menu = self.create_toolbar_with_menu("")
menu.addAction(_("&New Heir"), self.bal_window.new_heir_dialog) menu.addAction(_("&New Heir"), self.bal_window.new_heir_dialog)
@@ -2623,6 +2629,7 @@ class HeirList(MyTreeView, MessageBoxMixin):
menu.addAction(_("Export"), lambda: self.bal_window.export_heirs()) menu.addAction(_("Export"), lambda: self.bal_window.export_heirs())
self.heir_locktime = HeirsLockTimeEdit(self, 0) self.heir_locktime = HeirsLockTimeEdit(self, 0)
def on_heir_locktime(): def on_heir_locktime():
if not self.heir_locktime.get_locktime(): if not self.heir_locktime.get_locktime():
self.heir_locktime.set_locktime("1y") self.heir_locktime.set_locktime("1y")
@@ -2713,7 +2720,6 @@ class HeirList(MyTreeView, MessageBoxMixin):
self.heir_threshold.set_locktime(self.bal_window.will_settings["threshold"]) self.heir_threshold.set_locktime(self.bal_window.will_settings["threshold"])
self.heir_tx_fees.setValue(int(self.bal_window.will_settings["baltx_fees"])) self.heir_tx_fees.setValue(int(self.bal_window.will_settings["baltx_fees"]))
except Exception as e: except Exception as e:
_logger.debug(f"Exception update_will_settings {e}") _logger.debug(f"Exception update_will_settings {e}")
@@ -2983,14 +2989,20 @@ class PreviewList(MyTreeView):
Will.add_willtree(self.bal_window.willitems) Will.add_willtree(self.bal_window.willitems)
all_utxos = self.bal_window.wallet.get_utxos() all_utxos = self.bal_window.wallet.get_utxos()
utxos_list = Will.utxos_strs(all_utxos) utxos_list = Will.utxos_strs(all_utxos)
Will.check_invalidated(self.bal_window.willitems,utxos_list,self.bal_window.wallet) Will.check_invalidated(
self.bal_window.willitems, utxos_list, self.bal_window.wallet
)
close_window = BalBuildWillDialog(self.bal_window) close_window = BalBuildWillDialog(self.bal_window)
close_window.build_will_task() close_window.build_will_task()
will = {} will = {}
for wid, w in self.bal_window.willitems.items(): for wid, w in self.bal_window.willitems.items():
if w.get_status("VALID") and w.get_status("PUSHED") and not w.get_status("CHECKED"): if (
w.get_status("VALID")
and w.get_status("PUSHED")
and not w.get_status("CHECKED")
):
will[wid] = w will[wid] = w
if will: if will:
self.bal_window.check_transactions(will) self.bal_window.check_transactions(will)
@@ -3434,16 +3446,33 @@ class WillExecutorList(MyTreeView):
labels[self.Columns.URL] = url labels[self.Columns.URL] = url
if Willexecutors.is_selected(value): if Willexecutors.is_selected(value):
labels[self.Columns.SELECTED] = [read_QIcon_from_bytes(self.parent.bal_plugin.read_file("icons/confirmed.png")),""] labels[self.Columns.SELECTED] = [
read_QIcon_from_bytes(
self.parent.bal_plugin.read_file("icons/confirmed.png")
),
"",
]
else: else:
labels[self.Columns.SELECTED] = "" labels[self.Columns.SELECTED] = ""
labels[self.Columns.BASE_FEE] = Util.decode_amount( labels[self.Columns.BASE_FEE] = Util.decode_amount(
value.get("base_fee", 0), self.get_decimal_point() value.get("base_fee", 0), self.get_decimal_point()
) )
if str(value.get("status", 0)) == "200": if str(value.get("status", 0)) == "200":
labels[self.Columns.STATUS] = [read_QIcon_from_bytes(self.parent.bal_plugin.read_file("icons/status_connected.png")),""] labels[self.Columns.STATUS] = [
read_QIcon_from_bytes(
self.parent.bal_plugin.read_file(
"icons/status_connected.png"
)
),
"",
]
else: else:
labels[self.Columns.STATUS] = [read_QIcon_from_bytes(self.parent.bal_plugin.read_file("icons/unconfirmed.png")),""] labels[self.Columns.STATUS] = [
read_QIcon_from_bytes(
self.parent.bal_plugin.read_file("icons/unconfirmed.png")
),
"",
]
labels[self.Columns.ADDRESS] = str(value.get("address", "")) labels[self.Columns.ADDRESS] = str(value.get("address", ""))
labels[self.Columns.INFO] = str(value.get("info", "")) labels[self.Columns.INFO] = str(value.get("info", ""))
@@ -3463,10 +3492,18 @@ class WillExecutorList(MyTreeView):
items[self.Columns.BASE_FEE].setEditable(True) items[self.Columns.BASE_FEE].setEditable(True)
items[self.Columns.STATUS].setEditable(False) items[self.Columns.STATUS].setEditable(False)
items[self.Columns.URL].setData(url, self.ROLE_HEIR_KEY + self.Columns.URL) items[self.Columns.URL].setData(
items[self.Columns.BASE_FEE].setData(url, self.ROLE_HEIR_KEY + self.Columns.BASE_FEE) url, self.ROLE_HEIR_KEY + self.Columns.URL
items[self.Columns.INFO].setData(url, self.ROLE_HEIR_KEY + self.Columns.INFO) )
items[self.Columns.ADDRESS].setData(url, self.ROLE_HEIR_KEY + self.Columns.ADDRESS) items[self.Columns.BASE_FEE].setData(
url, self.ROLE_HEIR_KEY + self.Columns.BASE_FEE
)
items[self.Columns.INFO].setData(
url, self.ROLE_HEIR_KEY + self.Columns.INFO
)
items[self.Columns.ADDRESS].setData(
url, self.ROLE_HEIR_KEY + self.Columns.ADDRESS
)
row_count = self.model().rowCount() row_count = self.model().rowCount()
self.model().insertRow(row_count, items) self.model().insertRow(row_count, items)
if url == current_key: if url == current_key:

12
will.py
View File

@@ -161,6 +161,7 @@ class Will:
""" """
def check_anticipate(ow: "WillItem", nw: "WillItem"): def check_anticipate(ow: "WillItem", nw: "WillItem"):
anticipate = Util.anticipate_locktime(ow.tx.locktime, days=1) anticipate = Util.anticipate_locktime(ow.tx.locktime, days=1)
if int(nw.tx.locktime) >= int(anticipate): if int(nw.tx.locktime) >= int(anticipate):
@@ -297,7 +298,6 @@ class Will:
Will.search_anticipate_rec(will, old_inputs) Will.search_anticipate_rec(will, old_inputs)
def update_will(old_will, new_will): def update_will(old_will, new_will):
all_old_inputs = Will.get_all_inputs(old_will, only_valid=True) 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) all_inputs_min_locktime = Will.get_all_inputs_min_locktime(all_old_inputs)
@@ -439,7 +439,11 @@ class Will:
# check if transactions are stil valid tecnically valid # check if transactions are stil valid tecnically valid
def check_invalidated(willtree, utxos_list, wallet): def check_invalidated(willtree, utxos_list, wallet):
for wid, w in willtree.items(): for wid, w in willtree.items():
if not w.father or willtree[w.father].get_status("CONFIRMED") or willtree[w.father].get_status("PENDING"): if (
not w.father
or willtree[w.father].get_status("CONFIRMED")
or willtree[w.father].get_status("PENDING")
):
for inp in w.tx.inputs(): for inp in w.tx.inputs():
inp_str = Util.utxo_to_str(inp) inp_str = Util.utxo_to_str(inp)
if not inp_str in utxos_list: if not inp_str in utxos_list:
@@ -886,9 +890,5 @@ class FixedAmountException(AmountException):
pass pass
def test_check_invalidated(): def test_check_invalidated():
Will.check_invalidated(will, utxos_list, wallet) Will.check_invalidated(will, utxos_list, wallet)

View File

@@ -35,7 +35,9 @@ class Willexecutors:
continue continue
Willexecutors.initialize_willexecutor(willexecutors[w], w) Willexecutors.initialize_willexecutor(willexecutors[w], w)
for w in to_del: for w in to_del:
_logger.error("error Willexecutor to delete type:{} ", type(willexecutor[w]),w) _logger.error(
"error Willexecutor to delete type:{} ", type(willexecutor[w]), w
)
del willexecutors[w] del willexecutors[w]
bal = bal_plugin.WILLEXECUTORS.default.get(constants.net.NET_NAME, {}) bal = bal_plugin.WILLEXECUTORS.default.get(constants.net.NET_NAME, {})
for bal_url, bal_executor in bal.items(): for bal_url, bal_executor in bal.items():