Compare commits
	
		
			No commits in common. "main" and "c0d64201a802ae373415fce17cde5334950a16da" have entirely different histories.
		
	
	
		
			main
			...
			c0d64201a8
		
	
		
							
								
								
									
										19
									
								
								__init__.py
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								__init__.py
									
									
									
									
									
								
							| @ -1 +1,20 @@ | |||||||
|  | from electrum.i18n import _ | ||||||
|  | import subprocess | ||||||
|  | from . import bal_resources | ||||||
|  | BUILD_NUMBER = 1  | ||||||
|  | REVISION_NUMBER = 1 | ||||||
|  | VERSION_NUMBER = 0 | ||||||
|  | def _version(): | ||||||
|  |     return f'{VERSION_NUMBER}.{REVISION_NUMBER}-{BUILD_NUMBER}' | ||||||
| 
 | 
 | ||||||
|  | version = _version() | ||||||
|  | author = "Bal Enterprise inc." | ||||||
|  | fullname = _('B.A.L.') | ||||||
|  | description = ''.join([ | ||||||
|  |     "<img src='",bal_resources.icon_path('bal16x16.png'),"'>", _("Bitcoin After Life"), '<br/>', | ||||||
|  |     _("For more information, visit"), | ||||||
|  |     " <a href=\"https://bitcoin-after.life/\">https://bitcoin-after.life/</a><br/>", | ||||||
|  |     "<p style='font-size:8pt;vertialAlign:bottom'>Version: ", _version(),"</p>" | ||||||
|  | ]) | ||||||
|  | #available_for = ['qt', 'cmdline', 'qml'] | ||||||
|  | available_for = ['qt'] | ||||||
|  | |||||||
							
								
								
									
										162
									
								
								bal.py
									
									
									
									
									
								
							
							
						
						
									
										162
									
								
								bal.py
									
									
									
									
									
								
							| @ -1,128 +1,126 @@ | |||||||
| import random | import random | ||||||
| import os | import os | ||||||
| import zipfile as zipfile_lib | 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) | ||||||
| json_db.register_dict('will_settings', lambda x:x, None) | json_db.register_dict('will_settings', lambda x:x, None) | ||||||
| 
 |  | ||||||
| from electrum.logging import get_logger | from electrum.logging import get_logger | ||||||
| 
 |  | ||||||
| def get_will(x): | def get_will(x): | ||||||
|     try: |     try: | ||||||
|  |         #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 | ||||||
| 
 | 
 | ||||||
| class BalConfig(): |  | ||||||
|     def __init__(self, config, name, default): |  | ||||||
|         self.config = config |  | ||||||
|         self.name = name |  | ||||||
|         self.default = default |  | ||||||
| 
 | 
 | ||||||
|     def get(self,default=None): |  | ||||||
|         v = self.config.get(self.name, default) |  | ||||||
|         if v is None: |  | ||||||
|             if not default is None: |  | ||||||
|                 v = default |  | ||||||
|             else: |  | ||||||
|                 v = self.default |  | ||||||
|         return v |  | ||||||
|      |  | ||||||
|     def set(self,value,save=True): |  | ||||||
|         self.config.set_key(self.name,value,save=save) |  | ||||||
| 
 | 
 | ||||||
