balconfig

This commit is contained in:
bitcoinafterlife 2025-08-11 16:44:55 -04:00
parent a7b778e0b2
commit d6b37005e8
Signed by: bitcoinafterlife
GPG Key ID: FE756706E833E0D1
6 changed files with 135 additions and 110 deletions

143
bal.py
View File

@ -9,112 +9,109 @@ import os
json_db.register_dict('heirs', tuple, None) json_db.register_dict('heirs', tuple, None)
json_db.register_dict('will', lambda x: get_will(x), None) json_db.register_dict('will', lambda x: get_will(x), None)
json_db.register_dict('will_settings', lambda x:x, None) json_db.register_dict('will_settings', lambda x:x, None)
from electrum.logging import get_logger from electrum.logging import get_logger
def get_will(x): def get_will(x):
try: try:
x['tx']=tx_from_any(x['tx']) x['tx']=tx_from_any(x['tx'])
except Exception as e: except Exception as e:
raise e raise e
return x return x
class BalConfig():
def __init__(self, config, name, default):
self.config = config
self.name = name
self.default = default
def get(self,default=None):
v = self.config.get(self.name, default)
if v is None:
if not default is None:
v = default
else:
v = v.default
return v
def set(self,value,save=True):
self.config.set_key(self.name,value,save=save)
class BalPlugin(BasePlugin): class BalPlugin(BasePlugin):
LOCKTIME_TIME = "bal_locktime_time" LATEST_VERSION = '1'
LOCKTIME_BLOCKS = "bal_locktime_blocks" KNOWN_VERSIONS = ('0', '1')
LOCKTIMEDELTA_TIME = "bal_locktimedelta_time" assert LATEST_VERSION in KNOWN_VERSIONS
LOCKTIMEDELTA_BLOCKS = "bal_locktimedelta_blocks" def version():
TX_FEES = "bal_tx_fees" try:
BROADCAST = "bal_broadcast" f=""
ASK_BROADCAST = "bal_ask_broadcast" with open("VERSION","r") as f:
INVALIDATE = "bal_invalidate" f = str(f.readline())
ASK_INVALIDATE = "bal_ask_invalidate" return f
PREVIEW = "bal_preview" except:
SAVE_TXS = "bal_save_txs" return "unknown"
WILLEXECUTORS = "bal_willexecutors" SIZE = (159, 97)
PING_WILLEXECUTORS = "bal_ping_willexecutors"
ASK_PING_WILLEXECUTORS = "bal_ask_ping_willexecutors"
NO_WILLEXECUTOR = "bal_no_willexecutor"
HIDE_REPLACED = "bal_hide_replaced"
HIDE_INVALIDATED = "bal_hide_invalidated"
ALLOW_REPUSH = "bal_allow_repush"
def __init__(self, parent, config, name):
self.logger = get_logger(__name__)
BasePlugin.__init__(self, parent, config, name)
self.base_dir = os.path.join(config.electrum_path(), 'bal')
self.plugin_dir = os.path.split(os.path.realpath(__file__))[0]
self.logger.info(self.base_dir)
self.parent = parent
self.config = config
self.name = name
DEFAULT_SETTINGS={ self.ASK_BROADCAST = BalConfig(config, "bal_ask_broadcast",True)
LOCKTIME_TIME: 90, self.BROADCAST = BalConfig(config, "bal_broadcast",True)
LOCKTIME_BLOCKS: 144*90, self.LOCKTIME_TIME = BalConfig(config, "bal_locktime_time",90)
LOCKTIMEDELTA_TIME: 7, self.LOCKTIME_BLOCKS = BalConfig(config, "bal_locktime_blocks",144*90)
LOCKTIMEDELTA_BLOCKS:144*7, self.LOCKTIMEDELTA_TIME = BalConfig(config, "bal_locktimedelta_time",7)
TX_FEES: 100, self.LOCKTIMEDELTA_BLOCKS = BalConfig(config, "bal_locktimedelta_blocks",144*7)
BROADCAST: True, self.TX_FEES = BalConfig(config, "bal_tx_fees",100)
ASK_BROADCAST: True, self.INVALIDATE = BalConfig(config, "bal_invalidate",True)
INVALIDATE: True, self.ASK_INVALIDATE = BalConfig(config, "bal_ask_invalidate",True)
ASK_INVALIDATE: True, self.PREVIEW = BalConfig(config, "bal_preview",True)
PREVIEW: True, self.SAVE_TXS = BalConfig(config, "bal_save_txs",True)
SAVE_TXS: True, self.WILLEXECUTORS = BalConfig(config, "bal_willexecutors",True)
PING_WILLEXECUTORS: False, self.PING_WILLEXECUTORS = BalConfig(config, "bal_ping_willexecutors",True)
ASK_PING_WILLEXECUTORS: False, self.ASK_PING_WILLEXECUTORS = BalConfig(config, "bal_ask_ping_willexecutors",True)
NO_WILLEXECUTOR: False, self.NO_WILLEXECUTOR = BalConfig(config, "bal_no_willexecutor",True)
HIDE_REPLACED:True, self.HIDE_REPLACED = BalConfig(config, "bal_hide_replaced",True)
HIDE_INVALIDATED:True, self.HIDE_INVALIDATED = BalConfig(config, "bal_hide_invalidated",True)
ALLOW_REPUSH: False, self.ALLOW_REPUSH = BalConfig(config, "bal_allow_repush",True)
WILLEXECUTORS: { self.FIRST_EXECUTION = BalConfig(config, "bal_first_execution",True)
'https://bitcoin-after.life:9137': { self.WILLEXECUTORS = BalConfig(config, "bal_willexecutors", {
'https://we.bitcoin-after.life': {
"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":"bcrt1qa5cntu4hgadw8zd3n6sq2nzjy34sxdtd9u0gp7",
"selected":True "selected":True
} }
}, })
} self.WILL_SETTINGS = BalConfig(config, "bal_will_settings", {
'tx_fees':100,
'threshold':'180d',
'locktime':'1y',
})
LATEST_VERSION = '1'
KNOWN_VERSIONS = ('0', '1')
assert LATEST_VERSION in KNOWN_VERSIONS
SIZE = (159, 97) self._hide_invalidated= self.HIDE_INVALIDATED.get()
self._hide_replaced= self.HIDE_REPLACED.get()
def __init__(self, parent, config, name):
self.logger= get_logger(__name__)
BasePlugin.__init__(self, parent, config, name)
self.base_dir = os.path.join(config.electrum_path(), 'bal')
self.logger.info(self.base_dir)
self.parent = parent
self.config = config
self.name = name
self._hide_invalidated= self.config_get(self.HIDE_INVALIDATED)
self._hide_replaced= self.config_get(self.HIDE_REPLACED)
self.plugin_dir = os.path.split(os.path.realpath(__file__))[0]
def resource_path(self,*parts): def resource_path(self,*parts):
return os.path.join(self.plugin_dir, *parts) return os.path.join(self.plugin_dir, *parts)
def config_get(self,key):
v = self.config.get(key,None)
if v is None:
self.config.set_key(key,self.DEFAULT_SETTINGS[key])
v = self.DEFAULT_SETTINGS[key]
return v
def hide_invalidated(self): def hide_invalidated(self):
self._hide_invalidated = not self._hide_invalidated self._hide_invalidated = not self._hide_invalidated
self.config.set_key(BalPlugin.HIDE_INVALIDATED,self.hide_invalidated,save=True) self.HIDE_INVALIDATED.set(self._hide_invalidated)
def hide_replaced(self): def hide_replaced(self):
self._hide_replaced = not self._hide_replaced self._hide_replaced = not self._hide_replaced
self.config.set_key(BalPlugin.HIDE_REPLACED,self.hide_invalidated,save=True) self.HIDE_REPLACED.set(self._hide_replaced)
def default_will_settings(self):
return {
'tx_fees':100,
'threshold':'180d',
'locktime':'1y',
}
def validate_will_settings(self,will_settings): def validate_will_settings(self,will_settings):
if int(will_settings.get('tx_fees',1))<1: if int(will_settings.get('tx_fees',1))<1:
will_settings['tx_fees']=1 will_settings['tx_fees']=1

