385 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			385 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from .baldialog import BalDialog
 | |
| from . import qt_resources
 | |
| 
 | |
| if qt_resources.QT_VERSION == 5:
 | |
|     from PyQt5.QtCore import Qt
 | |
|     from PyQt5.QtWidgets import QLabel, QVBoxLayout, QCheckBox,QWidget
 | |
|     from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject,QEventLoop
 | |
| else:
 | |
|     from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject,QEventLoop
 | |
|     from PyQt6.QtCore import Qt
 | |
|     from PyQt6.QtWidgets import QLabel, QVBoxLayout, QCheckBox, QWidget
 | |
| import time
 | |
| from electrum.i18n import _
 | |
| from electrum.gui.qt.util import WindowModalDialog, TaskThread
 | |
| from electrum.network import Network,TxBroadcastError, BestEffortRequestFailed
 | |
| from electrum.logging import get_logger
 | |
| 
 | |
| 
 | |
| from functools import partial
 | |
| import copy
 | |
| 
 | |
| from .. import util as Util
 | |
| from .. import will as Will
 | |
| 
 | |
| from .. import willexecutors as Willexecutors
 | |
| _logger = get_logger(__name__)
 | |
| 
 | |
| 
 | |
| class BalCloseDialog(BalDialog):
 | |
|     updatemessage=pyqtSignal()
 | |
|     def __init__(self,bal_window):
 | |
|         BalDialog.__init__(self,bal_window.window,"Closing BAL")
 | |
|         self.updatemessage.connect(self.update)
 | |
|         self.bal_window=bal_window
 | |
|         self.message_label = QLabel("Closing BAL:")
 | |
|         self.vbox = QVBoxLayout(self)
 | |
|         self.vbox.addWidget(self.message_label)
 | |
|         self.qwidget=QWidget()
 | |
|         self.vbox.addWidget(self.qwidget)
 | |
|         self.labels=[]
 | |
|         #self.checking.connect(self.msg_set_checking)
 | |
|         #self.invalidating.connect(self.msg_set_invalidating)
 | |
|         #self.building.connect(self.msg_set_building)
 | |
|         #self.signing.connect(self.msg_set_signing)
 | |
|         #self.pushing.connect(self.msg_set_pushing)
 | |
| 
 | |
|         #self.askpassword.connect(self.ask_password)
 | |
|         #self.passworddone.connect(self.password_done)
 | |
|         self.check_row = None
 | |
|         self.inval_row = None
 | |
|         self.build_row = None
 | |
|         self.sign_row = None
 | |
|         self.push_row = None
 | |
|         self.network = Network.get_instance()
 | |
|         self._stopping = False
 | |
|         self.thread = TaskThread(self)
 | |
|         self.thread.finished.connect(self.task_finished)  # see #3956
 | |
|     def task_finished(self):
 | |
|         pass
 | |
|         #_logger.trace("task finished")
 | |
|         
 | |
|     def close_plugin_task(self):
 | |
|         _logger.debug("close task to be started")
 | |
|         self.thread.add(self.task_phase1,on_success=self.on_success_phase1,on_done=self.on_accept,on_error=self.on_error_phase1)
 | |
|         self.show()
 | |
|         self.exec()
 | |
| 
 | |
|     def task_phase1(self):
 | |
|         _logger.debug("close plugin phase 1 started")
 | |
|         try:
 | |
|             self.bal_window.init_class_variables()
 | |
|         except Will.NoHeirsException:
 | |
|             return False, None
 | |
|         self.msg_set_status("checking variables","Waiting")
 | |
|         try:
 | |
|             Will.check_amounts(self.bal_window.heirs,self.bal_window.willexecutors,self.bal_window.window.wallet.get_utxos(),self.bal_window.date_to_check,self.bal_window.window.wallet.dust_threshold())
 | |
|         except Will.AmountException:
 | |
|             self.msg_edit_row('<font color="#ff0000">'+_("In the inheritance process, the entire wallet will always be fully emptied. Your settings require an adjustment of the amounts"+"</font>"))
 | |
|             #self.bal_window.show_warning(_("In the inheritance process, the entire wallet will always be fully emptied. Your settings require an adjustment of the amounts"),parent=self)
 | |
| 
 | |
|         self.msg_set_checking()
 | |
|         have_to_build=False
 | |
|         try:
 | |
|             self.bal_window.check_will()
 | |
|             self.msg_set_checking('Ok')
 | |
|         except Will.WillExpiredException as e:
 | |
|             self.msg_set_checking("Expired")
 | |
|             fee_per_byte=self.bal_window.will_settings.get('tx_fees',1)
 | |
|             return None, Will.invalidate_will(self.bal_window.willitems,self.bal_window.wallet,fee_per_byte)
 | |
