Compare commits

...

No commits in common. "main" and "0.1.1" have entirely different histories.
main ... 0.1.1

11 changed files with 1378 additions and 3171 deletions

View File

@ -1,8 +1,8 @@
from electrum.i18n import _ from electrum.i18n import _
import subprocess import subprocess
from . import bal_resources from . import bal_resources
BUILD_NUMBER = 3 BUILD_NUMBER = 1
REVISION_NUMBER = 2 REVISION_NUMBER = 1
VERSION_NUMBER = 0 VERSION_NUMBER = 0
def _version(): def _version():
return f'{VERSION_NUMBER}.{REVISION_NUMBER}-{BUILD_NUMBER}' return f'{VERSION_NUMBER}.{REVISION_NUMBER}-{BUILD_NUMBER}'

14
bal.py
View File

@ -1,10 +1,15 @@
import random import random
import os import os
from hashlib import sha256
from typing import NamedTuple, Optional, Dict, Tuple
from electrum.plugin import BasePlugin from electrum.plugin import BasePlugin
from electrum.util import to_bytes, bfh
from electrum import json_db from electrum import json_db
from electrum.transaction import tx_from_any from electrum.transaction import tx_from_any
from . import util as Util
from . import willexecutors as Willexecutors
import os 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)
@ -12,10 +17,11 @@ 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:
#print("______________________________________________________________________________________________________")
#print(x)
x['tx']=tx_from_any(x['tx']) x['tx']=tx_from_any(x['tx'])
except Exception as e: except Exception as e:
#Util.print_var(x)
raise e raise e
return x return x
@ -63,7 +69,7 @@ class BalPlugin(BasePlugin):
HIDE_INVALIDATED:True, HIDE_INVALIDATED:True,
ALLOW_REPUSH: False, ALLOW_REPUSH: False,
WILLEXECUTORS: { WILLEXECUTORS: {
'https://bitcoin-after.life:9137': { 'http://bitcoin-after.life:9137': {
"base_fee": 100000, "base_fee": 100000,
"status": "New", "status": "New",
"info":"Bitcoin After Life Will Executor", "info":"Bitcoin After Life Will Executor",
@ -97,7 +103,7 @@ class BalPlugin(BasePlugin):
def config_get(self,key): def config_get(self,key):
v = self.config.get(key,None) v = self.config.get(key,None)
if v is None: if v is None:
self.config.set_key(key,self.DEFAULT_SETTINGS[key]) self.config.set_key(key,self.DEFAULT_SETTINGS[key],save=True)
v = self.DEFAULT_SETTINGS[key] v = self.DEFAULT_SETTINGS[key]
return v return v

View File

@ -95,8 +95,25 @@ class bal_checkbox(QCheckBox):
def __init__(self, plugin,variable,window=None): def __init__(self, plugin,variable,window=None):
QCheckBox.__init__(self) QCheckBox.__init__(self)
self.setChecked(plugin.config_get(variable)) self.setChecked(plugin.config_get(variable))
window=window
def on_check(v): def on_check(v):
plugin.config.set_key(variable, v == 2) plugin.config.set_key(variable, v == Qt.CheckState.Checked, save=True)
plugin.config_get(variable) if window:
plugin._hide_invalidated= plugin.config_get(plugin.HIDE_INVALIDATED)
plugin._hide_replaced= plugin.config_get(plugin.HIDE_REPLACED)
window.update_all()
self.stateChanged.connect(on_check) self.stateChanged.connect(on_check)
#TODO IMPLEMENT PREVIEW DIALOG
#tx list display txid, willexecutor, qrcode, button to sign
# :def preview_dialog(self, txs):
def preview_dialog(self, txs):
w=PreviewDialog(self,txs)
w.exec()
return w
def add_info_from_will(self,tx):
for input in tx.inputs():
pass

View File

@ -225,7 +225,7 @@ class BalCloseDialog(BalDialog):
#self._stopping=True #self._stopping=True
#self.on_success_phase2() #self.on_success_phase2()
# return # return
_logger.debug("have to sign {}".format(self.have_to_sign)) _logger.debug("have to sign",self.have_to_sign)
password=None password=None
if self.have_to_sign is None: if self.have_to_sign is None:
self.msg_set_invalidating() self.msg_set_invalidating()

View File

@ -199,6 +199,7 @@ class WillExecutorDialog(BalDialog,MessageBoxMixin):
def __init__(self, bal_window): def __init__(self, bal_window):
BalDialog.__init__(self,bal_window.window) BalDialog.__init__(self,bal_window.window)
self.bal_plugin = bal_window.bal_plugin self.bal_plugin = bal_window.bal_plugin
self.gui_object = self.bal_plugin.gui_object
self.config = self.bal_plugin.config self.config = self.bal_plugin.config
self.window = bal_window.window self.window = bal_window.window
self.bal_window = bal_window self.bal_window = bal_window

View File

@ -15,8 +15,8 @@ import datetime
import urllib.request import urllib.request
import urllib.parse import urllib.parse
from .bal import BalPlugin from .bal import BalPlugin
from .util import Util from . import util as Util
from .willexecutors import Willexecutors from . import willexecutors as Willexecutors
if TYPE_CHECKING: if TYPE_CHECKING:
from .wallet_db import WalletDB from .wallet_db import WalletDB
from .simple_config import SimpleConfig from .simple_config import SimpleConfig
@ -38,7 +38,6 @@ def reduce_outputs(in_amount, out_amount, fee, outputs):
for output in outputs: for output in outputs:
output.value = math.floor((in_amount-fee)/out_amount * output.value) output.value = math.floor((in_amount-fee)/out_amount * output.value)
"""
#TODO: put this method inside wallet.db to replace or complete get_locktime_for_new_transaction #TODO: put this method inside wallet.db to replace or complete get_locktime_for_new_transaction
def get_current_height(network:'Network'): def get_current_height(network:'Network'):
#if no network or not up to date, just set locktime to zero #if no network or not up to date, just set locktime to zero
@ -58,7 +57,7 @@ def get_current_height(network:'Network'):
# discourage "fee sniping" # discourage "fee sniping"
height = min(chain_height, server_height) height = min(chain_height, server_height)
return height return height
"""

View File

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

2224
qt.py

File diff suppressed because it is too large Load Diff

173
util.py
View File

@ -8,8 +8,7 @@ import urllib.parse
from electrum.util import write_json_file,FileImportFailed,FileExportFailed from electrum.util import write_json_file,FileImportFailed,FileExportFailed
LOCKTIME_THRESHOLD = 500000000 LOCKTIME_THRESHOLD = 500000000
class Util: def locktime_to_str(locktime):
def locktime_to_str(locktime):
try: try:
locktime=int(locktime) locktime=int(locktime)
if locktime > LOCKTIME_THRESHOLD: if locktime > LOCKTIME_THRESHOLD:
@ -17,26 +16,29 @@ class Util:
return dt return dt
except Exception as e: except Exception as e:
#print(e)
pass pass
return str(locktime) return str(locktime)
def str_to_locktime(locktime): def str_to_locktime(locktime):
try: try:
if locktime[-1] in ('y','d','b'): if locktime[-1] in ('y','d','b'):
return locktime return locktime
else: return int(locktime) else: return int(locktime)
except Exception as e: except Exception as e:
pass pass
#print(e)
dt_object = datetime.fromisoformat(locktime) dt_object = datetime.fromisoformat(locktime)
timestamp = dt_object.timestamp() timestamp = dt_object.timestamp()
return int(timestamp) return int(timestamp)
def parse_locktime_string(locktime,w=None): def parse_locktime_string(locktime,w=None):
try: try:
return int(locktime) return int(locktime)
except Exception as e: except Exception as e:
pass pass
#print("parse_locktime_string",e)
try: try:
now = datetime.now() now = datetime.now()
if locktime[-1] == 'y': if locktime[-1] == 'y':
@ -47,19 +49,20 @@ class Util:
locktime = int(locktime[:-1]) locktime = int(locktime[:-1])
height = 0 height = 0
if w: if w:
height = Util.get_current_height(w.network) height = get_current_height(w.network)
locktime+=int(height) locktime+=int(height)
return int(locktime) return int(locktime)
except Exception as e: except Exception as e:
pass print("parse_locktime_string",e)
#raise e
return 0 return 0
def int_locktime(seconds=0,minutes=0,hours=0, days=0, blocks = 0): def int_locktime(seconds=0,minutes=0,hours=0, days=0, blocks = 0):
return int(seconds + minutes*60 + hours*60*60 + days*60*60*24 + blocks * 600) return int(seconds + minutes*60 + hours*60*60 + days*60*60*24 + blocks * 600)
def encode_amount(amount, decimal_point): def encode_amount(amount, decimal_point):
if Util.is_perc(amount): if is_perc(amount):
return amount return amount
else: else:
try: try:
@ -67,21 +70,21 @@ class Util:
except: except:
return 0 return 0
def decode_amount(amount,decimal_point): def decode_amount(amount,decimal_point):
if Util.is_perc(amount): if is_perc(amount):
return amount return amount
else: else:
num=8-decimal_point num=8-decimal_point
basestr="{{:0{}.{}f}}".format(num,num) basestr="{{:0{}.{}f}}".format(num,num)
return "{:08.8f}".format(float(amount)/pow(10,decimal_point)) return "{:08.8f}".format(float(amount)/pow(10,decimal_point))
def is_perc(value): def is_perc(value):
try: try:
return value[-1] == '%' return value[-1] == '%'
except: except:
return False return False
def cmp_array(heira,heirb): def cmp_array(heira,heirb):
try: try:
if not len(heira) == len(heirb): if not len(heira) == len(heirb):
return False return False
@ -92,12 +95,12 @@ class Util:
except: except:
return False return False
def cmp_heir(heira,heirb): def cmp_heir(heira,heirb):
if heira[0] == heirb[0] and heira[1] == heirb[1]: if heira[0] == heirb[0] and heira[1] == heirb[1]:
return True return True
return False return False
def cmp_willexecutor(willexecutora,willexecutorb): def cmp_willexecutor(willexecutora,willexecutorb):
if willexecutora == willexecutorb: if willexecutora == willexecutorb:
return True return True
try: try:
@ -107,7 +110,8 @@ class Util:
return False return False
return False return False
def search_heir_by_values(heirs,heir,values): def search_heir_by_values(heirs,heir,values):
#print()
for h,v in heirs.items(): for h,v in heirs.items():
found = False found = False
for val in values: for val in values:
@ -118,86 +122,98 @@ class Util:
return h return h
return False return False
def cmp_heir_by_values(heira,heirb,values): def cmp_heir_by_values(heira,heirb,values):
for v in values: for v in values:
if heira[v] != heirb[v]: if heira[v] != heirb[v]:
return False return False
return True return True
def cmp_heirs_by_values(heirsa,heirsb,values,exclude_willexecutors=False,reverse = True): def cmp_heirs_by_values(heirsa,heirsb,values,exclude_willexecutors=False,reverse = True):
for heira in heirsa: for heira in heirsa:
if (exclude_willexecutors and not "w!ll3x3c\"" in heira) or not exclude_willexecutors: if (exclude_willexecutors and not "w!ll3x3c\"" in heira) or not exclude_willexecutors:
found = False found = False
for heirb in heirsb: for heirb in heirsb:
if Util.cmp_heir_by_values(heirsa[heira],heirsb[heirb],values): if cmp_heir_by_values(heirsa[heira],heirsb[heirb],values):
found=True found=True
if not found: if not found:
#print(f"not_found {heira}--{heirsa[heira]}")
return False return False
if reverse: if reverse:
return Util.cmp_heirs_by_values(heirsb,heirsa,values,exclude_willexecutors=exclude_willexecutors,reverse=False) return cmp_heirs_by_values(heirsb,heirsa,values,exclude_willexecutors=exclude_willexecutors,reverse=False)
else: else:
return True return True
def cmp_heirs(heirsa,heirsb,cmp_function = lambda x,y: x[0]==y[0] and x[3]==y[3],reverse=True): def cmp_heirs(heirsa,heirsb,cmp_function = lambda x,y: x[0]==y[0] and x[3]==y[3],reverse=True):
try: try:
for heir in heirsa: for heir in heirsa:
if not "w!ll3x3c\"" in heir: if not "w!ll3x3c\"" in heir:
if not heir in heirsb or not cmp_function(heirsa[heir],heirsb[heir]): if not heir in heirsb or not cmp_function(heirsa[heir],heirsb[heir]):
if not Util.search_heir_by_values(heirsb,heirsa[heir],[0,3]): if not search_heir_by_values(heirsb,heirsa[heir],[0,3]):
return False return False
if reverse: if reverse:
return Util.cmp_heirs(heirsb,heirsa,cmp_function,False) return cmp_heirs(heirsb,heirsa,cmp_function,False)
else: else:
return True return True
except Exception as e: except Exception as e:
raise e raise e
return False return False
def cmp_inputs(inputsa,inputsb): def cmp_inputs(inputsa,inputsb):
if len(inputsa) != len(inputsb): if len(inputsa) != len(inputsb):
return False return False
for inputa in inputsa: for inputa in inputsa:
if not Util.in_utxo(inputa,inputsb): if not in_utxo(inputa,inputsb):
return False return False
return True return True
def cmp_outputs(outputsa,outputsb,willexecutor_output = None): def cmp_outputs(outputsa,outputsb,willexecutor_output = None):
if len(outputsa) != len(outputsb): if len(outputsa) != len(outputsb):
return False return False
for outputa in outputsa: for outputa in outputsa:
if not Util.cmp_output(outputa,willexecutor_output): if not cmp_output(outputa,willexecutor_output):
if not Util.in_output(outputa,outputsb): if not in_output(outputa,outputsb):
return False return False
return True return True
def cmp_txs(txa,txb): def cmp_txs(txa,txb):
if not Util.cmp_inputs(txa.inputs(),txb.inputs()): if not cmp_inputs(txa.inputs(),txb.inputs()):
return False return False
if not Util.cmp_outputs(txa.outputs(),txb.outputs()): if not cmp_outputs(txa.outputs(),txb.outputs()):
return False return False
return True return True
def get_value_amount(txa,txb): def get_value_amount(txa,txb):
outputsa=txa.outputs() outputsa=txa.outputs()
outputsb=txb.outputs() outputsb=txb.outputs()
value_amount = 0 value_amount = 0
#if len(outputsa) != len(outputsb):
# print("outputlen is different")
# return False
for outa in outputsa: for outa in outputsa:
same_amount,same_address = Util.in_output(outa,txb.outputs()) same_amount,same_address = in_output(outa,txb.outputs())
if not (same_amount or same_address): if not (same_amount or same_address):
#print("outa notin txb", same_amount,same_address)
return False return False
if same_amount and same_address: if same_amount and same_address:
value_amount+=outa.value value_amount+=outa.value
if same_amount: if same_amount:
pass pass
#print("same amount")
if same_address: if same_address:
pass pass
#print("same address")
return value_amount return value_amount
#not needed
#for outb in outputsb:
# if not in_output(outb,txa.outputs()):
# print("outb notin txb")
# return False
def chk_locktime(timestamp_to_check,block_height_to_check,locktime): def chk_locktime(timestamp_to_check,block_height_to_check,locktime):
#TODO BUG: WHAT HAPPEN AT THRESHOLD? #TODO BUG: WHAT HAPPEN AT THRESHOLD?
locktime=int(locktime) locktime=int(locktime)
if locktime > LOCKTIME_THRESHOLD and locktime > timestamp_to_check: if locktime > LOCKTIME_THRESHOLD and locktime > timestamp_to_check:
@ -207,7 +223,7 @@ class Util:
else: else:
return False return False
def anticipate_locktime(locktime,blocks=0,hours=0,days=0): def anticipate_locktime(locktime,blocks=0,hours=0,days=0):
locktime = int(locktime) locktime = int(locktime)
out=0 out=0
if locktime> LOCKTIME_THRESHOLD: if locktime> LOCKTIME_THRESHOLD:
@ -223,13 +239,13 @@ class Util:
out = 1 out = 1
return out return out
def cmp_locktime(locktimea,locktimeb): def cmp_locktime(locktimea,locktimeb):
if locktimea==locktimeb: if locktimea==locktimeb:
return 0 return 0
strlocktime = str(locktimea) strlocktime = str(locktimea)
strlocktimeb = str(locktimeb) strlocktimeb = str(locktimeb)
intlocktimea = Util.str_to_locktime(strlocktimea) intlocktimea = str_to_locktime(strlocktimea)
intlocktimeb = Util.str_to_locktime(strlocktimeb) intlocktimeb = str_to_locktime(strlocktimeb)
if locktimea[-1] in "ydb": if locktimea[-1] in "ydb":
if locktimeb[-1] == locktimea[-1]: if locktimeb[-1] == locktimea[-1]:
return int(strlocktimea[-1])-int(strlocktimeb[-1]) return int(strlocktimea[-1])-int(strlocktimeb[-1])
@ -237,22 +253,23 @@ class Util:
return int(locktimea)-(locktimeb) return int(locktimea)-(locktimeb)
def get_lowest_valid_tx(available_utxos,will): def get_lowest_valid_tx(available_utxos,will):
will = sorted(will.items(),key = lambda x: x[1]['tx'].locktime) will = sorted(will.items(),key = lambda x: x[1]['tx'].locktime)
for txid,willitem in will.items(): for txid,willitem in will.items():
pass pass
def get_locktimes(will): def get_locktimes(will):
locktimes = {} locktimes = {}
for txid,willitem in will.items(): for txid,willitem in will.items():
locktimes[willitem['tx'].locktime]=True locktimes[willitem['tx'].locktime]=True
return locktimes.keys() return locktimes.keys()
def get_lowest_locktimes(locktimes): def get_lowest_locktimes(locktimes):
sorted_timestamp=[] sorted_timestamp=[]
sorted_block=[] sorted_block=[]
for l in locktimes: for l in locktimes:
l=Util.parse_locktime_string(l) #print("locktime:",parse_locktime_string(l))
l=parse_locktime_string(l)
if l < LOCKTIME_THRESHOLD: if l < LOCKTIME_THRESHOLD:
bisect.insort(sorted_block,l) bisect.insort(sorted_block,l)
else: else:
@ -260,83 +277,86 @@ class Util:
return sorted(sorted_timestamp), sorted(sorted_block) return sorted(sorted_timestamp), sorted(sorted_block)
def get_lowest_locktimes_from_will(will): def get_lowest_locktimes_from_will(will):
return Util.get_lowest_locktimes(Util.get_locktimes(will)) return get_lowest_locktimes(get_locktimes(will))
def search_willtx_per_io(will,tx): def search_willtx_per_io(will,tx):
for wid, w in will.items(): for wid, w in will.items():
if Util.cmp_txs(w['tx'],tx['tx']): if cmp_txs(w['tx'],tx['tx']):
return wid,w return wid,w
return None, None return None, None
def invalidate_will(will): def invalidate_will(will):
raise Exception("not implemented") raise Exception("not implemented")
def get_will_spent_utxos(will): def get_will_spent_utxos(will):
utxos=[] utxos=[]
for txid,willitem in will.items(): for txid,willitem in will.items():
utxos+=willitem['tx'].inputs() utxos+=willitem['tx'].inputs()
return utxos return utxos
def utxo_to_str(utxo): def utxo_to_str(utxo):
try: return utxo.to_str() try: return utxo.to_str()
except Exception as e: pass except Exception as e: pass
try: return utxo.prevout.to_str() try: return utxo.prevout.to_str()
except Exception as e: pass except Exception as e: pass
return str(utxo) return str(utxo)
def cmp_utxo(utxoa,utxob): def cmp_utxo(utxoa,utxob):
utxoa=Util.utxo_to_str(utxoa) utxoa=utxo_to_str(utxoa)
utxob=Util.utxo_to_str(utxob) utxob=utxo_to_str(utxob)
if utxoa == utxob: if utxoa == utxob:
#if utxoa.prevout.txid==utxob.prevout.txid and utxoa.prevout.out_idx == utxob.prevout.out_idx:
return True return True
else: else:
return False return False
def in_utxo(utxo, utxos): def in_utxo(utxo, utxos):
for s_u in utxos: for s_u in utxos:
if Util.cmp_utxo(s_u,utxo): if cmp_utxo(s_u,utxo):
return True return True
return False return False
def txid_in_utxo(txid,utxos): def txid_in_utxo(txid,utxos):
for s_u in utxos: for s_u in utxos:
if s_u.prevout.txid == txid: if s_u.prevout.txid == txid:
return True return True
return False return False
def cmp_output(outputa,outputb): def cmp_output(outputa,outputb):
return outputa.address == outputb.address and outputa.value == outputb.value return outputa.address == outputb.address and outputa.value == outputb.value
def in_output(output,outputs): def in_output(output,outputs):
for s_o in outputs: for s_o in outputs:
if Util.cmp_output(s_o,output): if cmp_output(s_o,output):
return True return True
return False return False
#check all output with the same amount if none have the same address it can be a change #check all output with the same amount if none have the same address it can be a change
#return true true same address same amount #return true true same address same amount
#return true false same amount different address #return true false same amount different address
#return false false different amount, different address not found #return false false different amount, different address not found
def din_output(out,outputs): def din_output(out,outputs):
same_amount=[] same_amount=[]
for s_o in outputs: for s_o in outputs:
if int(out.value) == int(s_o.value): if int(out.value) == int(s_o.value):
same_amount.append(s_o) same_amount.append(s_o)
if out.address==s_o.address: if out.address==s_o.address:
#print("SAME_:",out.address,s_o.address)
return True, True return True, True
else: else:
pass pass
#print("NOT SAME_:",out.address,s_o.address)
if len(same_amount)>0: if len(same_amount)>0:
return True, False return True, False
else:return False, False else:return False, False
def get_change_output(wallet,in_amount,out_amount,fee): def get_change_output(wallet,in_amount,out_amount,fee):
change_amount = int(in_amount - out_amount - fee) change_amount = int(in_amount - out_amount - fee)
if change_amount > wallet.dust_threshold(): if change_amount > wallet.dust_threshold():
change_addresses = wallet.get_change_addresses_for_new_transaction() change_addresses = wallet.get_change_addresses_for_new_transaction()
@ -345,7 +365,7 @@ class Util:
return out return out
def get_current_height(network:'Network'): def get_current_height(network:'Network'):
#if no network or not up to date, just set locktime to zero #if no network or not up to date, just set locktime to zero
if not network: if not network:
return 0 return 0
@ -365,7 +385,7 @@ class Util:
return height return height
def print_var(var,name = "",veryverbose=False): def print_var(var,name = "",veryverbose=False):
print(f"---{name}---") print(f"---{name}---")
if not var is None: if not var is None:
try: try:
@ -395,24 +415,25 @@ class Util:
print(f"---end {name}---") print(f"---end {name}---")
def print_utxo(utxo, name = ""): def print_utxo(utxo, name = ""):
print(f"---utxo-{name}---") print(f"---utxo-{name}---")
Util.print_var(utxo,name) print_var(utxo,name)
Util.print_prevout(utxo.prevout,name) print_prevout(utxo.prevout,name)
Util.print_var(utxo.script_sig,f"{name}-script-sig") print_var(utxo.script_sig,f"{name}-script-sig")
Util.print_var(utxo.witness,f"{name}-witness") print_var(utxo.witness,f"{name}-witness")
#print("madonnamaiala_TXInput__scriptpubkey:",utxo._TXInput__scriptpubkey)
print("_TxInput__address:",utxo._TxInput__address) print("_TxInput__address:",utxo._TxInput__address)
print("_TxInput__scriptpubkey:",utxo._TxInput__scriptpubkey) print("_TxInput__scriptpubkey:",utxo._TxInput__scriptpubkey)
print("_TxInput__value_sats:",utxo._TxInput__value_sats) print("_TxInput__value_sats:",utxo._TxInput__value_sats)
print(f"---utxo-end {name}---") print(f"---utxo-end {name}---")
def print_prevout(prevout, name = ""): def print_prevout(prevout, name = ""):
print(f"---prevout-{name}---") print(f"---prevout-{name}---")
Util.print_var(prevout,f"{name}-prevout") print_var(prevout,f"{name}-prevout")
Util.print_var(prevout._asdict()) print_var(prevout._asdict())
print(f"---prevout-end {name}---") print(f"---prevout-end {name}---")
def export_meta_gui(electrum_window: 'ElectrumWindow', title, exporter): def export_meta_gui(electrum_window: 'ElectrumWindow', title, exporter):
filter_ = "All files (*)" filter_ = "All files (*)"
filename = getSaveFileName( filename = getSaveFileName(
parent=electrum_window, parent=electrum_window,
@ -432,6 +453,6 @@ class Util:
.format(title, str(filename))) .format(title, str(filename)))
def copy(dicto,dictfrom): def copy(dicto,dictfrom):
for k,v in dictfrom.items(): for k,v in dictfrom.items():
dicto[k]=v dicto[k]=v

184
will.py
View File

@ -1,7 +1,7 @@
import copy import copy
from .willexecutors import Willexecutors from . import willexecutors as Willexecutors
from .util import Util from . import util as Util
from electrum.i18n import _ from electrum.i18n import _
@ -16,8 +16,7 @@ MIN_BLOCK = 1
_logger = get_logger(__name__) _logger = get_logger(__name__)
#return an array with the list of children #return an array with the list of children
class Will: def get_children(will,willid):
def get_children(will,willid):
out = [] out = []
for _id in will: for _id in will:
inputs = will[_id].tx.inputs() inputs = will[_id].tx.inputs()
@ -27,26 +26,26 @@ class Will:
out.append([_id,idi,_input.prevout.out_idx]) out.append([_id,idi,_input.prevout.out_idx])
return out return out
#build a tree with parent transactions #build a tree with parent transactions
def add_willtree(will): def add_willtree(will):
for willid in will: for willid in will:
will[willid].children = Will.get_children(will,willid) will[willid].children = get_children(will,willid)
for child in will[willid].children: for child in will[willid].children:
if not will[child[0]].father: if not will[child[0]].father:
will[child[0]].father = willid will[child[0]].father = willid
#return a list of will sorted by locktime #return a list of will sorted by locktime
def get_sorted_will(will): def get_sorted_will(will):
return sorted(will.items(),key = lambda x: x[1]['tx'].locktime) return sorted(will.items(),key = lambda x: x[1]['tx'].locktime)
def only_valid(will): def only_valid(will):
for k,v in will.items(): for k,v in will.items():
if v.get_status('VALID'): if v.get_status('VALID'):
yield k yield k
def search_equal_tx(will,tx,wid): def search_equal_tx(will,tx,wid):
for w in will: for w in will:
if w != wid and not tx.to_json() != will[w]['tx'].to_json(): if w != wid and not tx.to_json() != will[w]['tx'].to_json():
if will[w]['tx'].txid() != tx.txid(): if will[w]['tx'].txid() != tx.txid():
@ -54,7 +53,7 @@ class Will:
return will[w]['tx'] return will[w]['tx']
return False return False
def get_tx_from_any(x): def get_tx_from_any(x):
try: try:
a=str(x) a=str(x)
return tx_from_any(a) return tx_from_any(a)
@ -64,9 +63,9 @@ class Will:
return x return x
def add_info_from_will(will,wid,wallet): def add_info_from_will(will,wid,wallet):
if isinstance(will[wid].tx,str): if isinstance(will[wid].tx,str):
will[wid].tx = Will.get_tx_from_any(will[wid].tx) will[wid].tx = get_tx_from_any(will[wid].tx)
if wallet: if wallet:
will[wid].tx.add_info_from_wallet(wallet) will[wid].tx.add_info_from_wallet(wallet)
for txin in will[wid].tx.inputs(): for txin in will[wid].tx.inputs():
@ -84,12 +83,12 @@ class Will:
txin._TxInput__value_sats = change.value txin._TxInput__value_sats = change.value
txin._trusted_value_sats = change.value txin._trusted_value_sats = change.value
def normalize_will(will,wallet = None,others_inputs = {}): def normalize_will(will,wallet = None,others_inputs = {}):
to_delete = [] to_delete = []
to_add = {} to_add = {}
#add info from wallet #add info from wallet
for wid in will: for wid in will:
Will.add_info_from_will(will,wid,wallet) add_info_from_will(will,wid,wallet)
errors ={} errors ={}
for wid in will: for wid in will:
@ -113,7 +112,7 @@ class Will:
will[wid]=ow.to_dict() will[wid]=ow.to_dict()
for i in range(0,len(outputs)): for i in range(0,len(outputs)):
Will.change_input(will,wid,i,outputs[i],others_inputs,to_delete,to_add) change_input(will,wid,i,outputs[i],others_inputs,to_delete,to_add)
to_delete.append(wid) to_delete.append(wid)
to_add[ow.tx.txid()]=ow.to_dict() to_add[ow.tx.txid()]=ow.to_dict()
@ -128,7 +127,7 @@ class Will:
if wid in will: if wid in will:
del will[wid] del will[wid]
def new_input(txid,idx,change): def new_input(txid,idx,change):
prevout = TxOutpoint(txid=bfh(txid), out_idx=idx) prevout = TxOutpoint(txid=bfh(txid), out_idx=idx)
inp = PartialTxInput(prevout=prevout) inp = PartialTxInput(prevout=prevout)
inp._trusted_value_sats = change.value inp._trusted_value_sats = change.value
@ -138,20 +137,27 @@ class Will:
inp._TxInput__value_sats = change.value inp._TxInput__value_sats = change.value
return inp return inp
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):
if Util.cmp_heirs_by_values(ow.heirs,nw.heirs,[0,1],exclude_willexecutors = True): if Util.cmp_heirs_by_values(ow.heirs,nw.heirs,[0,1],exclude_willexecutors = True):
print("same heirs",ow._id,nw._id)
if nw.we and ow.we: if nw.we and ow.we:
if ow.we['url'] == nw.we['url']: if ow.we['url'] == nw.we['url']:
print("same willexecutors", ow.we['url'],nw.we['url'])
if int(ow.we['base_fee'])>int(nw.we['base_fee']): if int(ow.we['base_fee'])>int(nw.we['base_fee']):
print("anticipate")
return anticipate return anticipate
else: else:
if int(ow.tx_fees) != int(nw.tx_fees): if int(ow.tx_fees) != int(nw.tx_fees):
return anticipate return anticipate
else: else:
print("keep the same")
#_logger.debug("ow,base fee > nw.base_fee")
ow.tx.locktime ow.tx.locktime
else: else:
#_logger.debug("ow.we['url']({ow.we['url']}) == nw.we['url']({nw.we['url']})")
print("keep the same")
ow.tx.locktime ow.tx.locktime
else: else:
if nw.we == ow.we: if nw.we == ow.we:
@ -166,7 +172,7 @@ class Will:
return 4294967295+1 return 4294967295+1
def change_input(will, otxid, idx, change,others_inputs,to_delete,to_append): def change_input(will, otxid, idx, change,others_inputs,to_delete,to_append):
ow = will[otxid] ow = will[otxid]
ntxid = ow.tx.txid() ntxid = ow.tx.txid()
if otxid != ntxid: if otxid != ntxid:
@ -182,7 +188,7 @@ class Will:
if isinstance(w.tx,Transaction): if isinstance(w.tx,Transaction):
will[wid].tx = PartialTransaction.from_tx(w.tx) will[wid].tx = PartialTransaction.from_tx(w.tx)
will[wid].tx.set_rbf(True) will[wid].tx.set_rbf(True)
will[wid].tx._inputs[i]=Will.new_input(wid,idx,change) will[wid].tx._inputs[i]=new_input(wid,idx,change)
found = True found = True
if found == True: if found == True:
pass pass
@ -193,9 +199,9 @@ class Will:
to_append[new_txid]=will[wid] to_append[new_txid]=will[wid]
outputs = will[wid].tx.outputs() outputs = will[wid].tx.outputs()
for i in range(0,len(outputs)): for i in range(0,len(outputs)):
Will.change_input(will, wid, i, outputs[i],others_inputs,to_delete,to_append) change_input(will, wid, i, outputs[i],others_inputs,to_delete,to_append)
def get_all_inputs(will,only_valid = False): def get_all_inputs(will,only_valid = False):
all_inputs = {} all_inputs = {}
for w,wi in will.items(): for w,wi in will.items():
if not only_valid or wi.get_status('VALID'): if not only_valid or wi.get_status('VALID'):
@ -209,7 +215,7 @@ class Will:
all_inputs[prevout_str].append(inp) all_inputs[prevout_str].append(inp)
return all_inputs return all_inputs
def get_all_inputs_min_locktime(all_inputs): def get_all_inputs_min_locktime(all_inputs):
all_inputs_min_locktime = {} all_inputs_min_locktime = {}
for i,values in all_inputs.items(): for i,values in all_inputs.items():
@ -224,11 +230,11 @@ class Will:
return all_inputs_min_locktime return all_inputs_min_locktime
def search_anticipate_rec(will,old_inputs): def search_anticipate_rec(will,old_inputs):
redo = False redo = False
to_delete = [] to_delete = []
to_append = {} to_append = {}
new_inputs = Will.get_all_inputs(will,only_valid = True) new_inputs = 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) or nwi.search_anticipate(old_inputs):
if nid != nwi.tx.txid(): if nid != nwi.tx.txid():
@ -237,7 +243,7 @@ class Will:
to_append[nwi.tx.txid()] = nwi to_append[nwi.tx.txid()] = nwi
outputs = nwi.tx.outputs() outputs = nwi.tx.outputs()
for i in range(0,len(outputs)): for i in range(0,len(outputs)):
Will.change_input(will,nid,i,outputs[i],new_inputs,to_delete,to_append) change_input(will,nid,i,outputs[i],new_inputs,to_delete,to_append)
for w in to_delete: for w in to_delete:
@ -248,25 +254,25 @@ 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) search_anticipate_rec(will,old_inputs)
def update_will(old_will,new_will): def update_will(old_will,new_will):
all_old_inputs = Will.get_all_inputs(old_will,only_valid=True) all_old_inputs = get_all_inputs(old_will,only_valid=True)
all_inputs_min_locktime = Will.get_all_inputs_min_locktime(all_old_inputs) all_inputs_min_locktime = get_all_inputs_min_locktime(all_old_inputs)
all_new_inputs = Will.get_all_inputs(new_will) all_new_inputs = get_all_inputs(new_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) search_anticipate_rec(new_will,all_old_inputs)
other_inputs = Will.get_all_inputs(old_will,{}) other_inputs = get_all_inputs(old_will,{})
try: try:
Will.normalize_will(new_will,others_inputs=other_inputs) normalize_will(new_will,others_inputs=other_inputs)
except Exception as e: except Exception as e:
raise e raise e
for oid in Will.only_valid(old_will): for oid in only_valid(old_will):
if oid in new_will: if oid in new_will:
new_heirs = new_will[oid].heirs new_heirs = new_will[oid].heirs
new_we = new_will[oid].we new_we = new_will[oid].we
@ -274,12 +280,14 @@ class Will:
new_will[oid]=old_will[oid] new_will[oid]=old_will[oid]
new_will[oid].heirs = new_heirs new_will[oid].heirs = new_heirs
new_will[oid].we = new_we new_will[oid].we = new_we
print(f"found {oid}")
continue continue
else: else:
print(f"not found {oid}")
continue continue
def get_higher_input_for_tx(will): def get_higher_input_for_tx(will):
out = {} out = {}
for wid in will: for wid in will:
wtx = will[wid].tx wtx = will[wid].tx
@ -292,9 +300,9 @@ class Will:
out[inp.prevout.to_str()] = inp out[inp.prevout.to_str()] = inp
return out return out
def invalidate_will(will,wallet,fees_per_byte): def invalidate_will(will,wallet,fees_per_byte):
will_only_valid = Will.only_valid_list(will) will_only_valid = only_valid_list(will)
inputs = Will.get_all_inputs(will_only_valid) inputs = get_all_inputs(will_only_valid)
utxos = wallet.get_utxos() utxos = wallet.get_utxos()
filtered_inputs = [] filtered_inputs = []
prevout_to_spend = [] prevout_to_spend = []
@ -336,13 +344,13 @@ class Will:
pass pass
def is_new(will): def is_new(will):
for wid,w in will.items(): for wid,w in will.items():
if w.get_status('VALID') and not w.get_status('COMPLETE'): if w.get_status('VALID') and not w.get_status('COMPLETE'):
return True return True
def search_rai (all_inputs,all_utxos,will,wallet): def search_rai (all_inputs,all_utxos,will,wallet):
will_only_valid = Will.only_valid_or_replaced_list(will) will_only_valid = only_valid_or_replaced_list(will)
for inp,ws in all_inputs.items(): for inp,ws in all_inputs.items():
inutxo = Util.in_utxo(inp,all_utxos) inutxo = Util.in_utxo(inp,all_utxos)
for w in ws: for w in ws:
@ -362,6 +370,16 @@ class Will:
wi.set_status('CONFIRMED',True) wi.set_status('CONFIRMED',True)
else: else:
wi.set_status('INVALIDATED',True) wi.set_status('INVALIDATED',True)
#else:
# if prevout_id in will:
# wo = will[prevout_id]
# ttx= wallet.db.get_transaction(prevout_id)
# if ttx:
# _logger.error("transaction in wallet should be early detected")
# #wi.set_status('CONFIRMED',True)
# #else:
# # _logger.error("transaction not in will or utxo")
# # wi.set_status('INVALIDATED',True)
for child in wi.search(all_inputs): for child in wi.search(all_inputs):
if child.tx.locktime < wi.tx.locktime: if child.tx.locktime < wi.tx.locktime:
@ -370,38 +388,44 @@ class Will:
else: else:
pass pass
def utxos_strs(utxos): def utxos_strs(utxos):
return [Util.utxo_to_str(u) for u in utxos] return [Util.utxo_to_str(u) for u in utxos]
def set_invalidate(wid,will=[]): def set_invalidate(wid,will=[]):
will[wid].set_status("INVALIDATED",True) will[wid].set_status("INVALIDATED",True)
if will[wid].children: if will[wid].children:
for c in self.children.items(): for c in self.children.items():
Will.set_invalidate(c[0],will) set_invalidate(c[0],will)
def check_tx_height(tx, wallet): def check_tx_height(tx, wallet):
info=wallet.get_tx_info(tx) info=wallet.get_tx_info(tx)
return info.tx_mined_status.height return info.tx_mined_status.height
#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:
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)
#print(utxos_list)
#print(inp_str)
#print(inp_str in utxos_list)
#print("notin: ",not inp_str in utxos_list)
if not inp_str in utxos_list: if not inp_str in utxos_list:
#print("quindi qua non ci arrivo?")
if wallet: if wallet:
height= Will.check_tx_height(w.tx,wallet) height= check_tx_height(w.tx,wallet)
if height < 0: if height < 0:
Will.set_invalidate(wid,willtree) #_logger.debug(f"heigth {height}")
set_invalidate(wid,willtree)
elif height == 0: elif height == 0:
w.set_status("PENDING",True) w.set_status("PENDING",True)
else: else:
w.set_status('CONFIRMED',True) w.set_status('CONFIRMED',True)
def reflect_to_children(treeitem): def reflect_to_children(treeitem):
if not treeitem.get_status("VALID"): if not treeitem.get_status("VALID"):
_logger.debug(f"{tree:item._id} status not valid looking for children") _logger.debug(f"{tree:item._id} status not valid looking for children")
for child in treeitem.children: for child in treeitem.children:
@ -412,9 +436,9 @@ class Will:
if treeitem.get_status("REPLACED"): if treeitem.get_status("REPLACED"):
wc.set_status("REPLACED",True) wc.set_status("REPLACED",True)
if wc.children: if wc.children:
Will.reflect_to_children(wc) 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 = heirs.fixed_percent_lists_amount(timestamp_to_check,dust,reverse=True) fixed_heirs,fixed_amount,perc_heirs,perc_amount = heirs.fixed_percent_lists_amount(timestamp_to_check,dust,reverse=True)
wallet_balance = 0 wallet_balance = 0
for utxo in all_utxos: for utxo in all_utxos:
@ -432,33 +456,36 @@ class Will:
raise FixedAmountException(f"Willexecutor{url} excess base fee({wex['base_fee']}), {fixed_amount} >={temp_balance}") raise FixedAmountException(f"Willexecutor{url} excess base fee({wex['base_fee']}), {fixed_amount} >={temp_balance}")
def check_will(will,all_utxos,wallet,block_to_check,timestamp_to_check): def check_will(will,all_utxos,wallet,block_to_check,timestamp_to_check):
Will.add_willtree(will) add_willtree(will)
utxos_list= Will.utxos_strs(all_utxos) utxos_list= utxos_strs(all_utxos)
Will.check_invalidated(will,utxos_list,wallet) check_invalidated(will,utxos_list,wallet)
#from pprint import pprint
#for wid,w in will.items():
# pprint(w.to_dict())
all_inputs=Will.get_all_inputs(will,only_valid = True) all_inputs=get_all_inputs(will,only_valid = True)
all_inputs_min_locktime = Will.get_all_inputs_min_locktime(all_inputs) all_inputs_min_locktime = get_all_inputs_min_locktime(all_inputs)
Will.check_will_expired(all_inputs_min_locktime,block_to_check,timestamp_to_check) check_will_expired(all_inputs_min_locktime,block_to_check,timestamp_to_check)
all_inputs=Will.get_all_inputs(will,only_valid = True) all_inputs=get_all_inputs(will,only_valid = True)
Will.search_rai(all_inputs,all_utxos,will,wallet) search_rai(all_inputs,all_utxos,will,wallet)
def is_will_valid(will, block_to_check, timestamp_to_check, tx_fees, all_utxos,heirs={},willexecutors={},self_willexecutor=False, wallet=False, callback_not_valid_tx=None): def is_will_valid(will, block_to_check, timestamp_to_check, tx_fees, all_utxos,heirs={},willexecutors={},self_willexecutor=False, wallet=False, callback_not_valid_tx=None):
Will.check_will(will,all_utxos,wallet,block_to_check,timestamp_to_check) check_will(will,all_utxos,wallet,block_to_check,timestamp_to_check)
if heirs: if heirs:
if not Will.check_willexecutors_and_heirs(will,heirs,willexecutors,self_willexecutor,timestamp_to_check,tx_fees): if not check_willexecutors_and_heirs(will,heirs,willexecutors,self_willexecutor,timestamp_to_check,tx_fees):
raise NotCompleteWillException() raise NotCompleteWillException()
all_inputs=Will.get_all_inputs(will,only_valid = True) all_inputs=get_all_inputs(will,only_valid = True)
_logger.info('check all utxo in wallet are spent') _logger.info('check all utxo in wallet are spent')
if all_inputs: if all_inputs:
@ -472,7 +499,7 @@ class Will:
_logger.info('will ok') _logger.info('will ok')
return True return True
def check_will_expired(all_inputs_min_locktime,block_to_check,timestamp_to_check): def check_will_expired(all_inputs_min_locktime,block_to_check,timestamp_to_check):
_logger.info("check if some transaction is expired") _logger.info("check if some transaction is expired")
for prevout_str, wid in all_inputs_min_locktime.items(): for prevout_str, wid in all_inputs_min_locktime.items():
for w in wid: for w in wid:
@ -485,7 +512,7 @@ class Will:
if locktime < int(timestamp_to_check): if locktime < int(timestamp_to_check):
raise WillExpiredException(f"Will Expired {wid[0][0]}: {locktime}<{timestamp_to_check}") raise WillExpiredException(f"Will Expired {wid[0][0]}: {locktime}<{timestamp_to_check}")
def check_all_input_spent_are_in_wallet(): def check_all_input_spent_are_in_wallet():
_logger.info("check all input spent are in wallet or valid txs") _logger.info("check all input spent are in wallet or valid txs")
for inp,ws in all_inputs.items(): for inp,ws in all_inputs.items():
if not Util.in_utxo(inp,all_utxos): if not Util.in_utxo(inp,all_utxos):
@ -497,14 +524,14 @@ class Will:
w[1].set_status('INVALIDATED',True) w[1].set_status('INVALIDATED',True)
def only_valid_list(will): def only_valid_list(will):
out={} out={}
for wid,w in will.items(): for wid,w in will.items():
if w.get_status('VALID'): if w.get_status('VALID'):
out[wid]=w out[wid]=w
return out return out
def only_valid_or_replaced_list(will): def only_valid_or_replaced_list(will):
out=[] out=[]
for wid,w in will.items(): for wid,w in will.items():
wi = w wi = w
@ -512,17 +539,18 @@ class Will:
out.append(wid) out.append(wid)
return out return out
def check_willexecutors_and_heirs(will,heirs,willexecutors,self_willexecutor,check_date,tx_fees): def check_willexecutors_and_heirs(will,heirs,willexecutors,self_willexecutor,check_date,tx_fees):
_logger.debug("check willexecutors heirs") _logger.debug("check willexecutors heirs")
no_willexecutor = 0 no_willexecutor = 0
willexecutors_found = {} willexecutors_found = {}
heirs_found = {} heirs_found = {}
will_only_valid = Will.only_valid_list(will) will_only_valid = only_valid_list(will)
if len(will_only_valid)<1: if len(will_only_valid)<1:
return False return False
for wid in Will.only_valid_list(will): for wid in only_valid_list(will):
w = will[wid] w = will[wid]
if w.tx_fees != tx_fees: if w.tx_fees != tx_fees:
#w.set_status('VALID',False)
raise TxFeesChangedException(f"{tx_fees}:",w.tx_fees) raise TxFeesChangedException(f"{tx_fees}:",w.tx_fees)
for wheir in w.heirs: for wheir in w.heirs:
if not 'w!ll3x3c"' == wheir[:9]: if not 'w!ll3x3c"' == wheir[:9]:
@ -602,6 +630,8 @@ class WillItem(Logger):
self.STATUS['PUSH_FAIL'][1] = False self.STATUS['PUSH_FAIL'][1] = False
self.STATUS['CHECK_FAIL'][1] = False self.STATUS['CHECK_FAIL'][1] = False
#if status in ['CHECK_FAIL']:
# self.STATUS['PUSHED'][1] = False
if status in ['CHECKED']: if status in ['CHECKED']:
self.STATUS['PUSHED'][1] = True self.STATUS['PUSHED'][1] = True
@ -616,7 +646,7 @@ class WillItem(Logger):
if isinstance(w,WillItem,): if isinstance(w,WillItem,):
self.__dict__ = w.__dict__.copy() self.__dict__ = w.__dict__.copy()
else: else:
self.tx = Will.get_tx_from_any(w['tx']) self.tx = get_tx_from_any(w['tx'])
self.heirs = w.get('heirs',None) self.heirs = w.get('heirs',None)
self.we = w.get('willexecutor',None) self.we = w.get('willexecutor',None)
self.status = w.get('status',None) self.status = w.get('status',None)
@ -670,11 +700,13 @@ class WillItem(Logger):
return str(self.to_dict()) return str(self.to_dict())
def set_anticipate(self, ow:'WillItem'): def set_anticipate(self, ow:'WillItem'):
nl = min(ow.tx.locktime,Will.check_anticipate(ow,self)) nl = min(ow.tx.locktime,check_anticipate(ow,self))
if int(nl) < self.tx.locktime: if int(nl) < self.tx.locktime:
#_logger.debug("actually anticipating")
self.tx.locktime = int(nl) self.tx.locktime = int(nl)
return True return True
else: else:
#_logger.debug("keeping the same locktime")
return False return False

