10 Commits

Author SHA1 Message Date
609db44c1a blacked 2026-02-09 12:01:06 -04:00
0df6786f50 dust bug fix 2026-02-09 11:58:07 -04:00
2a4eab81fd print 2026-02-05 17:51:17 -04:00
d86b941fcb refresh button. locktim is correctly saved, minor bugfix in checking confirmed transaction 2026-02-05 17:11:11 -04:00
1836cdd892 version 2026-02-03 22:12:30 -04:00
2416d0ce8d fix willexecutor list edit 2026-02-03 22:11:33 -04:00
8e4e401d1b fix plugin settings removed willexecutor ping 2026-02-03 16:14:12 -04:00
b8859ee5c1 qt thread panic
heirs import wizard
2026-02-03 13:56:47 -04:00
faeff1ff3c gui willexecutor list status moved after url to be more visible.
heirs tab start hidden
2026-02-03 11:25:21 -04:00
437105477d missing icons 2026-01-28 14:47:24 -04:00
10 changed files with 274 additions and 157 deletions

View File

@@ -1 +1 @@
0.2.3
0.2.4

27
bal.py
View File

@@ -1,6 +1,7 @@
import os
#import random
#import zipfile as zipfile_lib
# import random
# import zipfile as zipfile_lib
from electrum import json_db
from electrum.logging import get_logger
@@ -9,7 +10,7 @@ from electrum.transaction import tx_from_any
def get_will_settings(x):
#print(x)
# print(x)
pass
@@ -18,7 +19,6 @@ json_db.register_dict("will", dict, None)
json_db.register_dict("will_settings", lambda x: x, None)
def get_will(x):
try:
x["tx"] = tx_from_any(x["tx"])
@@ -68,15 +68,9 @@ class BalPlugin(BasePlugin):
self.base_dir = os.path.join(config.electrum_path(), "bal")
self.plugin_dir = os.path.split(os.path.realpath(__file__))[0]
zipfile = "/".join(self.plugin_dir.split("/")[:-1])
# print("real path",os.path.realpath(__file__))
# self.logger.info(self.base_dir)
# print("base_dir:", self.base_dir)
# print("suca:",zipfile)
# print("plugin_dir:", self.plugin_dir)
import sys
sys.path.insert(0, zipfile)
# print("sono state listate?")
self.parent = parent
self.config = config
self.name = name
@@ -96,10 +90,10 @@ class BalPlugin(BasePlugin):
self.PREVIEW = BalConfig(config, "bal_preview", True)
self.SAVE_TXS = BalConfig(config, "bal_save_txs", True)
self.WILLEXECUTORS = BalConfig(config, "bal_willexecutors", True)
self.PING_WILLEXECUTORS = BalConfig(config, "bal_ping_willexecutors", True)
self.ASK_PING_WILLEXECUTORS = BalConfig(
config, "bal_ask_ping_willexecutors", True
)
# self.PING_WILLEXECUTORS = BalConfig(config, "bal_ping_willexecutors", True)
# self.ASK_PING_WILLEXECUTORS = BalConfig(
# config, "bal_ask_ping_willexecutors", True
# )
self.NO_WILLEXECUTOR = BalConfig(config, "bal_no_willexecutor", True)
self.HIDE_REPLACED = BalConfig(config, "bal_hide_replaced", True)
self.HIDE_INVALIDATED = BalConfig(config, "bal_hide_invalidated", True)
@@ -145,16 +139,13 @@ class BalPlugin(BasePlugin):
self.HIDE_REPLACED.set(self._hide_replaced)
def validate_will_settings(self, will_settings):
# print(type(will_settings))
# print(will_settings.get('baltx_fees',1),1)
if int(will_settings.get("baltx_fees", 1)) < 1:
will_settings["baltx_fees"] = 1
if not will_settings.get("threshold"):
will_settings["threshold"] = "180d"
if not will_settings.get("locktime") == "":
if not will_settings.get("locktime"):
will_settings["locktime"] = "1y"
return will_settings
def default_will_settings(self):
return {"baltx_fees": 100, "threshold": "180d", "locktime": "1y"}

View File

