fix tx_fees wallet and other bug fixes to be compatible with electrum 0.6.2-master(0.6.3)

This commit is contained in:
bitcoinafterlife 2025-11-29 23:42:54 -04:00
parent 7c1fc04add
commit 9817546064
Signed by: bitcoinafterlife
GPG Key ID: FE756706E833E0D1
8 changed files with 348 additions and 31 deletions

View File

@ -1 +1 @@
0.2.2a
0.2.2b

27
bal.py
View File

@ -7,11 +7,16 @@ from electrum import json_db
from electrum.transaction import tx_from_any
import os
json_db.register_dict('heirs', tuple, None)
json_db.register_dict('will', lambda x: get_will(x), None)
json_db.register_dict('will_settings', lambda x:x, None)
def get_will_settings(x):
print(x)
json_db.register_dict('heirs', tuple, None)
json_db.register_dict('will', dict,None)
json_db.register_dict('will_settings', lambda x:x,None)
#{'rubiconda': ['bcrt1qgv0wu4v6kjzef5mnxfh2m9z6y7mez0ja0tt8mu', '45%', '1y'], 'veronica': ['bcrt1q6vxuvwrt8x5c9u9u29y5uq7frscr0vgc2dy60j', '15%', '1y']}
from electrum.logging import get_logger
def get_will_settings(x):
print(x)
def get_will(x):
try:
@ -22,6 +27,7 @@ def get_will(x):
class BalConfig():
def __init__(self, config, name, default):
print("init bal_config")
self.config = config
self.name = name
self.default = default
@ -53,6 +59,7 @@ class BalPlugin(BasePlugin):
SIZE = (159, 97)
def __init__(self, parent, config, name):
print("init bal_plugin")
self.logger = get_logger(__name__)
BasePlugin.__init__(self, parent, config, name)
self.base_dir = os.path.join(config.electrum_path(), 'bal')
@ -102,7 +109,7 @@ class BalPlugin(BasePlugin):
}
})
self.WILL_SETTINGS = BalConfig(config, "bal_will_settings", {
'tx_fees':100,
'baltx_fees':100,
'threshold':'180d',
'locktime':'1y',
})
@ -124,11 +131,19 @@ class BalPlugin(BasePlugin):
self.HIDE_REPLACED.set(self._hide_replaced)
def validate_will_settings(self,will_settings):
if int(will_settings.get('tx_fees',1))<1:
will_settings['tx_fees']=1
print(type(will_settings))
print(will_settings.get('baltx_fees',1),1)
if int(will_settings.get('baltx_fees',1))<1:
will_settings['baltx_fees']=1
if not will_settings.get('threshold'):
will_settings['threshold']='180d'
if not will_settings.get('locktime')=='':
will_settings['locktime']='1y'
return will_settings
def default_will_settings(self):
return {
'baltx_fees':100,
'threshold':'180d',
'locktime':'1y'
}

61
bal_wallet_utils.py Executable file
View File

@ -0,0 +1,61 @@
#!env/bin/python3
#same as qt but for command line, useful if you are going to fix various wallet
#also easier to read the code
from electrum.storage import WalletStorage
from electrum.util import MyEncoder
import json
import sys
import getpass
import os
default_fees= 100
def fix_will_settings_tx_fees(json_wallet):
tx_fees = json_wallet.get('will_settings',{}).get('tx_fees',False)
if tx_fees:
json_wallet['will_settings']['baltx_fees']=json_wallet.get('will_settings',{}).get('tx_fees',default_fees)
del json_wallet['will_settings']['tx_fees']
return True
return False
def uninstall_bal(json_wallet):
del json_wallet['will_settings']
del json_wallet['will']
del json_wallet['heirs']
return True
def save(json_wallet,storage):
human_readable=not storage.is_encrypted()
storage.write(json.dumps(
json_wallet,
indent=4 if human_readable else None,
sort_keys=bool(human_readable),
cls=MyEncoder,
))
if __name__ == '__main__':
if len(sys.argv) <3:
print("usage: ./bal_wallet_utils <command> <wallet path>")
print("available commands: uninstall, fix")
exit(1)
if not os.path.exists(sys.argv[2]):
print("Error: wallet not found")
exit(1)
command = sys.argv[1]
path = sys.argv[2]
storage=WalletStorage(path)
if storage.is_encrypted():
password = getpass.getpass("Enter wallet password: ", stream = None)
storage.decrypt(password)
data=storage.read()
json_wallet=json.loads(data)
have_to_save=False
if command == 'fix':
have_to_save = fix_will_settings_tx_fees(json_wallet)
if command == 'uninstall':
have_to_save = uninstall_bal(json_wallet)
if have_to_save:
save(json_wallet,storage)
else:
print("nothing to do")

