257 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			257 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import json
 | |
| from datetime import datetime
 | |
| from functools import partial
 | |
| from aiohttp import ClientResponse
 | |
| 
 | |
| from electrum.network import Network
 | |
| from electrum import constants
 | |
| from electrum.logging import get_logger
 | |
| from electrum.gui.qt.util import WaitingDialog
 | |
| from electrum.i18n import _
 | |
| 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']
 | |
|         )
 | |
| 
 | |
|         
 |