def on_btnShowCollateralPathAddress_clicked(self, checked): if self.masternode.collateralBip32Path: try: if self.main_dlg.connect_hardware_wallet(): hw_session = self.main_dlg.hw_session addr = hw_intf.get_address( hw_session, self.masternode.collateralBip32Path, True, f'Displaying address for the BIP32 path <b>{self.masternode.collateralBip32Path}</b>.' f'<br>Click the confirmation button on your device.') except CancelException: pass
def on_btnBip32PathToAddress_clicked(self, checked): if self.masternode.collateralBip32Path: if self.main_dlg.connect_hardware_wallet(): try: hw_session = self.main_dlg.hw_session addr = hw_intf.get_address(hw_session, self.masternode.collateralBip32Path, show_display=True) if addr: self.masternode.collateralAddress = addr.strip() self.edtCollateralAddress.setText(addr.strip()) self.set_modified() self.update_ui_controls_state() except CancelException: pass
def on_btnLoadTransactions_clicked(self): if not self.main_ui.connectHardwareWallet(): return try: path = self.edtSourceBip32Path.text() if path: # check if address mathes address read from bip32 path try: address = get_address(self.main_ui, path) if not address: self.errorMsg( "Couldn't read address for the specified BIP32 path: %s." % path) self.utxos_source = [(address, path)] self.load_utxos() if len(self.utxos) == 0: self.setMessage( '<span style="color:red">There is no Unspent Transaction Outputs ' '<i>(UTXOs)</i> for this address: %s.</span>' % address) else: self.setMessage( 'Unspent Transaction Outputs <i>(UTXOs)</i> for: %s.</span>' % address) except HardwareWalletPinException as e: raise except Exception as e: logging.exception( 'Exception while reading address for BIP32 path (%s).' % path) self.errorMsg('Invalid BIP32 path.') self.edtSourceBip32Path.setFocus() else: self.errorMsg('Enter the BIP32 path.') self.edtSourceBip32Path.setFocus() except Exception: logging.exception( 'Exception while loading unspent transaction outputs.') raise
def on_btnSend_clicked(self): """ Sends funds to GoByte address specified by user. """ amount, utxos = self.get_selected_utxos() if len(utxos): try: if not self.main_ui.connect_hardware_wallet(): return except HardwareWalletCancelException: return bip32_to_address = {} # for saving addresses read from HW by BIP32 path total_satoshis = 0 coinbase_locked_exist = False # verify if: # - utxo is the masternode collateral transation # - the utxo GoByte (signing) address matches the hardware wallet address for a given path for utxo_idx, utxo in enumerate(utxos): total_satoshis += utxo['satoshis'] logging.info(f'UTXO satosis: {utxo["satoshis"]}') if utxo['collateral']: if self.queryDlg( "Warning: you are going to transfer masternode's collateral (1000 GoByte) transaction " "output. Proceeding will result in broken masternode.\n\n" "Do you really want to continue?", buttons=QMessageBox.Yes | QMessageBox.Cancel, default_button=QMessageBox.Cancel, icon=QMessageBox.Warning) == QMessageBox.Cancel: return if utxo['coinbase_locked']: coinbase_locked_exist = True bip32_path = utxo.get('bip32_path', None) if not bip32_path: self.errorMsg('No BIP32 path for UTXO: %s. Cannot continue.' % utxo['txid']) return addr_hw = bip32_to_address.get(bip32_path, None) if not addr_hw: addr_hw = get_address(self.main_ui.hw_session, bip32_path) bip32_to_address[bip32_path] = addr_hw if addr_hw != utxo['address']: self.errorMsg("<html style=\"font-weight:normal\">GoByte address inconsistency between UTXO (%d) and HW path: %s.<br><br>" "<b>HW address</b>: %s<br>" "<b>UTXO address</b>: %s<br><br>" "Cannot continue.</html>" % (utxo_idx+1, bip32_path, addr_hw, utxo['address'])) return if coinbase_locked_exist: if self.queryDlg( "Warning: you have selected at least one coinbase transaction without the required number of " "confirmations (100). Your transaction will probably be rejected by the network.\n\n" "Do you really want to continue?", buttons=QMessageBox.Yes | QMessageBox.Cancel, default_button=QMessageBox.Cancel, icon=QMessageBox.Warning) == QMessageBox.Cancel: return try: dest_data = self.wdg_dest_adresses.get_tx_destination_data() if dest_data: total_satoshis_actual = 0 for dd in dest_data: total_satoshis_actual += dd[1] logging.info(f'dest amount: {dd[1]}') fee = self.wdg_dest_adresses.get_tx_fee() use_is = self.wdg_dest_adresses.get_use_instant_send() logging.info(f'fee: {fee}') if total_satoshis != total_satoshis_actual + fee: logging.warning(f'total_satoshis ({total_satoshis}) != total_satoshis_real ' f'({total_satoshis_actual}) + fee ({fee})') logging.warning(f'total_satoshis_real + fee: {total_satoshis_actual + fee}') if abs(total_satoshis - total_satoshis_actual - fee) > 10: raise Exception('Data validation failure') try: serialized_tx, amount_to_send = prepare_transfer_tx( self.main_ui.hw_session, utxos, dest_data, fee, self.rawtransactions) except HardwareWalletCancelException: # user cancelled the operations hw_intf.cancel_hw_operation(self.main_ui.hw_session.hw_client) return except Exception: logging.exception('Exception when preparing the transaction.') raise tx_hex = serialized_tx.hex() logging.info('Raw signed transaction: ' + tx_hex) if len(tx_hex) > 90000: self.errorMsg("Transaction's length exceeds 90000 bytes. Select less UTXOs and try again.") else: tx_dlg = TransactionDlg(self, self.main_ui.config, self.gobyted_intf, tx_hex, use_is) if tx_dlg.exec_(): amount, sel_utxos = self.get_selected_utxos() if sel_utxos: # mark and uncheck all spent utxox for utxo_idx, utxo in enumerate(sel_utxos): utxo['spent_date'] = time.time() self.table_model.beginResetModel() self.table_model.endResetModel() except Exception as e: logging.exception('Unknown error occurred.') self.errorMsg(str(e)) else: self.errorMsg('No UTXO to send.')
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 on_btnSend_clicked(self): """ Sends funds to Dash address specified by user. """ utxos = self.table_model.getSelectedUtxos() if len(utxos): address = self.edtDestAddress.text() if address: if not self.main_ui.connectHardwareWallet(): return bip32_to_address = { } # for saving addresses read from HW by BIP32 path # check if user selected masternode collateral transaction; if so display warning # also check if UTXO dash address matches address of BIP32 path in HW for utxo_idx, utxo in enumerate(utxos): if utxo['collateral']: if self.queryDlg( "Warning: you are going to transfer masternode's collateral (1000 Dash) transaction " "output. Proceeding will result in broken masternode.\n\n" "Do you really want to continue?", buttons=QMessageBox.Yes | QMessageBox.Cancel, default_button=QMessageBox.Cancel, icon=QMessageBox.Warning ) == QMessageBox.Cancel: return bip32_path = utxo.get('bip32_path', None) if not bip32_path: self.errorMsg( 'No BIP32 path for UTXO: %s. Cannot continue.' % utxo['txid']) return addr_hw = bip32_to_address.get(bip32_path, None) if not addr_hw: addr_hw = get_address(self.main_ui, bip32_path) bip32_to_address[bip32_path] = addr_hw if addr_hw != utxo['address']: self.errorMsg( "Dash address inconsistency between UTXO (%d) and a HW's path: %s.\n\n" "<b>HW address</b>: %s\n" "<b>UTXO address</b>: %s\n\n" "Cannot continue." % (utxo_idx + 1, bip32_path, addr_hw, utxo['address'])) return try: if self.dashd_intf.validateaddress(address).get( 'isvalid', False): fee = self.edtTxFee.value() * 1e8 try: serialized_tx, amount_to_send = prepare_transfer_tx( self.main_ui, utxos, address, fee, self.rawtransactions) except Exception: logging.exception( 'Exception when preparing the transaction.') raise tx_hex = serialized_tx.hex() logging.info('Raw signed transaction: ' + tx_hex) if len(tx_hex) > 90000: self.errorMsg( "Transaction's length exceeds 90000 bytes. Select less UTXOs and try again." ) else: if SCREENSHOT_MODE: self.warnMsg('Inside screenshot mode') if self.queryDlg( 'Broadcast signed transaction?\n\n' '<b>Destination address</b>: %s\n' '<b>Amount to send</b>: %s Dash\n' '<b>Fee</b>: %s Dash\n' '<b>Size</b>: %d bytes' % (address, str(round(amount_to_send / 1e8, 8)), str(round(fee / 1e8, 8)), len(tx_hex) / 2), buttons=QMessageBox.Yes | QMessageBox.Cancel, default_button=QMessageBox.Yes ) == QMessageBox.Yes: # decoded_tx = self.dashd_intf.decoderawtransaction(tx_hex) if SCREENSHOT_MODE: txid = '2195aecd5575e37fedf30e6a7ae317c6ba3650a004dc7e901210ac454f61a2e8' else: txid = self.dashd_intf.sendrawtransaction( tx_hex) if txid: block_explorer = self.main_ui.config.block_explorer_tx if block_explorer: url = block_explorer.replace( '%TXID%', txid) message = 'Transaction sent. TX ID: <a href="%s">%s</a>' % ( url, txid) else: message = 'Transaction sent. TX ID: %s' % txid logging.info('Sent transaction: ' + txid) self.infoMsg(message) else: self.errorMsg( 'Problem with sending transaction: no txid returned' ) else: self.errorMsg( 'Invalid destination Dash address (%s).' % address) except Exception as e: self.errorMsg(str(e)) else: self.errorMsg('Missing destination Dash address.') else: self.errorMsg('No UTXO to send.')