import json from datetime import datetime from functools import partial from aiohttp import ClientResponse from electrum import constants from electrum.gui.qt.util import WaitingDialog from electrum.i18n import _ from electrum.logging import get_logger from electrum.network import Network from .bal import BalPlugin from .util import Util DEFAULT_TIMEOUT = 5 _logger = get_logger(__name__) class Willexecutors: def save(bal_plugin, willexecutors): aw = bal_plugin.WILLEXECUTORS.get() aw[constants.net.NET_NAME] = willexecutors bal_plugin.WILLEXECUTORS.set(aw) def get_willexecutors( bal_plugin, update=False, bal_window=False, force=False, task=True ): willexecutors = bal_plugin.WILLEXECUTORS.get() willexecutors = willexecutors.get(constants.net.NET_NAME, {}) to_del = [] for w in willexecutors: if not isinstance(willexecutors[w], dict): to_del.append(w) 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(): Willexecutors.get_info_task(url, we) def get_info_task(url, willexecutor): w = None try: _logger.info("GETINFO_WILLEXECUTOR") _logger.debug(url) netname = "bitcoin" 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 if not status is None: willexecutor["status"] = status willexecutor["selected"] = Willexecutors.is_selected(willexecutor, selected) def download_list(bal_plugin): try: 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: _logger.error(f"Failed to download willexecutors list: {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.WILLEXECUTORS.set(willexecutors) return h except Exception as e: _logger.error(f"error opening willexecutors json: {e}") return {} def check_transaction(txid, url): _logger.debug(f"{url}:{txid}") try: w = Willexecutors.send_request( "post", url + "/searchtx", data=txid.encode("ascii") ) return w except Exception as e: _logger.error(f"error contacting {url} for checking txs {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"])