View File

@ -14,7 +14,6 @@ from electrum.transaction import PartialTxInput, PartialTxOutput,TxOutpoint,Part
import datetime import datetime
import urllib.request import urllib.request
import urllib.parse import urllib.parse
from .bal import BalPlugin
from .util import Util from .util import Util
from .willexecutors import Willexecutors from .willexecutors import Willexecutors
if TYPE_CHECKING: if TYPE_CHECKING:
@ -387,7 +386,7 @@ class Heirs(dict, Logger):
utxos = wallet.get_utxos() utxos = wallet.get_utxos()
willexecutors = Willexecutors.get_willexecutors(bal_plugin) or {} willexecutors = Willexecutors.get_willexecutors(bal_plugin) or {}
self.decimal_point=bal_plugin.config.get_decimal_point() self.decimal_point=bal_plugin.config.get_decimal_point()
no_willexecutors = bal_plugin.config_get(BalPlugin.NO_WILLEXECUTOR) no_willexecutors = bal_plugin.NO_WILLEXECUTOR.get()
for utxo in utxos: for utxo in utxos:
if utxo.value_sats()> 0*tx_fees: if utxo.value_sats()> 0*tx_fees:
balance += utxo.value_sats() balance += utxo.value_sats()

View File

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

43
qt.py
View File

