14 Commits

8 changed files with 383 additions and 202 deletions

View File

@@ -1 +1 @@
0.2.6 0.2.8

29
bal.py
View File

@@ -47,18 +47,19 @@ class BalConfig:
class BalPlugin(BasePlugin): class BalPlugin(BasePlugin):
LATEST_VERSION = "1" _version=None
KNOWN_VERSIONS = ("0", "1") __version__ = "0.2.8" #AUTOMATICALLY GENERATED DO NOT EDIT
assert LATEST_VERSION in KNOWN_VERSIONS def version(self):
if not self._version:
def version(): try:
try: f = ""
f = "" with open("{}/VERSION".format(self.plugin_dir), "r") as fi:
with open("VERSION", "r") as fi: f = str(fi.read())
f = str(fi.readline()) self._version = f.strip()
return f except Exception as e:
except Exception: _logger.error(f"failed to get version: {e}")
return "unknown" self._version="unknown"
return self._version
SIZE = (159, 97) SIZE = (159, 97)
@@ -108,7 +109,7 @@ class BalPlugin(BasePlugin):
"base_fee": 100000, "base_fee": 100000,
"status": "New", "status": "New",
"info": "Bitcoin After Life Will Executor", "info": "Bitcoin After Life Will Executor",
"address": "bcrt1qa5cntu4hgadw8zd3n6sq2nzjy34sxdtd9u0gp7", "address": "bc1qusymuetsz2psaqzqxv8qmzcy64d9meckj3lxxf",
"selected": True, "selected": True,
} }
}, },
@@ -148,6 +149,8 @@ class BalPlugin(BasePlugin):
self.HIDE_REPLACED.set(self._hide_replaced) self.HIDE_REPLACED.set(self._hide_replaced)
def validate_will_settings(self, will_settings): def validate_will_settings(self, will_settings):
if not will_settings:
will_settings=[]
if int(will_settings.get("baltx_fees", 1)) < 1: if int(will_settings.get("baltx_fees", 1)) < 1:
will_settings["baltx_fees"] = 1 will_settings["baltx_fees"] = 1
if not will_settings.get("threshold"): if not will_settings.get("threshold"):

View File

@@ -30,9 +30,11 @@ from electrum.transaction import (
PartialTransaction, PartialTransaction,
PartialTxInput, PartialTxInput,
PartialTxOutput, PartialTxOutput,
TxOutput,
TxOutpoint, TxOutpoint,
# TxOutput, # TxOutput,
) )
from electrum.payment_identifier import PaymentIdentifier
from electrum.util import ( from electrum.util import (
bfh, bfh,
read_json_file, read_json_file,
@@ -44,7 +46,6 @@ from electrum.util import (
from .util import Util from .util import Util
from .willexecutors import Willexecutors from .willexecutors import Willexecutors
from electrum.util import BitcoinException from electrum.util import BitcoinException
if TYPE_CHECKING: if TYPE_CHECKING:
from .simple_config import SimpleConfig from .simple_config import SimpleConfig
@@ -71,28 +72,22 @@ def reduce_outputs(in_amount, out_amount, fee, outputs):
output.value = math.floor((in_amount - fee) / out_amount * output.value) output.value = math.floor((in_amount - fee) / out_amount * output.value)
""" def create_op_return_script(data_hex: str) -> bytes:
#TODO: put this method inside wallet.db to replace or complete get_locktime_for_new_transaction """Crea scriptpubkey OP_RETURN in bytes"""
def get_current_height(network:'Network'): data = bytes.fromhex(data_hex)
#if no network or not up to date, just set locktime to zero
if not network: if len(data) > 80:
return 0 raise ValueError("OP_RETURN data too big (max 80 bytes)")
chain = network.blockchain()
if chain.is_tip_stale(): # Costruzione manuale: OP_RETURN + push data
return 0 if len(data) <= 75:
# figure out current block height # Formato più comune: OP_RETURN + 1-byte length + data
chain_height = chain.height() # learnt from all connected servers, SPV-checked script = b'\x6a' + bytes([len(data)]) + data
server_height = network.get_server_height() # height claimed by main server, unverified else:
# note: main server might be lagging (either is slow, is malicious, or there is an SPV-invisible-hard-fork) # Per dati più grandi (fino a 80) si usa OP_PUSHDATA1
# - if it's lagging too much, it is the network's job to switch away script = b'\x6a\x4c' + bytes([len(data)]) + data
if server_height < chain_height - 10:
# the diff is suspiciously large... give up and use something non-fingerprintable return script
return 0
# discourage "fee sniping"
height = min(chain_height, server_height)
return height
"""
def prepare_transactions(locktimes, available_utxos, fees, wallet): def prepare_transactions(locktimes, available_utxos, fees, wallet):
available_utxos = sorted( available_utxos = sorted(
@@ -167,6 +162,13 @@ def prepare_transactions(locktimes, available_utxos, fees, wallet):
outputs.append(change) outputs.append(change)
for i in range(0, 100): for i in range(0, 100):
random.shuffle(outputs) random.shuffle(outputs)
#op_return_text = "Hello Bal!"
## Convert text to hex
#op_return_hex = op_return_text.encode('utf-8').hex()
#op_return_script = create_op_return_script(op_return_hex)
#outputs.append(PartialTxOutput(value=0, scriptpubkey=op_return_script))
tx = PartialTransaction.from_io( tx = PartialTransaction.from_io(
used_utxos, used_utxos,
outputs, outputs,
@@ -424,6 +426,8 @@ class Heirs(dict, Logger):
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
): ):
if balance<total_fees or balance < wallet.dust_threshold():
raise BalanceTooLowException(balance,wallet.dust_threshold(),total_fees)
willexecutors_amount = 0 willexecutors_amount = 0
willexecutors = {} willexecutors = {}
heir_list = {} heir_list = {}
@@ -461,7 +465,6 @@ class Heirs(dict, Logger):
percent_amount, percent_amount,
fixed_amount_with_dust, 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
@@ -471,14 +474,12 @@ class Heirs(dict, Logger):
heir_list.update(fixed_heirs) heir_list.update(fixed_heirs)
newbalance -= fixed_amount newbalance -= fixed_amount
if newbalance > 0: if newbalance > 0:
perc_amount = self.normalize_perc( perc_amount = self.normalize_perc(
percent_heirs, newbalance, percent_amount, wallet percent_heirs, newbalance, percent_amount, wallet
) )
newbalance -= perc_amount newbalance -= perc_amount
heir_list.update(percent_heirs) heir_list.update(percent_heirs)
if newbalance > 0: if newbalance > 0:
newbalance += fixed_amount newbalance += fixed_amount
fixed_amount = self.normalize_perc( fixed_amount = self.normalize_perc(
@@ -537,7 +538,7 @@ class Heirs(dict, Logger):
break break
elif 0 <= j: elif 0 <= j:
url, willexecutor = willexecutorsitems[j] url, willexecutor = willexecutorsitems[j]
if not Willexecutors.is_selected(willexecutor): if not Willexecutors.is_selected(willexecutor) or willexecutor["base_fee"] < wallet.dust_threshold():
continue continue
else: else:
willexecutor["url"] = url willexecutor["url"] = url
@@ -782,3 +783,10 @@ class WillExecutorFeeException(Exception):
return "WillExecutorFeeException: {} fee:{}".format( return "WillExecutorFeeException: {} fee:{}".format(
self.willexecutor["url"], self.willexecutor["base_fee"] self.willexecutor["url"], self.willexecutor["base_fee"]
) )
class BalanceTooLowException(Exception):
def __init__(self,balance, dust_threshold, fees):
self.balance=balance
self.dust_threshold = dust_threshold
self.fees = fees
def __str__(self):
return f"Balance too low, balance: {self.balance}, dust threshold: {self.dust_threshold}, fees: {self.fees}"

View File

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

440
qt.py
View File

@@ -15,13 +15,14 @@ from datetime import datetime
from decimal import Decimal from decimal import Decimal
from functools import partial from functools import partial
from typing import TYPE_CHECKING, Any, Callable, Mapping, Optional, Union from typing import TYPE_CHECKING, Any, Callable, Mapping, Optional, Union
import traceback
try: try:
QT_VERSION = sys._GUI_QT_VERSION QT_VERSION = sys._GUI_QT_VERSION
except Exception: except Exception:
QT_VERSION = 6 QT_VERSION = 6
if QT_VERSION == 5: if QT_VERSION == 5:
from PyQt5.QtCore import QThread, QCoreApplication
from PyQt5.QtCore import ( from PyQt5.QtCore import (
QDateTime, QDateTime,
QModelIndex, QModelIndex,
@@ -57,6 +58,7 @@ if QT_VERSION == 5:
QWidget, QWidget,
) )
else: # QT6 else: # QT6
from PyQt6.QtCore import QThread, QCoreApplication
from PyQt6.QtCore import ( from PyQt6.QtCore import (
QDateTime, QDateTime,
QModelIndex, QModelIndex,
@@ -402,9 +404,13 @@ class BalWindow(Logger):
self.bal_plugin.get_decimal_point = self.window.get_decimal_point self.bal_plugin.get_decimal_point = self.window.get_decimal_point
if self.window.wallet: if self.window.wallet:
self.wallet = self.window.wallet
if not self.will_settings:
self.will_settings = self.bal_plugin.WILL_SETTINGS.get()
Util.fix_will_settings_tx_fees(self.will_settings)
self.heirs_tab = self.create_heirs_tab() self.heirs_tab = self.create_heirs_tab()
self.will_tab = self.create_will_tab() self.will_tab = self.create_will_tab()
self.wallet = self.window.wallet
self.heirs_tab.wallet = self.wallet self.heirs_tab.wallet = self.wallet
self.will_tab.wallet = self.wallet self.will_tab.wallet = self.wallet
@@ -486,17 +492,17 @@ class BalWindow(Logger):
self.close_wallet() self.close_wallet()
return return
if not self.will_settings: #if not self.will_settings:
self.will_settings = self.wallet.db.get_dict("will_settings") # self.will_settings = self.wallet.db.get_dict("will_settings")
Util.fix_will_settings_tx_fees(self.will_settings) # Util.fix_will_settings_tx_fees(self.will_settings)
self.logger.info("will_settings: {}".format(self.will_settings)) # self.logger.info("will_settings: {}".format(self.will_settings))
if not self.will_settings: # if not self.will_settings:
Util.copy(self.will_settings, self.bal_plugin.default_will_settings()) # Util.copy(self.will_settings, self.bal_plugin.default_will_settings())
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_widget.update_will_settings()
self.heir_list.update() # self.heir_list_widget.update()
def init_wizard(self): def init_wizard(self):
wizard_dialog = BalWizardDialog(self) wizard_dialog = BalWizardDialog(self)
@@ -507,9 +513,9 @@ class BalWindow(Logger):
self.willexecutor_dialog.show() self.willexecutor_dialog.show()
def create_heirs_tab(self): def create_heirs_tab(self):
self.heir_list = HeirList(self, self.window) self.heir_list_widget = HeirListWidget(self, self.window)
tab = self.window.create_list_tab(self.heir_list) tab = self.window.create_list_tab(self.heir_list_widget)
tab.is_shown_cv = shown_cv(False) tab.is_shown_cv = shown_cv(False)
return tab return tab
@@ -601,14 +607,6 @@ class BalWindow(Logger):
except Exception as e: except Exception as e:
self.show_error(str(e)) self.show_error(str(e))
# def export_inheritance_handler(self,path):
# txs = self.build_inheritance_transaction(ignore_duplicate=True, keep_original=False)
# with open(path,"w") as f:
# for tx in txs:
# tx['status']+="."+BalPlugin.STATUS_EXPORTED
# f.write(str(tx['tx']))
# f.write('\n')
def set_heir(self, heir): def set_heir(self, heir):
heir = list(heir) heir = list(heir)
if not self.bal_plugin.ENABLE_MULTIVERSE.get(): if not self.bal_plugin.ENABLE_MULTIVERSE.get():
@@ -616,7 +614,7 @@ class BalWindow(Logger):
h = Heirs.validate_heir(heir[0], heir[1:]) h = Heirs.validate_heir(heir[0], heir[1:])
self.heirs[heir[0]] = h self.heirs[heir[0]] = h
self.heir_list.update() self.heir_list_widget.update()
return True return True
def delete_heirs(self, heirs): def delete_heirs(self, heirs):
@@ -627,14 +625,12 @@ class BalWindow(Logger):
_logger.debug(f"error deleting heir: {heir} {e}") _logger.debug(f"error deleting heir: {heir} {e}")
pass pass
self.heirs.save() self.heirs.save()
self.heir_list.update() self.heir_list_widget.update()
return True return True
def import_heirs( def import_heirs(self):
self,
):
import_meta_gui( import_meta_gui(
self.window, _("heirs"), self.heirs.import_file, self.heir_list.update self.window, _("heirs"), self.heirs.import_file, self.heir_list_widget.update
) )
def export_heirs(self): def export_heirs(self):
@@ -740,6 +736,35 @@ class BalWindow(Logger):
def show_critical(self, text): def show_critical(self, text):
self.window.show_critical(text) self.window.show_critical(text)
def update_locktime_widgets(self,locktime):
locktime = self.will_settings["locktime"] = (
locktime
if locktime
else "1y"
)
self.bal_plugin.WILL_SETTINGS.set(self.will_settings)
try:
self.heir_list_widget.heir_locktime.set_locktime(locktime)
except Exception as e:
pass
#self.preview_list.heirs_locktime.set_locktim(will_settings['thershold'])
def update_threshold_widgets(self,threshold):
threshold = self.will_settings["threshold"] = (
threshold
if threshold
else "1y"
)
self.bal_plugin.WILL_SETTINGS.set(self.will_settings)
try:
self.heir_list_widget.heir_threshold.set_locktime(threshold)
except Exception as e:
pass
try:
self.will_list.heir_threshold.set_locktime(threshold)
except Exception as e:
pass
def init_heirs_to_locktime(self, multiverse=False): def init_heirs_to_locktime(self, multiverse=False):
for heir in self.heirs: for heir in self.heirs:
h = self.heirs[heir] h = self.heirs[heir]
@@ -748,8 +773,7 @@ class BalWindow(Logger):
def init_class_variables(self): def init_class_variables(self):
if not self.heirs: if not self.heirs:
raise NoHeirsException() raise NoHeirsException(_("Heirs are not defined"))
return
try: try:
self.date_to_check = Util.parse_locktime_string( self.date_to_check = Util.parse_locktime_string(
self.will_settings["threshold"] self.will_settings["threshold"]
@@ -757,11 +781,13 @@ class BalWindow(Logger):
# found = False # found = False
self.locktime_blocks = self.bal_plugin.LOCKTIME_BLOCKS.get() self.locktime_blocks = self.bal_plugin.LOCKTIME_BLOCKS.get()
self.current_block = Util.get_current_height(self.wallet.network) self.current_block = Util.get_current_height(self.wallet.network)
self.block_to_check = self.current_block + self.locktime_blocks self.block_to_check=0
self.no_willexecutor = self.bal_plugin.NO_WILLEXECUTOR.get() self.no_willexecutor = self.bal_plugin.NO_WILLEXECUTOR.get()
self.willexecutors = Willexecutors.get_willexecutors( self.willexecutors = Willexecutors.get_willexecutors(
self.bal_plugin, update=True, bal_window=self, task=False self.bal_plugin, update=True, bal_window=self, task=False
) )
if self.date_to_check < datetime.now().timestamp():
raise CheckAliveException(self.date_to_check)
self.init_heirs_to_locktime(self.bal_plugin.ENABLE_MULTIVERSE.get()) self.init_heirs_to_locktime(self.bal_plugin.ENABLE_MULTIVERSE.get())
except Exception as e: except Exception as e:
@@ -776,8 +802,8 @@ class BalWindow(Logger):
if not self.heirs: if not self.heirs:
self.logger.warning("not heirs {}".format(self.heirs)) self.logger.warning("not heirs {}".format(self.heirs))
return return
self.init_class_variables()
try: try:
self.init_class_variables()
Will.check_amounts( Will.check_amounts(
self.heirs, self.heirs,
self.willexecutors, self.willexecutors,
@@ -788,9 +814,12 @@ class BalWindow(Logger):
except AmountException as e: except AmountException as e:
self.show_warning( self.show_warning(
_( _(
f"In the inheritance process, the entire wallet will always be fully emptied. Your settings require an adjustment of the amounts.\n{e}" f"In the inheritance process, the entire wallet will always be fully emptied. Your settings require an adjustment of the amounts.{e}"
) )
) )
except CheckAliveException:
self.show_error(_("CheckAlive is in the past please update it to a date in the future but less than locktime"))
return
locktime = Util.parse_locktime_string(self.will_settings["locktime"]) locktime = Util.parse_locktime_string(self.will_settings["locktime"])
if locktime < self.date_to_check: if locktime < self.date_to_check:
self.show_error(_("locktime is lower than threshold")) self.show_error(_("locktime is lower than threshold"))
@@ -1036,7 +1065,18 @@ class BalWindow(Logger):
) )
def on_failure(err): def on_failure(err):
a,b,c = err
self.logger.error(f"fail to broadcast transactions:{err}") self.logger.error(f"fail to broadcast transactions:{err}")
self.logger.error(f"error: {b}")
self.logger.error(f"traceback ")
tb = c
while tb is not None:
frame = tb.tb_frame
self.logger.error("file:", frame.f_code.co_filename)
self.logger.error("name:", frame.f_code.co_name)
self.logger.error("line:", tb.tb_lineno)
self.logger.error("lasti:", tb.tb_lasti)
tb = tb.tb_next
task = partial(self.push_transactions_to_willexecutors, force) task = partial(self.push_transactions_to_willexecutors, force)
msg = _("Selecting Will-Executors") msg = _("Selecting Will-Executors")
@@ -1079,7 +1119,13 @@ class BalWindow(Logger):
self.willitems[wid].we["url"], wid, "Waiting" self.willitems[wid].we["url"], wid, "Waiting"
) )
) )
self.willitems[wid].check_willexecutor() w = self.willitems[wid]
w.set_check_willexecutor(
Willexecutors.check_transaction(
wid,
w.we["url"]
)
)
self.waiting_dialog.update( self.waiting_dialog.update(
"checked {} - {} : {}".format( "checked {} - {} : {}".format(
self.willitems[wid].we["url"], self.willitems[wid].we["url"],
@@ -1128,7 +1174,9 @@ class BalWindow(Logger):
self.waiting_dialog.update( self.waiting_dialog.update(
"checking transaction: {}\n willexecutor: {}".format(wid, w.we["url"]) "checking transaction: {}\n willexecutor: {}".format(wid, w.we["url"])
) )
w.check_willexecutor()
w.set_check_willexecutor(Willexecutors.check_transaction(wid, w.we["url"]))
if time.time() - start < 3: if time.time() - start < 3:
time.sleep(3 - (time.time() - start)) time.sleep(3 - (time.time() - start))
@@ -1189,7 +1237,7 @@ class BalWindow(Logger):
def on_success(result): def on_success(result):
# del self.waiting_dialog # del self.waiting_dialog
try: try:
parent.willexecutor_list.update() parent.will_executor_list_widget.update()
except Exception as e: except Exception as e:
pass pass
try: try:
@@ -1214,10 +1262,20 @@ class BalWindow(Logger):
self.dw.show() self.dw.show()
def update_all(self): def update_all(self):
self.will_list.update_will(self.willitems) try:
self.heirs_tab.update() Will.add_willtree(self.willitems)
self.will_tab.update() all_utxos = self.wallet.get_utxos()
self.will_list.update() utxos_list = Will.utxos_strs(all_utxos)
Will.check_invalidated(
self.willitems, utxos_list, self.wallet
)
self.will_list.update_will(self.willitems)
self.heirs_tab.update()
self.will_tab.update()
self.will_list.update()
except Exception as e:
_logger.error(f"error while updating window: {e}")
def add_widget(grid, label, widget, row, help_): def add_widget(grid, label, widget, row, help_):
@@ -1293,8 +1351,8 @@ class HeirsLockTimeEdit(QWidget, _LockTimeEditor):
w.setEnabled(False) w.setEnabled(False)
prev_locktime = self.editor.get_locktime() prev_locktime = self.editor.get_locktime()
self.editor = self.option_index_to_editor_map[i] self.editor = self.option_index_to_editor_map[i]
if self.editor.is_acceptable_locktime(prev_locktime): #if self.editor.is_acceptable_locktime(prev_locktime):
self.editor.set_locktime(prev_locktime, force=True) # self.editor.set_locktime(prev_locktime, force=False)
self.editor.setVisible(True) self.editor.setVisible(True)
self.editor.setEnabled(True) self.editor.setEnabled(True)
@@ -1305,8 +1363,16 @@ class HeirsLockTimeEdit(QWidget, _LockTimeEditor):
self.combo.setCurrentIndex(index) self.combo.setCurrentIndex(index)
self.on_current_index_changed(index) self.on_current_index_changed(index)
def set_locktime(self, x: Any, force=True) -> None: def set_locktime(self, x: Any, force=None) -> None:
self.editor.set_locktime(x, force) if force is None:
force=True
try:
int(x)
self.set_index(1)
except:
if isinstance(x,str):
self.set_index(0)
self.editor.set_locktime(x, force=False)
class LockTimeRawEdit(QLineEdit, _LockTimeEditor): class LockTimeRawEdit(QLineEdit, _LockTimeEditor):
@@ -1605,17 +1671,19 @@ class BalWizardDialog(BalDialog):
) )
def on_accept(self): def on_accept(self):
self.bal_window.update_all()
pass pass
def on_reject(self): def on_reject(self):
pass pass
def on_close(self): def on_close(self):
self.bal_window.update_all()
pass pass
def closeEvent(self, event): def closeEvent(self, event):
self.bal_window.update_all()
self.bal_window.heir_list.update_will_settings() self.bal_window.heir_list_widget.update_will_settings()
pass pass
@@ -1692,7 +1760,7 @@ class BalWizardHeirsWidget(BalWizardWidget):
) )
def get_content(self): def get_content(self):
self.heirs_list = HeirList(self.bal_window, self) self.heir_list_widget = HeirListWidget(self.bal_window, self)
button_add = QPushButton(_("Add")) button_add = QPushButton(_("Add"))
button_add.clicked.connect(self.add_heir) button_add.clicked.connect(self.add_heir)
button_import = QPushButton(_("Import")) button_import = QPushButton(_("Import"))
@@ -1701,20 +1769,20 @@ class BalWizardHeirsWidget(BalWizardWidget):
button_export.clicked.connect(self.export_to_file) button_export.clicked.connect(self.export_to_file)
widget = QWidget() widget = QWidget()
vbox = QVBoxLayout(widget) vbox = QVBoxLayout(widget)
vbox.addWidget(self.heirs_list) vbox.addWidget(self.heir_list_widget)
vbox.addLayout(Buttons(button_add, button_import, button_export)) vbox.addLayout(Buttons(button_add, button_import, button_export))
return widget return widget
def import_from_file(self): def import_from_file(self):
self.bal_window.import_heirs() self.bal_window.import_heirs()
self.heirs_list.update() self.heir_list_widget.update()
def export_to_file(self): def export_to_file(self):
self.bal_window.export_heirs() self.bal_window.export_heirs()
def add_heir(self): def add_heir(self):
self.bal_window.new_heir_dialog() self.bal_window.new_heir_dialog()
self.heirs_list.update() self.heir_list_widget.update()
def validate(self): def validate(self):
return True return True
@@ -1785,7 +1853,7 @@ class BalWizardWEDownloadWidget(BalWizardWidget):
_logger.debug(f"Failed to download willexecutors list {fail}") _logger.debug(f"Failed to download willexecutors list {fail}")
pass pass
task = partial(Willexecutors.download_list, self.bal_window.bal_plugin,self.bal_window.willexecutors) task = partial(Willexecutors.download_list,self.bal_window.willexecutors)
msg = _("Downloading Will-Executors list") msg = _("Downloading Will-Executors list")
self.waiting_dialog = BalWaitingDialog( self.waiting_dialog = BalWaitingDialog(
self.bal_window, msg, task, on_success, on_failure, exe=False self.bal_window, msg, task, on_success, on_failure, exe=False
@@ -1832,19 +1900,14 @@ class BalWizardLocktimeAndFeeWidget(BalWizardWidget):
def get_content(self): def get_content(self):
widget = QWidget() widget = QWidget()
self.heir_locktime = HeirsLockTimeEdit(widget, 0) self.heir_locktime = HeirsLockTimeEdit(widget, 0)
will_settings = self.bal_window.bal_plugin.WILL_SETTINGS.get() #will_settings = self.bal_window.bal_plugin.WILL_SETTINGS.get()
will_settings = self.bal_window.will_settings will_settings = self.bal_window.will_settings
self.heir_locktime.set_locktime(will_settings["locktime"]) self.heir_locktime.set_locktime(will_settings["locktime"])
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")
self.bal_window.will_settings["locktime"] = ( self.bal_window.update_locktime_widgets(self.heir_locktime.get_locktime())
self.heir_locktime.get_locktime()
if self.heir_locktime.get_locktime()
else "1y"
)
self.bal_window.bal_plugin.WILL_SETTINGS.set(self.bal_window.will_settings)
self.heir_locktime.valueEdited.connect(on_heir_locktime) self.heir_locktime.valueEdited.connect(on_heir_locktime)
@@ -1854,11 +1917,7 @@ class BalWizardLocktimeAndFeeWidget(BalWizardWidget):
def on_heir_threshold(): def on_heir_threshold():
if not self.heir_threshold.get_locktime(): if not self.heir_threshold.get_locktime():
self.heir_threshold.set_locktime("180d") self.heir_threshold.set_locktime("180d")
self.bal_window.update_threshold_widgets(self.heir_threshold.get_locktime())
self.bal_window.will_settings["threshold"] = (
self.heir_threshold.get_locktime()
)
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)
@@ -2046,13 +2105,18 @@ class BalBuildWillDialog(BalDialog):
parent = bal_window.window parent = bal_window.window
BalDialog.__init__(self, parent, bal_window.bal_plugin, _("Building Will")) BalDialog.__init__(self, parent, bal_window.bal_plugin, _("Building Will"))
self.parent = parent self.parent = parent
self.updatemessage.connect(self.update) self.updatemessage.connect(self.msg_update)
self.bal_window = bal_window self.bal_window = bal_window
self.bal_plugin = bal_window.bal_plugin
self.message_label = QLabel(_("Building Will:")) self.message_label = QLabel(_("Building Will:"))
self.vbox = QVBoxLayout(self) self.vbox = QVBoxLayout(self)
self.vbox.addWidget(self.message_label) self.vbox.addWidget(self.message_label,0)
self.qwidget = QWidget() self.qwidget = QWidget(self)
self.vbox.addWidget(self.qwidget) self.vbox.addWidget(self.qwidget,1)
self.labelsbox=QVBoxLayout(self.qwidget)
self.setMinimumWidth(600)
self.setMinimumHeight(100)
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
self.labels = [] self.labels = []
self.check_row = None self.check_row = None
self.inval_row = None self.inval_row = None
@@ -2079,13 +2143,26 @@ class BalBuildWillDialog(BalDialog):
self.exec() self.exec()
def task_phase1(self): def task_phase1(self):
txs=None
_logger.debug("close plugin phase 1 started") _logger.debug("close plugin phase 1 started")
varrow = self.msg_set_status("checking variables")
try: try:
self.bal_window.init_class_variables() self.bal_window.init_class_variables()
except NoHeirsException: except CheckAliveException as cae:
_logger.error("no heirs exception") fee_per_byte = self.bal_window.will_settings.get("baltx_fees", 1)
return False, None tx = Will.invalidate_will(
varrow = self.msg_set_status("checking variables") self.bal_window.willitems, self.bal_window.wallet, fee_per_byte
)
if tx:
_logger.debug("during phase1 CAE: {}, Continue to invalidate".format(cae))
self.msg_set_checking(self.msg_warning("Check Alive Threshold Passed: you have to Invalidate your old Will"))
else:
raise cae
return None, tx
except Exception as e:
raise e
try: try:
_logger.debug("checking variables") _logger.debug("checking variables")
Will.check_amounts( Will.check_amounts(
@@ -2098,15 +2175,12 @@ class BalBuildWillDialog(BalDialog):
_logger.debug("variables ok") _logger.debug("variables ok")
self.msg_set_status("checking variables:", varrow, "Ok", self.COLOR_OK) self.msg_set_status("checking variables:", varrow, "Ok", self.COLOR_OK)
except AmountException: except AmountException:
self.msg_set_status( self.msg_set_checking(
"checking variables", self.msg_warning(
varrow,
_(
"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"
), )
self.COLOR_WARNING,
) )
self.msg_set_checking() self.msg_set_checking()
@@ -2121,9 +2195,10 @@ class BalBuildWillDialog(BalDialog):
return None, Will.invalidate_will( return None, Will.invalidate_will(
self.bal_window.willitems, self.bal_window.wallet, fee_per_byte self.bal_window.willitems, self.bal_window.wallet, fee_per_byte
) )
except NoHeirsException: except NoHeirsException as e:
_logger.debug("no heirs") _logger.debug("no heirs")
self.msg_set_checking("No Heirs") self.msg_set_checking("No Heirs")
raise e
except NotCompleteWillException as e: except NotCompleteWillException as e:
_logger.debug(f"not complete {e} true") _logger.debug(f"not complete {e} true")
message = False message = False
@@ -2147,13 +2222,15 @@ class BalBuildWillDialog(BalDialog):
if have_to_build: if have_to_build:
self.msg_set_building() self.msg_set_building()
try: try:
if not self.bal_window.build_will(): txs = self.bal_window.build_will()
if not txs:
self.msg_set_status( self.msg_set_status(
_("Balance is too low. No transaction was built"), _("Balance is too low. No transaction was built"),
None, None,
_("Skipped"), _("Skipped"),
self.COLOR_ERROR, self.COLOR_ERROR,
) )
return False,None
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):
@@ -2185,12 +2262,14 @@ class BalBuildWillDialog(BalDialog):
if not self.bal_window.willitems[wid].get_status("COMPLETE"): if not self.bal_window.willitems[wid].get_status("COMPLETE"):
have_to_sign = True have_to_sign = True
break break
return have_to_sign, None return have_to_sign, txs
def on_accept(self): def on_accept(self):
self.bal_window.update_all()
pass pass
def on_accept_phase2(self): def on_accept_phase2(self):
self.bal_window.update_all()
pass pass
def on_error_push(self): def on_error_push(self):
@@ -2228,6 +2307,7 @@ class BalBuildWillDialog(BalDialog):
self.msg_set_pushing(_("Broadcasting")) self.msg_set_pushing(_("Broadcasting"))
retry = False retry = False
try: try:
willexecutors = Willexecutors.get_willexecutor_transactions( willexecutors = Willexecutors.get_willexecutor_transactions(
self.bal_window.willitems self.bal_window.willitems
) )
@@ -2257,7 +2337,10 @@ class BalBuildWillDialog(BalDialog):
self.bal_window.willitems[wid].we["url"], wid, "Waiting" self.bal_window.willitems[wid].we["url"], wid, "Waiting"
) )
) )
self.bal_window.willitems[wid].check_willexecutor() self.bal_plugin = self.bal_window.bal_plugin
w = self.bal_window.willitems[wid]
w.set_check_willexecutor(Willexecutors.check_transaction(wid, w.we["url"]))
row = self.msg_edit_row( row = self.msg_edit_row(
"checked {} - {} : {}".format( "checked {} - {} : {}".format(
self.bal_window.willitems[wid].we["url"], self.bal_window.willitems[wid].we["url"],
@@ -2268,7 +2351,6 @@ class BalBuildWillDialog(BalDialog):
) )
except Exception as e: except Exception as e:
_logger.error(f"loop push error:{e}") _logger.error(f"loop push error:{e}")
raise e raise e
if retry: if retry:
@@ -2306,12 +2388,11 @@ class BalBuildWillDialog(BalDialog):
on_error=self.on_error_phase1, on_error=self.on_error_phase1,
) )
def on_error(self, error):
_logger.error(error)
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)
#if not tx:
# self.msg_edit_row(self.msg_error("Error, no tx was built"))
# return
_logger.debug("have to sign {}".format(self.have_to_sign)) _logger.debug("have to sign {}".format(self.have_to_sign))
password = None password = None
if self.have_to_sign is None: if self.have_to_sign is None:
@@ -2359,7 +2440,6 @@ class BalBuildWillDialog(BalDialog):
self.close() self.close()
def closeEvent(self, event): def closeEvent(self, event):
self.bal_window.update_all()
self._stopping = True self._stopping = True
self.thread.stop() self.thread.stop()
@@ -2388,15 +2468,26 @@ class BalBuildWillDialog(BalDialog):
self.msg_set_pushing(self.msg_ok()) self.msg_set_pushing(self.msg_ok())
except Exception as e: except Exception as e:
td = traceback.format_exc()
self.msg_set_pushing(self.msg_error(e)) self.msg_set_pushing(self.msg_error(e))
self.msg_edit_row(self.msg_ok()) self.msg_edit_row(self.msg_ok())
self.wait(5) self.wait(5)
def on_error(self, error):
_logger.error(error)
pass
def on_error_phase1(self, error): def on_error_phase1(self, error):
_logger.error(f"error phase1: {error}") self.bal_window.update_all()
a,b,c = error
self.msg_edit_row(self.msg_error(f"Error: {b}"))
_logger.error(f"error phase1: {b}")
def on_error_phase2(self, error): def on_error_phase2(self, error):
_logger.error(f"error phase2: { error}") self.bal_window.upade_all()
a,b,c = error
self.msg_edit_row(self.msg_error(f"Error: {b}"))
_logger.error(f"error phase2: {b}")
def msg_set_checking(self, status="Waiting", row=None): def msg_set_checking(self, status="Waiting", row=None):
row = self.check_row if row is None else row row = self.check_row if row is None else row
@@ -2448,10 +2539,11 @@ class BalBuildWillDialog(BalDialog):
def msg_edit_row(self, line, row=None): def msg_edit_row(self, line, row=None):
try: try:
self.labels[row] = line self.labels[row] = line
except Exception: except Exception as e:
self.labels.append(line) self.labels.append(line)
row = len(self.labels) - 1 row = len(self.labels) - 1
self.updatemessage.emit() self.updatemessage.emit()
return row return row
@@ -2463,13 +2555,36 @@ class BalBuildWillDialog(BalDialog):
pass pass
self.updatemessage.emit() self.updatemessage.emit()
def update(self): #def clear_layout(self,layout):
self.vbox.removeWidget(self.qwidget) # while layout.count():
self.qwidget = QWidget(self) # item = layout.takeAt(0)
labelsbox = QVBoxLayout(self.qwidget) # w = item.widget()
for label in self.labels: # if w:
labelsbox.addWidget(QLabel(label)) # w.setParent(None)
self.vbox.addWidget(self.qwidget) # w.deleteLater()
#def msg_update(self):
# self.clear_layout(self.labelsbox)
# for label in self.labels:
# label=label.replace("\n","<br>")
# qlabel=QLabel(label)
# qlabel.setWordWrap(True)
# self.labelsbox.addWidget(qlabel)
# self.labelsbox.activate()
# self.qwidget.setMinimumSize(self.labelsbox.sizeHint())
# self.qwidget.adjustSize()
# from PyQt6.QtWidgets import QApplication
# QApplication.processEvents()
#
# self.adjustSize()
def msg_update(self):
full_text = "<br><br>".join(self.labels).replace("\n", "<br>")
self.message_label.setText(full_text)
self.message_label.adjustSize()
#self.setMinimumHeight(len(self.labels)*40)
self.resize(self.sizeHint())
def get_text(self): def get_text(self):
return self.message_label.text() return self.message_label.text()
@@ -2477,7 +2592,7 @@ class BalBuildWillDialog(BalDialog):
pass pass
class HeirList(MyTreeView, MessageBoxMixin): class HeirListWidget(MyTreeView, MessageBoxMixin):
class Columns(MyTreeView.BaseColumnsEnum): class Columns(MyTreeView.BaseColumnsEnum):
NAME = enum.auto() NAME = enum.auto()
ADDRESS = enum.auto() ADDRESS = enum.auto()
@@ -2492,9 +2607,16 @@ class HeirList(MyTreeView, MessageBoxMixin):
ROLE_SORT_ORDER = Qt.ItemDataRole.UserRole + 1000 ROLE_SORT_ORDER = Qt.ItemDataRole.UserRole + 1000
ROLE_HEIR_KEY = Qt.ItemDataRole.UserRole + 1001 ROLE_HEIR_KEY = Qt.ItemDataRole.UserRole + 4000
key_role = ROLE_HEIR_KEY key_role = ROLE_HEIR_KEY
def createEditor(self, parent, option, index):
return QLineEdit(parent)
def setEditorData(self, editor, index):
editor.setText(index.data())
def setModelData(self, editor, model, index):
model.setData(index, editor.text())
def __init__(self, bal_window: "BalWindow", parent): def __init__(self, bal_window: "BalWindow", parent):
super().__init__( super().__init__(
parent=parent, parent=parent,
@@ -2509,6 +2631,7 @@ class HeirList(MyTreeView, MessageBoxMixin):
self.decimal_point = bal_window.window.get_decimal_point() self.decimal_point = bal_window.window.get_decimal_point()
self.bal_window = bal_window self.bal_window = bal_window
try: try:
self.setModel(QStandardItemModel(self)) self.setModel(QStandardItemModel(self))
self.sortByColumn(self.Columns.NAME, Qt.SortOrder.AscendingOrder) self.sortByColumn(self.Columns.NAME, Qt.SortOrder.AscendingOrder)
@@ -2592,12 +2715,6 @@ class HeirList(MyTreeView, MessageBoxMixin):
menu.addAction(_("Delete"), lambda: self.delete_heirs(selected_keys)) menu.addAction(_("Delete"), lambda: self.delete_heirs(selected_keys))
menu.exec(self.viewport().mapToGlobal(position)) menu.exec(self.viewport().mapToGlobal(position))
# def get_selected_keys(self):
# selected_keys = []
# for s_idx in self.selected_in_column(self.Columns.NAME):
# sel_key = self.model().itemFromIndex(s_idx).data(0)
# selected_keys.append(sel_key)
# return selected_keys
def update(self): def update(self):
current_key = self.get_role_data_for_current_item( current_key = self.get_role_data_for_current_item(
col=self.Columns.NAME, role=self.ROLE_HEIR_KEY col=self.Columns.NAME, role=self.ROLE_HEIR_KEY
@@ -2654,32 +2771,28 @@ class HeirList(MyTreeView, MessageBoxMixin):
menu.addAction(_("Import"), self.bal_window.import_heirs) menu.addAction(_("Import"), self.bal_window.import_heirs)
menu.addAction(_("Export"), lambda: self.bal_window.export_heirs()) menu.addAction(_("Export"), lambda: self.bal_window.export_heirs())
self.heir_locktime = HeirsLockTimeEdit(self, 0) threshold = self.bal_window.will_settings.get('threshold',None) if self.bal_window.will_settings else self.bal_window.window.wallet.db.get_dict("will_settings")['threshold']
locktime = self.bal_window.will_settings.get('locktime',None) if self.bal_window.will_settings else self.bal_window.window.wallet.db.get_dict("will_settings")['locktime']
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")
self.bal_window.will_settings["locktime"] = ( self.bal_window.update_locktime_widgets(self.heir_locktime.get_locktime())
self.heir_locktime.get_locktime()
if self.heir_locktime.get_locktime()
else "1y"
)
self.bal_window.bal_plugin.WILL_SETTINGS.set(self.bal_window.will_settings)
self.heir_locktime.valueEdited.connect(on_heir_locktime) self.heir_locktime.valueEdited.connect(on_heir_locktime)
self.heir_locktime.set_locktime(locktime)
self.heir_threshold = HeirsLockTimeEdit(self, 0) self.heir_threshold = HeirsLockTimeEdit(self, 0)
def on_heir_threshold(): def on_heir_threshold():
if not self.heir_threshold.get_locktime(): if not self.heir_threshold.get_locktime():
self.heir_threshold.set_locktime("180d") self.heir_threshold.set_locktime("180d")
self.bal_window.update_threshold_widgets(self.heir_threshold.get_locktime())
self.bal_window.will_settings["threshold"] = (
self.heir_threshold.get_locktime()
)
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_threshold.set_locktime(threshold)
self.heir_tx_fees = QSpinBox() self.heir_tx_fees = QSpinBox()
self.heir_tx_fees.setMinimum(1) self.heir_tx_fees.setMinimum(1)
@@ -2747,6 +2860,7 @@ class HeirList(MyTreeView, MessageBoxMixin):
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:
pass
_logger.debug(f"Exception update_will_settings {e}") _logger.debug(f"Exception update_will_settings {e}")
def build_transactions(self): def build_transactions(self):
@@ -2963,18 +3077,59 @@ class PreviewList(MyTreeView):
menu.addAction(_("Check"), self.check) menu.addAction(_("Check"), self.check)
menu.addAction(_("Invalidate"), self.invalidate_will) menu.addAction(_("Invalidate"), self.invalidate_will)
def make_hlayout(label, twidget, help_text):
tw = QWidget()
hlayout = QHBoxLayout(tw)
hlayout.addWidget(QLabel(label))
hlayout.addWidget(twidget)
hlayout.addWidget(HelpButton(help_text))
hlayout.addStretch(1)
spacer_widget = QWidget()
spacer_widget.setSizePolicy(
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding
)
hlayout.addWidget(spacer_widget)
return tw
wizard = QPushButton(_("Setup Wizard")) wizard = QPushButton(_("Setup Wizard"))
wizard.clicked.connect(self.bal_window.init_wizard) wizard.clicked.connect(self.bal_window.init_wizard)
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")) refresh = QPushButton(_("Refresh"))
display.clicked.connect(self.check) refresh.clicked.connect(self.check)
widget = QWidget() widget = QWidget()
hlayout = QHBoxLayout(widget) hlayout = QHBoxLayout(widget)
hlayout.addWidget(QLabel(_("Check Alive:")))
threshold = self.bal_window.will_settings.get('threshold',None) if self.bal_window.will_settings else self.bal_window.window.wallet.db.get_dict("will_settings")['threshold']
self.heir_threshold = HeirsLockTimeEdit(widget, 0)
self.heir_threshold.set_locktime(threshold)
def on_heir_threshold():
if not self.heir_threshold.get_locktime():
self.heir_threshold.set_locktime("180d")
self.bal_window.update_threshold_widgets(self.heir_threshold.get_locktime())
self.heir_threshold.valueEdited.connect(on_heir_threshold)
hlayout.addWidget(self.heir_threshold)
hlayout.addWidget(
HelpButton(
_(
"Check to ask for invalidation.\n"
+ "When less then this time is missing, ask to invalidate.\n"
+ "If you fail to invalidate during this time, your transactions will be delivered to your heirs.\n"
+ "if you choose Raw, you can insert various options based on suffix:\n"
+ " - d: number of days after current day(ex: 1d means tomorrow).\n"
+ " - y: number of years after currrent day(ex: 1y means one year from today).\n\n"
)
)
)
hlayout.addWidget(wizard) hlayout.addWidget(wizard)
hlayout.addWidget(display) hlayout.addWidget(refresh)
toolbar.insertWidget(2, widget) toolbar.insertWidget(2, widget)
self.menu = menu self.menu = menu
@@ -3012,13 +3167,6 @@ class PreviewList(MyTreeView):
self.update() self.update()
def check(self): 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 = BalBuildWillDialog(self.bal_window)
close_window.build_will_task() close_window.build_will_task()
@@ -3065,7 +3213,10 @@ class PreviewDialog(BalDialog, MessageBoxMixin):
self.size_label = QLabel() self.size_label = QLabel()
self.transactions_list = PreviewList(self.bal_window, self.will) self.transactions_list = PreviewList(self.bal_window, self.will)
self.bal_window.init_class_variables() try:
self.bal_window.init_class_variables()
except Exception as e:
_logger.error(f"PreviewDialog Exception: {e}")
self.check_will() self.check_will()
vbox = QVBoxLayout(self) vbox = QVBoxLayout(self)
@@ -3311,7 +3462,7 @@ class WillWidget(QWidget):
hlayout.addWidget(WillWidget(w, parent=parent)) hlayout.addWidget(WillWidget(w, parent=parent))
class WillExecutorList(MyTreeView): class WillExecutorListWidget(MyTreeView):
class Columns(MyTreeView.BaseColumnsEnum): class Columns(MyTreeView.BaseColumnsEnum):
SELECTED = enum.auto() SELECTED = enum.auto()
URL = enum.auto() URL = enum.auto()
@@ -3556,7 +3707,7 @@ class WillExecutorWidget(QWidget, MessageBoxMixin):
self.willexecutors_list = Willexecutors.get_willexecutors(self.bal_plugin) self.willexecutors_list = Willexecutors.get_willexecutors(self.bal_plugin)
self.size_label = QLabel() self.size_label = QLabel()
self.willexecutor_list = WillExecutorList(self) self.will_executor_list_widget = WillExecutorListWidget(self)
vbox = QVBoxLayout(self) vbox = QVBoxLayout(self)
vbox.addWidget(self.size_label) vbox.addWidget(self.size_label)
@@ -3573,7 +3724,7 @@ class WillExecutorWidget(QWidget, MessageBoxMixin):
hbox.addWidget(spacer_widget) hbox.addWidget(spacer_widget)
vbox.addWidget(widget) vbox.addWidget(widget)
vbox.addWidget(self.willexecutor_list) vbox.addWidget(self.will_executor_list_widget)
buttonbox = QHBoxLayout() buttonbox = QHBoxLayout()
b = QPushButton(_("Add")) b = QPushButton(_("Add"))
@@ -3597,7 +3748,7 @@ class WillExecutorWidget(QWidget, MessageBoxMixin):
buttonbox.addWidget(b) buttonbox.addWidget(b)
vbox.addLayout(buttonbox) vbox.addLayout(buttonbox)
# self.willexecutor_list.update() # self.will_executor_list_widget.update()
def add(self): def add(self):
self.willexecutors_list["http://localhost:8080"] = { self.willexecutors_list["http://localhost:8080"] = {
@@ -3605,11 +3756,11 @@ class WillExecutorWidget(QWidget, MessageBoxMixin):
"base_fee": 0, "base_fee": 0,
"status": "-1", "status": "-1",
} }
self.willexecutor_list.update() self.will_executor_list_widget.update()
def download_list(self): def download_list(self):
self.willexecutors_list.update(Willexecutors.download_list(self.bal_plugin,self.bal_window.willexecutors)) self.willexecutors_list.update(Willexecutors.download_list(self.bal_window.willexecutors))
self.willexecutor_list.update() self.will_executor_list_widget.update()
def export_file(self, path): def export_file(self, path):
Util.export_meta_gui( Util.export_meta_gui(
@@ -3632,13 +3783,13 @@ class WillExecutorWidget(QWidget, MessageBoxMixin):
wes = self.willexecutors_list wes = self.willexecutors_list
self.bal_window.ping_willexecutors(wes, self.parent) self.bal_window.ping_willexecutors(wes, self.parent)
self.willexecutors_list.update(wes) self.willexecutors_list.update(wes)
self.willexecutor_list.update() self.will_executor_list_widget.update()
def import_json_file(self, path): def import_json_file(self, path):
data = read_json_file(path) data = read_json_file(path)
data = self._validate(data) data = self._validate(data)
self.willexecutors_list.update(data) self.willexecutors_list.update(data)
self.willexecutor_list.update() self.will_executor_list_widget.update()
# TODO validate willexecutor json import file # TODO validate willexecutor json import file
def _validate(self, data): def _validate(self, data):
@@ -3662,10 +3813,10 @@ class WillExecutorDialog(BalDialog, MessageBoxMixin):
self.setMinimumSize(1000, 200) self.setMinimumSize(1000, 200)
vbox = QVBoxLayout(self) vbox = QVBoxLayout(self)
self.willexecutor_list = WillExecutorWidget( self.will_executor_list_widget = WillExecutorWidget(
self, self.bal_window, self.willexecutors_list self, self.bal_window, self.willexecutors_list
) )
vbox.addWidget(self.willexecutor_list) vbox.addWidget(self.will_executor_list_widget)
def is_hidden(self): def is_hidden(self):
return self.isMinimized() or self.isHidden() return self.isMinimized() or self.isHidden()
@@ -3682,3 +3833,10 @@ class WillExecutorDialog(BalDialog, MessageBoxMixin):
def closeEvent(self, event): def closeEvent(self, event):
event.accept() event.accept()
class CheckAliveException(Exception):
def __init__(self,timestamp_to_check):
self.timestamp_to_check = timestamp_to_check
def __str__(self):
return "Check alive expired please update it: {}".format(datetime.fromtimestamp(self.timestamp_to_check).isoformat())

13
util.py
View File

@@ -494,3 +494,16 @@ class Util:
del will[txid]["tx_fees"] del will[txid]["tx_fees"]
have_to_update = True have_to_update = True
return have_to_update return have_to_update
def text_to_hex(text: str) -> str:
"""Convert text to hexadecimal string"""
hex_string = text.encode('utf-8').hex()
return hex_string
def hex_to_text(hex_string: str) -> str:
"""Convert hexadecimal string back to text (for verification)"""
try:
return bytes.fromhex(hex_string).decode('utf-8')
except Exception:
return "Error: Invalid hex string"

31
will.py
View File

@@ -148,16 +148,6 @@ class Will:
inp._TxInput__value_sats = change.value inp._TxInput__value_sats = change.value
return inp 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"): 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):
@@ -339,6 +329,7 @@ class Will:
utxos = wallet.get_utxos() utxos = wallet.get_utxos()
filtered_inputs = [] filtered_inputs = []
prevout_to_spend = [] prevout_to_spend = []
current_height = Util.get_current_height(wallet.network)
for prevout_str, ws in inputs.items(): for prevout_str, ws in inputs.items():
for w in ws: for w in ws:
if w[0] not in filtered_inputs: if w[0] not in filtered_inputs:
@@ -348,6 +339,8 @@ class Will:
balance = 0 balance = 0
utxo_to_spend = [] utxo_to_spend = []
for utxo in utxos: for utxo in utxos:
if utxo.is_coinbase_output() and utxo.block_height < current_height+100:
continue
utxo_str = utxo.prevout.to_str() utxo_str = utxo.prevout.to_str()
if utxo_str in prevout_to_spend: if utxo_str in prevout_to_spend:
balance += inputs[utxo_str][0][2].value_sats() balance += inputs[utxo_str][0][2].value_sats()
@@ -356,7 +349,7 @@ class Will:
change_addresses = wallet.get_change_addresses_for_new_transaction() change_addresses = wallet.get_change_addresses_for_new_transaction()
out = PartialTxOutput.from_address_and_value(change_addresses[0], balance) out = PartialTxOutput.from_address_and_value(change_addresses[0], balance)
out.is_change = True out.is_change = True
locktime = Util.get_current_height(wallet.network) locktime = current_height
tx = PartialTransaction.from_io( tx = PartialTransaction.from_io(
utxo_to_spend, [out], locktime=locktime, version=2 utxo_to_spend, [out], locktime=locktime, version=2
) )
@@ -560,6 +553,9 @@ class Will:
raise WillExpiredException( raise WillExpiredException(
f"Will Expired {wid[0][0]}: {locktime}<{timestamp_to_check}" f"Will Expired {wid[0][0]}: {locktime}<{timestamp_to_check}"
) )
else:
from datetime import datetime
_logger.debug(f"Will Not Expired {wid[0][0]}: {datetime.fromtimestamp(locktime).isoformat()} > {datetime.fromtimestamp(timestamp_to_check).isoformat()}")
# def check_all_input_spent_are_in_wallet(): # def check_all_input_spent_are_in_wallet():
# _logger.info("check all input spent are in wallet or valid txs") # _logger.info("check all input spent are in wallet or valid txs")
@@ -793,9 +789,9 @@ class WillItem(Logger):
iw = inp[1] iw = inp[1]
self.set_anticipate(iw) self.set_anticipate(iw)
def check_willexecutor(self): def set_check_willexecutor(self,resp):
try: try:
if resp := Willexecutors.check_transaction(self._id, self.we["url"]): if resp :
if "tx" in resp and resp["tx"] == str(self.tx): if "tx" in resp and resp["tx"] == str(self.tx):
self.set_status("PUSHED") self.set_status("PUSHED")
self.set_status("CHECKED") self.set_status("CHECKED")
@@ -835,7 +831,12 @@ class WillItem(Logger):
class WillException(Exception): class WillException(Exception):
pass def __init__(self,msg="WillException"):
self.msg=msg
Exception.__init__(self)
def __str__(self):
return self.msg
class WillExpiredException(WillException): class WillExpiredException(WillException):
@@ -872,8 +873,6 @@ class WillExecutorNotPresent(NotCompleteWillException):
class NoHeirsException(WillException): class NoHeirsException(WillException):
pass pass
class AmountException(WillException): class AmountException(WillException):
pass pass

View File

@@ -132,7 +132,7 @@ class Willexecutors:
raise Exception("You are offline.") raise Exception("You are offline.")
_logger.debug(f"<-- {method} {url} {data}") _logger.debug(f"<-- {method} {url} {data}")
headers = {} headers = {}
headers["user-agent"] = f"BalPlugin v:{BalPlugin.version()}" headers["user-agent"] = f"BalPlugin v:{BalPlugin.__version__}"
headers["Content-Type"] = "text/plain" headers["Content-Type"] = "text/plain"
if not handle_response: if not handle_response:
handle_response = Willexecutors.handle_response handle_response = Willexecutors.handle_response
@@ -260,7 +260,7 @@ class Willexecutors:
def download_list(bal_plugin,old_willexecutors): def download_list(old_willexecutors):
try: try:
willexecutors = Willexecutors.send_request( willexecutors = Willexecutors.send_request(
"get", "get",
@@ -280,7 +280,7 @@ class Willexecutors:
_logger.error(f"Failed to download willexecutors list: {e}") _logger.error(f"Failed to download willexecutors list: {e}")
return {} return {}
def get_willexecutors_list_from_json(bal_plugin): def get_willexecutors_list_from_json():
try: try:
with open("willexecutors.json") as f: with open("willexecutors.json") as f:
willexecutors = json.load(f) willexecutors = json.load(f)