225
bal_wallet_utils_qt.py Executable file
View File

@ -0,0 +1,225 @@
#!/usr/bin/env python3
#this script will help to fix tx_fees wallet bug
#also added an uninstall button to remove any bal data from wallet
#still very work in progress.
#
#have to be executed from electrum source code directory in example /home/user/projects/electrum/
#
import sys
import os
import json
from PyQt6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout,
QLabel, QLineEdit, QPushButton, QWidget, QFileDialog,
QGroupBox, QTextEdit)
from PyQt6.QtCore import Qt
from electrum.storage import WalletStorage
from electrum.util import MyEncoder
default_fees = 100
class WalletUtilityGUI(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('BAL Wallet Utility')
self.setFixedSize(500, 400)
# Central widget
central_widget = QWidget()
self.setCentralWidget(central_widget)
# Main layout
layout = QVBoxLayout(central_widget)
# Wallet input group
wallet_group = QGroupBox("Wallet Settings")
wallet_layout = QVBoxLayout(wallet_group)
# Wallet path
wallet_path_layout = QHBoxLayout()
wallet_path_layout.addWidget(QLabel("Wallet Path:"))
self.wallet_path_edit = QLineEdit()
self.wallet_path_edit.setPlaceholderText("Select wallet path...")
wallet_path_layout.addWidget(self.wallet_path_edit)
self.browse_btn = QPushButton("Browse...")
self.browse_btn.clicked.connect(self.browse_wallet)
wallet_path_layout.addWidget(self.browse_btn)
wallet_layout.addLayout(wallet_path_layout)
# Password
password_layout = QHBoxLayout()
password_layout.addWidget(QLabel("Password:"))
self.password_edit = QLineEdit()
self.password_edit.setEchoMode(QLineEdit.EchoMode.Password)
self.password_edit.setPlaceholderText("Enter password (if encrypted)")
password_layout.addWidget(self.password_edit)
wallet_layout.addLayout(password_layout)
layout.addWidget(wallet_group)
# Output area
output_group = QGroupBox("Output")
output_layout = QVBoxLayout(output_group)
self.output_text = QTextEdit()
self.output_text.setReadOnly(True)
output_layout.addWidget(self.output_text)
layout.addWidget(output_group)
# Action buttons
buttons_layout = QHBoxLayout()
self.fix_btn = QPushButton("Fix")
self.fix_btn.clicked.connect(self.fix_wallet)
self.fix_btn.setEnabled(False)
buttons_layout.addWidget(self.fix_btn)
self.uninstall_btn = QPushButton("Uninstall")
self.uninstall_btn.clicked.connect(self.uninstall_wallet)
self.uninstall_btn.setEnabled(False)
buttons_layout.addWidget(self.uninstall_btn)
layout.addLayout(buttons_layout)
# Connections to enable buttons when path is entered
self.wallet_path_edit.textChanged.connect(self.check_inputs)
def browse_wallet(self):
file_path, _ = QFileDialog.getOpenFileName(
self,
"Select Wallet",
"",
"Electrum Wallet (*.dat)"
)
if file_path:
self.wallet_path_edit.setText(file_path)
def check_inputs(self):
wallet_path = self.wallet_path_edit.text().strip()
has_path = bool(wallet_path) and os.path.exists(wallet_path)
self.fix_btn.setEnabled(has_path)
self.uninstall_btn.setEnabled(has_path)
def log_message(self, message):
self.output_text.append(message)
def fix_will_settings_tx_fees(self, json_wallet):
tx_fees = json_wallet.get('will_settings',{}).get('tx_fees',False)
if tx_fees:
json_wallet['will_settings']['baltx_fees'] = json_wallet.get('will_settings',{}).get('tx_fees', default_fees)
del json_wallet['will_settings']['tx_fees']
return True
return False
def uninstall_bal(self, json_wallet):
if 'will_settings' in json_wallet:
del json_wallet['will_settings']
if 'will' in json_wallet:
del json_wallet['will']
if 'heirs' in json_wallet:
del json_wallet['heirs']
return True
def save_wallet(self, json_wallet, storage):
try:
human_readable = not storage.is_encrypted()
storage.write(json.dumps(
json_wallet,
indent=4 if human_readable else None,
sort_keys=bool(human_readable),
cls=MyEncoder,
))
return True
except Exception as e:
self.log_message(f"Save error: {str(e)}")
return False
def fix_wallet(self):
self.process_wallet('fix')
def uninstall_wallet(self):
self.log_message("WARNING: This will remove all BAL settings. This operation cannot be undone.")
self.process_wallet('uninstall')
def process_wallet(self, command):
wallet_path = self.wallet_path_edit.text().strip()
password = self.password_edit.text()
if not wallet_path:
self.log_message("ERROR: Please enter wallet path")
return
if not os.path.exists(wallet_path):
self.log_message("ERROR: Wallet not found")
return
try:
self.log_message(f"Processing wallet: {wallet_path}")
storage = WalletStorage(wallet_path)
# Decrypt if necessary
if storage.is_encrypted():
if not password:
self.log_message("ERROR: Wallet is encrypted, please enter password")
return
try:
storage.decrypt(password)
self.log_message("Wallet decrypted successfully")
except Exception as e:
self.log_message(f"ERROR: Wrong password: {str(e)}")
return
# Read wallet
data = storage.read()
json_wallet = json.loads(data)
have_to_save = False
message = ""
if command == 'fix':
have_to_save = self.fix_will_settings_tx_fees(json_wallet)
message = "Fix applied successfully" if have_to_save else "No fix needed"
elif command == 'uninstall':
have_to_save = self.uninstall_bal(json_wallet)
message = "BAL uninstalled successfully" if have_to_save else "No BAL settings found to uninstall"
if have_to_save:
if self.save_wallet(json_wallet, storage):
self.log_message(f"SUCCESS: {message}")
else:
self.log_message("ERROR: Failed to save wallet")
else:
self.log_message(f"INFO: {message}")
except Exception as e:
error_msg = f"ERROR: Processing failed: {str(e)}"
self.log_message(error_msg)
def main():
app = QApplication(sys.argv)
# Check if dependencies are available
try:
from electrum.storage import WalletStorage
from electrum.util import MyEncoder
except ImportError as e:
print(f"ERROR: Cannot import Electrum dependencies: {str(e)}")
return 1
window = WalletUtilityGUI()
window.show()
return app.exec()
if __name__ == '__main__':
sys.exit(main())

View File

@ -14,6 +14,7 @@ from electrum.transaction import PartialTxInput, PartialTxOutput,TxOutpoint,Part
import datetime
import urllib.request
import urllib.parse
import random
from .util import Util
from .willexecutors import Willexecutors
if TYPE_CHECKING:
@ -112,7 +113,9 @@ def prepare_transactions(locktimes, available_utxos, fees, wallet):
change = get_change_output(wallet, in_amount, out_amount, fee)
if change:
outputs.append(change)
for i in range(0,100):
random.shuffle(outputs)
print(outputs)
tx = PartialTransaction.from_io(used_utxos, outputs, locktime=Util.parse_locktime_string(locktime,wallet), version=2)
if len(description)>0: tx.description = description[:-1]
else: tx.description = ""
@ -385,7 +388,7 @@ class Heirs(dict, Logger):
if not utxos:
utxos = wallet.get_utxos()
willexecutors = Willexecutors.get_willexecutors(bal_plugin) or {}
self.decimal_point=bal_plugin.config.get_decimal_point()
self.decimal_point=bal_plugin.get_decimal_point()
no_willexecutors = bal_plugin.NO_WILLEXECUTOR.get()
for utxo in utxos:
if utxo.value_sats()> 0*tx_fees:

View File

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

51
qt.py
View File

@ -336,12 +336,14 @@ class Plugin(BalPlugin,Logger):
def __init__(self, parent, config, name):
Logger.__init__(self)
self.logger.info("INIT BALPLUGIN")
print("init bal_plugin")
BalPlugin.__init__(self, parent, config, name)
self.bal_windows={}
@hook
def init_qt(self,gui_object):
print("hook init qt")
self.logger.info("HOOK init qt")
try:
self.gui_object=gui_object
@ -396,10 +398,16 @@ class Plugin(BalPlugin,Logger):
@hook
def close_wallet(self,wallet):
print("HOOK close wallet")
for winid,win in self.bal_windows.items():
if win.wallet == wallet:
win.on_close()
@hook
def init_keystore(self):
print("init keystore")
@hook
def daemon_wallet_loaded(self,boh,wallet):
print("daemon wallet loaded")
def get_window(self,window):
w = self.bal_windows.get(window.winId,None)
if w is None:
@ -510,6 +518,7 @@ class BalWindow(Logger):
self.will_tab = self.create_will_tab()
self.ok= False
self.disable_plugin = True
self.bal_plugin.get_decimal_point = self.window.get_decimal_point
if self.window.wallet:
self.wallet = self.window.wallet
@ -628,7 +637,7 @@ class BalWindow(Logger):
heir_address.setText(str(heir[0]))
heir_amount = PercAmountEdit(self.window.get_decimal_point)
if heir:
heir_amount.setText(str(Util.decode_amount(heir[1],self.bal_plugin.config.get_decimal_point())))
heir_amount.setText(str(Util.decode_amount(heir[1],self.window.get_decimal_point())))
heir_locktime = HeirsLockTimeEdit(self.window,0)
if heir:
heir_locktime.set_locktime(heir[2])
@ -673,7 +682,7 @@ class BalWindow(Logger):
heir = [
heir_name.text(),
heir_address.text(),
Util.encode_amount(heir_amount.text(),self.bal_plugin.config.get_decimal_point()),
Util.encode_amount(heir_amount.text(),self.window.get_decimal_point()),
str(heir_locktime.get_locktime()),
]
try:
@ -744,7 +753,7 @@ class BalWindow(Logger):
f=True
if not f:
raise NoWillExecutorNotPresent("No Will-Executor or backup transaction selected")
txs = self.heirs.get_transactions(self.bal_plugin,self.window.wallet,self.will_settings['tx_fees'],None,self.date_to_check)
txs = self.heirs.get_transactions(self.bal_plugin,self.window.wallet,self.will_settings['baltx_fees'],None,self.date_to_check)
self.logger.info(txs)
creation_time = time.time()
if txs:
@ -758,7 +767,7 @@ class BalWindow(Logger):
tx['description'] = txs[txid].description
tx['willexecutor'] = copy.deepcopy(txs[txid].willexecutor)
tx['status'] = _("New")
tx['tx_fees'] = txs[txid].tx_fees
tx['baltx_fees'] = txs[txid].tx_fees
tx['time'] = creation_time
tx['heirs'] = copy.deepcopy(txs[txid].heirs)
tx['txchildren'] = []
@ -770,7 +779,7 @@ class BalWindow(Logger):
return self.willitems
def check_will(self):
return Will.is_will_valid(self.willitems, self.block_to_check, self.date_to_check, self.will_settings['tx_fees'],self.window.wallet.get_utxos(),heirs=self.heirs,willexecutors=self.willexecutors ,self_willexecutor=self.no_willexecutor, wallet = self.wallet, callback_not_valid_tx=self.delete_not_valid)
return Will.is_will_valid(self.willitems, self.block_to_check, self.date_to_check, self.will_settings['baltx_fees'],self.window.wallet.get_utxos(),heirs=self.heirs,willexecutors=self.willexecutors ,self_willexecutor=self.no_willexecutor, wallet = self.wallet, callback_not_valid_tx=self.delete_not_valid)
def show_message(self,text):
self.window.show_message(text)
def show_warning(self,text,parent =None):
@ -918,7 +927,7 @@ class BalWindow(Logger):
def on_failure(exec_info):
self.show_error(f"ERROR:{exec_info}")
fee_per_byte=self.will_settings.get('tx_fees',1)
fee_per_byte=self.will_settings.get('baltx_fees',1)
task = partial(Will.invalidate_will,self.willitems,self.wallet,fee_per_byte)
msg = _("Calculating Transactions")
self.waiting_dialog = BalWaitingDialog(self, msg, task, on_success, on_failure,exe=False)
@ -1107,7 +1116,6 @@ class BalWindow(Logger):
def check_transactions_task(self,will):
start = time.time()
for wid,w in will.items():
if w.we:
self.waiting_dialog.update("checking transaction: {}\n willexecutor: {}".format(wid,w.we['url']))
w.check_willexecutor()
@ -1722,11 +1730,11 @@ class BalWizardLocktimeAndFeeWidget(BalWizardWidget):
self.heir_tx_fees.setMinimum(1)
self.heir_tx_fees.setMaximum(10000)
self.heir_tx_fees.setValue(will_settings['tx_fees'])
self.heir_tx_fees.setValue(will_settings['baltx_fees'])
def on_heir_tx_fees():
if not self.heir_tx_fees.value():
self.heir_tx_fees.set_value(1)
self.bal_window.will_settings['tx_fees'] = self.heir_tx_fees.value()
self.bal_window.will_settings['baltx_fees'] = self.heir_tx_fees.value()
self.bal_window.bal_plugin.WILL_SETTINGS.set(self.bal_window.will_settings)
self.heir_tx_fees.valueChanged.connect(on_heir_tx_fees)
@ -1894,7 +1902,7 @@ class BalBuildWillDialog(BalDialog):
self.msg_set_checking('Ok')
except WillExpiredException as e:
self.msg_set_checking("Expired")
fee_per_byte=self.bal_window.will_settings.get('tx_fees',1)
fee_per_byte=self.bal_window.will_settings.get('baltx_fees',1)
return None, Will.invalidate_will(self.bal_window.willitems,self.bal_window.wallet,fee_per_byte)
except NoHeirsException:
self.msg_set_checking("No Heirs")
@ -2194,7 +2202,7 @@ class HeirList(MyTreeView,MessageBoxMixin):
stretch_column=self.Columns.NAME,
editable_columns=[self.Columns.NAME,self.Columns.ADDRESS,self.Columns.AMOUNT],
)
self.decimal_point = bal_window.bal_plugin.config.get_decimal_point()
self.decimal_point = bal_window.window.get_decimal_point()
self.bal_window = bal_window
try:
@ -2340,7 +2348,7 @@ class HeirList(MyTreeView,MessageBoxMixin):
def on_heir_tx_fees():
if not self.heir_tx_fees.value():
self.heir_tx_fees.set_value(1)
self.bal_window.will_settings['tx_fees'] = self.heir_tx_fees.value()
self.bal_window.will_settings['baltx_fees'] = self.heir_tx_fees.value()
self.bal_window.bal_plugin.WILL_SETTINGS.set(self.bal_window.will_settings)
self.heir_tx_fees.valueChanged.connect(on_heir_tx_fees)
@ -2382,7 +2390,7 @@ class HeirList(MyTreeView,MessageBoxMixin):
def update_will_settings(self):
try:
self.heir_locktime.set_locktime(self.bal_window.will_settings['locktime'])
self.heir_tx_fees.setValue(int(self.bal_window.will_settings['tx_fees']))
self.heir_tx_fees.setValue(int(self.bal_window.will_settings['baltx_fees']))
self.heir_threshold.set_locktime(self.bal_window.will_settings['threshold'])
except Exception as e:
@ -2416,7 +2424,7 @@ class PreviewList(MyTreeView):
)
self.parent=parent
self.bal_window=bal_window
self.decimal_point=bal_window.bal_plugin.config.get_decimal_point
self.decimal_point=bal_window.window.get_decimal_point
self.setModel(QStandardItemModel(self))
self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
@ -2631,7 +2639,11 @@ class PreviewList(MyTreeView):
self.update()
def check(self):
self.bal_window.check_transactions(self.bal_window.willitems)
will = {}
for wid, w in self.bal_window.willitems:
if w.get_status("VALID"):
will[wid]=w
self.bal_window.check_transactions(will)
self.update()
def invalidate_will(self):
@ -2733,7 +2745,7 @@ class WillDetailDialog(BalDialog):
self.format_fiat_and_units = bal_window.window.format_fiat_and_units
self.fx = bal_window.window.fx
self.format_fee_rate = bal_window.window.format_fee_rate
self.decimal_point = bal_window.bal_plugin.config.get_decimal_point()
self.decimal_point = bal_window.window.get_decimal_point()
self.base_unit_name = decimal_point_to_base_unit_name(self.decimal_point)
self.setWindowTitle(_('Will Details'))
self.setMinimumSize(670,700)
@ -2905,6 +2917,7 @@ class WillExecutorList(MyTreeView):
self.setSortingEnabled(True)
self.std_model = self.model()
self.config =parent.bal_plugin.config
self.get_decimal_point = parent.bal_plugin.get_decimal_point
self.update()
@ -2977,7 +2990,7 @@ class WillExecutorList(MyTreeView):
self.parent.willexecutors_list[text]=self.parent.willexecutors_list[edit_key]
del self.parent.willexecutors_list[edit_key]
if col == self.Columns.BASE_FEE:
self.parent.willexecutors_list[edit_key]["base_fee"] = Util.encode_amount(text,self.config.get_decimal_point())
self.parent.willexecutors_list[edit_key]["base_fee"] = Util.encode_amount(text,self.get_decimal_point())
if col == self.Columns.ADDRESS:
self.parent.willexecutors_list[edit_key]["address"] = text
if col == self.Columns.INFO:
@ -3004,7 +3017,7 @@ class WillExecutorList(MyTreeView):
labels[self.Columns.SELECTED] = [read_QIcon('confirmed.png'),'']
else:
labels[self.Columns.SELECTED] = ''
labels[self.Columns.BASE_FEE] = Util.decode_amount(value.get('base_fee',0),self.config.get_decimal_point())
labels[self.Columns.BASE_FEE] = Util.decode_amount(value.get('base_fee',0),self.get_decimal_point())
if str(value.get('status',0)) == "200":
labels[self.Columns.STATUS] = [read_QIcon('status_connected.png'),'']
else:

View File

@ -628,7 +628,7 @@ class WillItem(Logger):
self.description = w.get('description',None)
self.time = w.get('time',None)
self.change = w.get('change',None)
self.tx_fees = w.get('tx_fees',0)
self.tx_fees = w.get('baltx_fees',0)
self.father = w.get('Father',None)
self.children = w.get('Children',None)
self.STATUS = copy.deepcopy(WillItem.STATUS_DEFAULT)
@ -658,7 +658,7 @@ class WillItem(Logger):
'description':self.description,
'time':self.time,
'change':self.change,
'tx_fees':self.tx_fees
'baltx_fees':self.tx_fees
}
for key in self.STATUS:
try: