Beispiel #1
0
    def on_btnFindDMNTxHash_clicked(self, checked):
        if self.masternode and not self.updating_ui:
            found_protx = None
            if not ((self.masternode.ip and self.masternode.port) or
                    (self.masternode.collateralTx and self.masternode.collateralTxIndex)):
                WndUtils.errorMsg('To be able to locate the deterministic masternode transaction you need to '
                                  'provide the masternode ip + port or collateral tx + tx index.')
                return

            try:
                txes = self.crownd_intf.protx('list', 'registered', True)
                for protx in txes:
                    state = protx.get('state')
                    if state:
                        if (state.get('service') == self.masternode.ip + ':' + self.masternode.port) or \
                           (protx.get('collateralHash') == self.masternode.collateralTx and
                            str(protx.get('collateralIndex', '')) == self.masternode.collateralTxIndex):
                            found_protx = protx
                            break
            except Exception as e:
                pass

            if found_protx:
                if self.masternode.dmn_tx_hash == protx.get('proTxHash'):
                    WndUtils.infoMsg('You have te correct DMN TX hash in the masternode configuration.')
                else:
                    self.edtDMNTxHash.setText(protx.get('proTxHash'))
                    self.masternode.dmn_tx_hash = protx.get('proTxHash')
                    self.set_modified()
            else:
                WndUtils.warnMsg('Couldn\'t find this masternode in the list of registered deterministic masternodes.')
            self.set_modified()
 def my_excepthook(type, value, tback):
     print('=========================')
     traceback.print_tb(tback)
     traceback.print_stack()
     sys.__excepthook__(type, value, tback)
     logging.exception('Exception occurred')
     WndUtils.errorMsg(str(value))
Beispiel #3
0
 def get_operator_key_to_display(self) -> str:
     ret = ''
     if self.masternode:
         if self.edit_mode:
             if self.masternode.dmn_operator_key_type == InputKeyType.PRIVATE:
                 ret = self.masternode.dmn_operator_private_key
             else:
                 ret = self.masternode.dmn_operator_public_key
         else:
             try:
                 if self.masternode.dmn_operator_key_type == InputKeyType.PRIVATE:
                     if self.act_view_as_operator_private_key.isChecked():
                         ret = self.masternode.dmn_operator_private_key
                     elif self.act_view_as_operator_public_key.isChecked():
                         ret = self.masternode.get_dmn_operator_pubkey()
                     else:
                         ret = '???'
                 else:
                     if self.act_view_as_operator_public_key.isChecked():
                         ret = self.masternode.dmn_operator_public_key
                     else:
                         ret = '???'
             except Exception as e:
                 msg = str(e)
                 if not msg:
                     msg = 'Key conversion error.'
                 WndUtils.errorMsg(msg)
     return ret
Beispiel #4
0
 def on_btnEnter_clicked(self):
     text = self.edtWord.text()
     if not text:
         WndUtils.errorMsg('Word cannot be empty.')
     elif text not in self.wordlist:
         WndUtils.errorMsg('Word is not in the allowed wordlist.')
     else:
         self.accept()
Beispiel #5
0
 def my_excepthook(type, value, tback):
     print('=========================')
     traceback.print_tb(tback)
     for fh in logging.RootLogger.root.handlers:
         if isinstance(fh, logging.FileHandler):
             traceback.print_exception(type, value, tback, file=fh.stream)
             fh.flush()
     WndUtils.errorMsg(str(value))
    def connect(self):
        import paramiko
        self.ssh = paramiko.SSHClient()
        self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        password = None
        pass_message = None

        while True:
            try:
                self.ssh.connect(self.host,
                                 port=int(self.port),
                                 username=self.username,
                                 password=password)
                self.connected = True
                if password:
                    SshPassCache.save_password(self.username, self.host,
                                               password)
                break
            except PasswordRequiredException as e:
                # private key with password protection is used; ask user for password
                pass_message = "Enter passphrase for <b>private key</b> or password for %s" % \
                               (self.username + '@' + self.host)
                while True:
                    password = SshPassCache.get_password(self.username,
                                                         self.host,
                                                         message=pass_message)
                    if password:
                        break

            except AuthenticationException as e:
                # This exception will be raised in the following cases:
                #  1. a private key with password protectection is used but the user enters incorrect password
                #  2. a private key exists but user's public key is not added to the server's allowed keys
                #  3. normal login to server is performed but the user enters bad password
                # So, in the first case, the second query for password will ask for normal password to server, not
                #  for a private key.

                WndUtils.errorMsg(message='Incorrect password, try again...')

                while True:
                    password = SshPassCache.get_password(self.username,
                                                         self.host,
                                                         message=pass_message)
                    if password:
                        break

            except SSHException as e:
                if e.args and e.args[
                        0] == 'No authentication methods available':
                    while True:
                        password = SshPassCache.get_password(
                            self.username, self.host)
                        if password:
                            break
                else:
                    raise
            except Exception as e:
                raise