@@ -29,6 +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
@@ -41,6 +43,7 @@ HEIR_ADDRESS = 0
HEIR_AMOUNT = 1
HEIR_LOCKTIME = 2
HEIR_REAL_AMOUNT = 3
HEIR_DUST_AMOUNT = 4
TRANSACTION_LABEL = "inheritance transaction"
@@ -88,6 +91,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 +105,27 @@ 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 +134,18 @@ 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 +278,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 +343,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 +469,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 +485,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 +527,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 +655,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 +684,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 +710,7 @@ class LocktimeNotValid(ValueError):
class HeirExpiredException(LocktimeNotValid):
pass
class HeirAmountIsDustException(Exception):
pass

BIN
icons/confirmed.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

BIN
icons/status_connected.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
icons/unconfirmed.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -1,7 +1,7 @@
{
"name": "BAL",
"fullname": "Bitcoin After Life",
"description": "Provides free and decentralized inheritance support<br> Version: 0.2.3",
"description": "Provides free and decentralized inheritance support<br> Version: 0.2.4",
"author":"Svatantrya",
"available_for": ["qt"],
"icon":"icons/bal32x32.png"

246
qt.py
View File

@@ -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,
@@ -295,8 +295,8 @@ class Plugin(BalPlugin, Logger):
lbl_logo = QLabel()
lbl_logo.setPixmap(qicon)
heir_ping_willexecutors = bal_checkbox(self.PING_WILLEXECUTORS)
heir_ask_ping_willexecutors = bal_checkbox(self.ASK_PING_WILLEXECUTORS)
# heir_ping_willexecutors = bal_checkbox(self.PING_WILLEXECUTORS)
# heir_ask_ping_willexecutors = bal_checkbox(self.ASK_PING_WILLEXECUTORS)
heir_no_willexecutor = bal_checkbox(self.NO_WILLEXECUTOR)
def on_multiverse_change():
@@ -327,27 +327,27 @@ class Plugin(BalPlugin, Logger):
2,
"Hide invalidated transactions from will detail and list",
)
add_widget(
grid,
"Ping Willexecutors",
heir_ping_willexecutors,
3,
"Ping willexecutors to get payment info before compiling will",
)
add_widget(
grid,
" - Ask before",
heir_ask_ping_willexecutors,
4,
"Ask before to ping willexecutor",
)
add_widget(
grid,
"Backup Transaction",
heir_no_willexecutor,
5,
"Add transactions without willexecutor",
)
# add_widget(
# grid,
# "Ping Willexecutors",
# heir_ping_willexecutors,
# 3,
# "Ping willexecutors to get payment info before compiling will",
# )
# add_widget(
# grid,
# " - Ask before",
# heir_ask_ping_willexecutors,
# 4,
# "Ask before to ping willexecutor",
# )
# add_widget(
# grid,
# "Backup Transaction",
# heir_no_willexecutor,
# 5,
# "Add transactions without willexecutor",
# )
# add_widget(grid,"Enable Multiverse(EXPERIMENTAL/BROKEN)",heir_enable_multiverse,6,"enable multiple locktimes, will import.... ")
grid.addWidget(heir_repush, 7, 0)
grid.addWidget(
@@ -401,13 +401,13 @@ class BalWindow(Logger):
self.willitems = {}
self.willexecutors = {}
self.will_settings = None
self.heirs_tab = self.create_heirs_tab()
self.will_tab = self.create_will_tab()
self.ok = False
self.disable_plugin = True
self.bal_plugin.get_decimal_point = self.window.get_decimal_point
if self.window.wallet:
self.heirs_tab = self.create_heirs_tab()
self.will_tab = self.create_will_tab()
self.wallet = self.window.wallet
self.heirs_tab.wallet = self.wallet
self.will_tab.wallet = self.wallet
@@ -419,7 +419,7 @@ class BalWindow(Logger):
tab.tab_icon = icon
tab.tab_description = description
tab.tab_pos = len(tabs)
if tab.is_shown_cv:
if tab.is_shown_cv.get():
tabs.addTab(tab, icon, description.replace("&", ""))
def add_toggle_action(tab):
@@ -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)
@@ -498,7 +498,6 @@ class BalWindow(Logger):
if not self.will_settings:
Util.copy(self.will_settings, self.bal_plugin.default_will_settings())
self.logger.debug("not_will_settings {}".format(self.will_settings))
self.bal_plugin.validate_will_settings(self.will_settings)
self.heir_list.update_will_settings()
@@ -514,7 +513,7 @@ class BalWindow(Logger):
self.heir_list = l = HeirList(self, self.window)
tab = self.window.create_list_tab(l)
tab.is_shown_cv = shown_cv(True)
tab.is_shown_cv = shown_cv(False)
return tab
def create_will_tab(self):
@@ -655,7 +654,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 = {}
@@ -664,7 +663,6 @@ class BalWindow(Logger):
self.willexecutors = Willexecutors.get_willexecutors(
self.bal_plugin, update=False, bal_window=self
)
if not self.no_willexecutor:
f = False
@@ -672,6 +670,7 @@ 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"
)
@@ -682,7 +681,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:
@@ -701,7 +701,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
@@ -1179,7 +1185,7 @@ class BalWindow(Logger):
parent = self
def on_success(result):
#del self.waiting_dialog
# del self.waiting_dialog
try:
parent.willexecutor_list.update()
except Exception as e:
@@ -1577,7 +1583,7 @@ class BalWizardDialog(BalDialog):
)
def on_next_we(self):
close_window = BalBuildWillDialog(self.bal_window, self)
close_window = BalBuildWillDialog(self.bal_window)
close_window.build_will_task()
self.close()
# self.next_widget(BalWizardLocktimeAndFeeWidget(self.bal_window,self,self.on_next_locktimeandfee,self.on_next_wedonwload,self.on_next_wedonwload.on_cancel_heir))
@@ -1687,7 +1693,7 @@ class BalWizardHeirsWidget(BalWizardWidget):
button_import = QPushButton(_("Import"))
button_import.clicked.connect(self.import_from_file)
button_export = QPushButton(_("Export"))
button_import.clicked.connect(self.export_to_file)
button_export.clicked.connect(self.export_to_file)
widget = QWidget()
vbox = QVBoxLayout(widget)
vbox.addWidget(self.heirs_list)
@@ -1759,7 +1765,9 @@ class BalWizardWEDownloadWidget(BalWizardWidget):
def on_success(willexecutors):
self.bal_window.willexecutors.update(willexecutors)
self.bal_window.ping_willexecutors(self.bal_window.willexecutors)
self.bal_window.ping_willexecutors(
self.bal_window.willexecutors, False
)
if index < 1:
for we in self.bal_window.willexecutors:
if self.bal_window.willexecutors[we]["status"] == 200:
@@ -1820,6 +1828,7 @@ class BalWizardLocktimeAndFeeWidget(BalWizardWidget):
widget = QWidget()
self.heir_locktime = HeirsLockTimeEdit(widget, 0)
will_settings = self.bal_window.bal_plugin.WILL_SETTINGS.get()
will_settings = self.bal_window.will_settings
self.heir_locktime.set_locktime(will_settings["locktime"])
def on_heir_locktime():
@@ -2028,6 +2037,7 @@ class BalBuildWillDialog(BalDialog):
if not parent:
parent = bal_window.window
BalDialog.__init__(self, parent, bal_window.bal_plugin, "Building Will")
self.parent = parent
self.updatemessage.connect(self.update)
self.bal_window = bal_window
self.message_label = QLabel("Building Will:")
@@ -2078,7 +2088,7 @@ class BalBuildWillDialog(BalDialog):
self.bal_window.window.wallet.dust_threshold(),
)
_logger.debug("variables ok")
self.msg_set_status("checking variables:", varrow,"Ok")
self.msg_set_status("checking variables:", varrow, "Ok")
except AmountException:
self.msg_set_status(
"checking variables",
@@ -2108,7 +2118,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):
@@ -2122,7 +2132,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")
@@ -2138,6 +2148,18 @@ 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'<font color="red">{hid},{heir[HEIR_DUST_AMOUNT]} is DUST',
None,
f"Excluded from will {wid}</font>",
)
have_to_sign = False
for wid in Will.only_valid(self.bal_window.willitems):
if not self.bal_window.willitems[wid].get_status("COMPLETE"):
@@ -2238,7 +2260,7 @@ class BalBuildWillDialog(BalDialog):
if not self._stopping:
self.loop_push()
def invalidate_task(self,password,bal_window,tx):
def invalidate_task(self, password, bal_window, tx):
_logger.debug(f"invalidate tx: {tx}")
fee_per_byte = bal_window.will_settings.get("baltx_fees", 1)
tx = self.bal_window.wallet.sign_transaction(tx, password)
@@ -2267,6 +2289,7 @@ class BalBuildWillDialog(BalDialog):
def on_error(self, error):
_logger.error(error)
pass
def on_success_phase1(self, result):
self.have_to_sign, tx = list(result)
_logger.debug("have to sign {}".format(self.have_to_sign))
@@ -2285,7 +2308,7 @@ class BalBuildWillDialog(BalDialog):
self.close()
return
self.thread.add(
partial(self.invalidate_task,password,self.bal_window,tx),
partial(self.invalidate_task, password, self.bal_window, tx),
on_success=self.on_success_invalidate,
on_done=self.on_accept,
on_error=self.on_error,
@@ -2397,8 +2420,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:
@@ -2571,9 +2592,15 @@ class HeirList(MyTreeView, MessageBoxMixin):
items[self.Columns.NAME].setEditable(True)
items[self.Columns.ADDRESS].setEditable(True)
items[self.Columns.AMOUNT].setEditable(True)
items[self.Columns.NAME].setData(key, self.ROLE_HEIR_KEY + 1)
items[self.Columns.ADDRESS].setData(key, self.ROLE_HEIR_KEY + 2)
items[self.Columns.AMOUNT].setData(key, self.ROLE_HEIR_KEY + 3)
items[self.Columns.NAME].setData(
key, self.ROLE_HEIR_KEY + self.Columns.NAME
)
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()
self.model().insertRow(row_count, items)
@@ -2592,9 +2619,8 @@ class HeirList(MyTreeView, MessageBoxMixin):
pass
def get_edit_key_from_coordinate(self, row, col):
return self.get_role_data_from_coordinate(
row, col, role=self.ROLE_HEIR_KEY + col + 1
)
a = self.get_role_data_from_coordinate(row, col, role=self.ROLE_HEIR_KEY + col)
return a
def create_toolbar(self, config):
toolbar, menu = self.create_toolbar_with_menu("")
@@ -2691,8 +2717,8 @@ class HeirList(MyTreeView, MessageBoxMixin):
def update_will_settings(self):
try:
self.heir_locktime.set_locktime(self.bal_window.will_settings["locktime"])
self.heir_tx_fees.setValue(int(self.bal_window.will_settings["baltx_fees"]))
self.heir_threshold.set_locktime(self.bal_window.will_settings["threshold"])
self.heir_tx_fees.setValue(int(self.bal_window.will_settings["baltx_fees"]))
except Exception as e:
_logger.debug(f"Exception update_will_settings {e}")
@@ -2737,6 +2763,7 @@ class PreviewList(MyTreeView):
self.wallet = bal_window.window.wallet
self.setModel(QStandardItemModel(self))
self.sortByColumn(self.Columns.LOCKTIME, Qt.SortOrder.AscendingOrder)
self.setSortingEnabled(True)
self.std_model = self.model()
self.config = bal_window.bal_plugin.config
@@ -2895,6 +2922,8 @@ class PreviewList(MyTreeView):
tmp = self.replace(set_current, current_key, txid, bal_tx)
if tmp:
set_current = tmp
self.sortByColumn(self.Columns.LOCKTIME, Qt.SortOrder.AscendingOrder)
self.setSortingEnabled(True)
def create_toolbar(self, config):
toolbar, menu = self.create_toolbar_with_menu("")
@@ -2913,6 +2942,9 @@ class PreviewList(MyTreeView):
display = QPushButton(_("Display"))
display.clicked.connect(self.bal_window.preview_modal_dialog)
display = QPushButton(_("refresh"))
display.clicked.connect(self.check)
widget = QWidget()
hlayout = QHBoxLayout(widget)
hlayout.addWidget(wizard)
@@ -2954,11 +2986,26 @@ class PreviewList(MyTreeView):
self.update()
def check(self):
Will.add_willtree(self.bal_window.willitems)
all_utxos = self.bal_window.wallet.get_utxos()
utxos_list = Will.utxos_strs(all_utxos)
Will.check_invalidated(
self.bal_window.willitems, utxos_list, self.bal_window.wallet
)
close_window = BalBuildWillDialog(self.bal_window)
close_window.build_will_task()
will = {}
for wid, w in self.bal_window.willitems.items():
if w.get_status("VALID"):
if (
w.get_status("VALID")
and w.get_status("PUSHED")
and not w.get_status("CHECKED")
):
will[wid] = w
self.bal_window.check_transactions(will)
if will:
self.bal_window.check_transactions(will)
self.update()
def invalidate_will(self):
@@ -2991,6 +3038,10 @@ class PreviewDialog(BalDialog, MessageBoxMixin):
self.setMinimumSize(1000, 200)
self.size_label = QLabel()
self.transactions_list = PreviewList(self.bal_window, self.will)
self.bal_window.init_class_variables()
self.check_will()
vbox = QVBoxLayout(self)
vbox.addWidget(self.size_label)
vbox.addWidget(self.transactions_list)
@@ -3041,14 +3092,6 @@ class PreviewDialog(BalDialog, MessageBoxMixin):
event.accept()
def read_bal_QIcon(icon_basename: str = DEFAULT_ICON) -> QIcon:
return QIcon(icon_path(icon_basename))
def read_bal_QPixmap(icon_basename: str = DEFAULT_ICON) -> QPixmap:
return QPixmap(icon_path(icon_basename))
class WillDetailDialog(BalDialog):
def __init__(self, bal_window):
@@ -3246,24 +3289,27 @@ class WillExecutorList(MyTreeView):
class Columns(MyTreeView.BaseColumnsEnum):
SELECTED = enum.auto()
URL = enum.auto()
STATUS = enum.auto()
BASE_FEE = enum.auto()
INFO = enum.auto()
ADDRESS = enum.auto()
STATUS = enum.auto()
headers = {
Columns.SELECTED: _(""),
Columns.URL: _("Url"),
Columns.STATUS: _("S"),
Columns.BASE_FEE: _("Base fee"),
Columns.INFO: _("Info"),
Columns.ADDRESS: _("Default Address"),
Columns.STATUS: _("S"),
}
ROLE_HEIR_KEY = Qt.ItemDataRole.UserRole + 2000
filter_columns = [Columns.URL]
ROLE_SORT_ORDER = Qt.ItemDataRole.UserRole + 3000
ROLE_HEIR_KEY = Qt.ItemDataRole.UserRole + 3001
key_role = ROLE_HEIR_KEY
def __init__(self, parent: "WillExecutorDialog"):
def __init__(self, parent: "WillExecutorWidget"):
super().__init__(
parent=parent,
stretch_column=self.Columns.ADDRESS,
@@ -3275,7 +3321,12 @@ class WillExecutorList(MyTreeView):
],
)
self.parent = parent
self.setModel(QStandardItemModel(self))
try:
self.setModel(QStandardItemModel(self))
self.sortByColumn(self.Columns.SELECTED, Qt.SortOrder.AscendingOrder)
self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
except Exception:
pass
self.setSortingEnabled(True)
self.std_model = self.model()
self.config = parent.bal_plugin.config
@@ -3335,7 +3386,8 @@ class WillExecutorList(MyTreeView):
self.update()
def get_edit_key_from_coordinate(self, row, col):
a = self.get_role_data_from_coordinate(row, col, role=self.ROLE_HEIR_KEY + col)
role = self.ROLE_HEIR_KEY + col
a = self.get_role_data_from_coordinate(row, col, role=role)
return a
def delete(self, selected_keys):
@@ -3394,16 +3446,33 @@ class WillExecutorList(MyTreeView):
labels[self.Columns.URL] = url
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:
labels[self.Columns.SELECTED] = ""
labels[self.Columns.BASE_FEE] = Util.decode_amount(
value.get("base_fee", 0), self.get_decimal_point()
)
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:
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.INFO] = str(value.get("info", ""))
@@ -3423,17 +3492,25 @@ class WillExecutorList(MyTreeView):
items[self.Columns.BASE_FEE].setEditable(True)
items[self.Columns.STATUS].setEditable(False)
items[self.Columns.URL].setData(url, self.ROLE_HEIR_KEY + 1)
items[self.Columns.BASE_FEE].setData(url, self.ROLE_HEIR_KEY + 2)
items[self.Columns.INFO].setData(url, self.ROLE_HEIR_KEY + 3)
items[self.Columns.ADDRESS].setData(url, self.ROLE_HEIR_KEY + 4)
items[self.Columns.URL].setData(
url, self.ROLE_HEIR_KEY + self.Columns.URL
)
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()
self.model().insertRow(row_count, items)
if url == current_key:
idx = self.model().index(row_count, self.Columns.NAME)
idx = self.model().index(row_count, self.Columns.URL)
set_current = QPersistentModelIndex(idx)
self.set_current_idx(set_current)
self.filter()
self.parent.save_willexecutors()
except Exception as e:
@@ -3494,7 +3571,7 @@ class WillExecutorWidget(QWidget, MessageBoxMixin):
buttonbox.addWidget(b)
vbox.addLayout(buttonbox)
self.willexecutor_list.update()
# self.willexecutor_list.update()
def add(self):
self.willexecutors_list["http://localhost:8080"] = {
@@ -3526,17 +3603,10 @@ class WillExecutorWidget(QWidget, MessageBoxMixin):
def update_willexecutors(self, wes=None):
if not wes:
self.willexecutors_list = Willexecutors.get_willexecutors(
self.bal_plugin,
update=True,
bal_window=self.bal_window,
force=True,
task=self,
)
else:
self.bal_window.ping_willexecutors(wes, self.parent)
self.willexecutors_list.update(wes)
self.willexecutor_list.update()
wes = self.willexecutors_list
self.bal_window.ping_willexecutors(wes, self.parent)
self.willexecutors_list.update(wes)
self.willexecutor_list.update()
def import_json_file(self, path):
data = read_json_file(path)

45
will.py
View File

@@ -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:
@@ -82,8 +81,6 @@ class Will:
for txin in will[wid].tx.inputs():
txid = txin.prevout.txid.hex()
if txid in will:
# print(will[txid].tx.outputs())
# print(txin.prevout.out_idx)
change = will[txid].tx.outputs()[txin.prevout.out_idx]
txin._trusted_value_sats = change.value
try:
@@ -155,6 +152,16 @@ 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):
@@ -258,7 +265,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)
@@ -268,6 +275,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:
@@ -277,6 +295,7 @@ 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):
@@ -286,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)
@@ -421,7 +439,11 @@ 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:
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:
@@ -632,7 +654,6 @@ class Will:
class WillItem(Logger):
STATUS_DEFAULT = {
"ANTICIPATED": ["Anticipated", False],
"BROADCASTED": ["Broadcasted", False],
@@ -654,15 +675,15 @@ class WillItem(Logger):
}
def set_status(self, status, value=True):
#_logger.trace(
# _logger.trace(
# "set status {} - {} {} -> {}".format(
# self._id, status, self.STATUS[status][1], value
# )
#)
# )
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 +888,7 @@ class PercAmountException(AmountException):
class FixedAmountException(AmountException):
pass
def test_check_invalidated():
Will.check_invalidated(will, utxos_list, wallet)