|         except Will.NoHeirsException:
 | |
|             self.msg_set_checking("No Heirs")
 | |
|         except Will.NotCompleteWillException as e:
 | |
|             message = False
 | |
|             have_to_build=True
 | |
|             if isinstance(e,Will.HeirChangeException):
 | |
|                 message ="Heirs changed:"
 | |
|             elif isinstance(e,Will.WillExecutorNotPresent):
 | |
|                 message = "Will-Executor not present"
 | |
|             elif isinstance(e,Will.WillexecutorChangeException):
 | |
|                 message = "Will-Executor changed"
 | |
|             elif isinstance(e,Will.TxFeesChangedException):
 | |
|                 message = "Txfees are changed"
 | |
|             elif isinstance(e,Will.HeirNotFoundException):
 | |
|                 message = "Heir not found"
 | |
|             if message:
 | |
|                 self.msg_set_checking(message)
 | |
|             else:
 | |
|                 self.msg_set_checking("New")
 | |
| 
 | |
|         if have_to_build:
 | |
|             self.msg_set_building()
 | |
|             try:
 | |
|                 self.bal_window.build_will()
 | |
|                 self.bal_window.check_will()
 | |
|                 for wid in Will.only_valid(self.bal_window.willitems):
 | |
|                     self.bal_window.wallet.set_label(wid,"BAL Transaction")
 | |
|                 self.msg_set_building("Ok")
 | |
|             except Exception as e:
 | |
|                 self.msg_set_building(self.msg_error(e))
 | |
|                 return False,None
 | |
|         have_to_sign = False
 | |
|         for wid in Will.only_valid(self.bal_window.willitems):
 | |
|             if not self.bal_window.willitems[wid].get_status("COMPLETE"):
 | |
|                 have_to_sign = True
 | |
|                 break
 | |
|         return have_to_sign, None
 | |
|         
 | |
|     def on_accept(self):
 | |
|         pass
 | |
| 
 | |
|     def on_accept_phase2(self):
 | |
|         pass
 | |
| 
 | |
|     def on_error_push(self):
 | |
|         pass
 | |
| 
 | |
|     def wait(self,secs):
 | |
|         wait_row=None
 | |
|         for i in range(secs,0,-1):
 | |
|             if self._stopping:
 | |
|                 return
 | |
|             wait_row = self.msg_edit_row(f"Please wait {i}secs",      wait_row)
 | |
|             time.sleep(1)
 | |
|         self.msg_del_row(wait_row)
 | |
| 
 | |
|     def loop_broadcast_invalidating(self,tx):
 | |
|         self.msg_set_invalidating("Broadcasting")
 | |
|         try:
 | |
|             tx.add_info_from_wallet(self.bal_window.wallet)
 | |
|             self.network.run_from_another_thread(tx.add_info_from_network(self.network))
 | |
|             txid = self.network.run_from_another_thread(self.network.broadcast_transaction(tx,timeout=120),timeout=120)
 | |
|             self.msg_set_invalidating("Ok")
 | |
|             if not txid:
 | |
|                 _logger.debug(f"should not be none txid: {txid}")
 | |
|             
 | |
| 
 | |
|         except TxBroadcastError as e:
 | |
|             _logger.error(e)
 | |
|             msg = e.get_message_for_gui()
 | |
|             self.msg_set_invalidating(self.msg_error(msg))
 | |
|         except BestEffortRequestFailed as e:
 | |
|             self.msg_set_invalidating(self.msg_error(e))
 | |
|         #    self.loop_broadcast_invalidating(tx)
 | |
| 
 | |
|     def loop_push(self):
 | |
|         self.msg_set_pushing("Broadcasting")
 | |
|         retry = False
 | |
|         try:
 | |
|             willexecutors=Willexecutors.get_willexecutor_transactions(self.bal_window.willitems)
 | |
|             for url,willexecutor in willexecutors.items():
 | |
|                 try:
 | |
|                     if Willexecutors.is_selected(self.bal_window.willexecutors.get(url)):
 | |
|                         _logger.debug(f"{url}: {willexecutor}")
 | |
|                         if not Willexecutors.push_transactions_to_willexecutor(willexecutor):
 | |
|                             for wid in willexecutor['txsids']:
 | |
|                                 self.bal_window.willitems[wid].set_status('PUSH_FAIL',True)
 | |
|                             retry=True
 | |
|                         else:
 | |
|                             for wid in willexecutor['txsids']:
 | |
|                                 self.bal_window.willitems[wid].set_status('PUSHED',True)
 | |
|                 except Willexecutors.AlreadyPresentException:
 | |
|                     for wid in willexecutor['txsids']:
 | |