Beispiel #7
0
 def rpc_command(self, command: str, *args):
     if self.main_dlg.dashd_intf:
         ret = self.main_dlg.dashd_intf.rpc_call(command, *args)
         try:
             if isinstance(ret, str):
                 ret = json.loads(ret)
             ret = json.dumps(ret, default=EncodeDecimal, indent = 4, separators = (',', ': '))
         except Exception:
             pass
         self.message(ret, style="white-space: pre-wrap;")
         return True
     else:
         WndUtils.errorMsg('Not connected to a GINcoin node')
         return False
 def my_excepthook(type, value, tback):
     print('=========================')
     traceback.print_tb(tback)
     for fh in logging.RootLogger.root.handlers:
         if isinstance(fh, logging.FileHandler):
             traceback.print_exception(type, value, tback, file=fh.stream)
             fh.flush()
     msg = str(value)
     if not msg:
         try:
             msg = 'An unhandled exception accurred: ' + value.__class__.__name__ + '.'
         except:
             msg = 'An unhandled exception accurred.'
     WndUtils.errorMsg(msg)
Beispiel #9
0
    def exec_columns_dialog(self, parent_window: QWidget):
        try:
            cols = []
            for col in sorted(self._columns, key=lambda x: x.visual_index):
                cols.append([col.caption, col.visible, col])

            ui = ColumnsConfigDlg(parent_window, columns=cols)
            ret = ui.exec_()
            if ret > 0:
                for visual_idx, (_, visible, col) in enumerate(cols):
                    col.visual_index = visual_idx
                    col.visible = visible
                self._apply_columns_to_ui()
        except Exception as e:
            logging.exception('Exception while configuring table view columns')
            WndUtils.errorMsg(str(e))