| class BalPlugin(BasePlugin): | class BalPlugin(BasePlugin): | ||||||
|  |     LOCKTIME_TIME = "bal_locktime_time" | ||||||
|  |     LOCKTIME_BLOCKS = "bal_locktime_blocks" | ||||||
|  |     LOCKTIMEDELTA_TIME = "bal_locktimedelta_time" | ||||||
|  |     LOCKTIMEDELTA_BLOCKS = "bal_locktimedelta_blocks" | ||||||
|  |     TX_FEES = "bal_tx_fees" | ||||||
|  |     BROADCAST = "bal_broadcast" | ||||||
|  |     ASK_BROADCAST = "bal_ask_broadcast" | ||||||
|  |     INVALIDATE = "bal_invalidate" | ||||||
|  |     ASK_INVALIDATE = "bal_ask_invalidate" | ||||||
|  |     PREVIEW = "bal_preview" | ||||||
|  |     SAVE_TXS = "bal_save_txs" | ||||||
|  |     WILLEXECUTORS = "bal_willexecutors" | ||||||
|  |     PING_WILLEXECUTORS = "bal_ping_willexecutors" | ||||||
|  |     ASK_PING_WILLEXECUTORS = "bal_ask_ping_willexecutors" | ||||||
|  |     NO_WILLEXECUTOR = "bal_no_willexecutor" | ||||||
|  |     HIDE_REPLACED = "bal_hide_replaced" | ||||||
|  |     HIDE_INVALIDATED = "bal_hide_invalidated" | ||||||
|  |     ALLOW_REPUSH = "bal_allow_repush" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     DEFAULT_SETTINGS={ | ||||||
|  |         LOCKTIME_TIME: 90, | ||||||
|  |         LOCKTIME_BLOCKS: 144*90, | ||||||
|  |         LOCKTIMEDELTA_TIME: 7, | ||||||
|  |         LOCKTIMEDELTA_BLOCKS:144*7, | ||||||
|  |         TX_FEES: 100, | ||||||
|  |         BROADCAST: True, | ||||||
|  |         ASK_BROADCAST: True, | ||||||
|  |         INVALIDATE: True, | ||||||
|  |         ASK_INVALIDATE: True, | ||||||
|  |         PREVIEW: True, | ||||||
|  |         SAVE_TXS: True, | ||||||
|  |         PING_WILLEXECUTORS: False, | ||||||
|  |         ASK_PING_WILLEXECUTORS: False, | ||||||
|  |         NO_WILLEXECUTOR: False, | ||||||
|  |         HIDE_REPLACED:True, | ||||||
|  |         HIDE_INVALIDATED:True, | ||||||
|  |         ALLOW_REPUSH: False, | ||||||
|  |         WILLEXECUTORS:  { | ||||||
|  |             'http://bitcoin-after.life:9137': { | ||||||
|  |                 "base_fee": 100000, | ||||||
|  |                 "status": "New", | ||||||
|  |                 "info":"Bitcoin After Life Will Executor", | ||||||
|  |                 "address":"bcrt1qa5cntu4hgadw8zd3n6sq2nzjy34sxdtd9u0gp7", | ||||||
|  |                 "selected":True | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     LATEST_VERSION = '1' |     LATEST_VERSION = '1' | ||||||
|     KNOWN_VERSIONS = ('0', '1') |     KNOWN_VERSIONS = ('0', '1') | ||||||
|     assert LATEST_VERSION in KNOWN_VERSIONS |     assert LATEST_VERSION in KNOWN_VERSIONS | ||||||
|     def version(): | 
 | ||||||
|         try: |  | ||||||
|             f="" |  | ||||||
|             with open("VERSION","r") as f: |  | ||||||
|                 f = str(f.readline()) |  | ||||||
|             return f |  | ||||||
|         except: |  | ||||||
|             return "unknown" |  | ||||||
|     SIZE = (159, 97) |     SIZE = (159, 97) | ||||||
| 
 | 
 | ||||||
|     def __init__(self, parent, config, name): |     def __init__(self, parent, config, name): | ||||||
|         self.logger = get_logger(__name__) |         self.logger= get_logger(__name__) | ||||||
|         BasePlugin.__init__(self, parent, config, name) |         BasePlugin.__init__(self, parent, config, name) | ||||||
|         self.base_dir = os.path.join(config.electrum_path(), 'bal') |         self.base_dir = os.path.join(config.electrum_path(), 'bal') | ||||||
|         self.plugin_dir = os.path.split(os.path.realpath(__file__))[0] |         self.logger.info(self.base_dir) | ||||||
|         zipfile="/".join(self.plugin_dir.split("/")[:-1]) |  | ||||||
|         #print("real path",os.path.realpath(__file__)) |  | ||||||
|         #self.logger.info(self.base_dir) |  | ||||||
|         #print("base_dir:", self.base_dir) |  | ||||||
|         #print("suca:",zipfile) |  | ||||||
|         #print("plugin_dir:", self.plugin_dir) |  | ||||||
|         import sys |  | ||||||
|         sys.path.insert(0, zipfile) |  | ||||||
|         #print("sono state listate?") |  | ||||||
|         self.parent = parent |         self.parent = parent | ||||||
|         self.config = config |         self.config = config | ||||||
|         self.name = name |         self.name = name | ||||||
| 
 |         self._hide_invalidated= self.config_get(self.HIDE_INVALIDATED) | ||||||
|         self.ASK_BROADCAST              = BalConfig(config, "bal_ask_broadcast",            True) |         self._hide_replaced= self.config_get(self.HIDE_REPLACED) | ||||||
|         self.BROADCAST                  = BalConfig(config, "bal_broadcast",                True) |         self.plugin_dir = os.path.split(os.path.realpath(__file__))[0] | ||||||
|         self.LOCKTIME_TIME              = BalConfig(config, "bal_locktime_time",            90) |  | ||||||
|         self.LOCKTIME_BLOCKS            = BalConfig(config, "bal_locktime_blocks",          144*90) |  | ||||||
|         self.LOCKTIMEDELTA_TIME         = BalConfig(config, "bal_locktimedelta_time",       7) |  | ||||||
|         self.LOCKTIMEDELTA_BLOCKS       = BalConfig(config, "bal_locktimedelta_blocks",     144*7) |  | ||||||
|         self.ENABLE_MULTIVERSE          = BalConfig(config, "bal_enable_multiverse",        False) |  | ||||||
|         self.TX_FEES                    = BalConfig(config, "bal_tx_fees",                  100) |  | ||||||
|         self.INVALIDATE                 = BalConfig(config, "bal_invalidate",               True) |  | ||||||
|         self.ASK_INVALIDATE             = BalConfig(config, "bal_ask_invalidate",           True) |  | ||||||
|         self.PREVIEW                    = BalConfig(config, "bal_preview",                  True) |  | ||||||
|         self.SAVE_TXS                   = BalConfig(config, "bal_save_txs",                 True) |  | ||||||
|         self.WILLEXECUTORS              = BalConfig(config, "bal_willexecutors",            True) |  | ||||||
|         self.PING_WILLEXECUTORS         = BalConfig(config, "bal_ping_willexecutors",       True) |  | ||||||
|         self.ASK_PING_WILLEXECUTORS     = BalConfig(config, "bal_ask_ping_willexecutors",   True) |  | ||||||
|         self.NO_WILLEXECUTOR            = BalConfig(config, "bal_no_willexecutor",          True)  |  | ||||||
|         self.HIDE_REPLACED              = BalConfig(config, "bal_hide_replaced",            True) |  | ||||||
|         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.WILLEXECUTORS              = BalConfig(config, "bal_willexecutors", { |  | ||||||
|                                                                 "mainnet":  { |  | ||||||
|                                                                     '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", { |  | ||||||
|                                                                 'tx_fees':100,  |  | ||||||
|                                                                 'threshold':'180d', |  | ||||||
|                                                                 'locktime':'1y', |  | ||||||
|                                                             }) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         self._hide_invalidated= self.HIDE_INVALIDATED.get() |  | ||||||
|         self._hide_replaced= self.HIDE_REPLACED.get() |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|     def resource_path(self,*parts): |     def resource_path(self,*parts): | ||||||
|         return os.path.join(self.plugin_dir, *parts) |         return os.path.join(self.plugin_dir, *parts) | ||||||
| 
 | 
 | ||||||
|  |     def config_get(self,key): | ||||||
|  |         v = self.config.get(key,None) | ||||||
|  |         if v is None: | ||||||
|  |             self.config.set_key(key,self.DEFAULT_SETTINGS[key],save=True) | ||||||
|  |             v = self.DEFAULT_SETTINGS[key] | ||||||
|  |         return v | ||||||
|  | 
 | ||||||
|     def hide_invalidated(self): |     def hide_invalidated(self): | ||||||
|         self._hide_invalidated = not self._hide_invalidated |         self._hide_invalidated = not self._hide_invalidated | ||||||
|         self.HIDE_INVALIDATED.set(self._hide_invalidated) |         self.config.set_key(BalPlugin.HIDE_INVALIDATED,self.hide_invalidated,save=True) | ||||||
| 
 | 
 | ||||||
|     def hide_replaced(self): |     def hide_replaced(self): | ||||||
|         self._hide_replaced = not self._hide_replaced |         self._hide_replaced = not self._hide_replaced | ||||||
|         self.HIDE_REPLACED.set(self._hide_replaced) |         self.config.set_key(BalPlugin.HIDE_REPLACED,self.hide_invalidated,save=True) | ||||||
| 
 | 
 | ||||||
|  |     def default_will_settings(self): | ||||||
|  |         return { | ||||||
|  |             'tx_fees':100,  | ||||||
|  |             'threshold':'180d', | ||||||
|  |             'locktime':'1y', | ||||||
|  |         } | ||||||
|     def validate_will_settings(self,will_settings): |     def validate_will_settings(self,will_settings): | ||||||
|         if int(will_settings.get('tx_fees',1))<1: |         if int(will_settings.get('tx_fees',1))<1: | ||||||
|             will_settings['tx_fees']=1 |             will_settings['tx_fees']=1 | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ import os | |||||||
| 
 | 
 | ||||||
| PLUGIN_DIR = os.path.split(os.path.realpath(__file__))[0] | PLUGIN_DIR = os.path.split(os.path.realpath(__file__))[0] | ||||||
| DEFAULT_ICON = 'bal32x32.png' | DEFAULT_ICON = 'bal32x32.png' | ||||||
| DEFAULT_ICON_PATH = '' | DEFAULT_ICON_PATH = 'icons' | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def icon_path(icon_basename: str = DEFAULT_ICON): | def icon_path(icon_basename: str = DEFAULT_ICON): | ||||||
|  | |||||||
| @ -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 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | |||||||
| @ -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() | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
							
								
								
									
										18
									
								
								heirs.py
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								heirs.py
									
									
									
									
									
								
							| @ -14,8 +14,9 @@ from electrum.transaction import PartialTxInput, PartialTxOutput,TxOutpoint,Part | |||||||
| import datetime | import datetime | ||||||
| import urllib.request | import urllib.request | ||||||
| import urllib.parse | import urllib.parse | ||||||
| from .util import Util | from .bal import BalPlugin | ||||||
| from .willexecutors import Willexecutors | from . import util as Util | ||||||
|  | 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 | ||||||
| @ -37,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 | ||||||
| @ -57,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 | ||||||
| """ | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -386,7 +386,7 @@ class Heirs(dict, Logger): | |||||||
|             utxos = wallet.get_utxos() |             utxos = wallet.get_utxos() | ||||||
|         willexecutors = Willexecutors.get_willexecutors(bal_plugin) or {} |         willexecutors = Willexecutors.get_willexecutors(bal_plugin) or {} | ||||||
|         self.decimal_point=bal_plugin.config.get_decimal_point() |         self.decimal_point=bal_plugin.config.get_decimal_point() | ||||||
|         no_willexecutors = bal_plugin.NO_WILLEXECUTOR.get() |         no_willexecutors = bal_plugin.config_get(BalPlugin.NO_WILLEXECUTOR) | ||||||
|         for utxo in utxos: |         for utxo in utxos: | ||||||
|             if utxo.value_sats()> 0*tx_fees: |             if utxo.value_sats()> 0*tx_fees: | ||||||
|                 balance += utxo.value_sats() |                 balance += utxo.value_sats() | ||||||
| @ -563,7 +563,10 @@ class Heirs(dict, Logger): | |||||||
| 
 | 
 | ||||||
|     def validate_amount(amount): |     def validate_amount(amount): | ||||||
|         try: |         try: | ||||||
|             famount = float(amount[:-1]) if Util.is_perc(amount) else float(amount) |             if Util.is_perc(amount): | ||||||
|  |                 famount = float(amount[:-1]) | ||||||
|  |             else: | ||||||
|  |                 famount = float(amount) | ||||||
|             if famount <= 0.00000001: |             if famount <= 0.00000001: | ||||||
|                 raise AmountNotValid(f"amount have to be positive {famount} < 0") |                 raise AmountNotValid(f"amount have to be positive {famount} < 0") | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
| @ -572,8 +575,9 @@ class Heirs(dict, Logger): | |||||||
| 
 | 
 | ||||||
|     def validate_locktime(locktime,timestamp_to_check=False): |     def validate_locktime(locktime,timestamp_to_check=False): | ||||||
|         try: |         try: | ||||||
|  |             locktime = Util.parse_locktime_string(locktime,None) | ||||||
|             if timestamp_to_check: |             if timestamp_to_check: | ||||||
|                 if Util.parse_locktime_string(locktime,None) < timestamp_to_check: |                 if locktime < timestamp_to_check: | ||||||
|                     raise HeirExpiredException() |                     raise HeirExpiredException() | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             raise LocktimeNotValid(f"locktime string not properly formatted, {e}") |             raise LocktimeNotValid(f"locktime string not properly formatted, {e}") | ||||||
|  | |||||||
| @ -1,9 +0,0 @@ | |||||||
| { |  | ||||||
|  "name": "BAL", |  | ||||||
|  "fullname": "Bitcoin After Life", |  | ||||||
|  "description": "Provides free and decentralized inheritance support<br> Version: 0.2.2a", |  | ||||||
|  "author":"Svatantrya", |  | ||||||
|  "available_for": ["qt"], |  | ||||||
|  "icon":"icons/bal32x32.png" |  | ||||||
|  } |  | ||||||
| 
 |  | ||||||
							
								
								
									
										811
									
								
								util.py
									
									
									
									
									
								
							
							
						
						
									
										811
									
								
								util.py
									
									
									
									
									
								
							| @ -8,430 +8,451 @@ 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: | ||||||
|  |         locktime=int(locktime) | ||||||
|  |         if locktime > LOCKTIME_THRESHOLD: | ||||||
|  |             dt = datetime.fromtimestamp(locktime).isoformat() | ||||||
|  |             return dt | ||||||
|  | 
 | ||||||
|  |     except Exception as e: | ||||||
|  |         #print(e) | ||||||
|  |         pass | ||||||
|  |     return str(locktime) | ||||||
|  | 
 | ||||||
|  | def str_to_locktime(locktime): | ||||||
|  |     try: | ||||||
|  |         if locktime[-1] in ('y','d','b'): | ||||||
|  |           return locktime | ||||||
|  |         else: return int(locktime) | ||||||
|  |     except Exception as e: | ||||||
|  |         pass | ||||||
|  |         #print(e) | ||||||
|  |     dt_object = datetime.fromisoformat(locktime) | ||||||
|  |     timestamp = dt_object.timestamp() | ||||||
|  |     return int(timestamp) | ||||||
|  | 
 | ||||||
|  | def parse_locktime_string(locktime,w=None):  | ||||||
|  |     try:  | ||||||
|  |         return int(locktime)  | ||||||
|  |   | ||||||
|  |     except Exception as e:  | ||||||
|  |         pass | ||||||
|  |         #print("parse_locktime_string",e)  | ||||||
|  |     try:  | ||||||
|  |         now = datetime.now()  | ||||||
|  |         if locktime[-1] == 'y':  | ||||||
|  |             locktime = str(int(locktime[:-1])*365) + "d"  | ||||||
|  |         if locktime[-1] == 'd':  | ||||||
|  |             return int((now + timedelta(days = int(locktime[:-1]))).replace(hour=0,minute=0,second=0,microsecond=0).timestamp())  | ||||||
|  |         if locktime[-1] == 'b':  | ||||||
|  |             locktime = int(locktime[:-1])  | ||||||
|  |             height = 0  | ||||||
|  |             if w:  | ||||||
|  |                 height = get_current_height(w.network)  | ||||||
|  |             locktime+=int(height)  | ||||||
|  |         return int(locktime)  | ||||||
|  |     except Exception as e:  | ||||||
|  |         print("parse_locktime_string",e)  | ||||||
|  |         #raise e | ||||||
|  |     return 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) | ||||||
|  | 
 | ||||||
|  | def encode_amount(amount, decimal_point): | ||||||
|  |     if is_perc(amount): | ||||||
|  |         return amount | ||||||
|  |     else: | ||||||
|         try: |         try: | ||||||
|             locktime=int(locktime) |             return int(float(amount)*pow(10,decimal_point)) | ||||||
|             if locktime > LOCKTIME_THRESHOLD: |         except: | ||||||
|                 dt = datetime.fromtimestamp(locktime).isoformat() |             return 0 | ||||||
|                 return dt |  | ||||||
| 
 | 
 | ||||||
|         except Exception as e: | def decode_amount(amount,decimal_point): | ||||||
|             pass |     if is_perc(amount): | ||||||
|         return str(locktime) |         return amount | ||||||
|  |     else: | ||||||
|  |         num=8-decimal_point | ||||||
|  |         basestr="{{:0{}.{}f}}".format(num,num) | ||||||
|  |         return "{:08.8f}".format(float(amount)/pow(10,decimal_point)) | ||||||
| 
 | 
 | ||||||
|     def str_to_locktime(locktime): | def is_perc(value):  | ||||||
|         try: |  | ||||||
|             if locktime[-1] in ('y','d','b'): |  | ||||||
|               return locktime |  | ||||||
|             else: return int(locktime) |  | ||||||
|         except Exception as e: |  | ||||||
|             pass |  | ||||||
|         dt_object = datetime.fromisoformat(locktime) |  | ||||||
|         timestamp = dt_object.timestamp() |  | ||||||
|         return int(timestamp) |  | ||||||
| 
 |  | ||||||
|     def parse_locktime_string(locktime,w=None):  |  | ||||||
|         try:  |  | ||||||
|             return int(locktime)  |  | ||||||
|       |  | ||||||
|         except Exception as e:  |  | ||||||
|             pass |  | ||||||
|         try:  |  | ||||||
|             now = datetime.now()  |  | ||||||
|             if locktime[-1] == 'y':  |  | ||||||
|                 locktime = str(int(locktime[:-1])*365) + "d"  |  | ||||||
|             if locktime[-1] == 'd':  |  | ||||||
|                 return int((now + timedelta(days = int(locktime[:-1]))).replace(hour=0,minute=0,second=0,microsecond=0).timestamp())  |  | ||||||
|             if locktime[-1] == 'b':  |  | ||||||
|                 locktime = int(locktime[:-1])  |  | ||||||
|                 height = 0  |  | ||||||
|                 if w:  |  | ||||||
|                     height = Util.get_current_height(w.network)  |  | ||||||
|                 locktime+=int(height)  |  | ||||||
|             return int(locktime)  |  | ||||||
|         except Exception as e:  |  | ||||||
|             pass |  | ||||||
|         return 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) |  | ||||||
| 
 |  | ||||||
|     def encode_amount(amount, decimal_point): |  | ||||||
|         if Util.is_perc(amount): |  | ||||||
|             return amount |  | ||||||
|         else: |  | ||||||
|             try: |  | ||||||
|                 return int(float(amount)*pow(10,decimal_point)) |  | ||||||
|             except: |  | ||||||
|                 return 0 |  | ||||||
| 
 |  | ||||||
|     def decode_amount(amount,decimal_point): |  | ||||||
|         if Util.is_perc(amount): |  | ||||||
|             return amount |  | ||||||
|         else: |  | ||||||
|             num=8-decimal_point |  | ||||||
|             basestr="{{:0{}.{}f}}".format(num,num) |  | ||||||
|             return "{:08.8f}".format(float(amount)/pow(10,decimal_point)) |  | ||||||
| 
 |  | ||||||
|     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 | ||||||
|  |         for h in range(0,len(heira)): | ||||||
|  |             if not heira[h] == heirb[h]: | ||||||
|                 return False |                 return False | ||||||
|             for h in range(0,len(heira)): |         return True | ||||||
|                 if not heira[h] == heirb[h]: |     except: | ||||||
|                     return False |  | ||||||
|             return True |  | ||||||
|         except: |  | ||||||
|             return False |  | ||||||
| 
 |  | ||||||
|     def cmp_heir(heira,heirb): |  | ||||||
|         if heira[0] == heirb[0] and heira[1] == heirb[1]:  |  | ||||||
|             return True |  | ||||||
|         return False |         return False | ||||||
| 
 | 
 | ||||||
|     def cmp_willexecutor(willexecutora,willexecutorb): | def cmp_heir(heira,heirb): | ||||||
|         if willexecutora == willexecutorb: |     if heira[0] == heirb[0] and heira[1] == heirb[1]:  | ||||||
|             return True |         return True | ||||||
|         try: |     return False | ||||||
|             if willexecutora['url']==willexecutorb['url'] and willexecutora['address'] == willexecutorb['address'] and willexecutora['base_fee']==willexecutorb['base_fee']: |  | ||||||
|                 return True |  | ||||||
|         except: |  | ||||||
|             return False |  | ||||||
|         return False |  | ||||||
| 
 | 
 | ||||||
|     def search_heir_by_values(heirs,heir,values): | def cmp_willexecutor(willexecutora,willexecutorb): | ||||||
|         for h,v in heirs.items(): |     if willexecutora == willexecutorb: | ||||||
|  |         return True | ||||||
|  |     try: | ||||||
|  |         if willexecutora['url']==willexecutorb['url'] and willexecutora['address'] == willexecutorb['address'] and willexecutora['base_fee']==willexecutorb['base_fee']: | ||||||
|  |             return True | ||||||
|  |     except: | ||||||
|  |         return False | ||||||
|  |     return False | ||||||
|  | 
 | ||||||
|  | def search_heir_by_values(heirs,heir,values): | ||||||
|  |     #print() | ||||||
|  |     for h,v in heirs.items(): | ||||||
|  |         found = False | ||||||
|  |         for val in values: | ||||||
|  |             if val in v and v[val] != heir[val]: | ||||||
|  |                 found = True | ||||||
|  | 
 | ||||||
|  |         if not found: | ||||||
|  |             return h | ||||||
|  |     return False | ||||||
|  | 
 | ||||||
|  | def cmp_heir_by_values(heira,heirb,values): | ||||||
|  |     for v in values: | ||||||
|  |         if heira[v] != heirb[v]: | ||||||
|  |             return False | ||||||
|  |     return True | ||||||
|  | 
 | ||||||
|  | def cmp_heirs_by_values(heirsa,heirsb,values,exclude_willexecutors=False,reverse = True): | ||||||
|  |     for heira in heirsa: | ||||||
|  |         if (exclude_willexecutors and not "w!ll3x3c\"" in heira) or not exclude_willexecutors: | ||||||
|             found = False |             found = False | ||||||
|             for val in values: |             for heirb in heirsb: | ||||||
|                 if val in v and v[val] != heir[val]: |                 if cmp_heir_by_values(heirsa[heira],heirsb[heirb],values): | ||||||
|                     found = True |                     found=True | ||||||
| 
 |  | ||||||
|             if not found: |             if not found: | ||||||
|                 return h |                 #print(f"not_found {heira}--{heirsa[heira]}") | ||||||
|  |                 return False | ||||||
|  |     if reverse: | ||||||
|  |         return cmp_heirs_by_values(heirsb,heirsa,values,exclude_willexecutors=exclude_willexecutors,reverse=False) | ||||||
|  |     else: | ||||||
|  |         return True | ||||||
|  |      | ||||||
|  | def cmp_heirs(heirsa,heirsb,cmp_function = lambda x,y: x[0]==y[0] and x[3]==y[3],reverse=True): | ||||||
|  |     try: | ||||||
|  |         for heir in heirsa: | ||||||
|  |             if not "w!ll3x3c\"" in heir: | ||||||
|  |                 if not heir in heirsb or not cmp_function(heirsa[heir],heirsb[heir]): | ||||||
|  |                     if not search_heir_by_values(heirsb,heirsa[heir],[0,3]): | ||||||
|  |                         return False | ||||||
|  |         if reverse: | ||||||
|  |             return cmp_heirs(heirsb,heirsa,cmp_function,False) | ||||||
|  |         else: | ||||||
|  |             return True | ||||||
|  |     except Exception as e: | ||||||
|  |         raise e | ||||||
|         return False |         return False | ||||||
| 
 | 
 | ||||||
|     def cmp_heir_by_values(heira,heirb,values): | def cmp_inputs(inputsa,inputsb): | ||||||
|         for v in values: |     if len(inputsa) != len(inputsb):  | ||||||
|             if heira[v] != heirb[v]: |         return False  | ||||||
|                 return False |     for inputa in inputsa: | ||||||
|         return True |         if not in_utxo(inputa,inputsb): | ||||||
|  |             return False | ||||||
|  |     return True | ||||||
| 
 | 
 | ||||||
|     def cmp_heirs_by_values(heirsa,heirsb,values,exclude_willexecutors=False,reverse = True): | def cmp_outputs(outputsa,outputsb,willexecutor_output = None): | ||||||
|         for heira in heirsa: |     if len(outputsa) != len(outputsb):  | ||||||
|             if (exclude_willexecutors and not "w!ll3x3c\"" in heira) or not exclude_willexecutors: |         return False  | ||||||
|                 found = False |     for outputa in outputsa:  | ||||||
|                 for heirb in heirsb: |         if not cmp_output(outputa,willexecutor_output): | ||||||
|                     if Util.cmp_heir_by_values(heirsa[heira],heirsb[heirb],values): |             if not in_output(outputa,outputsb):  | ||||||
|                         found=True |                 return False | ||||||
|                 if not found: |     return True | ||||||
|                     return False | 
 | ||||||
|         if reverse: | def cmp_txs(txa,txb): | ||||||
|             return Util.cmp_heirs_by_values(heirsb,heirsa,values,exclude_willexecutors=exclude_willexecutors,reverse=False) |     if not cmp_inputs(txa.inputs(),txb.inputs()): | ||||||
|  |         return False | ||||||
|  |     if not cmp_outputs(txa.outputs(),txb.outputs()): | ||||||
|  |         return False | ||||||
|  |     return True | ||||||
|  | 
 | ||||||
|  | def get_value_amount(txa,txb): | ||||||
|  |     outputsa=txa.outputs() | ||||||
|  |     outputsb=txb.outputs() | ||||||
|  |     value_amount = 0 | ||||||
|  |     #if len(outputsa) != len(outputsb): | ||||||
|  |     #    print("outputlen is different") | ||||||
|  |     #    return False | ||||||
|  | 
 | ||||||
|  |     for outa in outputsa: | ||||||
|  |         same_amount,same_address = in_output(outa,txb.outputs()) | ||||||
|  |         if not (same_amount or same_address): | ||||||
|  |             #print("outa notin txb", same_amount,same_address) | ||||||
|  |             return False | ||||||
|  |         if same_amount and same_address: | ||||||
|  |             value_amount+=outa.value | ||||||
|  |         if same_amount: | ||||||
|  |             pass | ||||||
|  |             #print("same amount") | ||||||
|  |         if same_address: | ||||||
|  |             pass | ||||||
|  |             #print("same address") | ||||||
|  | 
 | ||||||
|  |     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): | ||||||
|  |     #TODO BUG:  WHAT HAPPEN AT THRESHOLD? | ||||||
|  |     locktime=int(locktime) | ||||||
|  |     if locktime > LOCKTIME_THRESHOLD and locktime > timestamp_to_check: | ||||||
|  |         return True | ||||||
|  |     elif locktime < LOCKTIME_THRESHOLD and locktime > block_height_to_check: | ||||||
|  |         return True | ||||||
|  |     else: | ||||||
|  |         return False | ||||||
|  | 
 | ||||||
|  | def anticipate_locktime(locktime,blocks=0,hours=0,days=0): | ||||||
|  |     locktime = int(locktime) | ||||||
|  |     out=0 | ||||||
|  |     if locktime> LOCKTIME_THRESHOLD: | ||||||
|  |         seconds = blocks*600 + hours*3600 + days*86400 | ||||||
|  |         dt = datetime.fromtimestamp(locktime) | ||||||
|  |         dt -= timedelta(seconds=seconds) | ||||||
|  |         out = dt.timestamp() | ||||||
|  |     else: | ||||||
|  |         blocks -= hours*6 + days*144 | ||||||
|  |         out = locktime + blocks | ||||||
|  | 
 | ||||||
|  |     if out < 1: | ||||||
|  |         out = 1  | ||||||
|  |     return out | ||||||
|  | 
 | ||||||
|  | def cmp_locktime(locktimea,locktimeb): | ||||||
|  |     if locktimea==locktimeb: | ||||||
|  |         return 0 | ||||||
|  |     strlocktime = str(locktimea) | ||||||
|  |     strlocktimeb = str(locktimeb) | ||||||
|  |     intlocktimea = str_to_locktime(strlocktimea) | ||||||
|  |     intlocktimeb = str_to_locktime(strlocktimeb) | ||||||
|  |     if locktimea[-1] in "ydb": | ||||||
|  |         if locktimeb[-1] == locktimea[-1]: | ||||||
|  |             return int(strlocktimea[-1])-int(strlocktimeb[-1]) | ||||||
|         else: |         else: | ||||||
|             return True |             return int(locktimea)-(locktimeb) | ||||||
|  |      | ||||||
|  | 
 | ||||||
|  | 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 | ||||||
|  | 
 | ||||||
|  | def get_locktimes(will): | ||||||
|  |     locktimes = {} | ||||||
|  |     for txid,willitem in will.items(): | ||||||
|  |         locktimes[willitem['tx'].locktime]=True | ||||||
|  |     return locktimes.keys() | ||||||
|  | 
 | ||||||
|  | def get_lowest_locktimes(locktimes): | ||||||
|  |     sorted_timestamp=[] | ||||||
|  |     sorted_block=[] | ||||||
|  |     for l in locktimes: | ||||||
|  |         #print("locktime:",parse_locktime_string(l)) | ||||||
|  |         l=parse_locktime_string(l) | ||||||
|  |         if l < LOCKTIME_THRESHOLD: | ||||||
|  |             bisect.insort(sorted_block,l) | ||||||
|  |         else: | ||||||
|  |             bisect.insort(sorted_timestamp,l) | ||||||
|  | 
 | ||||||
|  |     return sorted(sorted_timestamp), sorted(sorted_block) | ||||||
|  | 
 | ||||||
|  | def get_lowest_locktimes_from_will(will): | ||||||
|  |     return get_lowest_locktimes(get_locktimes(will)) | ||||||
|  | 
 | ||||||
|  | def search_willtx_per_io(will,tx): | ||||||
|  |     for wid, w in will.items(): | ||||||
|  |         if cmp_txs(w['tx'],tx['tx']): | ||||||
|  |             return wid,w | ||||||
|  |     return None, None | ||||||
|  | 
 | ||||||
|  | def invalidate_will(will): | ||||||
|  |     raise Exception("not implemented") | ||||||
|  | 
 | ||||||
|  | def get_will_spent_utxos(will): | ||||||
|  |     utxos=[] | ||||||
|  |     for txid,willitem in will.items(): | ||||||
|  |         utxos+=willitem['tx'].inputs() | ||||||
|          |          | ||||||
|     def cmp_heirs(heirsa,heirsb,cmp_function = lambda x,y: x[0]==y[0] and x[3]==y[3],reverse=True): |     return utxos | ||||||
|         try: | 
 | ||||||
|             for heir in heirsa: | def utxo_to_str(utxo): | ||||||
|                 if not "w!ll3x3c\"" in heir: |     try: return utxo.to_str() | ||||||
|                     if not heir in heirsb or not cmp_function(heirsa[heir],heirsb[heir]): |     except Exception as e: pass | ||||||
|                         if not Util.search_heir_by_values(heirsb,heirsa[heir],[0,3]): |     try: return utxo.prevout.to_str() | ||||||
|                             return False |     except Exception as e: pass | ||||||
|             if reverse: |     return str(utxo) | ||||||
|                 return Util.cmp_heirs(heirsb,heirsa,cmp_function,False) | 
 | ||||||
|  | def cmp_utxo(utxoa,utxob): | ||||||
|  |     utxoa=utxo_to_str(utxoa) | ||||||
|  |     utxob=utxo_to_str(utxob) | ||||||
|  |     if utxoa == utxob: | ||||||
|  |     #if utxoa.prevout.txid==utxob.prevout.txid and utxoa.prevout.out_idx == utxob.prevout.out_idx: | ||||||
|  |         return True | ||||||
|  |     else: | ||||||
|  |         return False | ||||||
|  | 
 | ||||||
|  | def in_utxo(utxo, utxos): | ||||||
|  |     for s_u in utxos: | ||||||
|  |         if cmp_utxo(s_u,utxo): | ||||||
|  |             return True | ||||||
|  |     return False | ||||||
|  | 
 | ||||||
|  | def txid_in_utxo(txid,utxos): | ||||||
|  |     for s_u in utxos: | ||||||
|  |         if s_u.prevout.txid == txid: | ||||||
|  |             return True | ||||||
|  |     return False | ||||||
|  | 
 | ||||||
|  | def cmp_output(outputa,outputb): | ||||||
|  |     return outputa.address == outputb.address and outputa.value == outputb.value | ||||||
|  | 
 | ||||||
|  | def in_output(output,outputs): | ||||||
|  |     for s_o in outputs: | ||||||
|  |         if cmp_output(s_o,output): | ||||||
|  |             return True | ||||||
|  |     return False | ||||||
|  | 
 | ||||||
|  | #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 false same amount different address | ||||||
|  | #return false false different amount, different address not found | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def din_output(out,outputs): | ||||||
|  |     same_amount=[] | ||||||
|  |     for s_o in outputs: | ||||||
|  |         if int(out.value) == int(s_o.value): | ||||||
|  |             same_amount.append(s_o) | ||||||
|  |             if out.address==s_o.address: | ||||||
|  |                 #print("SAME_:",out.address,s_o.address) | ||||||
|  |                 return True, True | ||||||
|             else: |             else: | ||||||
|                 return True |  | ||||||
|         except Exception as e: |  | ||||||
|             raise e |  | ||||||
|             return False |  | ||||||
| 
 |  | ||||||
|     def cmp_inputs(inputsa,inputsb): |  | ||||||
|         if len(inputsa) != len(inputsb):  |  | ||||||
|             return False  |  | ||||||
|         for inputa in inputsa: |  | ||||||
|             if not Util.in_utxo(inputa,inputsb): |  | ||||||
|                 return False |  | ||||||
|         return True |  | ||||||
| 
 |  | ||||||
|     def cmp_outputs(outputsa,outputsb,willexecutor_output = None): |  | ||||||
|         if len(outputsa) != len(outputsb):  |  | ||||||
|             return False  |  | ||||||
|         for outputa in outputsa:  |  | ||||||
|             if not Util.cmp_output(outputa,willexecutor_output): |  | ||||||
|                 if not Util.in_output(outputa,outputsb):  |  | ||||||
|                     return False |  | ||||||
|         return True |  | ||||||
| 
 |  | ||||||
|     def cmp_txs(txa,txb): |  | ||||||
|         if not Util.cmp_inputs(txa.inputs(),txb.inputs()): |  | ||||||
|             return False |  | ||||||
|         if not Util.cmp_outputs(txa.outputs(),txb.outputs()): |  | ||||||
|             return False |  | ||||||
|         return True |  | ||||||
| 
 |  | ||||||
|     def get_value_amount(txa,txb): |  | ||||||
|         outputsa=txa.outputs() |  | ||||||
|         outputsb=txb.outputs() |  | ||||||
|         value_amount = 0 |  | ||||||
| 
 |  | ||||||
|         for outa in outputsa: |  | ||||||
|             same_amount,same_address = Util.in_output(outa,txb.outputs()) |  | ||||||
|             if not (same_amount or same_address): |  | ||||||
|                 return False |  | ||||||
|             if same_amount and same_address: |  | ||||||
|                 value_amount+=outa.value |  | ||||||
|             if same_amount: |  | ||||||
|                 pass |  | ||||||
|             if same_address: |  | ||||||
|                 pass |                 pass | ||||||
|  |                 #print("NOT  SAME_:",out.address,s_o.address) | ||||||
| 
 | 
 | ||||||
|         return value_amount |     if len(same_amount)>0: | ||||||
|  |         return True, False | ||||||
|  |     else:return False, False | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | def get_change_output(wallet,in_amount,out_amount,fee):  | ||||||
|     def chk_locktime(timestamp_to_check,block_height_to_check,locktime): |     change_amount = int(in_amount - out_amount - fee)  | ||||||
|         #TODO BUG:  WHAT HAPPEN AT THRESHOLD? |     if change_amount > wallet.dust_threshold():  | ||||||
|         locktime=int(locktime) |         change_addresses = wallet.get_change_addresses_for_new_transaction()  | ||||||
|         if locktime > LOCKTIME_THRESHOLD and locktime > timestamp_to_check: |         out = PartialTxOutput.from_address_and_value(change_addresses[0], change_amount)  | ||||||
|             return True |         out.is_change = True  | ||||||
|         elif locktime < LOCKTIME_THRESHOLD and locktime > block_height_to_check: |  | ||||||
|             return True |  | ||||||
|         else: |  | ||||||
|             return False |  | ||||||
| 
 |  | ||||||
|     def anticipate_locktime(locktime,blocks=0,hours=0,days=0): |  | ||||||
|         locktime = int(locktime) |  | ||||||
|         out=0 |  | ||||||
|         if locktime> LOCKTIME_THRESHOLD: |  | ||||||
|             seconds = blocks*600 + hours*3600 + days*86400 |  | ||||||
|             dt = datetime.fromtimestamp(locktime) |  | ||||||
|             dt -= timedelta(seconds=seconds) |  | ||||||
|             out = dt.timestamp() |  | ||||||
|         else: |  | ||||||
|             blocks -= hours*6 + days*144 |  | ||||||
|             out = locktime + blocks |  | ||||||
| 
 |  | ||||||
|         if out < 1: |  | ||||||
|             out = 1  |  | ||||||
|         return out |         return out | ||||||
| 
 | 
 | ||||||
|     def cmp_locktime(locktimea,locktimeb): |  | ||||||
|         if locktimea==locktimeb: |  | ||||||
|             return 0 |  | ||||||
|         strlocktime = str(locktimea) |  | ||||||
|         strlocktimeb = str(locktimeb) |  | ||||||
|         intlocktimea = Util.str_to_locktime(strlocktimea) |  | ||||||
|         intlocktimeb = Util.str_to_locktime(strlocktimeb) |  | ||||||
|         if locktimea[-1] in "ydb": |  | ||||||
|             if locktimeb[-1] == locktimea[-1]: |  | ||||||
|                 return int(strlocktimea[-1])-int(strlocktimeb[-1]) |  | ||||||
|             else: |  | ||||||
|                 return int(locktimea)-(locktimeb) |  | ||||||
|          |  | ||||||
| 
 | 
 | ||||||
|     def get_lowest_valid_tx(available_utxos,will): | def get_current_height(network:'Network'): | ||||||
|         will = sorted(will.items(),key = lambda x: x[1]['tx'].locktime) |     #if no network or not up to date, just set locktime to zero | ||||||
|         for txid,willitem in will.items(): |     if not network: | ||||||
|             pass |         return 0 | ||||||
| 
 |     chain = network.blockchain() | ||||||
|     def get_locktimes(will): |     if chain.is_tip_stale(): | ||||||
|         locktimes = {} |         return 0 | ||||||
|         for txid,willitem in will.items(): |     # figure out current block height | ||||||
|             locktimes[willitem['tx'].locktime]=True |     chain_height = chain.height()  # learnt from all connected servers, SPV-checked | ||||||
|         return locktimes.keys() |     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) | ||||||
|     def get_lowest_locktimes(locktimes): |     #       - if it's lagging too much, it is the network's job to switch away | ||||||
|         sorted_timestamp=[] |     if server_height < chain_height - 10: | ||||||
|         sorted_block=[] |         # the diff is suspiciously large... give up and use something non-fingerprintable | ||||||
|         for l in locktimes: |         return 0 | ||||||
|             l=Util.parse_locktime_string(l) |     # discourage "fee sniping" | ||||||
|             if l < LOCKTIME_THRESHOLD: |     height = min(chain_height, server_height) | ||||||
|                 bisect.insort(sorted_block,l) |     return height | ||||||
|             else: |  | ||||||
|                 bisect.insort(sorted_timestamp,l) |  | ||||||
| 
 |  | ||||||
|         return sorted(sorted_timestamp), sorted(sorted_block) |  | ||||||
| 
 |  | ||||||
|     def get_lowest_locktimes_from_will(will): |  | ||||||
|         return Util.get_lowest_locktimes(Util.get_locktimes(will)) |  | ||||||
| 
 |  | ||||||
|     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 |  | ||||||
| 
 |  | ||||||
|     def invalidate_will(will): |  | ||||||
|         raise Exception("not implemented") |  | ||||||
| 
 |  | ||||||
|     def get_will_spent_utxos(will): |  | ||||||
|         utxos=[] |  | ||||||
|         for txid,willitem in will.items(): |  | ||||||
|             utxos+=willitem['tx'].inputs() |  | ||||||
|              |  | ||||||
|         return utxos |  | ||||||
| 
 |  | ||||||
|     def utxo_to_str(utxo): |  | ||||||
|         try: return utxo.to_str() |  | ||||||
|         except Exception as e: pass |  | ||||||
|         try: return utxo.prevout.to_str() |  | ||||||
|         except Exception as e: pass |  | ||||||
|         return str(utxo) |  | ||||||
| 
 |  | ||||||
|     def cmp_utxo(utxoa,utxob): |  | ||||||
|         utxoa=Util.utxo_to_str(utxoa) |  | ||||||
|         utxob=Util.utxo_to_str(utxob) |  | ||||||
|         if utxoa == utxob: |  | ||||||
|             return True |  | ||||||
|         else: |  | ||||||
|             return False |  | ||||||
| 
 |  | ||||||
|     def in_utxo(utxo, utxos): |  | ||||||
|         for s_u in utxos: |  | ||||||
|             if Util.cmp_utxo(s_u,utxo): |  | ||||||
|                 return True |  | ||||||
|         return False |  | ||||||
| 
 |  | ||||||
|     def txid_in_utxo(txid,utxos): |  | ||||||
|         for s_u in utxos: |  | ||||||
|             if s_u.prevout.txid == txid: |  | ||||||
|                 return True |  | ||||||
|         return False |  | ||||||
| 
 |  | ||||||
|     def cmp_output(outputa,outputb): |  | ||||||
|         return outputa.address == outputb.address and outputa.value == outputb.value |  | ||||||
| 
 |  | ||||||
|     def in_output(output,outputs): |  | ||||||
|         for s_o in outputs: |  | ||||||
|             if Util.cmp_output(s_o,output): |  | ||||||
|                 return True |  | ||||||
|         return False |  | ||||||
| 
 |  | ||||||
|     #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 false same amount different address |  | ||||||
|     #return false false different amount, different address not found |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     def din_output(out,outputs): | def print_var(var,name = "",veryverbose=False): | ||||||
|         same_amount=[] |     print(f"---{name}---") | ||||||
|         for s_o in outputs: |     if not var is None: | ||||||
|             if int(out.value) == int(s_o.value): |  | ||||||
|                 same_amount.append(s_o) |  | ||||||
|                 if out.address==s_o.address: |  | ||||||
|                     return True, True |  | ||||||
|                 else: |  | ||||||
|                     pass |  | ||||||
| 
 |  | ||||||
|         if len(same_amount)>0: |  | ||||||
|             return True, False |  | ||||||
|         else:return False, False |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     def get_change_output(wallet,in_amount,out_amount,fee):  |  | ||||||
|         change_amount = int(in_amount - out_amount - fee)  |  | ||||||
|         if change_amount > wallet.dust_threshold():  |  | ||||||
|             change_addresses = wallet.get_change_addresses_for_new_transaction()  |  | ||||||
|             out = PartialTxOutput.from_address_and_value(change_addresses[0], change_amount)  |  | ||||||
|             out.is_change = True  |  | ||||||
|             return out |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     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 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     def print_var(var,name = "",veryverbose=False): |  | ||||||
|         print(f"---{name}---") |  | ||||||
|         if not var is None: |  | ||||||
|             try: |  | ||||||
|                 print("doc:",doc(var)) |  | ||||||
|             except: pass |  | ||||||
|             try: |  | ||||||
|                 print("str:",str(var)) |  | ||||||
|             except: pass |  | ||||||
|             try: |  | ||||||
|                 print("repr",repr(var)) |  | ||||||
|             except:pass |  | ||||||
|             try: |  | ||||||
|                 print("dict",dict(var)) |  | ||||||
|             except:pass |  | ||||||
|             try: |  | ||||||
|                 print("dir",dir(var)) |  | ||||||
|             except:pass |  | ||||||
|             try: |  | ||||||
|                 print("type",type(var)) |  | ||||||
|             except:pass |  | ||||||
|             try: |  | ||||||
|                 print("to_json",var.to_json()) |  | ||||||
|             except: pass |  | ||||||
|             try: |  | ||||||
|                 print("__slotnames__",var.__slotnames__) |  | ||||||
|             except:pass |  | ||||||
| 
 |  | ||||||
|         print(f"---end {name}---") |  | ||||||
| 
 |  | ||||||
|     def print_utxo(utxo, name = ""): |  | ||||||
|         print(f"---utxo-{name}---") |  | ||||||
|         Util.print_var(utxo,name) |  | ||||||
|         Util.print_prevout(utxo.prevout,name) |  | ||||||
|         Util.print_var(utxo.script_sig,f"{name}-script-sig") |  | ||||||
|         Util.print_var(utxo.witness,f"{name}-witness") |  | ||||||
|         print("_TxInput__address:",utxo._TxInput__address) |  | ||||||
|         print("_TxInput__scriptpubkey:",utxo._TxInput__scriptpubkey) |  | ||||||
|         print("_TxInput__value_sats:",utxo._TxInput__value_sats) |  | ||||||
|         print(f"---utxo-end {name}---") |  | ||||||
| 
 |  | ||||||
|     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: 'ElectrumWindow', 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: |         try: | ||||||
|             exporter(filename) |             print("doc:",doc(var)) | ||||||
|         except FileExportFailed as e: |         except: pass | ||||||
|             electrum_window.show_critical(str(e)) |         try: | ||||||
|         else: |             print("str:",str(var)) | ||||||
|             electrum_window.show_message(_("Your {0} were exported to '{1}'") |         except: pass | ||||||
|                                      .format(title, str(filename))) |         try: | ||||||
|  |             print("repr",repr(var)) | ||||||
|  |         except:pass | ||||||
|  |         try: | ||||||
|  |             print("dict",dict(var)) | ||||||
|  |         except:pass | ||||||
|  |         try: | ||||||
|  |             print("dir",dir(var)) | ||||||
|  |         except:pass | ||||||
|  |         try: | ||||||
|  |             print("type",type(var)) | ||||||
|  |         except:pass | ||||||
|  |         try: | ||||||
|  |             print("to_json",var.to_json()) | ||||||
|  |         except: pass | ||||||
|  |         try: | ||||||
|  |             print("__slotnames__",var.__slotnames__) | ||||||
|  |         except:pass | ||||||
|  | 
 | ||||||
|  |     print(f"---end {name}---") | ||||||
|  | 
 | ||||||
|  | def print_utxo(utxo, name = ""): | ||||||
|  |     print(f"---utxo-{name}---") | ||||||
|  |     print_var(utxo,name) | ||||||
|  |     print_prevout(utxo.prevout,name) | ||||||
|  |     print_var(utxo.script_sig,f"{name}-script-sig") | ||||||
|  |     print_var(utxo.witness,f"{name}-witness") | ||||||
|  |     #print("madonnamaiala_TXInput__scriptpubkey:",utxo._TXInput__scriptpubkey) | ||||||
|  |     print("_TxInput__address:",utxo._TxInput__address) | ||||||
|  |     print("_TxInput__scriptpubkey:",utxo._TxInput__scriptpubkey) | ||||||
|  |     print("_TxInput__value_sats:",utxo._TxInput__value_sats) | ||||||
|  |     print(f"---utxo-end {name}---") | ||||||
|  | 
 | ||||||
|  | def print_prevout(prevout, name = ""): | ||||||
|  |     print(f"---prevout-{name}---") | ||||||
|  |     print_var(prevout,f"{name}-prevout") | ||||||
|  |     print_var(prevout._asdict()) | ||||||
|  |     print(f"---prevout-end {name}---") | ||||||
|  | 
 | ||||||
|  | def export_meta_gui(electrum_window: 'ElectrumWindow', 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))) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     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 | ||||||
|  | |||||||
							
								
								
									
										413
									
								
								willexecutors.py
									
									
									
									
									
								
							
							
						
						
									
										413
									
								
								willexecutors.py
									
									
									
									
									
								
							| @ -8,249 +8,196 @@ from electrum import constants | |||||||
| from electrum.logging import get_logger | from electrum.logging import get_logger | ||||||
| from electrum.gui.qt.util import WaitingDialog | from electrum.gui.qt.util import WaitingDialog | ||||||
| from electrum.i18n import _ | from electrum.i18n import _ | ||||||
| from .bal import BalPlugin | 
 | ||||||
| from .util import Util | from .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 save(bal_plugin, willexecutors): | def get_willexecutors(bal_plugin, update = False,bal_window=False,force=False,task=True): | ||||||
|         aw=bal_plugin.WILLEXECUTORS.get() |     willexecutors = bal_plugin.config_get(bal_plugin.WILLEXECUTORS) | ||||||
|         aw[constants.net.NET_NAME]=willexecutors |     for w in willexecutors: | ||||||
|         bal_plugin.WILLEXECUTORS.set(aw) |         initialize_willexecutor(willexecutors[w],w) | ||||||
| 
 | 
 | ||||||
|     def get_willexecutors(bal_plugin, update = False,bal_window=False,force=False,task=True): |     bal=bal_plugin.DEFAULT_SETTINGS[bal_plugin.WILLEXECUTORS] | ||||||
|         willexecutors = bal_plugin.WILLEXECUTORS.get() |     for bal_url,bal_executor in bal.items(): | ||||||
|         willexecutors=willexecutors.get(constants.net.NET_NAME,{}) |         if not bal_url in willexecutors: | ||||||
|         to_del=[] |             _logger.debug("replace bal") | ||||||
|         for w in willexecutors: |             willexecutors[bal_url]=bal_executor | ||||||
|             if not  isinstance(willexecutors[w],dict): |     if update: | ||||||
|                 to_del.append(w) |         found = False | ||||||
|                 continue |  | ||||||
|             Willexecutors.initialize_willexecutor(willexecutors[w],w) |  | ||||||
|         for w in to_del: |  | ||||||
|             print("ERROR: WILLEXECUTOR TO DELETE:", w) |  | ||||||
|             del willexecutors[w] |  | ||||||
|         bal = bal_plugin.WILLEXECUTORS.default.get(constants.net.NET_NAME,{}) |  | ||||||
|         for bal_url,bal_executor in bal.items(): |  | ||||||
|             if not bal_url in willexecutors: |  | ||||||
|                 _logger.debug(f"force add {bal_url} willexecutor") |  | ||||||
|                 willexecutors[bal_url] = bal_executor |  | ||||||
|         if update: |  | ||||||
|             found = False |  | ||||||
|             for url,we in willexecutors.items(): |  | ||||||
|                 if Willexecutors.is_selected(we): |  | ||||||
|                     found = True |  | ||||||
|             if found or force: |  | ||||||
|                 if bal_plugin.PING_WILLEXECUTORS.get() or force: |  | ||||||
|                     ping_willexecutors = True |  | ||||||
|                     if bal_plugin.ASK_PING_WILLEXECUTORS.get() and not force: |  | ||||||
|                         if bal_window: |  | ||||||
|                             ping_willexecutors = bal_window.window.question(_("Contact willexecutors servers to update payment informations?")) |  | ||||||
| 
 |  | ||||||
|                     if ping_willexecutors: |  | ||||||
|                         if task: |  | ||||||
|                             bal_window.ping_willexecutors(willexecutors,task) |  | ||||||
|                         else: |  | ||||||
|                             bal_window.ping_willexecutors_task(willexecutors) |  | ||||||
|         w_sorted = dict(sorted(willexecutors.items(), key=lambda w:w[1].get('sort',0),reverse=True)) |  | ||||||
|         return w_sorted |  | ||||||
|     def is_selected(willexecutor,value=None): |  | ||||||
|         if not willexecutor: |  | ||||||
|             return False |  | ||||||
|         if not value is None: |  | ||||||
|             willexecutor['selected']=value |  | ||||||
|         try: |  | ||||||
|             return willexecutor['selected'] |  | ||||||
|         except: |  | ||||||
|             willexecutor['selected']=False |  | ||||||
|             return False |  | ||||||
| 
 |  | ||||||
|     def get_willexecutor_transactions(will, force=False): |  | ||||||
|         willexecutors ={} |  | ||||||
|         for wid,willitem in will.items(): |  | ||||||
|             if willitem.get_status('VALID'): |  | ||||||
|                 if willitem.get_status('COMPLETE'): |  | ||||||
|                     if not willitem.get_status('PUSHED') or force: |  | ||||||
|                         if willexecutor := willitem.we: |  | ||||||
|                             url=willexecutor['url'] |  | ||||||
|                             if  willexecutor and Willexecutors.is_selected(willexecutor): |  | ||||||
|                                 if not url in willexecutors: |  | ||||||
|                                     willexecutor['txs']="" |  | ||||||
|                                     willexecutor['txsids']=[] |  | ||||||
|                                     willexecutor['broadcast_status']= _("Waiting...") |  | ||||||
|                                     willexecutors[url]=willexecutor |  | ||||||
|                                 willexecutors[url]['txs']+=str(willitem.tx)+"\n" |  | ||||||
|                                 willexecutors[url]['txsids'].append(wid) |  | ||||||
| 
 |  | ||||||
|         return willexecutors |  | ||||||
| 
 |  | ||||||
|     def only_selected_list(willexecutors): |  | ||||||
|         out = {} |  | ||||||
|         for url,v in willexecutors.items(): |  | ||||||
|             if Willexecutors.is_selected(willexecutor): |  | ||||||
|                 out[url]=v |  | ||||||
|     def push_transactions_to_willexecutors(will): |  | ||||||
|         willexecutors = get_transactions_to_be_pushed() |  | ||||||
|         for url in willexecutors: |  | ||||||
|             willexecutor = willexecutors[url] |  | ||||||
|             if Willexecutors.is_selected(willexecutor): |  | ||||||
|                 if 'txs' in willexecutor: |  | ||||||
|                     Willexecutors.push_transactions_to_willexecutor(willexecutors[url]['txs'],url) |  | ||||||
| 
 |  | ||||||
|     def send_request(method, url, data=None, *, timeout=10): |  | ||||||
|         network = Network.get_instance() |  | ||||||
|         if not network: |  | ||||||
|             raise ErrorConnectingServer('You are offline.') |  | ||||||
|         _logger.debug(f'<-- {method} {url} {data}') |  | ||||||
|         headers = {} |  | ||||||
|         headers['user-agent'] = f"BalPlugin v:{BalPlugin.version()}" |  | ||||||
|         headers['Content-Type']='text/plain' |  | ||||||
| 
 |  | ||||||
|         try: |  | ||||||
|             if method == 'get': |  | ||||||
|                 response = Network.send_http_on_proxy(method, url, |  | ||||||
|                                                       params=data, |  | ||||||
|                                                       headers=headers, |  | ||||||
|                                                       on_finish=Willexecutors.handle_response, |  | ||||||
|                                                       timeout=timeout) |  | ||||||
|             elif method == 'post': |  | ||||||
|                 response = Network.send_http_on_proxy(method, url, |  | ||||||
|                                                       body=data, |  | ||||||
|                                                       headers=headers, |  | ||||||
|                                                       on_finish=Willexecutors.handle_response, |  | ||||||
|                                                       timeout=timeout) |  | ||||||
|             else: |  | ||||||
|                 raise Exception(f"unexpected {method=!r}") |  | ||||||
|         except Exception as e: |  | ||||||
|             _logger.error(f"exception sending request {e}") |  | ||||||
|             raise e |  | ||||||
|         else: |  | ||||||
|             _logger.debug(f'--> {response}') |  | ||||||
|             return response |  | ||||||
|     async def handle_response(resp:ClientResponse): |  | ||||||
|         r=await resp.text() |  | ||||||
|         try: |  | ||||||
|             r=json.loads(r) |  | ||||||
|             r['status'] = resp.status |  | ||||||
|             r['selected']=Willexecutors.is_selected(willexecutor) |  | ||||||
|             r['url']=url |  | ||||||
|         except: |  | ||||||
|             pass     |  | ||||||
|         return r |  | ||||||
| 
 |  | ||||||
|     class AlreadyPresentException(Exception): |  | ||||||
|         pass |  | ||||||
|     def push_transactions_to_willexecutor(willexecutor): |  | ||||||
|         out=True |  | ||||||
|         try: |  | ||||||
| 
 |  | ||||||
|             _logger.debug(f"willexecutor['txs']") |  | ||||||
|             if w:=Willexecutors.send_request('post', willexecutor['url']+"/"+constants.net.NET_NAME+"/pushtxs", data=willexecutor['txs'].encode('ascii')): |  | ||||||
|                 willexecutor['broadcast_status'] = _("Success") |  | ||||||
|                 _logger.debug(f"pushed: {w}") |  | ||||||
|                 if w !='thx': |  | ||||||
|                     _logger.debug(f"error: {w}") |  | ||||||
|                     raise Exception(w) |  | ||||||
|             else: |  | ||||||
|                 raise Exception("empty reply from:{willexecutor['url']}") |  | ||||||
|         except Exception as e: |  | ||||||
|             _logger.debug(f"error:{e}") |  | ||||||
|             if str(e) == "already present": |  | ||||||
|                 raise Willexecutors.AlreadyPresentException() |  | ||||||
|             out=False |  | ||||||
|             willexecutor['broadcast_status'] = _("Failed") |  | ||||||
| 
 |  | ||||||
|         return out |  | ||||||
| 
 |  | ||||||
|     def ping_servers(willexecutors): |  | ||||||
|         for url,we in willexecutors.items(): |         for url,we in willexecutors.items(): | ||||||
|             Willexecutors.get_info_task(url,we) |             if is_selected(we): | ||||||
|  |                 found = True | ||||||
|  |         if found or force: | ||||||
|  |             if bal_plugin.config_get(bal_plugin.PING_WILLEXECUTORS) or force: | ||||||
|  |                 ping_willexecutors = True | ||||||
|  |                 if bal_plugin.config_get(bal_plugin.ASK_PING_WILLEXECUTORS) and not force: | ||||||
|  |                     ping_willexecutors = bal_window.window.question(_("Contact willexecutors servers to update payment informations?")) | ||||||
|  |                 if ping_willexecutors: | ||||||
|  |                     if task: | ||||||
|  |                         bal_window.ping_willexecutors(willexecutors) | ||||||
|  |                     else: | ||||||
|  |                         bal_window.ping_willexecutors_task(willexecutors) | ||||||
|  |     return willexecutors | ||||||
|  | 
 | ||||||
|  | def is_selected(willexecutor,value=None): | ||||||
|  |     if not willexecutor: | ||||||
|  |         return False | ||||||
|  |     if not value is None: | ||||||
|  |         willexecutor['selected']=value | ||||||
|  |     try: | ||||||
|  |         return willexecutor['selected'] | ||||||
|  |     except: | ||||||
|  |         willexecutor['selected']=False | ||||||
|  |         return False | ||||||
|  | 
 | ||||||
|  | def get_willexecutor_transactions(will, force=False): | ||||||
|  |     willexecutors ={} | ||||||
|  |     for wid,willitem in will.items(): | ||||||
|  |         if willitem.get_status('VALID'): | ||||||
|  |             if willitem.get_status('COMPLETE'): | ||||||
|  |                 if not willitem.get_status('PUSHED') or force: | ||||||
|  |                     if willexecutor := willitem.we: | ||||||
|  |                         url=willexecutor['url'] | ||||||
|  |                         if  willexecutor and is_selected(willexecutor): | ||||||
|  |                             if not url in willexecutors: | ||||||
|  |                                 willexecutor['txs']="" | ||||||
|  |                                 willexecutor['txsids']=[] | ||||||
|  |                                 willexecutor['broadcast_status']= _("Waiting...") | ||||||
|  |                                 willexecutors[url]=willexecutor | ||||||
|  |                             willexecutors[url]['txs']+=str(willitem.tx)+"\n" | ||||||
|  |                             willexecutors[url]['txsids'].append(wid) | ||||||
|  | 
 | ||||||
|  |     return willexecutors | ||||||
|  | 
 | ||||||
|  | def only_selected_list(willexecutors): | ||||||
|  |     out = {} | ||||||
|  |     for url,v in willexectors.items(): | ||||||
|  |         if is_selected(willexecutor): | ||||||
|  |             out[url]=v | ||||||
|  | def push_transactions_to_willexecutors(will): | ||||||
|  |     willexecutors = get_transactions_to_be_pushed() | ||||||
|  |     for url in willexecutors: | ||||||
|  |         willexecutor = willexecutors[url] | ||||||
|  |         if is_selected(willexecutor): | ||||||
|  |             if 'txs' in willexecutor: | ||||||
|  |                 push_transactions_to_willexecutor(willexecutors[url]['txs'],url) | ||||||
|  | 
 | ||||||
|  | def send_request(method, url, data=None, *, timeout=10): | ||||||
|  |     network = Network.get_instance() | ||||||
|  |     if not network: | ||||||
|  |         raise ErrorConnectingServer('You are offline.') | ||||||
|  |     _logger.debug(f'<-- {method} {url} {data}') | ||||||
|  |     headers = {} | ||||||
|  |     headers['user-agent'] = 'BalPlugin' | ||||||
|  |     headers['Content-Type']='text/plain' | ||||||
|  | 
 | ||||||
|  |     try: | ||||||
|  |         if method == 'get': | ||||||
|  |             response = Network.send_http_on_proxy(method, url, | ||||||
|  |                                                   params=data, | ||||||
|  |                                                   headers=headers, | ||||||
|  |                                                   on_finish=handle_response, | ||||||
|  |                                                   timeout=timeout) | ||||||
|  |         elif method == 'post': | ||||||
|  |             response = Network.send_http_on_proxy(method, url, | ||||||
|  |                                                   body=data, | ||||||
|  |                                                   headers=headers, | ||||||
|  |                                                   on_finish=handle_response, | ||||||
|  |                                                   timeout=timeout) | ||||||
|  |         else: | ||||||
|  |             raise Exception(f"unexpected {method=!r}") | ||||||
|  |     except Exception as e: | ||||||
|  |         _logger.error(f"exception sending request {e}") | ||||||
|  |         raise e | ||||||
|  |     else: | ||||||
|  |         _logger.debug(f'--> {response}') | ||||||
|  |         return response | ||||||
|  | async def handle_response(resp:ClientResponse): | ||||||
|  |     r=await resp.text() | ||||||
|  |     try: | ||||||
|  |         r=json.loads(r) | ||||||
|  |         r['status'] = resp.status | ||||||
|  |         r['selected']=is_selected(willexecutor) | ||||||
|  |         r['url']=url | ||||||
|  |     except: | ||||||
|  |         pass     | ||||||
|  |     return r | ||||||
|  | 
 | ||||||
|  | class AlreadyPresentException(Exception): | ||||||
|  |     pass | ||||||
|  | def push_transactions_to_willexecutor(willexecutor): | ||||||
|  |     out=True | ||||||
|  |     try: | ||||||
|  |         _logger.debug(f"willexecutor['txs']") | ||||||
|  |         if w:=send_request('post', willexecutor['url']+"/"+constants.net.NET_NAME+"/pushtxs", data=willexecutor['txs'].encode('ascii')): | ||||||
|  |             willexecutor['broadcast_stauts'] = _("Success") | ||||||
|  |             _logger.debug(f"pushed: {w}") | ||||||
|  |             if w !='thx': | ||||||
|  |                 _logger.debug(f"error: {w}") | ||||||
|  |                 raise Exception(w) | ||||||
|  |         else: | ||||||
|  |             raise Exception("empty reply from:{willexecutor['url']}") | ||||||
|  |     except Exception as e: | ||||||
|  |         _logger.debug(f"error:{e}") | ||||||
|  |         if str(e) == "already present": | ||||||
|  |             raise AlreadyPresentException() | ||||||
|  |         out=False | ||||||
|  |         willexecutor['broadcast_stauts'] = _("Failed") | ||||||
|  | 
 | ||||||
|  |     return out | ||||||
|  | 
 | ||||||
|  | def ping_servers(willexecutors): | ||||||
|  |     for url,we in willexecutors.items(): | ||||||
|  |         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['status'] = w['status'] |  | ||||||
|             willexecutor['base_fee'] = w['base_fee'] |  | ||||||
|             willexecutor['address'] = w['address'] |  | ||||||
|             if not willexecutor['info']: |  | ||||||
|                 willexecutor['info'] = w['info'] |  | ||||||
|             _logger.debug(f"response_data {w['address']}") |  | ||||||
|         except Exception as e: |  | ||||||
|             _logger.error(f"error {e} contacting {url}: {w}") |  | ||||||
|             willexecutor['status']="KO" |  | ||||||
| 
 |  | ||||||
|         willexecutor['last_update'] = datetime.now().timestamp() |  | ||||||
|         return willexecutor |  | ||||||
| 
 |  | ||||||
|     def initialize_willexecutor(willexecutor,url,status=None,selected=None): |  | ||||||
|         willexecutor['url']=url |         willexecutor['url']=url | ||||||
|         if not status is None: |         willexecutor['status'] = w['status'] | ||||||
|             willexecutor['status'] = status |         willexecutor['base_fee'] = w['base_fee'] | ||||||
|         willexecutor['selected'] = Willexecutors.is_selected(willexecutor,selected) |         willexecutor['address'] = w['address'] | ||||||
|  |         if not willexecutor['info']: | ||||||
|  |             willexecutor['info'] = w['info'] | ||||||
|  |         _logger.debug(f"response_data {w['address']}") | ||||||
|  |     except Exception as e: | ||||||
|  |         _logger.error(f"error {e} contacting {url}: {w}") | ||||||
|  |         willexecutor['status']="KO" | ||||||
| 
 | 
 | ||||||
|     def download_list(bal_plugin): |     willexecutor['last_update'] = datetime.now().timestamp() | ||||||
|         try: |     return willexecutor | ||||||
|             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.WILLEXECUTORS.set(l) |  | ||||||
|             #bal_plugin.config.set_key(bal_plugin.WILLEXECUTORS,l,save=True) |  | ||||||
|             return l |  | ||||||
| 
 | 
 | ||||||
|         except Exception as e: | def initialize_willexecutor(willexecutor,url,status=None,selected=None): | ||||||
|             _logger.error(f"Failed to download willexecutors list: {e}") |     willexecutor['url']=url | ||||||
|             return {} |     if not status is None: | ||||||
|     def get_willexecutors_list_from_json(bal_plugin): |         willexecutor['status'] = status | ||||||
|         try: |     willexecutor['selected'] = is_selected(willexecutor,selected) | ||||||
|             with open("willexecutors.json") as f: |  | ||||||
|                 willexecutors = json.load(f) |  | ||||||
|                 for w in willexecutors: |  | ||||||
|                     willexecutor=willexecutors[w] |  | ||||||
|                     Willexecutors.initialize_willexecutor(willexecutor,w,'New',False) |  | ||||||
|                 #bal_plugin.WILLEXECUTORS.set(willexecutors) |  | ||||||
|                 return h |  | ||||||
|         except Exception as e: |  | ||||||
|             _logger.error(f"error opening willexecutors json: {e}") |  | ||||||
| 
 | 
 | ||||||
|             return {} | def get_willexecutors_list_from_json(bal_plugin): | ||||||
|  |     try: | ||||||
|  |         with open("willexecutors.json") as f: | ||||||
|  |             willexecutors = json.load(f) | ||||||
|  |             for w in willexecutors: | ||||||
|  |                 willexecutor=willexecutors[w] | ||||||
|  |                 willexecutors.initialize_willexecutor(willexecutor,w,'New',False) | ||||||
|  |             bal_plugin.config.set_key(bal_plugin.WILLEXECUTORS,willexecutors,save=True) | ||||||
|  |             return h | ||||||
|  |     except Exception as e: | ||||||
|  |         _logger.error(f"errore aprendo willexecutors.json: {e}") | ||||||
|  |         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}") | ||||||
|             raise e |         raise e | ||||||
| 
 |  | ||||||
| class WillExecutor: |  | ||||||
|     def __init__(self,url,base_fee,chain,info,version): |  | ||||||
|         self.url            = url |  | ||||||
|         self.base_fee       = base_fee |  | ||||||
|         self.chain          = chain |  | ||||||
|         self.info           = info |  | ||||||
|         self.version        = version |  | ||||||
| 
 |  | ||||||
|     def from_dict(d): |  | ||||||
|         we = WillExecutor( |  | ||||||
|                     d['url'], |  | ||||||
|                     d['base_fee'], |  | ||||||
|                     d['chain'], |  | ||||||
|                     d['info'], |  | ||||||
|                     d['version'] |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|          |  | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user