|                         row = self.msg_edit_row("checking {} - {} : {}".format(self.bal_window.willitems[wid].we['url'],wid, "Waiting"))
 | |
|                         self.bal_window.willitems[wid].check_willexecutor()
 | |
|                         row = self.msg_edit_row("checked {} - {} : {}".format(self.bal_window.willitems[wid].we['url'],wid,self.bal_window.willitems[wid].get_status("CHECKED" )),row)
 | |
|                         
 | |
|                             
 | |
|                 except Exception as e:
 | |
| 
 | |
|                     _logger.error(e)
 | |
|                     raise e
 | |
|             if retry:
 | |
|                 raise Exception("retry")
 | |
| 
 | |
|         except Exception as e:
 | |
|             self.msg_set_pushing(self.msg_error(e))
 | |
|             self.wait(10)
 | |
|             if not self._stopping:
 | |
|                 self.loop_push()
 | |
| 
 | |
| 
 | |
|     def invalidate_task(self,tx,password):
 | |
|         _logger.debug(f"invalidate tx: {tx}")
 | |
|         tx = self.bal_window.wallet.sign_transaction(tx,password)
 | |
|         try:
 | |
|             if tx:
 | |
|                 if tx.is_complete():
 | |
|                     self.loop_broadcast_invalidating(tx)
 | |
|                     self.wait(5)
 | |
|                 else:
 | |
|                     raise
 | |
|             else:
 | |
|                 raise
 | |
|         except Exception as e:
 | |
|             self.msg_set_invalidating("Error")
 | |
|             raise Exception("Impossible to sign")
 | |
|     def on_success_invalidate(self,success):
 | |
|         self.thread.add(self.task_phase1,on_success=self.on_success_phase1,on_done=self.on_accept,on_error=self.on_error_phase1)
 | |
|     def on_error(self,error):
 | |
|         _logger.error(error)
 | |
|         pass
 | |
|     def on_success_phase1(self,result):
 | |
|         self.have_to_sign,tx = list(result)
 | |
|         #if have_to_sign is False and tx is None:
 | |
|             #self._stopping=True
 | |
|             #self.on_success_phase2()
 | |
|         #   return
 | |
|         _logger.debug("have to sign {}".format(self.have_to_sign))
 | |
|         password=None
 | |
|         if self.have_to_sign is None:
 | |
|             self.msg_set_invalidating()
 | |
|            #need to sign invalidate and restart phase 1
 | |
| 
 | |
|             password = self.bal_window.get_wallet_password("Invalidate your old will",parent=self.bal_window.window)
 | |
|             if password is False:
 | |
|                 self.msg_set_invalidating("Aborted")
 | |
|                 self.wait(3)
 | |
|                 self.close()
 | |
|                 return
 | |
|             self.thread.add(partial(self.invalidate_task,tx,password),on_success=self.on_success_invalidate, on_done=self.on_accept, on_error=self.on_error)
 | |
|             
 | |
|             return
 | |
|         
 | |
|         elif self.have_to_sign:
 | |
|             password = self.bal_window.get_wallet_password("Sign your will",parent=self.bal_window.window)
 | |
|             if password is False:
 | |
|                 self.msg_set_signing('Aborted')
 | |
|         else:
 | |
|             self.msg_set_signing('Nothing to do')
 | |
|         self.thread.add(partial(self.task_phase2,password),on_success=self.on_success_phase2,on_done=self.on_accept_phase2,on_error=self.on_error_phase2)
 | |
|         return
 | |
|     
 | |
|     def on_success_phase2(self,arg=False):
 | |
|         self.thread.stop()
 | |
|         self.bal_window.save_willitems()
 | |
|         self.msg_edit_row("Finished")
 | |
|         self.close()
 | |
| 
 | |
|     def closeEvent(self,event):
 | |
|         self._stopping=True
 | |
|         self.thread.stop()
 | |
| 
 | |
|     def task_phase2(self,password):
 | |
|         if self.have_to_sign:
 | |
|             try:
 | |
|                 if txs:=self.bal_window.sign_transactions(password):
 | |
|                     for txid,tx in txs.items():
 | |
|                         self.bal_window.willitems[txid].tx = copy.deepcopy(tx)
 | |
|                     self.bal_window.save_willitems()
 | |
|                     self.msg_set_signing("Ok")
 | |
|             except Exception as e:
 | |
|                 self.msg_set_signing(self.msg_error(e))
 | |
| 
 | |
|         self.msg_set_pushing()
 | |
|         have_to_push = False
 | |
|         for wid in Will.only_valid(self.bal_window.willitems):
 | |
|             w=self.bal_window.willitems[wid]
 | |
|             if w.we and w.get_status("COMPLETE") and not w.get_status("PUSHED"):
 | |