Beispiel #10
0
    def get_owner_key_to_display(self) -> str:
        ret = ''
        if self.masternode:
            if self.edit_mode:
                if self.masternode.dmn_owner_key_type == InputKeyType.PRIVATE:
                    ret = self.masternode.dmn_owner_private_key
                else:
                    ret = self.masternode.dmn_owner_address
            else:
                try:
                    if self.masternode.dmn_owner_key_type == InputKeyType.PRIVATE:
                        if self.act_view_as_owner_private_key.isChecked():
                            ret = self.masternode.dmn_owner_private_key
                        elif self.act_view_as_owner_public_address.isChecked():
                            if self.masternode.dmn_owner_private_key:
                                ret = crown_utils.wif_privkey_to_address(self.masternode.dmn_owner_private_key,
                                                                        self.app_config.crown_network)
                        elif self.act_view_as_owner_public_key.isChecked():
                            if self.masternode.dmn_owner_private_key:
                                ret = crown_utils.wif_privkey_to_pubkey(self.masternode.dmn_owner_private_key)
                        elif self.act_view_as_owner_public_key_hash.isChecked():
                            if self.masternode.dmn_owner_private_key:
                                pubkey = crown_utils.wif_privkey_to_pubkey(self.masternode.dmn_owner_private_key)
                                pubkey_bin = bytes.fromhex(pubkey)
                                pub_hash = bitcoin.bin_hash160(pubkey_bin)
                                ret = pub_hash.hex()
                        else:
                            ret = '???'
                    else:
                        if self.act_view_as_owner_public_address.isChecked():
                            ret = self.masternode.dmn_owner_address
                        elif self.act_view_as_owner_public_key_hash.isChecked():
                            ret = self.masternode.get_dmn_owner_pubkey_hash()
                        else:
                            ret = '???'
                except Exception as e:
                    msg = str(e)
                    if not msg:
                        msg = 'Key conversion error.'
                    WndUtils.errorMsg(msg)

        return ret
    def init(self, app_path):
        """ Initialize configuration after openning the application. """
        self.app_path = app_path

        try:
            with open(os.path.join(app_path, 'version.txt')) as fptr:
                lines = fptr.read().splitlines()
                self.app_version = app_utils.extract_app_version(lines)
        except:
            pass

        parser = argparse.ArgumentParser()
        parser.add_argument('--config', help="Path to a configuration file", dest='config')
        parser.add_argument('--data-dir', help="Root directory for configuration file, cache and log dubdirs",
                            dest='data_dir')
        args = parser.parse_args()

        app_user_dir = ''
        if args.data_dir:
            if os.path.exists(args.data_dir):
                if os.path.isdir(args.data_dir):
                    app_user_dir = args.data_dir
                else:
                    WndUtils.errorMsg('--data-dir parameter doesn\'t point to a directory. Using the default '
                                      'data directory.')
            else:
                WndUtils.errorMsg('--data-dir parameter doesn\'t point to an existing directory. Using the default '
                                  'data directory.')

        if not app_user_dir:
            home_dir = expanduser('~')
            app_user_dir = os.path.join(home_dir, APP_NAME_SHORT)
            if not os.path.exists(app_user_dir):
                os.makedirs(app_user_dir)

        self.cache_dir = os.path.join(app_user_dir, 'cache')
        if not os.path.exists(self.cache_dir):
            os.makedirs(self.cache_dir)
        cache.init(self.cache_dir, self.app_version)
        self.app_last_version = cache.get_value('app_version', '', str)
        self.app_config_file_name = ''

        if args.config is not None:
            self.app_config_file_name = args.config
            if not os.path.exists(self.app_config_file_name):
                msg = 'Config file "%s" does not exist.' % self.app_config_file_name
                print(msg)
                raise Exception(msg)

        if not self.app_config_file_name:
            self.app_config_file_name = os.path.join(app_user_dir, 'config.ini')

        # setup logging
        self.log_dir = os.path.join(app_user_dir, 'logs')
        self.log_file = os.path.join(self.log_dir, 'dmt.log')
        if not os.path.exists(self.log_dir):
            os.makedirs(self.log_dir)

        self.log_level_str = 'INFO'
        log_exists = os.path.exists(self.log_file)
        handler = RotatingFileHandler(filename=self.log_file, mode='a', backupCount=30)
        logger = logging.getLogger()
        formatter = logging.Formatter(fmt='%(asctime)s %(levelname)s |%(threadName)s |%(filename)s |%(funcName)s '
                                          '|%(message)s', datefmt='%Y-%m-%d %H:%M:%S')
        handler.setFormatter(formatter)
        logger.addHandler(handler)
        logger.setLevel(self.log_level_str)
        if log_exists:
            handler.doRollover()
        logging.info('App started')

        # database (SQLITE) cache for caching bigger datasets:
        self.db_cache_file_name = os.path.join(self.cache_dir, 'dmt_cache.db')

        try:
            self.db_intf = DBCache(self.db_cache_file_name)
        except Exception as e:
            logging.exception('SQLite initialization error')

        # directory for configuration backups:
        self.cfg_backup_dir = os.path.join(app_user_dir, 'backup')
        if not os.path.exists(self.cfg_backup_dir):
            os.makedirs(self.cfg_backup_dir)

        try:
            # read configuration from a file
            self.read_from_file()
        except:
            pass

        if not self.app_last_version or \
           app_utils.version_str_to_number(self.app_last_version) < app_utils.version_str_to_number(self.app_version):
            cache.save_data()
        self.initialized = True
