6 Commits

10 changed files with 849 additions and 925 deletions

67
bal.py
View File

@@ -1,14 +1,14 @@
import os
from datetime import date, datetime, timedelta
import platform
# import random
# import zipfile as zipfile_lib
from electrum import constants, json_db
from electrum import json_db
from electrum.logging import get_logger
from electrum.plugin import BasePlugin
from electrum.transaction import tx_from_any
_logger = get_logger(__name__)
def get_will_settings(x):
# print(x)
pass
@@ -48,13 +48,7 @@ class BalConfig:
class BalPlugin(BasePlugin):
_version=None
__version__ = "0.2.8" #AUTOMATICALLY GENERATED DO NOT EDIT
default_app={
"Linux":"xdg-open",
"Window":"start",
"Darwin":"open"
}
chainname = constants.net.NET_NAME if constants.net.NET_NAME != "mainnet" else "bitcoin"
__version__ = "0.2.7" #AUTOMATICALLY GENERATED DO NOT EDIT
def version(self):
if not self._version:
try:
@@ -65,7 +59,7 @@ class BalPlugin(BasePlugin):
except Exception as e:
_logger.error(f"failed to get version: {e}")
self._version="unknown"
return self._version
return self._version
SIZE = (159, 97)
@@ -106,7 +100,6 @@ class BalPlugin(BasePlugin):
self.HIDE_INVALIDATED = BalConfig(config, "bal_hide_invalidated", True)
self.ALLOW_REPUSH = BalConfig(config, "bal_allow_repush", True)
self.FIRST_EXECUTION = BalConfig(config, "bal_first_execution", True)
self.WELIST_SERVER = BalConfig(config,"bal_welist_server","https://welist.bitcoin-after.life/")
self.WILLEXECUTORS = BalConfig(
config,
"bal_willexecutors",
@@ -129,33 +122,18 @@ class BalPlugin(BasePlugin):
"selected": True,
}
},
"testnet4": {
"https://we.bitcoin-after.life": {
"base_fee": 100000,
"status": "New",
"info": "Bitcoin After Life Will Executor",
"address": "bcrt1qa5cntu4hgadw8zd3n6sq2nzjy34sxdtd9u0gp7",
"selected": True,
}
},
"regtest": {
"https://we.bitcoin-after.life": {
"base_fee": 100000,
"status": "New",
"info": "Bitcoin After Life Will Executor",
"address": "bcrt1qa5cntu4hgadw8zd3n6sq2nzjy34sxdtd9u0gp7",
"selected": True,
}
},
},
)
self.WILL_SETTINGS = BalConfig(
config,
"bal_will_settings",
BalPlugin.default_will_settings(),
{
"baltx_fees": 100,
"threshold": "180d",
"locktime": "1y",
},
)
self.system = platform.system()
self.CALENDAR_APP = BalConfig(config,"bal_open_app",self.default_app[self.system])
self._hide_invalidated = self.HIDE_INVALIDATED.get()
self._hide_replaced = self.HIDE_REPLACED.get()
@@ -171,22 +149,13 @@ class BalPlugin(BasePlugin):
self.HIDE_REPLACED.set(self._hide_replaced)
def validate_will_settings(self, will_settings):
defaults=BalPlugin.default_will_settings()
if not will_settings:
will_settings=[]
if int(will_settings.get("baltx_fees", 0)) < 1:
will_settings["baltx_fees"] = defaults['baltx_fees']
if int(will_settings.get("baltx_fees", 1)) < 1:
will_settings["baltx_fees"] = 1
if not will_settings.get("threshold"):
will_settings["threshold"] = defaults['threshold']
will_settings["threshold"] = "180d"
if not will_settings.get("locktime"):
will_settings["locktime"] = defaults['locktime']
will_settings["locktime"] = "1y"
return will_settings
@staticmethod
def default_will_settings():
today = date.today()
dt = datetime(today.year, today.month, today.day, 0, 0, 0)
threshold =(dt + timedelta(days=180)).timestamp()
locktime =(dt + timedelta(days=365)).timestamp()
return {"baltx_fees": 100, "threshold": threshold, "locktime": locktime}
def default_will_settings(self):
return {"baltx_fees": 100, "threshold": "180d", "locktime": "1y"}

View File

