11 Commits

11 changed files with 403 additions and 229 deletions

View File

@@ -1 +1 @@
0.2.3 0.2.4

27
bal.py
View File

@@ -1,6 +1,7 @@
import os import os
#import random
#import zipfile as zipfile_lib # import random
# import zipfile as zipfile_lib
from electrum import json_db from electrum import json_db
from electrum.logging import get_logger from electrum.logging import get_logger
@@ -9,7 +10,7 @@ from electrum.transaction import tx_from_any
def get_will_settings(x): def get_will_settings(x):
#print(x) # print(x)
pass pass
@@ -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"])
@@ -68,15 +68,9 @@ class BalPlugin(BasePlugin):
self.base_dir = os.path.join(config.electrum_path(), "bal") self.base_dir = os.path.join(config.electrum_path(), "bal")
self.plugin_dir = os.path.split(os.path.realpath(__file__))[0] self.plugin_dir = os.path.split(os.path.realpath(__file__))[0]
zipfile = "/".join(self.plugin_dir.split("/")[:-1]) 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 import sys
sys.path.insert(0, zipfile) sys.path.insert(0, zipfile)
# print("sono state listate?")
self.parent = parent self.parent = parent
self.config = config self.config = config
self.name = name self.name = name
@@ -96,10 +90,10 @@ class BalPlugin(BasePlugin):
self.PREVIEW = BalConfig(config, "bal_preview", True) self.PREVIEW = BalConfig(config, "bal_preview", True)
self.SAVE_TXS = BalConfig(config, "bal_save_txs", True) self.SAVE_TXS = BalConfig(config, "bal_save_txs", True)
self.WILLEXECUTORS = BalConfig(config, "bal_willexecutors", True) self.WILLEXECUTORS = BalConfig(config, "bal_willexecutors", True)
self.PING_WILLEXECUTORS = BalConfig(config, "bal_ping_willexecutors", True) # self.PING_WILLEXECUTORS = BalConfig(config, "bal_ping_willexecutors", True)
self.ASK_PING_WILLEXECUTORS = BalConfig( # self.ASK_PING_WILLEXECUTORS = BalConfig(
config, "bal_ask_ping_willexecutors", True # config, "bal_ask_ping_willexecutors", True
) # )
self.NO_WILLEXECUTOR = BalConfig(config, "bal_no_willexecutor", True) self.NO_WILLEXECUTOR = BalConfig(config, "bal_no_willexecutor", True)
self.HIDE_REPLACED = BalConfig(config, "bal_hide_replaced", True) self.HIDE_REPLACED = BalConfig(config, "bal_hide_replaced", True)
self.HIDE_INVALIDATED = BalConfig(config, "bal_hide_invalidated", True) self.HIDE_INVALIDATED = BalConfig(config, "bal_hide_invalidated", True)
@@ -145,16 +139,13 @@ 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):
# print(type(will_settings))
# print(will_settings.get('baltx_fees',1),1)
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"):
will_settings["threshold"] = "180d" will_settings["threshold"] = "180d"
if not will_settings.get("locktime") == "": if not will_settings.get("locktime"):
will_settings["locktime"] = "1y" will_settings["locktime"] = "1y"
return will_settings return will_settings
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"}

103
heirs.py
View File