@ -446,14 +446,14 @@ class Plugin(BalPlugin,Logger):
lbl_logo = QLabel() lbl_logo = QLabel()
lbl_logo.setPixmap(qicon) lbl_logo.setPixmap(qicon)
heir_ping_willexecutors = bal_checkbox(self, BalPlugin.PING_WILLEXECUTORS) heir_ping_willexecutors = bal_checkbox(self.PING_WILLEXECUTORS)
heir_ask_ping_willexecutors = bal_checkbox(self, BalPlugin.ASK_PING_WILLEXECUTORS) heir_ask_ping_willexecutors = bal_checkbox(self.ASK_PING_WILLEXECUTORS)
heir_no_willexecutor = bal_checkbox(self, BalPlugin.NO_WILLEXECUTOR) heir_no_willexecutor = bal_checkbox(self.NO_WILLEXECUTOR)
heir_hide_replaced = bal_checkbox(self,BalPlugin.HIDE_REPLACED,self) heir_hide_replaced = bal_checkbox(self.HIDE_REPLACED,self)
heir_hide_invalidated = bal_checkbox(self,BalPlugin.HIDE_INVALIDATED,self) heir_hide_invalidated = bal_checkbox(self.HIDE_INVALIDATED,self)
heir_repush = QPushButton("Rebroadcast transactions") heir_repush = QPushButton("Rebroadcast transactions")
heir_repush.clicked.connect(partial(self.broadcast_transactions,True)) heir_repush.clicked.connect(partial(self.broadcast_transactions,True))
grid=QGridLayout(d) grid=QGridLayout(d)
@ -641,13 +641,13 @@ 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): #def export_inheritance_handler(self,path):
txs = self.build_inheritance_transaction(ignore_duplicate=True, keep_original=False) # txs = self.build_inheritance_transaction(ignore_duplicate=True, keep_original=False)
with open(path,"w") as f: # with open(path,"w") as f:
for tx in txs: # for tx in txs:
tx['status']+="."+BalPlugin.STATUS_EXPORTED # tx['status']+="."+BalPlugin.STATUS_EXPORTED
f.write(str(tx['tx'])) # f.write(str(tx['tx']))
f.write('\n') # f.write('\n')
def set_heir(self,heir): def set_heir(self,heir):
heir=list(heir) heir=list(heir)
@ -750,10 +750,10 @@ class BalWindow(Logger):
try: try:
self.date_to_check = Util.parse_locktime_string(self.will_settings['threshold']) self.date_to_check = Util.parse_locktime_string(self.will_settings['threshold'])
found = False found = False
self.locktime_blocks=self.bal_plugin.config_get(BalPlugin.LOCKTIME_BLOCKS) 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 = self.current_block + self.locktime_blocks
self.no_willexecutor = self.bal_plugin.config_get(BalPlugin.NO_WILLEXECUTOR) self.no_willexecutor = self.bal_plugin.NO_WILLEXECUTOR.get()
self.willexecutors = Willexecutors.get_willexecutors(self.bal_plugin,update=True,bal_window=self,task=False) self.willexecutors = Willexecutors.get_willexecutors(self.bal_plugin,update=True,bal_window=self,task=False)
self.init_heirs_to_locktime() self.init_heirs_to_locktime()
@ -1511,12 +1511,12 @@ class BalBlockingWaitingDialog(BalDialog):
self.accept() self.accept()
class bal_checkbox(QCheckBox): class bal_checkbox(QCheckBox):
def __init__(self, plugin,variable,window=None): def __init__(self, variable,window=None):
QCheckBox.__init__(self) QCheckBox.__init__(self)
self.setChecked(plugin.config_get(variable)) self.setChecked(variable.get())
def on_check(v): def on_check(v):
plugin.config.set_key(variable, v == 2) variable.set(v == 2)
plugin.config_get(variable) variable.get()
self.stateChanged.connect(on_check) self.stateChanged.connect(on_check)
@ -2584,8 +2584,10 @@ class WillExecutorList(MyTreeView):
for k in selected_keys: for k in selected_keys:
wout[k]=self.parent.willexecutors_list[k] wout[k]=self.parent.willexecutors_list[k]
self.parent.update_willexecutors(wout) self.parent.update_willexecutors(wout)
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) a= self.get_role_data_from_coordinate(row, col, role=self.ROLE_HEIR_KEY+col)
return a return a
@ -2625,6 +2627,7 @@ class WillExecutorList(MyTreeView):
pass pass
def update(self): def update(self):
if self.parent.willexecutors_list is None: if self.parent.willexecutors_list is None:
return return
try: try:
@ -2676,7 +2679,9 @@ class WillExecutorList(MyTreeView):
idx = self.model().index(row_count, self.Columns.NAME) idx = self.model().index(row_count, self.Columns.NAME)
set_current = QPersistentModelIndex(idx) set_current = QPersistentModelIndex(idx)
self.set_current_idx(set_current) self.set_current_idx(set_current)
self.parent.save_willexecutors() self.parent.save_willexecutors()
except Exception as e: except Exception as e:
_logger.error(e) _logger.error(e)
@ -2779,5 +2784,5 @@ class WillExecutorDialog(BalDialog,MessageBoxMixin):
event.accept() event.accept()
def save_willexecutors(self): def save_willexecutors(self):
self.bal_plugin.config.set_key(self.bal_plugin.WILLEXECUTORS,self.willexecutors_list,save=True) self.bal_plugin.WILLEXECUTORS.set(self.willexecutors_list)