View File

@ -9,15 +9,16 @@ 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 .util import Util from .balqt.baldialog import BalWaitingDialog
from . import util as Util
DEFAULT_TIMEOUT = 5 DEFAULT_TIMEOUT = 5
_logger = get_logger(__name__) _logger = get_logger(__name__)
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.config_get(bal_plugin.WILLEXECUTORS)
for w in willexecutors: for w in willexecutors:
Willexecutors.initialize_willexecutor(willexecutors[w],w) initialize_willexecutor(willexecutors[w],w)
bal=bal_plugin.DEFAULT_SETTINGS[bal_plugin.WILLEXECUTORS] bal=bal_plugin.DEFAULT_SETTINGS[bal_plugin.WILLEXECUTORS]
for bal_url,bal_executor in bal.items(): for bal_url,bal_executor in bal.items():
@ -27,7 +28,7 @@ class Willexecutors:
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 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.config_get(bal_plugin.PING_WILLEXECUTORS) or force:
@ -41,7 +42,7 @@ class Willexecutors:
bal_window.ping_willexecutors_task(willexecutors) bal_window.ping_willexecutors_task(willexecutors)
return willexecutors return willexecutors
def is_selected(willexecutor,value=None): def is_selected(willexecutor,value=None):
if not willexecutor: if not willexecutor:
return False return False
if not value is None: if not value is None:
@ -52,7 +53,7 @@ class Willexecutors:
willexecutor['selected']=False willexecutor['selected']=False
return False return False
def get_willexecutor_transactions(will, force=False): def get_willexecutor_transactions(will, force=False):
willexecutors ={} willexecutors ={}
for wid,willitem in will.items(): for wid,willitem in will.items():
if willitem.get_status('VALID'): if willitem.get_status('VALID'):
@ -60,7 +61,7 @@ class Willexecutors:
if not willitem.get_status('PUSHED') or force: if not willitem.get_status('PUSHED') or force:
if willexecutor := willitem.we: if willexecutor := willitem.we:
url=willexecutor['url'] url=willexecutor['url']
if willexecutor and Willexecutors.is_selected(willexecutor): if willexecutor and is_selected(willexecutor):
if not url in willexecutors: if not url in willexecutors:
willexecutor['txs']="" willexecutor['txs']=""
willexecutor['txsids']=[] willexecutor['txsids']=[]
@ -71,20 +72,20 @@ class Willexecutors:
return willexecutors return willexecutors
def only_selected_list(willexecutors): def only_selected_list(willexecutors):
out = {} out = {}
for url,v in willexecutors.items(): for url,v in willexectors.items():
if Willexecutors.is_selected(willexecutor): if is_selected(willexecutor):
out[url]=v out[url]=v
def push_transactions_to_willexecutors(will): def push_transactions_to_willexecutors(will):
willexecutors = get_transactions_to_be_pushed() willexecutors = get_transactions_to_be_pushed()
for url in willexecutors: for url in willexecutors:
willexecutor = willexecutors[url] willexecutor = willexecutors[url]
if Willexecutors.is_selected(willexecutor): if is_selected(willexecutor):
if 'txs' in willexecutor: if 'txs' in willexecutor:
Willexecutors.push_transactions_to_willexecutor(willexecutors[url]['txs'],url) push_transactions_to_willexecutor(willexecutors[url]['txs'],url)
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 ErrorConnectingServer('You are offline.')
@ -98,13 +99,13 @@ class Willexecutors:
response = Network.send_http_on_proxy(method, url, response = Network.send_http_on_proxy(method, url,
params=data, params=data,
headers=headers, headers=headers,
on_finish=Willexecutors.handle_response, on_finish=handle_response,
timeout=timeout) timeout=timeout)
elif method == 'post': elif method == 'post':
response = Network.send_http_on_proxy(method, url, response = Network.send_http_on_proxy(method, url,
body=data, body=data,
headers=headers, headers=headers,
on_finish=Willexecutors.handle_response, on_finish=handle_response,
timeout=timeout) timeout=timeout)
else: else:
raise Exception(f"unexpected {method=!r}") raise Exception(f"unexpected {method=!r}")
@ -114,26 +115,25 @@ class Willexecutors:
else: else:
_logger.debug(f'--> {response}') _logger.debug(f'--> {response}')
return response return response
async def handle_response(resp:ClientResponse): async def handle_response(resp:ClientResponse):
r=await resp.text() r=await resp.text()
try: try:
r=json.loads(r) r=json.loads(r)
r['status'] = resp.status r['status'] = resp.status
r['selected']=Willexecutors.is_selected(willexecutor) r['selected']=is_selected(willexecutor)
r['url']=url r['url']=url
except: except:
pass pass
return r return r
class AlreadyPresentException(Exception): class AlreadyPresentException(Exception):
pass pass
def push_transactions_to_willexecutor(willexecutor): def push_transactions_to_willexecutor(willexecutor):
out=True out=True
try: try:
_logger.debug(f"willexecutor['txs']") _logger.debug(f"willexecutor['txs']")
if w:=Willexecutors.send_request('post', willexecutor['url']+"/"+constants.net.NET_NAME+"/pushtxs", data=willexecutor['txs'].encode('ascii')): if w:=send_request('post', willexecutor['url']+"/"+constants.net.NET_NAME+"/pushtxs", data=willexecutor['txs'].encode('ascii')):
willexecutor['broadcast_status'] = _("Success") willexecutor['broadcast_stauts'] = _("Success")
_logger.debug(f"pushed: {w}") _logger.debug(f"pushed: {w}")
if w !='thx': if w !='thx':
_logger.debug(f"error: {w}") _logger.debug(f"error: {w}")
@ -143,26 +143,23 @@ class Willexecutors:
except Exception as e: except Exception as e:
_logger.debug(f"error:{e}") _logger.debug(f"error:{e}")
if str(e) == "already present": if str(e) == "already present":
raise Willexecutors.AlreadyPresentException() raise AlreadyPresentException()
out=False out=False
willexecutor['broadcast_status'] = _("Failed") willexecutor['broadcast_stauts'] = _("Failed")
return out return out
def ping_servers(willexecutors): def ping_servers(willexecutors):
for url,we in willexecutors.items(): for url,we in willexecutors.items():
Willexecutors.get_info_task(url,we) get_info_task(url,we)
def get_info_task(url,willexecutor): def get_info_task(url,willexecutor):
w=None w=None
try: try:
_logger.info("GETINFO_WILLEXECUTOR") _logger.info("GETINFO_WILLEXECUTOR")
_logger.debug(url) _logger.debug(url)
netname="bitcoin" w = send_request('get',url+"/"+constants.net.NET_NAME+"/info")
if constants.net.NET_NAME!="mainnet":
netname=constants.net.NET_NAME
w = Willexecutors.send_request('get',url+"/"+netname+"/info")
willexecutor['url']=url willexecutor['url']=url
willexecutor['status'] = w['status'] willexecutor['status'] = w['status']
willexecutor['base_fee'] = w['base_fee'] willexecutor['base_fee'] = w['base_fee']
@ -177,41 +174,29 @@ class Willexecutors:
willexecutor['last_update'] = datetime.now().timestamp() willexecutor['last_update'] = datetime.now().timestamp()
return willexecutor return willexecutor
def initialize_willexecutor(willexecutor,url,status=None,selected=None): def initialize_willexecutor(willexecutor,url,status=None,selected=None):
willexecutor['url']=url willexecutor['url']=url
if not status is None: if not status is None:
willexecutor['status'] = status willexecutor['status'] = status
willexecutor['selected'] = Willexecutors.is_selected(willexecutor,selected) willexecutor['selected'] = is_selected(willexecutor,selected)
def download_list(bal_plugin):
try: def get_willexecutors_list_from_json(bal_plugin):
l = Willexecutors.send_request('get',"https://welist.bitcoin-after.life/data/bitcoin?page=0&limit=100")
del l['status']
for w in l:
willexecutor=l[w]
Willexecutors.initialize_willexecutor(willexecutor,w,'New',False)
bal_plugin.config.set_key(bal_plugin.WILLEXECUTORS,l,save=True)
return l
except Exception as e:
_logger.error(f"error downloading willexecutors list:{e}")
return {}
def get_willexecutors_list_from_json(bal_plugin):
try: try:
with open("willexecutors.json") as f: with open("willexecutors.json") as f:
willexecutors = json.load(f) willexecutors = json.load(f)
for w in willexecutors: for w in willexecutors:
willexecutor=willexecutors[w] willexecutor=willexecutors[w]
Willexecutors.initialize_willexecutor(willexecutor,w,'New',False) willexecutors.initialize_willexecutor(willexecutor,w,'New',False)
bal_plugin.config.set_key(bal_plugin.WILLEXECUTORS,willexecutors,save=True) bal_plugin.config.set_key(bal_plugin.WILLEXECUTORS,willexecutors,save=True)
return h return h
except Exception as e: except Exception as e:
_logger.error(f"error opening willexecutors json: {e}") _logger.error(f"errore aprendo willexecutors.json: {e}")
return {} return {}
def check_transaction(txid,url): def check_transaction(txid,url):
_logger.debug(f"{url}:{txid}") _logger.debug(f"{url}:{txid}")
try: try:
w = Willexecutors.send_request('post',url+"/searchtx",data=txid.encode('ascii')) w = send_request('post',url+"/searchtx",data=txid.encode('ascii'))
return w return w
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}")