|                 have_to_push = True
 | |
|         if not have_to_push:
 | |
|             self.msg_set_pushing("Nothing to do")
 | |
|         else:
 | |
|             try:
 | |
|                 self.loop_push()
 | |
|                 self.msg_set_pushing("Ok")
 | |
| 
 | |
|             except Exception as e:
 | |
|                 self.msg_set_pushing(self.msg_error(e))
 | |
|         self.msg_edit_row("Ok")
 | |
|         self.wait(5)
 | |
| 
 | |
|     def on_error_phase1(self,error):
 | |
|         _logger.error(f"error phase1: {error}")
 | |
| 
 | |
|     def on_error_phase2(self,error):
 | |
|         _logger.error("error phase2: { error}")
 | |
| 
 | |
| 
 | |
|     def msg_set_checking(self, status = None, row = None):
 | |
|         row = self.check_row if row is None else row
 | |
|         self.check_row = self.msg_set_status("Checking your will",    row,  status)
 | |
| 
 | |
|     def msg_set_invalidating(self, status = None, row = None):
 | |
|         row = self.inval_row if row is None else row
 | |
|         self.inval_row = self.msg_set_status("Invalidating old will",    self.inval_row,  status)
 | |
| 
 | |
|     def msg_set_building(self, status = None, row = None):
 | |
|         row = self.build_row if row is None else row
 | |
|         self.build_row = self.msg_set_status("Building your will",    self.build_row,  status)
 | |
| 
 | |
|     def msg_set_signing(self, status = None, row = None):
 | |
|         row = self.sign_row if row is None else row
 | |
|         self.sign_row = self.msg_set_status("Signing your will",      self.sign_row,   status)
 | |
| 
 | |
|     def msg_set_pushing(self, status = None, row = None):
 | |
|         row = self.push_row if row is None else row
 | |
|         self.push_row = self.msg_set_status("Broadcasting your will to executors",      self.push_row,   status)
 | |
| 
 | |
|     def msg_set_waiting(self, status = None, row = None):
 | |
|         row = self.wait_row if row is None else row
 | |
|         self.wait_row = self.msg_edit_row(f"Please wait {status}secs",      self.wait_row)
 | |
| 
 | |
|     def msg_error(self,e):
 | |
|         return "Error: {}".format(e)
 | |
| 
 | |
|     def msg_set_status(self,msg,row,status=None):
 | |
|         status= "Wait" if status is None else status
 | |
|         line="{}:\t{}".format(_(msg), status)
 | |
|         return self.msg_edit_row(line,row)
 | |
|         
 | |
|         #return v$msg_edit_row("{}:\t{}".format(_(msg), status), row)
 | |
|     
 | |
|     def ask_password(self,msg=None):
 | |
|         self.password=self.bal_window.get_wallet_password(msg,parent=self)
 | |
| 
 | |
|     def msg_edit_row(self,line,row=None):
 | |
|         _logger.debug(f"{row},{line}")
 | |
|         
 | |
|         #msg=self.get_text()
 | |
|         #rows=msg.split("\n")
 | |
|         #try:
 | |
|         #    rows[row]=line
 | |
|         #except Exception as e:
 | |
|         #    rows.append(line)
 | |
|         #row=len(rows)-1
 | |
|         #self.update("\n".join(rows))
 | |
|     
 | |
|         #return row
 | |
| 
 | |
|     #def msg_edit_label(self,line,row=None):
 | |
|         #_logger.trace(f"{row},{line}")
 | |
|        
 | |
|         #msg=self.get_text()
 | |
|         #rows=msg.split("\n")
 | |
|         try:
 | |
|             self.labels[row]=line
 | |
|         except Exception as e:
 | |
|             self.labels.append(line)
 | |
|             row=len(self.labels)-1
 | |
|         
 | |
|         self.updatemessage.emit()
 | |
|     
 | |
|         return row
 | |
| 
 | |
|     def msg_del_row(self,row):
 | |
|         #_logger.trace(f"del row: {row}")
 | |
|         try:
 | |
|             del self.labels[row]
 | |
|         except Exception as e:
 | |
|             pass
 | |
|         self.updatemessage.emit()
 | |
| 
 | |
|     def update(self):
 | |
|         self.vbox.removeWidget(self.qwidget)
 | |
|         self.qwidget=QWidget(self)
 | |
|         labelsbox = QVBoxLayout(self.qwidget)
 | |
|         for label in self.labels:
 | |
|             labelsbox.addWidget(QLabel(label))
 | |
|         self.vbox.addWidget(self.qwidget)
 | |
| 
 | |
|     def get_text(self):
 | |
|         return self.message_label.text()
 | |
| def ThreadStopped(Exception):
 | |
|     pass
 |