View File

@ -319,6 +319,7 @@ class Will:
locktime = Util.get_current_height(wallet.network) locktime = Util.get_current_height(wallet.network)
tx = PartialTransaction.from_io(utxo_to_spend, [out], locktime=locktime, version=2) tx = PartialTransaction.from_io(utxo_to_spend, [out], locktime=locktime, version=2)
tx.set_rbf(True) tx.set_rbf(True)
print("fees_per_byte:",fees_per_byte)
fee=tx.estimated_size()*fees_per_byte fee=tx.estimated_size()*fees_per_byte
if balance -fee >0: if balance -fee >0:
out = PartialTxOutput.from_address_and_value(change_addresses[0],balance - fee) out = PartialTxOutput.from_address_and_value(change_addresses[0],balance - fee)
@ -329,7 +330,7 @@ class Will:
return tx return tx
else: else:
_logger.debug("balance - fee <=0") _logger.debug(f"balance({balance}) - fee({fee}) <=0")
pass pass
else: else:
_logger.debug("len utxo_to_spend <=0") _logger.debug("len utxo_to_spend <=0")

View File

@ -8,31 +8,31 @@ from electrum import constants
from electrum.logging import get_logger from electrum.logging import get_logger
from electrum.gui.qt.util import WaitingDialog from electrum.gui.qt.util import WaitingDialog
from electrum.i18n import _ from electrum.i18n import _
from .bal import BalPlugin
from .util import Util from .util import Util
DEFAULT_TIMEOUT = 5 DEFAULT_TIMEOUT = 5
_logger = get_logger(__name__) _logger = get_logger(__name__)
class Willexecutors: class Willexecutors:
def get_willexecutors(bal_plugin, update = False,bal_window=False,force=False,task=True): def get_willexecutors(bal_plugin, update = False,bal_window=False,force=False,task=True):
willexecutors = bal_plugin.config_get(bal_plugin.WILLEXECUTORS) willexecutors = bal_plugin.WILLEXECUTORS.get()
for w in willexecutors: for w in willexecutors:
Willexecutors.initialize_willexecutor(willexecutors[w],w) Willexecutors.initialize_willexecutor(willexecutors[w],w)
bal=bal_plugin.DEFAULT_SETTINGS[bal_plugin.WILLEXECUTORS] bal = bal_plugin.WILLEXECUTORS.default
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("replace bal") _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.config_get(bal_plugin.PING_WILLEXECUTORS) or force: if bal_plugin.PING_WILLEXECUTORS.get() or force:
ping_willexecutors = True ping_willexecutors = True
if bal_plugin.config_get(bal_plugin.ASK_PING_WILLEXECUTORS) and not force: if bal_plugin.ASK_PING_WILLEXECUTORS.get() and not force:
ping_willexecutors = bal_window.window.question(_("Contact willexecutors servers to update payment informations?")) ping_willexecutors = bal_window.window.question(_("Contact willexecutors servers to update payment informations?"))
if ping_willexecutors: if ping_willexecutors:
if task: if task:
@ -40,6 +40,9 @@ class Willexecutors:
else: else:
bal_window.ping_willexecutors_task(willexecutors) bal_window.ping_willexecutors_task(willexecutors)
return willexecutors return willexecutors
def get_willexecutors_as_sorted_list(bal_plugin,update=False,bal_window=False,force=False,task=True):
willexecutors=Willexecutors.get_willexecutors(bal_plugin,update,bal_window,force,task)
return sorted(willexecutors,key=lambda w:w[1].get('sort',0))
def is_selected(willexecutor,value=None): def is_selected(willexecutor,value=None):
if not willexecutor: if not willexecutor:
@ -90,7 +93,7 @@ class Willexecutors:
raise ErrorConnectingServer('You are offline.') raise ErrorConnectingServer('You are offline.')
_logger.debug(f'<-- {method} {url} {data}') _logger.debug(f'<-- {method} {url} {data}')
headers = {} headers = {}
headers['user-agent'] = 'BalPlugin' headers['user-agent'] = f"BalPlugin v:{BalPlugin.version()}"
headers['Content-Type']='text/plain' headers['Content-Type']='text/plain'
try: try:
@ -216,3 +219,22 @@ class Willexecutors:
except Exception as e: except Exception as e:
_logger.error(f"error contacting {url} for checking txs {e}") _logger.error(f"error contacting {url} for checking txs {e}")
raise e raise e
class WillExecutor:
def __init__(self,url,base_fee,chain,info,version):
self.url = url
self.base_fee = base_fee
self.chain = chain
self.info = info
self.version = version
def from_dict(d):
we = WillExecutor(
d['url'],
d['base_fee'],
d['chain'],
d['info'],
d['version']
)