Beispiel #12
0
    def send_upd_tx(self):
        # verify the owner key used in the configuration
        if self.masternode.dmn_owner_key_type == InputKeyType.PRIVATE and self.masternode.dmn_owner_private_key:
            owner_address = wif_privkey_to_address(
                self.masternode.dmn_owner_private_key,
                self.app_config.crown_network)
            if owner_address != self.dmn_owner_address:
                raise Exception(
                    'Inconsistency of the owner key between the app configuration and the data '
                    'on the Crown network.')
        else:
            raise Exception(
                'To use this feature, you need to have the owner private key in your masternode '
                'configuration.')

        try:
            funding_address = ''
            params = [
                'update_registrar', self.dmn_protx_hash,
                self.dmn_new_operator_pubkey, self.dmn_new_voting_address,
                self.dmn_new_payout_address, funding_address
            ]

            try:
                upd_reg_support = self.crownd_intf.checkfeaturesupport(
                    'protx_update_registrar', self.app_config.app_version)
                if not upd_reg_support.get('enabled'):
                    if upd_reg_support.get('message'):
                        raise Exception(upd_reg_support.get('message'))
                    else:
                        raise Exception(
                            'The \'protx_update_registrar\' function is not supported by the RPC node '
                            'you are connected to.')
                public_proxy_node = True

                active = self.app_config.feature_update_registrar_automatic.get_value(
                )
                if not active:
                    msg = self.app_config.feature_update_registrar_automatic.get_message(
                    )
                    if not msg:
                        msg = 'The functionality of the automatic execution of the update_registrar command on the ' \
                              '"public" RPC nodes is inactive. Use the manual method or contact the program author ' \
                              'for details.'
                    raise Exception(msg)

            except JSONRPCException as e:
                public_proxy_node = False

            if not public_proxy_node:
                try:
                    # find an address to be used as the source of the transaction fees
                    min_fee = round(1024 * FEE_DUFF_PER_BYTE / 1e8, 8)
                    balances = self.crownd_intf.listaddressbalances(min_fee)
                    bal_list = []
                    for addr in balances:
                        bal_list.append({
                            'address': addr,
                            'amount': balances[addr]
                        })
                    bal_list.sort(key=lambda x: x['amount'])
                    if not bal_list:
                        raise Exception(
                            "No address can be found in the node's wallet with sufficient funds to "
                            "cover the transaction fees.")
                    params[5] = bal_list[0]['address']
                except JSONRPCException as e:
                    logging.warning(
                        "Couldn't list the node address balances. We assume you are using a "
                        "public RPC node and the funding address for the transaction fee will "
                        "be estimated during the `update_registrar` call")
            else:
                params.append(self.masternode.dmn_owner_private_key)

            upd_tx_hash = self.crownd_intf.rpc_call(True, False, 'protx',
                                                    *params)

            if upd_tx_hash:
                logging.info(
                    'update_registrar successfully executed, tx hash: ' +
                    upd_tx_hash)
                changed = False
                if self.dmn_new_voting_address != self.dmn_prev_voting_address:
                    changed = self.masternode.dmn_voting_key_type != self.dmn_voting_key_type
                    self.masternode.dmn_voting_key_type = self.dmn_voting_key_type
                    if self.dmn_voting_key_type == InputKeyType.PRIVATE:
                        changed = changed or self.masternode.dmn_voting_private_key != self.dmn_new_voting_privkey
                        self.masternode.dmn_voting_private_key = self.dmn_new_voting_privkey
                    else:
                        changed = changed or self.masternode.dmn_voting_address != self.dmn_new_voting_address
                        self.masternode.dmn_voting_address = self.dmn_new_voting_address

                if self.dmn_new_operator_pubkey != self.dmn_prev_operator_pubkey:
                    changed = changed or self.masternode.dmn_operator_key_type != self.dmn_operator_key_type
                    self.masternode.dmn_operator_key_type = self.dmn_operator_key_type
                    if self.dmn_operator_key_type == InputKeyType.PRIVATE:
                        changed = changed or self.masternode.dmn_operator_private_key != self.dmn_new_operator_privkey
                        self.masternode.dmn_operator_private_key = self.dmn_new_operator_privkey
                    else:
                        changed = changed or self.masternode.dmn_operator_public_key != self.dmn_new_operator_pubkey
                        self.masternode.dmn_operator_public_key = self.dmn_new_operator_pubkey

                if self.on_upd_success_callback:
                    self.on_upd_success_callback(self.masternode)

                self.btnSendUpdateTx.setDisabled(True)
                self.edtPayoutAddress.setReadOnly(True)
                self.edtOperatorKey.setReadOnly(True)
                self.edtVotingKey.setReadOnly(True)
                self.btnGenerateOperatorKey.setDisabled(True)
                self.btnGenerateVotingKey.setDisabled(True)
                self.btnClose.show()

                url = self.app_config.get_block_explorer_tx()
                if url:
                    url = url.replace('%TXID%', upd_tx_hash)
                    upd_tx_hash = f'<a href="{url}">{upd_tx_hash}</a>'

                msg = 'The update_registrar transaction has been successfully sent. ' \
                     f'Tx hash: {upd_tx_hash}. <br><br>' \
                     f'The new values ​​will be visible on the network after the transaction is confirmed, i.e. in ' \
                     f'about 2.5 minutes.'

                if changed:
                    msg += '<br><br>The app configuration has been updated accordingly.'

                WndUtils.infoMsg(msg)
        except Exception as e:
            if str(e).find('protx-dup') >= 0:
                WndUtils.errorMsg(
                    'The previous protx transaction has not been confirmed yet. Wait until it is '
                    'confirmed before sending a new transaction.')
            else:
                logging.error(
                    'Exception occurred while sending protx update_registrar.')
                WndUtils.errorMsg(str(e))
    def send_revoke_tx(self):
        try:
            funding_address = ''

            params = [
                'revoke', self.dmn_protx_hash,
                self.masternode.dmn_operator_private_key,
                self.revocation_reason, funding_address
            ]

            try:
                revoke_support = self.polisd_intf.checkfeaturesupport(
                    'protx_revoke', self.app_config.app_version)
                if not revoke_support.get('enabled'):
                    if revoke_support.get('message'):
                        raise Exception(revoke_support.get('message'))
                    else:
                        raise Exception(
                            'The \'protx_revoke\' function is not supported by the RPC node '
                            'you are connected to.')
                public_proxy_node = True

                active = self.app_config.feature_revoke_operator_automatic.get_value(
                )
                if not active:
                    msg = self.app_config.feature_revoke_operator_automatic.get_message(
                    )
                    if not msg:
                        msg = 'The functionality of the automatic execution of the revoke command on the ' \
                              '"public" RPC nodes is inactive. Use the manual method or contact the program author ' \
                              'for details.'
                    raise Exception(msg)

            except JSONRPCException as e:
                public_proxy_node = False

            if not public_proxy_node:
                try:
                    # find an address to be used as the source of the transaction fees
                    min_fee = round(1024 * FEE_DUFF_PER_BYTE / 1e8, 8)
                    balances = self.polisd_intf.listaddressbalances(min_fee)
                    bal_list = []
                    for addr in balances:
                        bal_list.append({
                            'address': addr,
                            'amount': balances[addr]
                        })
                    bal_list.sort(key=lambda x: x['amount'])
                    if not bal_list:
                        raise Exception(
                            "No address can be found in the node's wallet with sufficient funds to "
                            "cover the transaction fees.")
                    params[5] = bal_list[0]['address']
                except JSONRPCException as e:
                    logging.warning(
                        "Couldn't list the node address balances. We assume you are using a "
                        "public RPC node and the funding address for the transaction fee will "
                        "be estimated during the `update_registrar` call")

            upd_tx_hash = self.polisd_intf.rpc_call(True, False, 'protx',
                                                    *params)

            if upd_tx_hash:
                logging.info('revoke successfully executed, tx hash: ' +
                             upd_tx_hash)

                self.btnSendRevokeTx.setDisabled(True)
                self.cboReason.setDisabled(True)
                self.btnClose.show()

                url = self.app_config.get_block_explorer_tx()
                if url:
                    url = url.replace('%TXID%', upd_tx_hash)
                    upd_tx_hash = f'<a href="{url}">{upd_tx_hash}</a>'

                msg = 'The revoke transaction has been successfully sent. ' \
                     f'Tx hash: {upd_tx_hash}. <br><br>' \
                     f'The new values ​​will be visible on the network after the transaction is confirmed, i.e. in ' \
                     f'about 2 minutes.'

                WndUtils.infoMsg(msg)

        except Exception as e:
            if str(e).find('protx-dup') >= 0:
                WndUtils.errorMsg(
                    'The previous protx transaction has not been confirmed yet. Wait until it is '
                    'confirmed before sending a new transaction.')
            else:
                logging.error(
                    'Exception occurred while sending protx revoke: ' + str(e))
                WndUtils.errorMsg(str(e))
 def on_btn_main_accepted(self):
     if self.device_selected_index is None:
         WndUtils.errorMsg('No item selected.')
     else:
         self.accept()