View File

@@ -35,34 +35,36 @@ class Willexecutors:
continue
Willexecutors.initialize_willexecutor(willexecutors[w], w)
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]
bal = bal_plugin.WILLEXECUTORS.default.get(constants.net.NET_NAME, {})
for bal_url, bal_executor in bal.items():
if not bal_url in willexecutors:
_logger.debug(f"force add {bal_url} willexecutor")
willexecutors[bal_url] = bal_executor
if update:
found = False
for url, we in willexecutors.items():
if Willexecutors.is_selected(we):
found = True
if found or force:
if bal_plugin.PING_WILLEXECUTORS.get() or force:
ping_willexecutors = True
if bal_plugin.ASK_PING_WILLEXECUTORS.get() and not force:
if bal_window:
ping_willexecutors = bal_window.window.question(
_(
"Contact willexecutors servers to update payment informations?"
)
)
# if update:
# found = False
# for url, we in willexecutors.items():
# if Willexecutors.is_selected(we):
# found = True
# if found or force:
# if bal_plugin.PING_WILLEXECUTORS.get() or force:
# ping_willexecutors = True
# if bal_plugin.ASK_PING_WILLEXECUTORS.get() and not force:
# if bal_window:
# ping_willexecutors = bal_window.window.question(
# _(
# "Contact willexecutors servers to update payment informations?"
# )
# )
if ping_willexecutors:
if task:
bal_window.ping_willexecutors(willexecutors, task)
else:
bal_window.ping_willexecutors_task(willexecutors)
# if ping_willexecutors:
# if task:
# bal_window.ping_willexecutors(willexecutors, task)
# else:
# bal_window.ping_willexecutors_task(willexecutors)
w_sorted = dict(
sorted(
willexecutors.items(), key=lambda w: w[1].get("sort", 0), reverse=True
@@ -119,7 +121,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 +173,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",