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(''+_("In the inheritance process, the entire wallet will always be fully emptied. Your settings require an adjustment of the amounts"+"")) #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