Beispiel #15
0
    def send_upd_tx(self):
        try:
            funding_address = ''
            if self.dmn_new_ip:
                dmn_new_ip_port = self.dmn_new_ip + ':' + str(
                    self.dmn_new_port)
            else:
                dmn_new_ip_port = '"0"'

            params = [
                'update_service', self.dmn_protx_hash, dmn_new_ip_port,
                self.masternode.dmn_operator_private_key,
                self.dmn_new_operator_payout_address, funding_address
            ]

            try:
                upd_service_support = self.fixd_intf.checkfeaturesupport(
                    'protx_update_service', self.app_config.app_version)
                if not upd_service_support.get('enabled'):
                    if upd_service_support.get('message'):
                        raise Exception(upd_service_support.get('message'))
                    else:
                        raise Exception(
                            'The \'protx_update_service\' function is not supported by the RPC node '
                            'you are connected to.')
                public_proxy_node = True

                active = self.app_config.feature_update_service_automatic.get_value(
                )
                if not active:
                    msg = self.app_config.feature_update_service_automatic.get_message(
                    )
                    if not msg:
                        msg = 'The functionality of the automatic execution of the update_service command on the ' \
                              '"public" RPC nodes is inactive. Use the manual method or contact the program author ' \
                              'for details.'
                    raise Exception(msg)

            except JSONRPCException as e:
                public_proxy_node = False

            if not public_proxy_node:
                try:
                    # find an address to be used as the source of the transaction fees
                    min_fee = round(1024 * FEE_DUFF_PER_BYTE / 1e8, 8)
                    balances = self.fixd_intf.listaddressbalances(min_fee)
                    bal_list = []
                    for addr in balances:
                        bal_list.append({
                            'address': addr,
                            'amount': balances[addr]
                        })
                    bal_list.sort(key=lambda x: x['amount'])
                    if not bal_list:
                        raise Exception(
                            "No address can be found in the node's wallet with sufficient funds to "
                            "cover the transaction fees.")
                    params[5] = bal_list[0]['address']
                except JSONRPCException as e:
                    logging.warning(
                        "Couldn't list the node address balances. We assume you are using a "
                        "public RPC node and the funding address for the transaction fee will "
                        "be estimated during the `update_registrar` call")

            upd_tx_hash = self.fixd_intf.rpc_call(True, False, 'protx',
                                                  *params)

            if upd_tx_hash:
                logging.info(
                    'update_service successfully executed, tx hash: ' +
                    upd_tx_hash)

                self.btnSendUpdateTx.setDisabled(True)
                self.edtOperatorPayoutAddress.setReadOnly(True)
                self.edtIP.setReadOnly(True)
                self.edtPort.setReadOnly(True)
                self.btnClose.show()

                url = self.app_config.get_block_explorer_tx()
                if url:
                    url = url.replace('%TXID%', upd_tx_hash)
                    upd_tx_hash = f'<a href="{url}">{upd_tx_hash}</a>'

                msg = 'The update_service transaction has been successfully sent. ' \
                     f'Tx hash: {upd_tx_hash}. <br><br>' \
                     f'The new values ​​will be visible on the network after the transaction is confirmed, i.e. in ' \
                     f'about 2.5 minutes.'

                if bool(dmn_new_ip_port
                        ) and self.dmn_prev_ip_port != dmn_new_ip_port:
                    msg += '\n\nYou have changed the masternode IP/port. Do you want to automatically update ' \
                           'this in the app configuration?'

                    if self.queryDlg(
                            msg,
                            buttons=QMessageBox.Yes | QMessageBox.No,
                            default_button=QMessageBox.Yes,
                            icon=QMessageBox.Information) == QMessageBox.Yes:
                        self.masternode.ip = self.dmn_new_ip
                        self.masternode.port = str(self.dmn_new_port)

                        if self.on_mn_config_updated_callback:
                            self.on_mn_config_updated_callback(self.masternode)
                else:
                    WndUtils.infoMsg(msg)

        except Exception as e:
            if str(e).find('protx-dup') >= 0:
                WndUtils.errorMsg(
                    'The previous protx transaction has not been confirmed yet. Wait until it is '
                    'confirmed before sending a new transaction.')
            else:
                logging.error(
                    'Exception occurred while sending protx update_service: ' +
                    str(e))
                WndUtils.errorMsg(str(e))
    def get_collateral_tx_address_thread(self, ctrl: CtrlObject):
        txes_cnt = 0
        msg = ''
        break_scanning = False
        ctrl.dlg_config_fun(dlg_title="Validating collateral transaction.",
                            show_progress_bar=False)
        ctrl.display_msg_fun('Verifying collateral transaction...')

        def check_break_scanning():
            nonlocal break_scanning
            if self.finishing or break_scanning:
                # stop the scanning process if the dialog finishes or the address/bip32path has been found
                raise BreakFetchTransactionsException()

        def fetch_txes_feeback(tx_cnt: int):
            nonlocal msg, txes_cnt
            txes_cnt += tx_cnt
            ctrl.display_msg_fun(msg + '<br><br>' +
                                 'Number of transactions fetched so far: ' +
                                 str(txes_cnt))

        def on_msg_link_activated(link: str):
            nonlocal break_scanning
            if link == 'break':
                break_scanning = True

        try:
            tx = self.dashd_intf.getrawtransaction(self.dmn_collateral_tx,
                                                   1,
                                                   skip_cache=True)
        except Exception as e:
            raise Exception(
                'Cannot get the collateral transaction due to the following errror: '
                + str(e))

        vouts = tx.get('vout')
        if vouts:
            if self.dmn_collateral_tx_index < len(vouts):
                vout = vouts[self.dmn_collateral_tx_index]
                spk = vout.get('scriptPubKey')
                if not spk:
                    raise Exception(
                        f'The collateral transaction ({self.dmn_collateral_tx}) output '
                        f'({self.dmn_collateral_tx_index}) doesn\'t have value in the scriptPubKey '
                        f'field.')
                ads = spk.get('addresses')
                if not ads or len(ads) < 0:
                    raise Exception(
                        'The collateral transaction output doesn\'t have the Dash address assigned.'
                    )
                self.dmn_collateral_tx_address = ads[0]
            else:
                raise Exception(
                    f'Transaction {self.dmn_collateral_tx} doesn\'t have output with index: '
                    f'{self.dmn_collateral_tx_index}')
        else:
            raise Exception('Invalid collateral transaction')

        ctrl.display_msg_fun(
            'Verifying the collateral transaction address on your hardware wallet.'
        )
        if not self.main_dlg.connect_hardware_wallet():
            return False

        if self.dmn_collateral_tx_address_path:
            addr = hw_intf.get_address(self.main_dlg.hw_session,
                                       self.dmn_collateral_tx_address_path)
            msg = ''
            if addr != self.dmn_collateral_tx_address:
                log.warning(
                    f'The address returned by the hardware wallet ({addr}) for the BIP32 path '
                    f'{self.dmn_collateral_tx_address_path} differs from the address stored the mn configuration '
                    f'(self.dmn_collateral_tx_address). Need to scan wallet for a correct BIP32 path.'
                )

                msg = '<span style="color:red">The BIP32 path of the collateral address from your mn config is incorret.<br></span>' \
                      f'Trying to find the BIP32 path of the address {self.dmn_collateral_tx_address} in your wallet.' \
                      f'<br>This may take a while (<a href="break">break</a>)...'
                self.dmn_collateral_tx_address_path = ''
        else:
            msg = 'Looking for a BIP32 path of the Dash address related to the masternode collateral.<br>' \
                  'This may take a while (<a href="break">break</a>)....'

        if not self.dmn_collateral_tx_address_path and not self.finishing:
            lbl = ctrl.get_msg_label_control()
            if lbl:

                def set():
                    lbl.setOpenExternalLinks(False)
                    lbl.setTextInteractionFlags(lbl.textInteractionFlags()
                                                & ~Qt.TextSelectableByMouse)
                    lbl.linkActivated.connect(on_msg_link_activated)
                    lbl.repaint()

                WndUtils.call_in_main_thread(set)

            ctrl.display_msg_fun(msg)

            # fetch the transactions that involved the addresses stored in the wallet - during this
            # all the used addresses are revealed
            addr = self.bip44_wallet.scan_wallet_for_address(
                self.dmn_collateral_tx_address, check_break_scanning,
                fetch_txes_feeback)
            if not addr:
                if not break_scanning:
                    WndUtils.errorMsg(
                        f'Couldn\'t find a BIP32 path of the collateral address ({self.dmn_collateral_tx_address}).'
                    )
                return False
            else:
                self.dmn_collateral_tx_address_path = addr.bip32_path

        return True
    def proregtx_automatic_thread(self, ctrl):
        log.debug('Starting proregtx_prepare_thread')

        def set_text(widget, text: str):
            def call(widget, text):
                widget.setText(text)
                widget.repaint()
                widget.setVisible(True)

            WndUtils.call_in_main_thread(call, widget, text)

        def finished_with_success():
            def call():
                self.next_step()

            WndUtils.call_in_main_thread(call)

        try:
            # preparing protx message
            try:
                funding_address = ''
                if not self.dashd_intf.is_current_connection_public():
                    try:
                        # find an address to be used as the source of the transaction fees
                        min_fee = round(1024 * FEE_DUFF_PER_BYTE / 1e8, 8)
                        balances = self.dashd_intf.listaddressbalances(min_fee)
                        bal_list = []
                        for addr in balances:
                            bal_list.append({
                                'address': addr,
                                'amount': balances[addr]
                            })
                        bal_list.sort(key=lambda x: x['amount'])
                        if not bal_list:
                            raise Exception(
                                "No address can be found in the node's wallet with sufficient funds to "
                                "cover the transaction fees.")
                        funding_address = bal_list[0]['address']
                        self.dashd_intf.disable_conf_switching()
                    except JSONRPCException as e:
                        log.info(
                            "Couldn't list the node address balances. We assume you are using a public RPC node and "
                            "the funding address for the transaction fees will be estimated during the "
                            "`register_prepare` call")

                set_text(
                    self.lblProtxTransaction1,
                    '<b>1. Preparing a ProRegTx transaction on a remote node...</b>'
                )
                params = [
                    'register_prepare', self.dmn_collateral_tx,
                    self.dmn_collateral_tx_index, self.dmn_ip + ':' +
                    str(self.dmn_tcp_port) if self.dmn_ip else '0',
                    self.dmn_owner_privkey, self.dmn_operator_pubkey,
                    self.dmn_voting_address,
                    str(round(self.dmn_operator_reward,
                              2)), self.dmn_owner_payout_addr
                ]
                if funding_address:
                    params.append(funding_address)
                call_ret = self.dashd_intf.protx(*params)

                call_ret_str = json.dumps(call_ret, default=EncodeDecimal)
                msg_to_sign = call_ret.get('signMessage', '')
                protx_tx = call_ret.get('tx')

                log.debug('register_prepare returned: ' + call_ret_str)
                set_text(
                    self.lblProtxTransaction1,
                    '<b>1. Preparing a ProRegTx transaction on a remote node.</b> <span style="color:green">'
                    'Success.</span>')
            except Exception as e:
                set_text(
                    self.lblProtxTransaction1,
                    '<b>1. Preparing a ProRegTx transaction on a remote node.</b> <span style="color:red">Failed '
                    f'with the following error: {str(e)}</span>')
                return

            # diable config switching since the protx transaction has input associated with the specific node/wallet
            self.dashd_intf.disable_conf_switching()

            set_text(
                self.lblProtxTransaction2,
                '<b>Message to be signed:</b><br><code>' + msg_to_sign +
                '</code>')

            # signing message:
            set_text(self.lblProtxTransaction3,
                     '<b>2. Signing message with hardware wallet...</b>')
            try:
                payload_sig_str = self.sign_protx_message_with_hw(msg_to_sign)

                set_text(
                    self.lblProtxTransaction3,
                    '<b>2. Signing message with hardware wallet.</b> '
                    '<span style="color:green">Success.</span>')
            except HardwareWalletCancelException:
                set_text(
                    self.lblProtxTransaction3,
                    '<b>2. Signing message with hardware wallet.</b> <span style="color:red">Cancelled.</span>'
                )
                return
            except Exception as e:
                log.exception('Signature failed.')
                set_text(
                    self.lblProtxTransaction3,
                    '<b>2. Signing message with hardware wallet.</b> <span style="color:red">Failed with the '
                    f'following error: {str(e)}.</span>')
                return

            # submitting signed transaction
            set_text(
                self.lblProtxTransaction4,
                '<b>3. Submitting the signed protx transaction to the remote node...</b>'
            )
            try:
                self.dmn_reg_tx_hash = self.dashd_intf.protx(
                    'register_submit', protx_tx, payload_sig_str)
                # self.dmn_reg_tx_hash = 'dfb396d84373b305f7186984a969f92469d66c58b02fb3269a2ac8b67247dfe3'
                log.debug('protx register_submit returned: ' +
                          str(self.dmn_reg_tx_hash))
                set_text(
                    self.lblProtxTransaction4,
                    '<b>3. Submitting the signed protx transaction to the remote node.</b> <span style="'
                    'color:green">Success.</span>')
                finished_with_success()
            except Exception as e:
                log.exception('protx register_submit failed')
                set_text(
                    self.lblProtxTransaction4,
                    '<b>3. Submitting the signed protx transaction to the remote node.</b> '
                    f'<span style="color:red">Failed with the following error: {str(e)}</span>'
                )

        except Exception as e:
            log.exception('Exception occurred')
            WndUtils.errorMsg(str(e))

        finally:
            self.dashd_intf.enable_conf_switching()