@@ -29,6 +29,8 @@ 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 import constants
if TYPE_CHECKING: if TYPE_CHECKING:
from .simple_config import SimpleConfig from .simple_config import SimpleConfig
@@ -41,6 +43,7 @@ HEIR_ADDRESS = 0
HEIR_AMOUNT = 1 HEIR_AMOUNT = 1
HEIR_LOCKTIME = 2 HEIR_LOCKTIME = 2
HEIR_REAL_AMOUNT = 3 HEIR_REAL_AMOUNT = 3
HEIR_DUST_AMOUNT = 4
TRANSACTION_LABEL = "inheritance transaction" TRANSACTION_LABEL = "inheritance transaction"
@@ -88,35 +91,41 @@ def prepare_transactions(locktimes, available_utxos, fees, wallet):
txsout = {} txsout = {}
locktime, _ = Util.get_lowest_locktimes(locktimes) locktime, _ = Util.get_lowest_locktimes(locktimes)
if not locktime: if not locktime:
_logger.info("prepare transactions, no locktime")
return return
locktime = locktime[0] locktime = locktime[0]
heirs = locktimes[locktime] heirs = locktimes[locktime]
vero = True true = True
while vero: while true:
vero = False true = False
fee = fees.get(locktime, 0) fee = fees.get(locktime, 0)
out_amount = fee out_amount = fee
description = "" description = ""
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]
):
try: try:
if len(heir) > HEIR_REAL_AMOUNT:
real_amount = heir[HEIR_REAL_AMOUNT] real_amount = heir[HEIR_REAL_AMOUNT]
out_amount += real_amount
description += f"{name}\n"
paid_heirs[name] = heir
outputs.append( outputs.append(
PartialTxOutput.from_address_and_value( PartialTxOutput.from_address_and_value(
heir[HEIR_ADDRESS], real_amount heir[HEIR_ADDRESS], real_amount
) )
) )
else: out_amount += real_amount
pass 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: except Exception as e:
heir[HEIR_REAL_AMOUNT] = e
_logger.error(f"error preparing transactions: {e}")
pass pass
paid_heirs[name] = heir
in_amount = 0.0 in_amount = 0.0
used_utxos = [] used_utxos = []
@@ -125,13 +134,19 @@ def prepare_transactions(locktimes, available_utxos, fees, wallet):
value = utxo.value_sats() value = utxo.value_sats()
in_amount += value in_amount += value
used_utxos.append(utxo) used_utxos.append(utxo)
if in_amount > out_amount: if in_amount >= out_amount:
break break
except IndexError as e: except IndexError as e:
_logger.error(
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):
break _logger.error(
"error preparing transactions in_amount < out_amount ({} < {}) "
)
continue
heirsvalue = out_amount heirsvalue = out_amount
change = get_change_output(wallet, in_amount, out_amount, fee) change = get_change_output(wallet, in_amount, out_amount, fee)
if change: if change:
@@ -263,9 +278,10 @@ def get_change_output(wallet, in_amount, out_amount, fee):
class Heirs(dict, Logger): class Heirs(dict, Logger):
def __init__(self, db: "WalletDB"): def __init__(self, wallet):
Logger.__init__(self) Logger.__init__(self)
self.db = db self.db = wallet.db
self.wallet = wallet
d = self.db.get("heirs", {}) d = self.db.get("heirs", {})
try: try:
self.update(d) self.update(d)
@@ -303,7 +319,7 @@ class Heirs(dict, Logger):
locktime = Util.parse_locktime_string(self[key][HEIR_LOCKTIME]) locktime = Util.parse_locktime_string(self[key][HEIR_LOCKTIME])
if locktime > from_locktime and not a or locktime <= from_locktime and a: if locktime > from_locktime and not a or locktime <= from_locktime and a:
locktimes[int(locktime)] = None locktimes[int(locktime)] = None
return locktimes.keys() return list(locktimes.keys())
def check_locktime(self): def check_locktime(self):
return False return False
@@ -317,6 +333,8 @@ class Heirs(dict, Logger):
column = HEIR_AMOUNT column = HEIR_AMOUNT
if real: if real:
column = HEIR_REAL_AMOUNT column = HEIR_REAL_AMOUNT
if "DUST" in str(v[column]):
column = HEIR_DUST_AMOUNT
value = int( value = int(
math.floor( math.floor(
total_balance total_balance
@@ -327,6 +345,10 @@ class Heirs(dict, Logger):
if value > wallet.dust_threshold(): if value > wallet.dust_threshold():
heir_list[key].insert(HEIR_REAL_AMOUNT, value) heir_list[key].insert(HEIR_REAL_AMOUNT, value)
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: except Exception as e:
raise e raise e
@@ -346,27 +368,32 @@ class Heirs(dict, Logger):
fixed_amount = 0.0 fixed_amount = 0.0
percent_heirs = {} percent_heirs = {}
percent_amount = 0.0 percent_amount = 0.0
fixed_amount_with_dust =0.0
for key in self.keys(): for key in self.keys():
try: try:
cmp = ( cmp = (
Util.parse_locktime_string(self[key][HEIR_LOCKTIME]) - from_locktime Util.parse_locktime_string(self[key][HEIR_LOCKTIME]) - from_locktime
) )
if cmp <= 0: if cmp <= 0:
_logger.debug("cmp < 0 {} {} {} ".format(cmp, key, self[key][HEIR_LOCKTIME], from_locktime))
continue continue
if Util.is_perc(self[key][HEIR_AMOUNT]): if Util.is_perc(self[key][HEIR_AMOUNT]):
percent_amount += float(self[key][HEIR_AMOUNT][:-1]) percent_amount += float(self[key][HEIR_AMOUNT][:-1])
percent_heirs[key] = list(self[key]) percent_heirs[key] = list(self[key])
else: else:
heir_amount = int(math.floor(float(self[key][HEIR_AMOUNT]))) heir_amount = int(math.floor(float(self[key][HEIR_AMOUNT])))
fixed_amount_with_dust += heir_amount
fixed_heirs[key] = list(self[key])
if heir_amount > dust_threshold: if heir_amount > dust_threshold:
fixed_amount += heir_amount fixed_amount += heir_amount
fixed_heirs[key] = list(self[key])
fixed_heirs[key].insert(HEIR_REAL_AMOUNT, heir_amount) fixed_heirs[key].insert(HEIR_REAL_AMOUNT, heir_amount)
else: else:
pass fixed_heirs[key] = list(self[key])
fixed_heirs[key].insert(HEIR_REAL_AMOUNT, f"DUST: {heir_amount}")
fixed_heirs[key].insert(HEIR_DUST_AMOUNT, heir_amount)
except Exception as e: except Exception as e:
_logger.error(e) _logger.error(e)
return fixed_heirs, fixed_amount, percent_heirs, percent_amount return fixed_heirs, fixed_amount, percent_heirs, percent_amount, fixed_amount_with_dust
def prepare_lists( def prepare_lists(
self, balance, total_fees, wallet, willexecutor=False, from_locktime=0 self, balance, total_fees, wallet, willexecutor=False, from_locktime=0
@@ -399,9 +426,13 @@ class Heirs(dict, Logger):
), ),
heir_list.update(willexecutors) heir_list.update(willexecutors)
newbalance -= willexecutors_amount newbalance -= willexecutors_amount
fixed_heirs, fixed_amount, percent_heirs, percent_amount = ( if newbalance<0:
raise WillExecutorFeeException(willexecutor)
a=list(self.fixed_percent_lists_amount(from_locktime, wallet.dust_threshold()))
fixed_heirs, fixed_amount, percent_heirs, percent_amount,fixed_amount_with_dust = (
self.fixed_percent_lists_amount(from_locktime, wallet.dust_threshold()) self.fixed_percent_lists_amount(from_locktime, wallet.dust_threshold())
) )
if fixed_amount > newbalance: if fixed_amount > newbalance:
fixed_amount = self.normalize_perc( fixed_amount = self.normalize_perc(
fixed_heirs, newbalance, fixed_amount, wallet fixed_heirs, newbalance, fixed_amount, wallet
@@ -422,7 +453,7 @@ class Heirs(dict, Logger):
if newbalance > 0: if newbalance > 0:
newbalance += fixed_amount newbalance += fixed_amount
fixed_amount = self.normalize_perc( fixed_amount = self.normalize_perc(
fixed_heirs, newbalance, fixed_amount, wallet, real=True fixed_heirs, newbalance, fixed_amount_with_dust, wallet, real=True
) )
newbalance -= fixed_amount newbalance -= fixed_amount
heir_list.update(fixed_heirs) heir_list.update(fixed_heirs)
@@ -449,6 +480,7 @@ class Heirs(dict, Logger):
): ):
Heirs._validate(self) Heirs._validate(self)
if len(self) <= 0: if len(self) <= 0:
_logger.info("while building transactions there was no heirs")
return return
balance = 0.0 balance = 0.0
len_utxo_set = 0 len_utxo_set = 0
@@ -464,6 +496,7 @@ class Heirs(dict, Logger):
len_utxo_set += 1 len_utxo_set += 1
available_utxos.append(utxo) available_utxos.append(utxo)
if len_utxo_set == 0: if len_utxo_set == 0:
_logger.info("no usable utxos")
return return
j = -2 j = -2
willexecutorsitems = list(willexecutors.items()) willexecutorsitems = list(willexecutors.items())
@@ -487,7 +520,7 @@ class Heirs(dict, Logger):
break break
fees = {} fees = {}
i = 0 i = 0
while True: while i<10:
txs = {} txs = {}
redo = False redo = False
i += 1 i += 1
@@ -495,9 +528,14 @@ class Heirs(dict, Logger):
for fee in fees: for fee in fees:
total_fees += int(fees[fee]) total_fees += int(fees[fee])
newbalance = balance newbalance = balance
try:
locktimes, onlyfixed = self.prepare_lists( locktimes, onlyfixed = self.prepare_lists(
balance, total_fees, wallet, willexecutor, from_locktime balance, total_fees, wallet, willexecutor, from_locktime
) )
except WillExecutorFeeException as e:
i=10
continue
if locktimes:
try: try:
txs = prepare_transactions( txs = prepare_transactions(
locktimes, available_utxos[:], fees, wallet locktimes, available_utxos[:], fees, wallet
@@ -505,6 +543,7 @@ class Heirs(dict, Logger):
if not txs: if not txs:
return {} return {}
except Exception as e: except Exception as e:
_logger.error(f"build transactions: error preparing transactions: {e}")
try: try:
if "w!ll3x3c" in e.heirname: if "w!ll3x3c" in e.heirname:
Willexecutors.is_selected(willexecutors[w], False) Willexecutors.is_selected(willexecutors[w], False)
@@ -533,6 +572,9 @@ class Heirs(dict, Logger):
break break
if i >= 10: if i >= 10:
break break
else:
_logger.info(f"no locktimes for willexecutor {willexecutor} skipped")
break
alltxs.update(txs) alltxs.update(txs)
return alltxs return alltxs
@@ -632,7 +674,7 @@ class Heirs(dict, Logger):
return None return None
def validate_address(address): 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}") raise NotAnAddress(f"not an address,{address}")
return address return address
@@ -661,12 +703,14 @@ class Heirs(dict, Logger):
return (address, amount, locktime) return (address, amount, locktime)
def _validate(data, timestamp_to_check=False): def _validate(data, timestamp_to_check=False):
for k, v in list(data.items()): for k, v in list(data.items()):
if k == "heirs": if k == "heirs":
return Heirs._validate(v) return Heirs._validate(v, timestamp_to_check)
try: try:
Heirs.validate_heir(k, v) Heirs.validate_heir(k, v, timestamp_to_check)
except Exception as e: except Exception as e:
_logger.info(f"exception heir removed {e}")
data.pop(k) data.pop(k)
return data return data
@@ -685,3 +729,14 @@ class LocktimeNotValid(ValueError):
class HeirExpiredException(LocktimeNotValid): class HeirExpiredException(LocktimeNotValid):
pass pass
class HeirAmountIsDustException(Exception):
pass
class NoHeirsException(Exception):
pass
class WillExecutorFeeException(Exception):
def __init__(self,willexecutor):
self.willexecutor=willexecutor
def __str__(self):
return "WillExecutorFeeException: {} fee:{}".format(self.willexecutor['url'],self.willexecutor['base_fee'])

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", "name": "BAL",
"fullname": "Bitcoin After Life", "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", "author":"Svatantrya",
"available_for": ["qt"], "available_for": ["qt"],
"icon":"icons/bal32x32.png" "icon":"icons/bal32x32.png"

302
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 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,
@@ -295,8 +295,8 @@ class Plugin(BalPlugin, Logger):
lbl_logo = QLabel() lbl_logo = QLabel()
lbl_logo.setPixmap(qicon) lbl_logo.setPixmap(qicon)
heir_ping_willexecutors = bal_checkbox(self.PING_WILLEXECUTORS) # heir_ping_willexecutors = bal_checkbox(self.PING_WILLEXECUTORS)
heir_ask_ping_willexecutors = bal_checkbox(self.ASK_PING_WILLEXECUTORS) # heir_ask_ping_willexecutors = bal_checkbox(self.ASK_PING_WILLEXECUTORS)
heir_no_willexecutor = bal_checkbox(self.NO_WILLEXECUTOR) heir_no_willexecutor = bal_checkbox(self.NO_WILLEXECUTOR)
def on_multiverse_change(): def on_multiverse_change():
@@ -327,27 +327,27 @@ class Plugin(BalPlugin, Logger):
2, 2,
"Hide invalidated transactions from will detail and list", "Hide invalidated transactions from will detail and list",
) )
add_widget( # add_widget(
grid, # grid,
"Ping Willexecutors", # "Ping Willexecutors",
heir_ping_willexecutors, # heir_ping_willexecutors,
3, # 3,
"Ping willexecutors to get payment info before compiling will", # "Ping willexecutors to get payment info before compiling will",
) # )
add_widget( # add_widget(
grid, # grid,
" - Ask before", # " - Ask before",
heir_ask_ping_willexecutors, # heir_ask_ping_willexecutors,
4, # 4,
"Ask before to ping willexecutor", # "Ask before to ping willexecutor",
) # )
add_widget( # add_widget(
grid, # grid,
"Backup Transaction", # "Backup Transaction",
heir_no_willexecutor, # heir_no_willexecutor,
5, # 5,
"Add transactions without willexecutor", # "Add transactions without willexecutor",
) # )
# add_widget(grid,"Enable Multiverse(EXPERIMENTAL/BROKEN)",heir_enable_multiverse,6,"enable multiple locktimes, will import.... ") # add_widget(grid,"Enable Multiverse(EXPERIMENTAL/BROKEN)",heir_enable_multiverse,6,"enable multiple locktimes, will import.... ")
grid.addWidget(heir_repush, 7, 0) grid.addWidget(heir_repush, 7, 0)
grid.addWidget( grid.addWidget(
@@ -401,13 +401,13 @@ class BalWindow(Logger):
self.willitems = {} self.willitems = {}
self.willexecutors = {} self.willexecutors = {}
self.will_settings = None self.will_settings = None
self.heirs_tab = self.create_heirs_tab()
self.will_tab = self.create_will_tab()
self.ok = False self.ok = False
self.disable_plugin = True self.disable_plugin = True
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.heirs_tab = self.create_heirs_tab()
self.will_tab = self.create_will_tab()
self.wallet = self.window.wallet 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
@@ -419,7 +419,7 @@ class BalWindow(Logger):
tab.tab_icon = icon tab.tab_icon = icon
tab.tab_description = description tab.tab_description = description
tab.tab_pos = len(tabs) tab.tab_pos = len(tabs)
if tab.is_shown_cv: if tab.is_shown_cv.get():
tabs.addTab(tab, icon, description.replace("&", "")) tabs.addTab(tab, icon, description.replace("&", ""))
def add_toggle_action(tab): def add_toggle_action(tab):
@@ -473,7 +473,7 @@ class BalWindow(Logger):
self.bal_plugin, update=False, bal_window=self self.bal_plugin, update=False, bal_window=self
) )
if not self.heirs: if not self.heirs:
self.heirs = Heirs._validate(Heirs(self.wallet.db)) self.heirs = Heirs._validate(Heirs(self.wallet))
if not self.will: if not self.will:
self.will = self.wallet.db.get_dict("will") self.will = self.wallet.db.get_dict("will")
Util.fix_will_tx_fees(self.will) Util.fix_will_tx_fees(self.will)
@@ -498,9 +498,9 @@ class BalWindow(Logger):
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.update_will_settings()
self.heir_list.update()
def init_wizard(self): def init_wizard(self):
wizard_dialog = BalWizardDialog(self) wizard_dialog = BalWizardDialog(self)
@@ -514,7 +514,7 @@ class BalWindow(Logger):
self.heir_list = l = HeirList(self, self.window) self.heir_list = l = HeirList(self, self.window)
tab = self.window.create_list_tab(l) tab = self.window.create_list_tab(l)
tab.is_shown_cv = shown_cv(True) tab.is_shown_cv = shown_cv(False)
return tab return tab
def create_will_tab(self): def create_will_tab(self):
@@ -625,7 +625,11 @@ class BalWindow(Logger):
def delete_heirs(self, heirs): def delete_heirs(self, heirs):
for heir in heirs: for heir in heirs:
try:
del self.heirs[heir] del self.heirs[heir]
except Exception as e:
_logger.debug(f"error deleting heir: {heir} {e}")
pass
self.heirs.save() self.heirs.save()
self.heir_list.update() self.heir_list.update()
return True return True
@@ -655,7 +659,7 @@ class BalWindow(Logger):
Will.normalize_will(self.willitems, self.wallet) Will.normalize_will(self.willitems, self.wallet)
def build_will(self, ignore_duplicate=True, keep_original=True): def build_will(self, ignore_duplicate=True, keep_original=True):
_logger.debug("building will...")
will = {} will = {}
willtodelete = [] willtodelete = []
willtoappend = {} willtoappend = {}
@@ -664,7 +668,6 @@ class BalWindow(Logger):
self.willexecutors = Willexecutors.get_willexecutors( self.willexecutors = Willexecutors.get_willexecutors(
self.bal_plugin, update=False, bal_window=self self.bal_plugin, update=False, bal_window=self
) )
if not self.no_willexecutor: if not self.no_willexecutor:
f = False f = False
@@ -672,6 +675,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("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"
) )
@@ -682,7 +686,8 @@ class BalWindow(Logger):
None, None,
self.date_to_check, self.date_to_check,
) )
self.logger.info(txs)
self.logger.info(f"txs built: {txs}")
creation_time = time.time() creation_time = time.time()
if txs: if txs:
for txid in txs: for txid in txs:
@@ -701,7 +706,14 @@ class BalWindow(Logger):
tx["txchildren"] = [] tx["txchildren"] = []
will[txid] = WillItem(tx, _id=txid, wallet=self.wallet) will[txid] = WillItem(tx, _id=txid, wallet=self.wallet)
self.update_will(will) 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}")
return {}
except Exception as e: except Exception as e:
self.logger.info(f"Exception build_will: {e}")
raise e raise e
pass pass
return self.willitems return self.willitems
@@ -1179,7 +1191,7 @@ class BalWindow(Logger):
parent = self parent = self
def on_success(result): def on_success(result):
#del self.waiting_dialog # del self.waiting_dialog
try: try:
parent.willexecutor_list.update() parent.willexecutor_list.update()
except Exception as e: except Exception as e:
@@ -1577,7 +1589,7 @@ class BalWizardDialog(BalDialog):
) )
def on_next_we(self): def on_next_we(self):
close_window = BalBuildWillDialog(self.bal_window, self) close_window = BalBuildWillDialog(self.bal_window)
close_window.build_will_task() close_window.build_will_task()
self.close() 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)) # self.next_widget(BalWizardLocktimeAndFeeWidget(self.bal_window,self,self.on_next_locktimeandfee,self.on_next_wedonwload,self.on_next_wedonwload.on_cancel_heir))
@@ -1681,13 +1693,13 @@ class BalWizardHeirsWidget(BalWizardWidget):
) )
def get_content(self): def get_content(self):
self.heirs_list = HeirList(self.bal_window, self.parent) self.heirs_list = HeirList(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"))
button_import.clicked.connect(self.import_from_file) button_import.clicked.connect(self.import_from_file)
button_export = QPushButton(_("Export")) button_export = QPushButton(_("Export"))
button_import.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.heirs_list)
@@ -1759,7 +1771,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) 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:
@@ -1820,6 +1834,7 @@ class BalWizardLocktimeAndFeeWidget(BalWizardWidget):
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
self.heir_locktime.set_locktime(will_settings["locktime"]) self.heir_locktime.set_locktime(will_settings["locktime"])
def on_heir_locktime(): def on_heir_locktime():
@@ -2023,11 +2038,15 @@ class bal_checkbox(QCheckBox):
class BalBuildWillDialog(BalDialog): class BalBuildWillDialog(BalDialog):
updatemessage = pyqtSignal() updatemessage = pyqtSignal()
COLOR_WARNING='#cfa808'
COLOR_ERROR='#ff0000'
COLOR_OK='#05ad05'
def __init__(self, bal_window, parent=None): def __init__(self, bal_window, parent=None):
if not parent: if not parent:
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.updatemessage.connect(self.update) self.updatemessage.connect(self.update)
self.bal_window = bal_window self.bal_window = bal_window
self.message_label = QLabel("Building Will:") self.message_label = QLabel("Building Will:")
@@ -2078,25 +2097,24 @@ class BalBuildWillDialog(BalDialog):
self.bal_window.window.wallet.dust_threshold(), self.bal_window.window.wallet.dust_threshold(),
) )
_logger.debug("variables ok") _logger.debug("variables ok")
self.msg_set_status("checking variables:", varrow,"Ok") self.msg_set_status("checking variables:", varrow, "Ok",self.COLOR_OK)
except AmountException: except AmountException:
self.msg_set_status( self.msg_set_status(
"checking variables", "checking variables",
varrow, varrow,
'<font color="#ff0000">' _(
+ _(
"In the inheritance process, " "In the inheritance process, "
+ "the entire wallet will always be fully emptied. \n" + "the entire wallet will always be fully emptied. \n"
+ "Your settings require an adjustment of the amounts" + "Your settings require an adjustment of the amounts"
) ),
+ "</font>", self.COLOR_WARNING
) )
self.msg_set_checking() self.msg_set_checking()
have_to_build = False have_to_build = False
try: try:
self.bal_window.check_will() self.bal_window.check_will()
self.msg_set_checking("Ok") self.msg_set_checking(self.msg_ok())
except WillExpiredException: except WillExpiredException:
_logger.debug("expired") _logger.debug("expired")
self.msg_set_checking("Expired") self.msg_set_checking("Expired")
@@ -2108,7 +2126,7 @@ class BalBuildWillDialog(BalDialog):
_logger.debug("no heirs") _logger.debug("no heirs")
self.msg_set_checking("No Heirs") self.msg_set_checking("No Heirs")
except NotCompleteWillException as e: except NotCompleteWillException as e:
_logger.debug("not complete", e) _logger.debug(f"not complete {e} true")
message = False message = False
have_to_build = True have_to_build = True
if isinstance(e, HeirChangeException): if isinstance(e, HeirChangeException):
@@ -2122,7 +2140,7 @@ class BalBuildWillDialog(BalDialog):
elif isinstance(e, HeirNotFoundException): elif isinstance(e, HeirNotFoundException):
message = "Heir not found" message = "Heir not found"
if message: if message:
_logger.debug("message") _logger.debug(f"message: {message}")
self.msg_set_checking(message) self.msg_set_checking(message)
else: else:
self.msg_set_checking("New") self.msg_set_checking("New")
@@ -2130,14 +2148,39 @@ class BalBuildWillDialog(BalDialog):
if have_to_build: if have_to_build:
self.msg_set_building() self.msg_set_building()
try: try:
self.bal_window.build_will() if not self.bal_window.build_will():
self.msg_set_status(
_("Balance is too low. No transaction was built"),None,_("Skipped"),self.COLOR_ERROR
)
self.bal_window.check_will() self.bal_window.check_will()
for wid in Will.only_valid(self.bal_window.willitems): for wid in Will.only_valid(self.bal_window.willitems):
self.bal_window.wallet.set_label(wid, "BAL Transaction") self.bal_window.wallet.set_label(wid, "BAL Transaction")
self.msg_set_building("Ok") self.msg_set_building(self.msg_ok())
except WillExecutorNotPresent as e:
self.msg_set_status(
_("Will-Executor excluded"),
None,
_("Skipped"),
self.COLOR_ERROR
)
except Exception as e: except Exception as e:
self.msg_set_building(self.msg_error(e)) self.msg_set_building(self.msg_error(e))
return False, None return False, None
excluded_heirs = []
for wid in Will.only_valid(self.bal_window.willitems):
heirs = self.bal_window.willitems[wid].heirs
for hid, heir in heirs.items():
if "DUST" in str(heir[HEIR_REAL_AMOUNT]):
self.msg_set_status(
f'{hid},{heir[HEIR_DUST_AMOUNT]} is DUST',
None,
f"Excluded from will {wid}",
self.COLOR_WARNING
)
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):
if not self.bal_window.willitems[wid].get_status("COMPLETE"): if not self.bal_window.willitems[wid].get_status("COMPLETE"):
@@ -2171,7 +2214,7 @@ class BalBuildWillDialog(BalDialog):
txid = self.network.run_from_another_thread( txid = self.network.run_from_another_thread(
self.network.broadcast_transaction(tx, timeout=120), timeout=120 self.network.broadcast_transaction(tx, timeout=120), timeout=120
) )
self.msg_set_invalidating("Ok") self.msg_set_invalidating(self.msg_ok())
if not txid: if not txid:
_logger.debug(f"should not be none txid: {txid}") _logger.debug(f"should not be none txid: {txid}")
@@ -2238,7 +2281,7 @@ class BalBuildWillDialog(BalDialog):
if not self._stopping: if not self._stopping:
self.loop_push() 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}") _logger.debug(f"invalidate tx: {tx}")
fee_per_byte = bal_window.will_settings.get("baltx_fees", 1) fee_per_byte = bal_window.will_settings.get("baltx_fees", 1)
tx = self.bal_window.wallet.sign_transaction(tx, password) tx = self.bal_window.wallet.sign_transaction(tx, password)
@@ -2267,6 +2310,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))
@@ -2285,7 +2329,7 @@ class BalBuildWillDialog(BalDialog):
self.close() self.close()
return return
self.thread.add( 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_success=self.on_success_invalidate,
on_done=self.on_accept, on_done=self.on_accept,
on_error=self.on_error, on_error=self.on_error,
@@ -2327,7 +2371,7 @@ class BalBuildWillDialog(BalDialog):
for txid, tx in txs.items(): for txid, tx in txs.items():
self.bal_window.willitems[txid].tx = copy.deepcopy(tx) self.bal_window.willitems[txid].tx = copy.deepcopy(tx)
self.bal_window.save_willitems() self.bal_window.save_willitems()
self.msg_set_signing("Ok") self.msg_set_signing(self.msg_ok())
except Exception as e: except Exception as e:
self.msg_set_signing(self.msg_error(e)) self.msg_set_signing(self.msg_error(e))
@@ -2342,11 +2386,11 @@ class BalBuildWillDialog(BalDialog):
else: else:
try: try:
self.loop_push() self.loop_push()
self.msg_set_pushing("Ok") self.msg_set_pushing(self.msg_ok())
except Exception as e: except Exception as e:
self.msg_set_pushing(self.msg_error(e)) self.msg_set_pushing(self.msg_error(e))
self.msg_edit_row("Ok") self.msg_edit_row(self.msg_ok())
self.wait(5) self.wait(5)
def on_error_phase1(self, error): def on_error_phase1(self, error):
@@ -2386,19 +2430,24 @@ class BalBuildWillDialog(BalDialog):
self.wait_row = self.msg_edit_row(f"Please wait {status}secs", self.wait_row) self.wait_row = self.msg_edit_row(f"Please wait {status}secs", self.wait_row)
def msg_error(self, e): def msg_error(self, e):
return "Error: {}".format(e) return "<font color='{}'>{}</font>".format(self.COLOR_ERROR,e)
def msg_set_status(self, msg, row=None, status=None): def msg_ok(self, e="Ok"):
return "<font color='{}'>{}</font>".format(self.COLOR_OK,e)
def msg_warning(self, e):
return "<font color='{}'>{}</font".format(self.COLOR_WARNING,e)
def msg_set_status(self, msg, row=None, status=None,color="#000000"):
status = "Wait" if status is None else status status = "Wait" if status is None else status
line = "{}:\t{}".format(_(msg), status) line = "<font color={}>{}:\t{}</font>".format(color,_(msg), status)
return self.msg_edit_row(line, row) return self.msg_edit_row(line, row)
def ask_password(self, msg=None): def ask_password(self, msg=None):
self.password = self.bal_window.get_wallet_password(msg, parent=self) self.password = self.bal_window.get_wallet_password(msg, parent=self)
def msg_edit_row(self, line, row=None): def msg_edit_row(self, line, row=None):
_logger.debug(f"{row},{line}")
try: try:
self.labels[row] = line self.labels[row] = line
except Exception: except Exception:
@@ -2511,6 +2560,10 @@ class HeirList(MyTreeView, MessageBoxMixin):
except Exception: except Exception:
self.update() self.update()
def delete_heirs(self,selected_keys):
self.bal_window.delete_heirs(selected_keys)
self.update()
def create_menu(self, position): def create_menu(self, position):
menu = QMenu() menu = QMenu()
idx = self.indexAt(position) idx = self.indexAt(position)
@@ -2539,7 +2592,7 @@ class HeirList(MyTreeView, MessageBoxMixin):
lambda p=persistent: self.edit(QModelIndex(p)), lambda p=persistent: self.edit(QModelIndex(p)),
) )
menu.addAction( menu.addAction(
_("Delete"), lambda: self.bal_window.delete_heirs(selected_keys) _("Delete"), lambda: self.delete_heirs(selected_keys)
) )
menu.exec(self.viewport().mapToGlobal(position)) menu.exec(self.viewport().mapToGlobal(position))
@@ -2550,8 +2603,6 @@ class HeirList(MyTreeView, MessageBoxMixin):
# selected_keys.append(sel_key) # selected_keys.append(sel_key)
# return selected_keys # return selected_keys
def update(self): def update(self):
if self.maybe_defer_update():
return
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
) )
@@ -2571,9 +2622,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 + 1) items[self.Columns.NAME].setData(
items[self.Columns.ADDRESS].setData(key, self.ROLE_HEIR_KEY + 2) key, self.ROLE_HEIR_KEY + self.Columns.NAME
items[self.Columns.AMOUNT].setData(key, self.ROLE_HEIR_KEY + 3) )
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)
@@ -2592,9 +2649,8 @@ class HeirList(MyTreeView, MessageBoxMixin):
pass pass
def get_edit_key_from_coordinate(self, row, col): def get_edit_key_from_coordinate(self, row, col):
return 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 + 1 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("")
@@ -2691,8 +2747,8 @@ class HeirList(MyTreeView, MessageBoxMixin):
def update_will_settings(self): def update_will_settings(self):
try: try:
self.heir_locktime.set_locktime(self.bal_window.will_settings["locktime"]) 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_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: except Exception as e:
_logger.debug(f"Exception update_will_settings {e}") _logger.debug(f"Exception update_will_settings {e}")
@@ -2737,6 +2793,7 @@ class PreviewList(MyTreeView):
self.wallet = bal_window.window.wallet self.wallet = bal_window.window.wallet
self.setModel(QStandardItemModel(self)) self.setModel(QStandardItemModel(self))
self.sortByColumn(self.Columns.LOCKTIME, Qt.SortOrder.AscendingOrder)
self.setSortingEnabled(True) self.setSortingEnabled(True)
self.std_model = self.model() self.std_model = self.model()
self.config = bal_window.bal_plugin.config self.config = bal_window.bal_plugin.config
@@ -2895,6 +2952,8 @@ class PreviewList(MyTreeView):
tmp = self.replace(set_current, current_key, txid, bal_tx) tmp = self.replace(set_current, current_key, txid, bal_tx)
if tmp: if tmp:
set_current = tmp set_current = tmp
self.sortByColumn(self.Columns.LOCKTIME, Qt.SortOrder.AscendingOrder)
self.setSortingEnabled(True)
def create_toolbar(self, config): def create_toolbar(self, config):
toolbar, menu = self.create_toolbar_with_menu("") toolbar, menu = self.create_toolbar_with_menu("")
@@ -2913,6 +2972,9 @@ class PreviewList(MyTreeView):
display = QPushButton(_("Display")) display = QPushButton(_("Display"))
display.clicked.connect(self.bal_window.preview_modal_dialog) display.clicked.connect(self.bal_window.preview_modal_dialog)
display = QPushButton(_("Refresh"))
display.clicked.connect(self.check)
widget = QWidget() widget = QWidget()
hlayout = QHBoxLayout(widget) hlayout = QHBoxLayout(widget)
hlayout.addWidget(wizard) hlayout.addWidget(wizard)
@@ -2954,10 +3016,25 @@ 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.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"): if (
w.get_status("VALID")
and w.get_status("PUSHED")
and not w.get_status("CHECKED")
):
will[wid] = w will[wid] = w
if will:
self.bal_window.check_transactions(will) self.bal_window.check_transactions(will)
self.update() self.update()
@@ -2991,6 +3068,10 @@ class PreviewDialog(BalDialog, MessageBoxMixin):
self.setMinimumSize(1000, 200) self.setMinimumSize(1000, 200)
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()
self.check_will()
vbox = QVBoxLayout(self) vbox = QVBoxLayout(self)
vbox.addWidget(self.size_label) vbox.addWidget(self.size_label)
vbox.addWidget(self.transactions_list) vbox.addWidget(self.transactions_list)
@@ -3041,14 +3122,6 @@ class PreviewDialog(BalDialog, MessageBoxMixin):
event.accept() 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): class WillDetailDialog(BalDialog):
def __init__(self, bal_window): def __init__(self, bal_window):
@@ -3246,24 +3319,27 @@ class WillExecutorList(MyTreeView):
class Columns(MyTreeView.BaseColumnsEnum): class Columns(MyTreeView.BaseColumnsEnum):
SELECTED = enum.auto() SELECTED = enum.auto()
URL = enum.auto() URL = enum.auto()
STATUS = enum.auto()
BASE_FEE = enum.auto() BASE_FEE = enum.auto()
INFO = enum.auto() INFO = enum.auto()
ADDRESS = enum.auto() ADDRESS = enum.auto()
STATUS = enum.auto()
headers = { headers = {
Columns.SELECTED: _(""), Columns.SELECTED: _(""),
Columns.URL: _("Url"), Columns.URL: _("Url"),
Columns.STATUS: _("S"),
Columns.BASE_FEE: _("Base fee"), Columns.BASE_FEE: _("Base fee"),
Columns.INFO: _("Info"), Columns.INFO: _("Info"),
Columns.ADDRESS: _("Default Address"), 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 key_role = ROLE_HEIR_KEY
def __init__(self, parent: "WillExecutorDialog"): def __init__(self, parent: "WillExecutorWidget"):
super().__init__( super().__init__(
parent=parent, parent=parent,
stretch_column=self.Columns.ADDRESS, stretch_column=self.Columns.ADDRESS,
@@ -3275,7 +3351,12 @@ class WillExecutorList(MyTreeView):
], ],
) )
self.parent = parent self.parent = parent
try:
self.setModel(QStandardItemModel(self)) self.setModel(QStandardItemModel(self))
self.sortByColumn(self.Columns.SELECTED, Qt.SortOrder.AscendingOrder)
self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
except Exception:
pass
self.setSortingEnabled(True) self.setSortingEnabled(True)
self.std_model = self.model() self.std_model = self.model()
self.config = parent.bal_plugin.config self.config = parent.bal_plugin.config
@@ -3335,7 +3416,8 @@ class WillExecutorList(MyTreeView):
self.update() self.update()
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(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 return a
def delete(self, selected_keys): def delete(self, selected_keys):
@@ -3394,16 +3476,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", ""))
@@ -3423,17 +3522,25 @@ 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 + 1) items[self.Columns.URL].setData(
items[self.Columns.BASE_FEE].setData(url, self.ROLE_HEIR_KEY + 2) url, self.ROLE_HEIR_KEY + self.Columns.URL
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.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:
idx = self.model().index(row_count, self.Columns.NAME) idx = self.model().index(row_count, self.Columns.URL)
set_current = QPersistentModelIndex(idx) set_current = QPersistentModelIndex(idx)
self.set_current_idx(set_current) self.set_current_idx(set_current)
self.filter()
self.parent.save_willexecutors() self.parent.save_willexecutors()
except Exception as e: except Exception as e:
@@ -3494,7 +3601,7 @@ class WillExecutorWidget(QWidget, MessageBoxMixin):
buttonbox.addWidget(b) buttonbox.addWidget(b)
vbox.addLayout(buttonbox) vbox.addLayout(buttonbox)
self.willexecutor_list.update() # self.willexecutor_list.update()
def add(self): def add(self):
self.willexecutors_list["http://localhost:8080"] = { self.willexecutors_list["http://localhost:8080"] = {
@@ -3526,14 +3633,7 @@ class WillExecutorWidget(QWidget, MessageBoxMixin):
def update_willexecutors(self, wes=None): def update_willexecutors(self, wes=None):
if not wes: if not wes:
self.willexecutors_list = Willexecutors.get_willexecutors( wes = self.willexecutors_list
self.bal_plugin,
update=True,
bal_window=self.bal_window,
force=True,
task=self,
)
else:
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.willexecutor_list.update()

View File

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

53
will.py
View File

@@ -28,7 +28,6 @@ _logger = get_logger(__name__)
class Will: class Will:
# return an array with the list of children
def get_children(will, willid): def get_children(will, willid):
out = [] out = []
for _id in will: for _id in will:
@@ -82,8 +81,6 @@ class Will:
for txin in will[wid].tx.inputs(): for txin in will[wid].tx.inputs():
txid = txin.prevout.txid.hex() txid = txin.prevout.txid.hex()
if txid in will: if txid in will:
# print(will[txid].tx.outputs())
# print(txin.prevout.out_idx)
change = will[txid].tx.outputs()[txin.prevout.out_idx] change = will[txid].tx.outputs()[txin.prevout.out_idx]
txin._trusted_value_sats = change.value txin._trusted_value_sats = change.value
try: try:
@@ -155,6 +152,16 @@ 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):
@@ -258,7 +265,7 @@ class Will:
to_append = {} to_append = {}
new_inputs = Will.get_all_inputs(will, only_valid=True) new_inputs = Will.get_all_inputs(will, only_valid=True)
for nid, nwi in will.items(): 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(): if nid != nwi.tx.txid():
redo = True redo = True
to_delete.append(nid) to_delete.append(nid)
@@ -268,6 +275,17 @@ class Will:
Will.change_input( Will.change_input(
will, nid, i, outputs[i], new_inputs, to_delete, to_append 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: for w in to_delete:
try: try:
@@ -277,6 +295,7 @@ class Will:
for k, w in to_append.items(): for k, w in to_append.items():
will[k] = w will[k] = w
if redo: if redo:
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):
@@ -286,7 +305,6 @@ class Will:
# check if the new input is already spent by other transaction # check if the new input is already spent by other transaction
# if it is use the same locktime, or anticipate. # if it is use the same locktime, or anticipate.
Will.search_anticipate_rec(new_will, all_old_inputs) Will.search_anticipate_rec(new_will, all_old_inputs)
other_inputs = Will.get_all_inputs(old_will, {}) other_inputs = Will.get_all_inputs(old_will, {})
try: try:
Will.normalize_will(new_will, others_inputs=other_inputs) Will.normalize_will(new_will, others_inputs=other_inputs)
@@ -421,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: 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:
@@ -448,7 +470,7 @@ class Will:
Will.reflect_to_children(wc) Will.reflect_to_children(wc)
def check_amounts(heirs, willexecutors, all_utxos, timestamp_to_check, dust): def check_amounts(heirs, willexecutors, all_utxos, timestamp_to_check, dust):
fixed_heirs, fixed_amount, perc_heirs, perc_amount = ( fixed_heirs, fixed_amount, perc_heirs, perc_amount,fixed_amount_with_dust = (
heirs.fixed_percent_lists_amount(timestamp_to_check, dust, reverse=True) heirs.fixed_percent_lists_amount(timestamp_to_check, dust, reverse=True)
) )
wallet_balance = 0 wallet_balance = 0
@@ -598,9 +620,9 @@ class Will:
heirs_found[wheir] = count + 1 heirs_found[wheir] = count + 1
else: else:
_logger.debug( _logger.debug(
"heir not present transaction is not valid:", wid, w f"heir not present transaction is not valid:{wheir} {wid}, {w}"
) )
continue
if willexecutor := w.we: if willexecutor := w.we:
count = willexecutors_found.get(willexecutor["url"], 0) count = willexecutors_found.get(willexecutor["url"], 0)
if Util.cmp_willexecutor( if Util.cmp_willexecutor(
@@ -612,6 +634,7 @@ class Will:
no_willexecutor += 1 no_willexecutor += 1
count_heirs = 0 count_heirs = 0
for h in heirs: for h in heirs:
if Util.parse_locktime_string(heirs[h][2]) >= check_date: if Util.parse_locktime_string(heirs[h][2]) >= check_date:
count_heirs += 1 count_heirs += 1
if not h in heirs_found: if not h in heirs_found:
@@ -621,7 +644,6 @@ class Will:
raise NoHeirsException("there are not valid heirs") raise NoHeirsException("there are not valid heirs")
if self_willexecutor and no_willexecutor == 0: if self_willexecutor and no_willexecutor == 0:
raise NoWillExecutorNotPresent("Backup tx") raise NoWillExecutorNotPresent("Backup tx")
for url, we in willexecutors.items(): for url, we in willexecutors.items():
if Willexecutors.is_selected(we): if Willexecutors.is_selected(we):
if not url in willexecutors_found: if not url in willexecutors_found:
@@ -632,7 +654,6 @@ class Will:
class WillItem(Logger): class WillItem(Logger):
STATUS_DEFAULT = { STATUS_DEFAULT = {
"ANTICIPATED": ["Anticipated", False], "ANTICIPATED": ["Anticipated", False],
"BROADCASTED": ["Broadcasted", False], "BROADCASTED": ["Broadcasted", False],
@@ -654,15 +675,15 @@ class WillItem(Logger):
} }
def set_status(self, status, value=True): def set_status(self, status, value=True):
#_logger.trace( # _logger.trace(
# "set status {} - {} {} -> {}".format( # "set status {} - {} {} -> {}".format(
# self._id, status, self.STATUS[status][1], value # self._id, status, self.STATUS[status][1], value
# ) # )
#) # )
if self.STATUS[status][1] == bool(value): if self.STATUS[status][1] == bool(value):
return None 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) self.STATUS[status][1] = bool(value)
if value: if value:
if status in ["INVALIDATED", "REPLACED", "CONFIRMED", "PENDING"]: if status in ["INVALIDATED", "REPLACED", "CONFIRMED", "PENDING"]:
@@ -867,3 +888,7 @@ class PercAmountException(AmountException):
class FixedAmountException(AmountException): class FixedAmountException(AmountException):
pass pass
def test_check_invalidated():
Will.check_invalidated(will, utxos_list, wallet)

View File

@@ -35,34 +35,36 @@ 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():
if not bal_url in willexecutors: if not bal_url in willexecutors:
_logger.debug(f"force add {bal_url} willexecutor") _logger.debug(f"force add {bal_url} willexecutor")
willexecutors[bal_url] = bal_executor willexecutors[bal_url] = bal_executor
if update: # if update:
found = False # found = False
for url, we in willexecutors.items(): # for url, we in willexecutors.items():
if Willexecutors.is_selected(we): # if Willexecutors.is_selected(we):
found = True # found = True
if found or force: # if found or force:
if bal_plugin.PING_WILLEXECUTORS.get() or force: # if bal_plugin.PING_WILLEXECUTORS.get() or force:
ping_willexecutors = True # ping_willexecutors = True
if bal_plugin.ASK_PING_WILLEXECUTORS.get() and not force: # if bal_plugin.ASK_PING_WILLEXECUTORS.get() and not force:
if bal_window: # if bal_window:
ping_willexecutors = bal_window.window.question( # ping_willexecutors = bal_window.window.question(
_( # _(
"Contact willexecutors servers to update payment informations?" # "Contact willexecutors servers to update payment informations?"
) # )
) # )
if ping_willexecutors: # if ping_willexecutors:
if task: # if task:
bal_window.ping_willexecutors(willexecutors, task) # bal_window.ping_willexecutors(willexecutors, task)
else: # else:
bal_window.ping_willexecutors_task(willexecutors) # bal_window.ping_willexecutors_task(willexecutors)
w_sorted = dict( w_sorted = dict(
sorted( sorted(
willexecutors.items(), key=lambda w: w[1].get("sort", 0), reverse=True 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): def send_request(method, url, data=None, *, timeout=10):
network = Network.get_instance() network = Network.get_instance()
if not network: if not network:
raise ErrorConnectingServer("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()}"
@@ -170,8 +172,7 @@ class Willexecutors:
def push_transactions_to_willexecutor(willexecutor): def push_transactions_to_willexecutor(willexecutor):
out = True out = True
try: try:
_logger.debug(f"{willexecutor['url']}: {willexecutor['txs']}")
_logger.debug(f"willexecutor['txs']")
if w := Willexecutors.send_request( if w := Willexecutors.send_request(
"post", "post",
willexecutor["url"] + "/" + constants.net.NET_NAME + "/pushtxs", willexecutor["url"] + "/" + constants.net.NET_NAME + "/pushtxs",