Example #1
0
    def __init__(self, d):
        Hardware_KeyStore.__init__(self, d)
        # Errors and other user interaction is done through the wallet's
        # handler.  The handler is per-window and preserved across
        # device reconnects
        self.force_watching_only = False
        self.ux_busy = False

        # for multisig I need to know what wallet this keystore is part of
        # will be set by link_wallet
        self.my_wallet = None

        # Seems like only the derivation path and resulting **derived** xpub is stored in
        # the wallet file... however, we need to know at least the fingerprint of the master
        # xpub to verify against MiTM, and also so we can put the right value into the subkey paths
        # of PSBT files that might be generated offline. 
        # - save the fingerprint of the master xpub, as "xfp"
        # - it's a LE32 int, but hex BE32 is more natural way to view it
        # - device reports these value during encryption setup process
        # - full xpub value now optional
        lab = d['label']
        if hasattr(lab, 'xfp'):
            # initial setup
            self.ckcc_xfp = lab.xfp
            self.ckcc_xpub = getattr(lab, 'xpub', None)
        else:
            # wallet load: fatal if missing, we need them!
            self.ckcc_xfp = d['ckcc_xfp']
            self.ckcc_xpub = d.get('ckcc_xpub', None)
Example #2
0
 def __init__(self, d):
     Hardware_KeyStore.__init__(self, d)
     # Errors and other user interaction is done through the wallet's
     # handler.  The handler is per-window and preserved across
     # device reconnects
     self.force_watching_only = False
     self.signing = False
     self.cfg = d.get('cfg', {'mode': 0, 'pair': ''})
Example #3
0
    def __init__(self, d):
        Hardware_KeyStore.__init__(self, d)
        # Errors and other user interaction is done through the wallet's
        # handler.  The handler is per-window and preserved across
        # device reconnects
        self.force_watching_only = False
        self.ux_busy = False

        # we need to know at least the fingerprint of the master xpub to verify against MiTM
        # - device reports these value during encryption setup process
        # - full xpub value now optional
        self.ckcc_xpub = d.get('ckcc_xpub', None)
Example #4
0
    def dump(self):
        # our additions to the stored data about keystore -- only during creation?
        d = Hardware_KeyStore.dump(self)

        d['ckcc_xfp'] = self.ckcc_xfp
        d['ckcc_xpub'] = self.ckcc_xpub

        return d
Example #5
0
 def __init__(self, d):
     Hardware_KeyStore.__init__(self, d)
     self.force_watching_only = False
     self.maxInputs = 14  # maximum inputs per single sign command
Example #6
0
 def dump(self):
     obj = Hardware_KeyStore.dump(self)
     obj['cfg'] = self.cfg
     return obj
Example #7
0
    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 = bitbox02.btc.BTC
        if constants.net.TESTNET:
            coin = bitbox02.btc.TBTC

        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 == "p2wsh":
            if type(wallet) is Multisig_Wallet:
                tx_script_type = self.btc_multisig_config(
                    coin, full_path, wallet)
            else:
                raise Exception("Can only use 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,
                    ))

        if type(wallet) is Standard_Wallet:
            keypath_account = full_path[:3]
        elif type(wallet) is Multisig_Wallet:
            keypath_account = full_path[:4]
        else:
            raise Exception(
                "BitBox02 does not support this wallet type: {}".format(
                    type(wallet)))

        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)