@@ -34,7 +34,6 @@ from electrum.transaction import (
# TxOutput,
)
from electrum.util import (
BitcoinException,
bfh,
read_json_file,
to_string,
@@ -44,6 +43,7 @@ from electrum.util import (
from .util import Util
from .willexecutors import Willexecutors
from electrum.util import BitcoinException
if TYPE_CHECKING:
from .simple_config import SimpleConfig
@@ -71,22 +71,28 @@ def reduce_outputs(in_amount, out_amount, fee, outputs):
output.value = math.floor((in_amount - fee) / out_amount * output.value)
def create_op_return_script(data_hex: str) -> bytes:
"""Crea scriptpubkey OP_RETURN in bytes"""
data = bytes.fromhex(data_hex)
"""
#TODO: put this method inside wallet.db to replace or complete get_locktime_for_new_transaction
def get_current_height(network:'Network'):
#if no network or not up to date, just set locktime to zero
if not network:
return 0
chain = network.blockchain()
if chain.is_tip_stale():
return 0
# figure out current block height
chain_height = chain.height() # learnt from all connected servers, SPV-checked
server_height = network.get_server_height() # height claimed by main server, unverified
# note: main server might be lagging (either is slow, is malicious, or there is an SPV-invisible-hard-fork)
# - if it's lagging too much, it is the network's job to switch away
if server_height < chain_height - 10:
# the diff is suspiciously large... give up and use something non-fingerprintable
return 0
# discourage "fee sniping"
height = min(chain_height, server_height)
return height
"""
if len(data) > 80:
raise ValueError("OP_RETURN data too big (max 80 bytes)")
# Costruzione manuale: OP_RETURN + push data
if len(data) <= 75:
# Formato più comune: OP_RETURN + 1-byte length + data
script = b'\x6a' + bytes([len(data)]) + data
else:
# Per dati più grandi (fino a 80) si usa OP_PUSHDATA1
script = b'\x6a\x4c' + bytes([len(data)]) + data
return script
def prepare_transactions(locktimes, available_utxos, fees, wallet):
available_utxos = sorted(
@@ -161,13 +167,6 @@ def prepare_transactions(locktimes, available_utxos, fees, wallet):
outputs.append(change)
for i in range(0, 100):
random.shuffle(outputs)
#op_return_text = "Hello Bal!"
## Convert text to hex
#op_return_hex = op_return_text.encode('utf-8').hex()
#op_return_script = create_op_return_script(op_return_hex)
#outputs.append(PartialTxOutput(value=0, scriptpubkey=op_return_script))
tx = PartialTransaction.from_io(
used_utxos,
outputs,
@@ -183,7 +182,7 @@ def prepare_transactions(locktimes, available_utxos, fees, wallet):
tx.remove_signatures()
txid = tx.txid()
if txid is None:
raise Exception(f"txid is none: {tx}")
raise Exception("txid is none", tx)
tx.heirs = paid_heirs
tx.my_locktime = locktime

View File

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

1303
qt.py

File diff suppressed because it is too large Load Diff

81
util.py
View File

@@ -1,13 +1,15 @@
import bisect
from datetime import datetime, timedelta
from electrum.gui.qt.util import getSaveFileName
from electrum.i18n import _
from electrum.transaction import PartialTxOutput
from electrum.util import FileExportFailed
LOCKTIME_THRESHOLD = 500000000
class Util:
@staticmethod
def locktime_to_str(locktime):
try:
locktime = int(locktime)
@@ -19,7 +21,6 @@ class Util:
pass
return str(locktime)
@staticmethod
def str_to_locktime(locktime):
try:
if locktime[-1] in ("y", "d", "b"):
@@ -32,7 +33,6 @@ class Util:
timestamp = dt_object.timestamp()
return int(timestamp)
@staticmethod
def parse_locktime_string(locktime, w=None):
try:
return int(locktime)
@@ -60,7 +60,6 @@ class Util:
pass
return 0
@staticmethod
def int_locktime(seconds=0, minutes=0, hours=0, days=0, blocks=0):
return int(
seconds
@@ -70,7 +69,6 @@ class Util:
+ blocks * 600
)
@staticmethod
def encode_amount(amount, decimal_point):
if Util.is_perc(amount):
return amount
@@ -80,7 +78,6 @@ class Util:
except Exception:
return 0
@staticmethod
def decode_amount(amount, decimal_point):
if Util.is_perc(amount):
return amount
@@ -91,14 +88,12 @@ class Util:
except Exception:
return str(amount)
@staticmethod
def is_perc(value):
try:
return value[-1] == "%"
except Exception:
return False
@staticmethod
def cmp_array(heira, heirb):
try:
if len(heira) != len(heirb):
@@ -110,13 +105,11 @@ class Util:
except Exception:
return False
@staticmethod
def cmp_heir(heira, heirb):
if heira[0] == heirb[0] and heira[1] == heirb[1]:
return True
return False
@staticmethod
def cmp_willexecutor(willexecutora, willexecutorb):
if willexecutora == willexecutorb:
return True
@@ -131,7 +124,6 @@ class Util:
return False
return False
@staticmethod
def search_heir_by_values(heirs, heir, values):
for h, v in heirs.items():
found = False
@@ -143,14 +135,12 @@ class Util:
return h
return False
@staticmethod
def cmp_heir_by_values(heira, heirb, values):
for v in values:
if heira[v] != heirb[v]:
return False
return True
@staticmethod
def cmp_heirs_by_values(
heirsa, heirsb, values, exclude_willexecutors=False, reverse=True
):
@@ -175,7 +165,6 @@ class Util:
else:
return True
@staticmethod
def cmp_heirs(
heirsa,
heirsb,
@@ -198,7 +187,6 @@ class Util:
raise e
return False
@staticmethod
def cmp_inputs(inputsa, inputsb):
if len(inputsa) != len(inputsb):
return False
@@ -207,7 +195,6 @@ class Util:
return False
return True
@staticmethod
def cmp_outputs(outputsa, outputsb, willexecutor_output=None):
if len(outputsa) != len(outputsb):
return False
@@ -217,7 +204,6 @@ class Util:
return False
return True
@staticmethod
def cmp_txs(txa, txb):
if not Util.cmp_inputs(txa.inputs(), txb.inputs()):
return False
@@ -225,7 +211,6 @@ class Util:
return False
return True
@staticmethod
def get_value_amount(txa, txb):
outputsa = txa.outputs()
# outputsb = txb.outputs()
@@ -244,7 +229,6 @@ class Util:
return value_amount
@staticmethod
def chk_locktime(timestamp_to_check, block_height_to_check, locktime):
# TODO BUG: WHAT HAPPEN AT THRESHOLD?
locktime = int(locktime)
@@ -255,7 +239,6 @@ class Util:
else:
return False
@staticmethod
def anticipate_locktime(locktime, blocks=0, hours=0, days=0):
locktime = int(locktime)
out = 0
@@ -272,7 +255,6 @@ class Util:
out = 1
return out
@staticmethod
def cmp_locktime(locktimea, locktimeb):
if locktimea == locktimeb:
return 0
@@ -286,20 +268,17 @@ class Util:
else:
return int(locktimea) - (locktimeb)
@staticmethod
def get_lowest_valid_tx(available_utxos, will):
will = sorted(will.items(), key=lambda x: x[1]["tx"].locktime)
for txid, willitem in will.items():
pass
@staticmethod
def get_locktimes(will):
locktimes = {}
for txid, willitem in will.items():
locktimes[willitem["tx"].locktime] = True
return locktimes.keys()
@staticmethod
def get_lowest_locktimes(locktimes):
sorted_timestamp = []
sorted_block = []
@@ -312,22 +291,18 @@ class Util:
return sorted(sorted_timestamp), sorted(sorted_block)
@staticmethod
def get_lowest_locktimes_from_will(will):
return Util.get_lowest_locktimes(Util.get_locktimes(will))
@staticmethod
def search_willtx_per_io(will, tx):
for wid, w in will.items():
if Util.cmp_txs(w["tx"], tx["tx"]):
return wid, w
return None, None
@staticmethod
def invalidate_will(will):
raise Exception("not implemented")
@staticmethod
def get_will_spent_utxos(will):
utxos = []
for txid, willitem in will.items():
@@ -335,7 +310,6 @@ class Util:
return utxos
@staticmethod
def utxo_to_str(utxo):
try:
return utxo.to_str()
@@ -347,7 +321,6 @@ class Util:
pass
return str(utxo)
@staticmethod
def cmp_utxo(utxoa, utxob):
utxoa = Util.utxo_to_str(utxoa)
utxob = Util.utxo_to_str(utxob)
@@ -356,25 +329,21 @@ class Util:
else:
return False
@staticmethod
def in_utxo(utxo, utxos):
for s_u in utxos:
if Util.cmp_utxo(s_u, utxo):
return True
return False
@staticmethod
def txid_in_utxo(txid, utxos):
for s_u in utxos:
if s_u.prevout.txid == txid:
return True
return False
@staticmethod
def cmp_output(outputa, outputb):
return outputa.address == outputb.address and outputa.value == outputb.value
@staticmethod
def in_output(output, outputs):
for s_o in outputs:
if Util.cmp_output(s_o, output):
@@ -386,7 +355,6 @@ class Util:
# return true false same amount different address
# return false false different amount, different address not found
@staticmethod
def din_output(out, outputs):
same_amount = []
for s_o in outputs:
@@ -402,7 +370,6 @@ class Util:
else:
return False, False
@staticmethod
def get_change_output(wallet, in_amount, out_amount, fee):
change_amount = int(in_amount - out_amount - fee)
if change_amount > wallet.dust_threshold():
@@ -413,7 +380,6 @@ class Util:
out.is_change = True
return out
@staticmethod
def get_current_height(network):
# if no network or not up to date, just set locktime to zero
if not network:
@@ -435,7 +401,6 @@ class Util:
height = min(chain_height, server_height)
return height
@staticmethod
def print_var(var, name="", veryverbose=False):
print(f"---{name}---")
if var is not None:
@@ -470,7 +435,6 @@ class Util:
print(f"---end {name}---")
@staticmethod
def print_utxo(utxo, name=""):
print(f"---utxo-{name}---")
Util.print_var(utxo, name)
@@ -482,20 +446,36 @@ class Util:
print("_TxInput__value_sats:", utxo._TxInput__value_sats)
print(f"---utxo-end {name}---")
@staticmethod
def print_prevout(prevout, name=""):
print(f"---prevout-{name}---")
Util.print_var(prevout, f"{name}-prevout")
Util.print_var(prevout._asdict())
print(f"---prevout-end {name}---")
def export_meta_gui(electrum_window, title, exporter):
filter_ = "All files (*)"
filename = getSaveFileName(
parent=electrum_window,
title=_("Select file to save your {}".format(title)),
filename="BALplugin_{}".format(title),
filter=filter_,
config=electrum_window.config,
)
if not filename:
return
try:
exporter(filename)
except FileExportFailed as e:
electrum_window.show_critical(str(e))
else:
electrum_window.show_message(
_("Your {0} were exported to '{1}'".format(title, str(filename)))
)
@staticmethod
def copy(dicto, dictfrom):
for k, v in dictfrom.items():
dicto[k] = v
@staticmethod
def fix_will_settings_tx_fees(will_settings):
tx_fees = will_settings.get("tx_fees", False)
have_to_update = False
@@ -505,7 +485,6 @@ class Util:
have_to_update = True
return have_to_update
@staticmethod
def fix_will_tx_fees(will):
have_to_update = False
for txid, willitem in will.items():
@@ -515,19 +494,3 @@ class Util:
del will[txid]["tx_fees"]
have_to_update = True
return have_to_update
@staticmethod
def text_to_hex(text: str) -> str:
"""Convert text to hexadecimal string"""
hex_string = text.encode('utf-8').hex()
return hex_string
@staticmethod
def hex_to_text(hex_string: str) -> str:
"""Convert hexadecimal string back to text (for verification)"""
try:
return bytes.fromhex(hex_string).decode('utf-8')
except Exception:
return "Error: Invalid hex string"

View File

@@ -1,50 +0,0 @@
## README
### Overview
This tool provides two entry points: a CLI script (bal_wallet_utils.py) and a Qt GUI script (bal_wallet_utils_qt.py) that operate against an Electrum source tree.
### Installation / Preparation
1. Copy both files into the Electrum project root (the folder that contains the Electrum source package):
- bal_wallet_utils.py
- bal_wallet_utils_qt.py
2. Activate the Electrum Python environment (the virtualenv used to run Electrum). Example (PowerShell, adjust path to your venv):
```
.\env\Scripts\Activate.ps1
```
or (cmd):
```
env\Scripts\activate.bat
```
### Running
- CLI version:
```
python bal_wallet_utils.py
```
- Qt GUI version:
```
python bal_wallet_utils_qt.py
```
### Building a Windows executable with PyInstaller
From the project root (with the Electrum environment active), you can build the Qt executable using PyInstaller. Example command (adjust the paths if your environment path differs):
```
pyinstaller.exe --onefile --noconsole --add-data "electrum\currencies.json;electrum" --add-data "electrum\bip39_wallet_formats.json;electrum" --add-data "electrum\lnwire\peer_wire.csv;electrum\lnwire" --add-data "electrum\lnwire\onion_wire.csv;electrum\lnwire" --add-binary "env/Lib/site-packages\electrum_ecc\libsecp256k1-6.dll;electrum_ecc" bal_wallet_utils_qt.py
```
Notes:
- Run the command from the project root so relative paths resolve correctly.
- On Windows the --add-data and --add-binary arguments use ";" to separate source and destination.
- If electrum expects additional data files or native DLLs, include them with additional --add-data / --add-binary flags.
- For debugging include --onedir first to inspect the created folder before using --onefile.
### Troubleshooting
- If PyInstaller is not found, run it via Python:
```
python -m PyInstaller <same arguments>
```
- If the frozen exe fails because DLLs or JSON files are missing, add those files explicitly with --add-data or --add-binary.
- Test the build on a clean Windows VM to ensure all runtime dependencies are included.
License and attribution: include your preferred license or attribution details here.

View File

@@ -1,11 +1,10 @@
#!env/bin/python3
import getpass
import json
import os
import sys
from electrum.storage import WalletStorage
import json
from electrum.util import MyEncoder
import sys
import getpass
import os
default_fees = 100
@@ -27,12 +26,9 @@ def fix_will_settings_tx_fees(json_wallet):
def uninstall_bal(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"]
del json_wallet["will_settings"]
del json_wallet["will"]
del json_wallet["heirs"]
return True

View File

@@ -1,31 +1,30 @@
#!/usr/bin/env python3
import json
import os
import sys
from bal_wallet_utils import fix_will_settings_tx_fees, save, uninstall_bal
from electrum.storage import WalletStorage
import os
import json
from PyQt6.QtWidgets import (
QApplication,
QFileDialog,
QGroupBox,
QMainWindow,
QVBoxLayout,
QHBoxLayout,
QLabel,
QLineEdit,
QMainWindow,
QPushButton,
QTextEdit,
QVBoxLayout,
QWidget,
QFileDialog,
QGroupBox,
QTextEdit,
)
from electrum.storage import WalletStorage
from bal_wallet_utils import fix_will_settings_tx_fees, uninstall_bal, save
class WalletUtilityGUI(QMainWindow):
def __init__(self):
super().__init__()
self.init_ui()
self.initUI()
def init_ui(self):
def initUI(self):
self.setWindowTitle("BAL Wallet Utility")
self.setFixedSize(500, 400)

62
will.py
View File

@@ -24,7 +24,6 @@ _logger = get_logger(__name__)
class Will:
@staticmethod
def get_children(will, willid):
out = []
for _id in will:
@@ -36,7 +35,6 @@ class Will:
return out
# build a tree with parent transactions
@staticmethod
def add_willtree(will):
for willid in will:
will[willid].children = Will.get_children(will, willid)
@@ -45,17 +43,14 @@ class Will:
will[child[0]].father = willid
# return a list of will sorted by locktime
@staticmethod
def get_sorted_will(will):
return sorted(will.items(), key=lambda x: x[1]["tx"].locktime)
@staticmethod
def only_valid(will):
for k, v in will.items():
if v.get_status("VALID"):
yield k
@staticmethod
def search_equal_tx(will, tx, wid):
for w in will:
if w != wid and not tx.to_json() != will[w]["tx"].to_json():
@@ -64,7 +59,6 @@ class Will:
return will[w]["tx"]
return False
@staticmethod
def get_tx_from_any(x):
try:
a = str(x)
@@ -75,7 +69,6 @@ class Will:
return x
@staticmethod
def add_info_from_will(will, wid, wallet):
if isinstance(will[wid].tx, str):
will[wid].tx = Will.get_tx_from_any(will[wid].tx)
@@ -96,9 +89,7 @@ class Will:
txin._TxInput__value_sats = change.value
txin._trusted_value_sats = change.value
@staticmethod
def normalize_will(will, wallet=None, others_inputs=None):
others_input = others_inputs if others_inputs is not None else {}
def normalize_will(will, wallet=None, others_inputs={}):
to_delete = []
to_add = {}
# add info from wallet
@@ -147,7 +138,6 @@ class Will:
if wid in will:
del will[wid]
@staticmethod
def new_input(txid, idx, change):
prevout = TxOutpoint(txid=bfh(txid), out_idx=idx)
inp = PartialTxInput(prevout=prevout)
@@ -158,7 +148,16 @@ class Will:
inp._TxInput__value_sats = change.value
return inp
@staticmethod
"""
in questa situazione sono presenti due transazioni con id differente(quindi transazioni differenti)
per prima cosa controllo il locktime
se il locktime della nuova transazione e' maggiore del locktime della vecchia transazione, allora
confronto gli eredi, per locktime se corrispondono controllo i willexecutor
se hanno la stessa url ma le fee vecchie sono superiori alle fee nuove, allora anticipare.
"""
def check_anticipate(ow: "WillItem", nw: "WillItem"):
anticipate = Util.anticipate_locktime(ow.tx.locktime, days=1)
if int(nw.tx.locktime) >= int(anticipate):
@@ -188,7 +187,6 @@ class Will:
return anticipate
return 4294967295 + 1
@staticmethod
def change_input(will, otxid, idx, change, others_inputs, to_delete, to_append):
ow = will[otxid]
ntxid = ow.tx.txid()
@@ -229,7 +227,6 @@ class Will:
to_append,
)
@staticmethod
def get_all_inputs(will, only_valid=False):
all_inputs = {}
for w, wi in will.items():
@@ -244,7 +241,6 @@ class Will:
all_inputs[prevout_str].append(inp)
return all_inputs
@staticmethod
def get_all_inputs_min_locktime(all_inputs):
all_inputs_min_locktime = {}
@@ -259,7 +255,6 @@ class Will:
return all_inputs_min_locktime
@staticmethod
def search_anticipate_rec(will, old_inputs):
redo = False
to_delete = []
@@ -299,7 +294,6 @@ class Will:
Will.search_anticipate_rec(will, old_inputs)
@staticmethod
def update_will(old_will, new_will):
all_old_inputs = Will.get_all_inputs(old_will, only_valid=True)
# all_inputs_min_locktime = Will.get_all_inputs_min_locktime(all_old_inputs)
@@ -326,7 +320,6 @@ class Will:
else:
continue
@staticmethod
def get_higher_input_for_tx(will):
out = {}
for wid in will:
@@ -340,7 +333,6 @@ class Will:
out[inp.prevout.to_str()] = inp
return out
@staticmethod
def invalidate_will(will, wallet, fees_per_byte):
will_only_valid = Will.only_valid_list(will)
inputs = Will.get_all_inputs(will_only_valid)
@@ -392,13 +384,11 @@ class Will:
_logger.debug("len utxo_to_spend <=0")
pass
@staticmethod
def is_new(will):
for wid, w in will.items():
if w.get_status("VALID") and not w.get_status("COMPLETE"):
return True
@staticmethod
def search_rai(all_inputs, all_utxos, will, wallet):
# will_only_valid = Will.only_valid_or_replaced_list(will)
for inp, ws in all_inputs.items():
@@ -432,25 +422,20 @@ class Will:
else:
pass
@staticmethod
def utxos_strs(utxos):
return [Util.utxo_to_str(u) for u in utxos]
@staticmethod
def set_invalidate(wid, will=None):
will = will if will is not None else {}
def set_invalidate(wid, will=[]):
will[wid].set_status("INVALIDATED", True)
if will[wid].children:
for c in will[wid].children.items():
Will.set_invalidate(c[0], will)
@staticmethod
def check_tx_height(tx, wallet):
info = wallet.get_tx_info(tx)
return info.tx_mined_status.height()
# check if transactions are stil valid tecnically valid
@staticmethod
def check_invalidated(willtree, utxos_list, wallet):
for wid, w in willtree.items():
if (
@@ -483,7 +468,6 @@ class Will:
# if wc.children:
# Will.reflect_to_children(wc)
@staticmethod
def check_amounts(heirs, willexecutors, all_utxos, timestamp_to_check, dust):
fixed_heirs, fixed_amount, perc_heirs, perc_amount, fixed_amount_with_dust = (
heirs.fixed_percent_lists_amount(timestamp_to_check, dust, reverse=True)
@@ -507,7 +491,6 @@ class Will:
f"Willexecutor{url} excess base fee({wex['base_fee']}), {fixed_amount} >={temp_balance}"
)
@staticmethod
def check_will(will, all_utxos, wallet, block_to_check, timestamp_to_check):
Will.add_willtree(will)
utxos_list = Will.utxos_strs(all_utxos)
@@ -524,28 +507,18 @@ class Will:
Will.search_rai(all_inputs, all_utxos, will, wallet)
@staticmethod
def get_min_locktime(will,default_value=None):
return min((v.tx.locktime for v in will.values() if v.get_status('VALID')), default=default_value)
@staticmethod
def is_will_valid(
will,
block_to_check,
timestamp_to_check,
tx_fees,
all_utxos,
heirs=None,
willexecutors=None,
heirs={},
willexecutors={},
self_willexecutor=False,
wallet=False,
callback_not_valid_tx=None,
):
heirs = heirs if heirs is not None else {}
willexecutors= willexecutors if willexecutors is not None else {}
Will.check_will(will, all_utxos, wallet, block_to_check, timestamp_to_check)
if heirs:
if not Will.check_willexecutors_and_heirs(
@@ -574,7 +547,6 @@ class Will:
_logger.info("will ok")
return True
@staticmethod
def check_will_expired(all_inputs_min_locktime, block_to_check, timestamp_to_check):
_logger.info("check if some transaction is expired")
for prevout_str, wid in all_inputs_min_locktime.items():
@@ -606,7 +578,6 @@ class Will:
# if not parentwill or not parentwill.get_status("VALID"):
# w[1].set_status("INVALIDATED", True)
@staticmethod
def only_valid_list(will):
out = {}
for wid, w in will.items():
@@ -614,7 +585,6 @@ class Will:
out[wid] = w
return out
@staticmethod
def only_valid_or_replaced_list(will):
out = []
for wid, w in will.items():
@@ -623,7 +593,6 @@ class Will:
out.append(wid)
return out
@staticmethod
def check_willexecutors_and_heirs(
will, heirs, willexecutors, self_willexecutor, check_date, tx_fees
):
@@ -637,7 +606,7 @@ class Will:
for wid in Will.only_valid_list(will):
w = will[wid]
if w.tx_fees != tx_fees:
raise TxFeesChangedException(f"{tx_fees}: {w.tx_fees}")
raise TxFeesChangedException(f"{tx_fees}:", w.tx_fees)
for wheir in w.heirs:
if not 'w!ll3x3c"' == wheir[:9]:
their = will[wid].heirs[wheir]
@@ -686,7 +655,6 @@ class Will:
return True
class WillItem(Logger):
STATUS_DEFAULT = {
"ANTICIPATED": ["Anticipated", False],

View File

@@ -1,8 +1,9 @@
import json
import time
from datetime import datetime
import time
from aiohttp import ClientResponse
from electrum import constants
from electrum.i18n import _
from electrum.logging import get_logger
from electrum.network import Network
@@ -13,12 +14,11 @@ DEFAULT_TIMEOUT = 5
_logger = get_logger(__name__)
chainname = BalPlugin.chainname
chainname = constants.net.NET_NAME if constants.net.NET_NAME != "mainnet" else "bitcoin"
class Willexecutors:
@staticmethod
def save(bal_plugin, willexecutors):
_logger.debug(f"save {willexecutors},{chainname}")
aw = bal_plugin.WILLEXECUTORS.get()
@@ -27,7 +27,6 @@ class Willexecutors:
_logger.debug(f"saved: {aw}")
# bal_plugin.WILLEXECUTORS.set(willexecutors)
@staticmethod
def get_willexecutors(
bal_plugin, update=False, bal_window=False, force=False, task=True
):
@@ -79,7 +78,6 @@ class Willexecutors:
)
return w_sorted
@staticmethod
def is_selected(willexecutor, value=None):
if not willexecutor:
return False
@@ -91,7 +89,6 @@ class Willexecutors:
willexecutor["selected"] = False
return False
@staticmethod
def get_willexecutor_transactions(will, force=False):
willexecutors = {}
for wid, willitem in will.items():
@@ -127,7 +124,6 @@ class Willexecutors:
# willexecutors[url]["txs"], url
# )
@staticmethod
def send_request(
method, url, data=None, *, timeout=10, handle_response=None, count_reply=0
):
@@ -181,14 +177,12 @@ class Willexecutors:
_logger.debug(f"--> {response}")
return response
@staticmethod
def get_we_url_from_response(resp):
url_slices = str(resp.url).split("/")
if len(url_slices) > 2:
url_slices = url_slices[:-2]
return "/".join(url_slices)
@staticmethod
async def handle_response(resp: ClientResponse):
r = await resp.text()
try:
@@ -202,11 +196,9 @@ class Willexecutors:
pass
return r
@staticmethod
class AlreadyPresentException(Exception):
pass
@staticmethod
def push_transactions_to_willexecutor(willexecutor):
out = True
try:
@@ -232,12 +224,10 @@ class Willexecutors:
return out
@staticmethod
def ping_servers(willexecutors):
for url, we in willexecutors.items():
Willexecutors.get_info_task(url, we)
@staticmethod
def get_info_task(url, willexecutor):
w = None
try:
@@ -258,9 +248,7 @@ class Willexecutors:
willexecutor["last_update"] = datetime.now().timestamp()
return willexecutor
@staticmethod
def initialize_willexecutor(willexecutor, url, status=None, old_willexecutor=None):
old_willexecutor=old_willexecutor if old_willexecutor is not None else {}
def initialize_willexecutor(willexecutor, url, status=None, old_willexecutor={}):
willexecutor["url"] = url
if status is not None:
willexecutor["status"] = status
@@ -272,13 +260,11 @@ class Willexecutors:
@staticmethod
def download_list(old_willexecutors,welist_server):
def download_list(old_willexecutors):
try:
welist_server = welist_server if welist_server[-1] == '/' else welist_server+'/'
willexecutors = Willexecutors.send_request(
"get",
f"{welist_server}data/{chainname}?page=0&limit=100",
f"https://welist.bitcoin-after.life/data/{chainname}?page=0&limit=100",
)
# del willexecutors["status"]
for w in willexecutors:
@@ -294,7 +280,6 @@ class Willexecutors:
_logger.error(f"Failed to download willexecutors list: {e}")
return {}
@staticmethod
def get_willexecutors_list_from_json():
try:
with open("willexecutors.json") as f:
@@ -309,7 +294,6 @@ class Willexecutors:
return {}
@staticmethod
def check_transaction(txid, url):
_logger.debug(f"{url}:{txid}")
try:
@@ -321,54 +305,53 @@ class Willexecutors:
_logger.error(f"error contacting {url} for checking txs {e}")
raise e
@staticmethod
def compute_id(willexecutor):
return "{}-{}".format(willexecutor.get("url"), willexecutor.get("chain"))
#class WillExecutor:
# def __init__(
# self,
# url,
# base_fee,
# chain,
# info,
# version,
# status,
# is_selected=False,
# promo_code="",
# ):
# self.url = url
# self.base_fee = base_fee
# self.chain = chain
# self.info = info
# self.version = version
# self.status = status
# self.promo_code = promo_code
# self.is_selected = is_selected
# self.id = self.compute_id()
#
# def from_dict(d):
# return WillExecutor(
# url=d.get("url", "http://localhost:8000"),
# base_fee=d.get("base_fee", 1000),
# chain=d.get("chain", chainname),
# info=d.get("info", ""),
# version=d.get("version", 0),
# status=d.get("status", "Ko"),
# is_selected=d.get("is_selected", "False"),
# promo_code=d.get("promo_code", ""),
# )
#
# def to_dict(self):
# return {
# "url": self.url,
# "base_fee": self.base_fee,
# "chain": self.chain,
# "info": self.info,
# "version": self.version,
# "promo_code": self.promo_code,
# }
#
# def compute_id(self):
# return f"{self.url}-{self.chain}"
class WillExecutor:
def __init__(
self,
url,
base_fee,
chain,
info,
version,
status,
is_selected=False,
promo_code="",
):
self.url = url
self.base_fee = base_fee
self.chain = chain
self.info = info
self.version = version
self.status = status
self.promo_code = promo_code
self.is_selected = is_selected
self.id = self.compute_id()
def from_dict(d):
return WillExecutor(
url=d.get("url", "http://localhost:8000"),
base_fee=d.get("base_fee", 1000),
chain=d.get("chain", chainname),
info=d.get("info", ""),
version=d.get("version", 0),
status=d.get("status", "Ko"),
is_selected=d.get("is_selected", "False"),
promo_code=d.get("promo_code", ""),
)
def to_dict(self):
return {
"url": self.url,
"base_fee": self.base_fee,
"chain": self.chain,
"info": self.info,
"version": self.version,
"promo_code": self.promo_code,
}
def compute_id(self):
return f"{self.url}-{self.chain}"