def give_error(self, message: Exception, clear_client: bool = False): self.logger.info(message) if not self.ux_busy: self.handler.show_error(message) else: self.ux_busy = False if clear_client: self.client = None raise UserFacingException(message)
def trezor_validate_op_return_output_and_get_data(output: TxOutput) -> bytes: validate_op_return_output(output) script = output.scriptpubkey if not (script[0] == opcodes.OP_RETURN and script[1] == len(script) - 2 and script[1] <= 75): raise UserFacingException( _("Only OP_RETURN scripts, with one constant push, are supported.") ) return script[2:]
def dbb_load_backup(self, show_msg=True): backups = self.hid_send_encrypt(b'{"backup":"list"}') if 'error' in backups: raise UserFacingException(backups['error']['message']) f = self.handler.query_choice(_("Choose a backup file:"), backups['backup']) if f is None: return False # user cancelled key = self.backup_password_dialog() if key is None: raise Exception('Canceled by user') key = self.stretch_key(key) if show_msg: self.handler.show_message(_("Loading backup...") + "\n\n" + _("To continue, touch the Digital Bitbox's light for 3 seconds.") + "\n\n" + _("To cancel, briefly touch the light or wait for the timeout.")) msg = ('{"seed":{"source": "backup", "key": "%s", "filename": "%s"}}' % (key, backups['backup'][f])).encode('utf8') hid_reply = self.hid_send_encrypt(msg) self.handler.finished() if 'error' in hid_reply: raise UserFacingException(hid_reply['error']['message']) return True
def sign_transaction(self, tx, password): if tx.is_complete(): return # previous transactions used as inputs prev_tx = {} for txin in tx.inputs(): tx_hash = txin.prevout.txid.hex() if txin.utxo is None: raise UserFacingException(_('Missing previous tx.')) prev_tx[tx_hash] = txin.utxo self.plugin.sign_transaction(self, tx, prev_tx)
def manipulate_keystore_dict_during_wizard_setup(self, d: dict): master_xpub = self.dev.master_xpub if master_xpub is not None: try: node = BIP32Node.from_xkey(master_xpub) except InvalidMasterKeyVersionBytes: raise UserFacingException( _('Invalid xpub magic. Make sure your {} device is set to the correct chain.' ).format(self.device) + ' ' + _('You might have to unplug and plug it in again.') ) from None d['ckcc_xpub'] = master_xpub
def scan_and_create_client_for_device( self, *, device_id: str, wizard: 'BaseWizard') -> 'HardwareClientBase': devmgr = self.device_manager() client = wizard.run_task_without_blocking_gui( task=partial(devmgr.client_by_id, device_id)) if client is None: raise UserFacingException( _('Failed to create a client for this device.') + '\n' + _('Make sure it is in the correct state.')) client.handler = self.create_handler(wizard) return client
def btc_multisig_config( self, coin, bip32_path: List[int], wallet: Multisig_Wallet, xtype: str, ): """ Set and get a multisig config with the current device and some other arbitrary xpubs. Registers it on the device if not already registered. xtype: 'p2wsh' | 'p2wsh-p2sh' """ assert xtype in ("p2wsh", "p2wsh-p2sh") if self.bitbox02_device is None: raise Exception( "Need to setup communication first before attempting any BitBox02 calls" ) account_keypath = bip32_path[:-2] xpubs = wallet.get_master_public_keys() our_xpub = self.get_xpub( bip32.convert_bip32_intpath_to_strpath(account_keypath), xtype) multisig_config = bitbox02.btc.BTCScriptConfig( multisig=bitbox02.btc.BTCScriptConfig.Multisig( threshold=wallet.m, xpubs=[util.parse_xpub(xpub) for xpub in xpubs], our_xpub_index=xpubs.index(our_xpub), script_type={ "p2wsh": bitbox02.btc.BTCScriptConfig.Multisig.P2WSH, "p2wsh-p2sh": bitbox02.btc.BTCScriptConfig.Multisig.P2WSH_P2SH, }[xtype])) is_registered = self.bitbox02_device.btc_is_script_config_registered( coin, multisig_config, account_keypath) if not is_registered: name = self.handler.name_multisig_account() try: self.bitbox02_device.btc_register_script_config( coin=coin, script_config=multisig_config, keypath=account_keypath, name=name, ) except bitbox02.DuplicateEntryException: raise except: raise UserFacingException( "Failed to register multisig\naccount configuration on BitBox02" ) return multisig_config
def get_xpub(self, bip32_path, xtype): assert xtype in ColdcardPlugin.SUPPORTED_XTYPES _logger.info('Derive xtype = %r' % xtype) xpub = self.dev.send_recv(CCProtocolPacker.get_xpub(bip32_path), timeout=5000) # TODO handle timeout? # change type of xpub to the requested type try: node = BIP32Node.from_xkey(xpub) except InvalidMasterKeyVersionBytes: raise UserFacingException( _('Invalid xpub magic. Make sure your {} device is set to the correct chain.' ).format(self.device)) from None if xtype != 'standard': xpub = node._replace(xtype=xtype).to_xpub() return xpub
def recover_or_erase_dialog(self): msg = _("The Digital Bitbox is already seeded. Choose an option:") + "\n" choices = [ (_("Create a wallet using the current seed")), (_("Load a wallet from the micro SD card (the current seed is overwritten)")), (_("Erase the Digital Bitbox")) ] reply = self.handler.query_choice(msg, choices) if reply is None: return # user cancelled if reply == 2: self.dbb_erase() elif reply == 1: if not self.dbb_load_backup(): return else: if self.hid_send_encrypt(b'{"device":"info"}')['device']['lock']: raise UserFacingException(_("Full 2FA enabled. This is not supported yet.")) # Use existing seed self.isInitialized = True
def decrypt_message(self, sequence, message, password): raise UserFacingException( _('Encryption and decryption are not implemented by {}').format( self.device))
def decrypt_message(self, pubkey, message, password): raise UserFacingException( _('Encryption and decryption are currently not supported for {}'). format(self.device))
def decrypt_message(self, pubkey, message, password): raise UserFacingException( _("Message encryption, decryption and signing are currently not supported for {}" ).format(self.device))
def sign_transaction( self, keystore: Hardware_KeyStore, tx: PartialTransaction, wallet: Deterministic_Wallet, ): if tx.is_complete(): return if self.bitbox02_device is None: raise Exception( "Need to setup communication first before attempting any BitBox02 calls" ) coin = self._get_coin() tx_script_type = None # Build BTCInputType list inputs = [] for txin in tx.inputs(): my_pubkey, full_path = keystore.find_my_pubkey_in_txinout(txin) if full_path is None: raise Exception( "A wallet owned pubkey was not found in the transaction input to be signed" ) prev_tx = txin.utxo if prev_tx is None: raise UserFacingException(_('Missing previous tx.')) prev_inputs: List[bitbox02.BTCPrevTxInputType] = [] prev_outputs: List[bitbox02.BTCPrevTxOutputType] = [] for prev_txin in prev_tx.inputs(): prev_inputs.append({ "prev_out_hash": prev_txin.prevout.txid[::-1], "prev_out_index": prev_txin.prevout.out_idx, "signature_script": prev_txin.script_sig, "sequence": prev_txin.nsequence, }) for prev_txout in prev_tx.outputs(): prev_outputs.append({ "value": prev_txout.value, "pubkey_script": prev_txout.scriptpubkey, }) inputs.append({ "prev_out_hash": txin.prevout.txid[::-1], "prev_out_index": txin.prevout.out_idx, "prev_out_value": txin.value_sats(), "sequence": txin.nsequence, "keypath": full_path, "script_config_index": 0, "prev_tx": { "version": prev_tx.version, "locktime": prev_tx.locktime, "inputs": prev_inputs, "outputs": prev_outputs, }, }) if tx_script_type == None: tx_script_type = txin.script_type elif tx_script_type != txin.script_type: raise Exception("Cannot mix different input script types") if tx_script_type == "p2wpkh": tx_script_type = bitbox02.btc.BTCScriptConfig( simple_type=bitbox02.btc.BTCScriptConfig.P2WPKH) elif tx_script_type == "p2wpkh-p2sh": tx_script_type = bitbox02.btc.BTCScriptConfig( simple_type=bitbox02.btc.BTCScriptConfig.P2WPKH_P2SH) elif tx_script_type in ("p2wsh-p2sh", "p2wsh"): if type(wallet) is Multisig_Wallet: tx_script_type = self.btc_multisig_config( coin, full_path, wallet, tx_script_type) else: raise Exception( "Can only use p2wsh-p2sh or p2wsh with multisig wallets") else: raise UserFacingException( "invalid input script type: {} is not supported by the BitBox02" .format(tx_script_type)) # Build BTCOutputType list outputs = [] for txout in tx.outputs(): assert txout.address # check for change if txout.is_change: my_pubkey, change_pubkey_path = keystore.find_my_pubkey_in_txinout( txout) outputs.append( bitbox02.BTCOutputInternal( keypath=change_pubkey_path, value=txout.value, script_config_index=0, )) else: addrtype, pubkey_hash = bitcoin.address_to_hash(txout.address) if addrtype == OnchainOutputType.P2PKH: output_type = bitbox02.btc.P2PKH elif addrtype == OnchainOutputType.P2SH: output_type = bitbox02.btc.P2SH elif addrtype == OnchainOutputType.WITVER0_P2WPKH: output_type = bitbox02.btc.P2WPKH elif addrtype == OnchainOutputType.WITVER0_P2WSH: output_type = bitbox02.btc.P2WSH else: raise UserFacingException( "Received unsupported output type during transaction signing: {} is not supported by the BitBox02" .format(addrtype)) outputs.append( bitbox02.BTCOutputExternal( output_type=output_type, output_hash=pubkey_hash, value=txout.value, )) keypath_account = full_path[:-2] sigs = self.bitbox02_device.btc_sign( coin, [ bitbox02.btc.BTCScriptConfigWithKeypath( script_config=tx_script_type, keypath=keypath_account, ) ], inputs=inputs, outputs=outputs, locktime=tx.locktime, version=tx.version, ) # Fill signatures if len(sigs) != len(tx.inputs()): raise Exception( "Incorrect number of inputs signed.") # Should never occur signatures = [ bh2u(ecc.der_sig_from_sig_string(x[1])) + "01" for x in sigs ] tx.update_signatures(signatures)