def _sign_btc_tx(self) -> None:
     # pylint: disable=no-member
     # Dummy transaction to invoke a demo.
     bip44_account: int = 0 + HARDENED
     inputs: List[bitbox02.BTCInputType] = [
         {
             "prev_out_hash": b"11111111111111111111111111111111",
             "prev_out_index": 1,
             "prev_out_value": int(1e8 * 0.60005),
             "sequence": 0xFFFFFFFF,
             "keypath": [84 + HARDENED, 0 + HARDENED, bip44_account, 0, 0],
         },
         {
             "prev_out_hash": b"11111111111111111111111111111111",
             "prev_out_index": 1,
             "prev_out_value": int(1e8 * 0.60005),
             "sequence": 0xFFFFFFFF,
             "keypath": [84 + HARDENED, 0 + HARDENED, bip44_account, 0, 1],
         },
     ]
     outputs: List[bitbox02.BTCOutputType] = [
         bitbox02.BTCOutputInternal(
             keypath=[84 + HARDENED, 0 + HARDENED, bip44_account, 1, 0],
             value=int(1e8 * 1)),
         bitbox02.BTCOutputExternal(
             output_type=bitbox02.btc.P2WSH,
             output_hash=b"11111111111111111111111111111111",
             value=int(1e8 * 0.2),
         ),
     ]
     sigs = self._device.btc_sign(
         bitbox02.btc.BTC,
         bitbox02.btc.BTCScriptConfig(
             simple_type=bitbox02.btc.BTCScriptConfig.P2WPKH),
         keypath_account=[84 + HARDENED, 0 + HARDENED, bip44_account],
         inputs=inputs,
         outputs=outputs,
     )
     for input_index, sig in sigs:
         print("Signature for input {}: {}".format(input_index, sig.hex()))
Beispiel #2
0
    def sign_tx(self, psbt: PSBT) -> PSBT:
        """
        Sign a transaction with the BitBox02.

        he BitBox02 allows mixing inputs of different script types (e.g. and `p2wpkh-p2sh` `p2wpkh`), as
        long as the keypaths use the appropriate bip44 purpose field per input (e.g. `49'` and `84'`) and
        all account indexes are the same.

        Transactions with legacy inputs are not supported.
        """
        def find_our_key(
            keypaths: Dict[bytes, KeyOriginInfo]
        ) -> Tuple[Optional[bytes], Optional[Sequence[int]]]:
            """
            Keypaths is a map of pubkey to hd keypath, where the first element in the keypath is the master fingerprint. We attempt to find the key which belongs to the BitBox02 by matching the fingerprint, and then matching the pubkey.
            Returns the pubkey and the keypath, without the fingerprint.
            """
            for pubkey, origin in keypaths.items():
                # Cheap check if the key is ours.
                if origin.fingerprint != master_fp:
                    continue

                # Expensive check if the key is ours.
                # TODO: check for fingerprint collision
                # keypath_account = keypath[:-2]

                return pubkey, origin.path
            return None, None

        script_configs: List[bitbox02.btc.BTCScriptConfigWithKeypath] = []

        def add_script_config(
            script_config: bitbox02.btc.BTCScriptConfigWithKeypath
        ) -> int:
            # Find index of script config if already added.
            script_config_index = next(
                (
                    i
                    for i, e in builtins.enumerate(script_configs)
                    if e.SerializeToString() == script_config.SerializeToString()
                ),
                None,
            )
            if script_config_index is not None:
                return script_config_index
            script_configs.append(script_config)
            return len(script_configs) - 1

        def script_config_from_utxo(
            output: CTxOut,
            keypath: Sequence[int],
            redeem_script: bytes,
            witness_script: bytes,
        ) -> bitbox02.btc.BTCScriptConfigWithKeypath:
            if is_p2pkh(output.scriptPubKey):
                raise BadArgumentError(
                    "The BitBox02 does not support legacy p2pkh scripts"
                )
            if is_p2wpkh(output.scriptPubKey):
                return bitbox02.btc.BTCScriptConfigWithKeypath(
                    script_config=bitbox02.btc.BTCScriptConfig(
                        simple_type=bitbox02.btc.BTCScriptConfig.P2WPKH
                    ),
                    keypath=_keypath_hardened_prefix(keypath),
                )
            if output.is_p2sh() and is_p2wpkh(redeem_script):
                return bitbox02.btc.BTCScriptConfigWithKeypath(
                    script_config=bitbox02.btc.BTCScriptConfig(
                        simple_type=bitbox02.btc.BTCScriptConfig.P2WPKH_P2SH
                    ),
                    keypath=_keypath_hardened_prefix(keypath),
                )
            # Check for segwit multisig (p2wsh or p2wsh-p2sh).
            is_p2wsh_p2sh = output.is_p2sh() and is_p2wsh(redeem_script)
            if output.is_p2wsh() or is_p2wsh_p2sh:
                multisig = parse_multisig(witness_script)
                if multisig:
                    threshold, _ = multisig
                    # We assume that all xpubs in the PSBT are part of the multisig. This is okay
                    # since the BitBox02 enforces the same script type for all inputs and
                    # changes. If that should change, we need to find and use the subset of xpubs
                    # corresponding to the public keys in the current multisig script.
                    _, script_config = self._multisig_scriptconfig(
                        threshold,
                        psbt.xpub,
                        bitbox02.btc.BTCScriptConfig.Multisig.P2WSH
                        if output.is_p2wsh()
                        else bitbox02.btc.BTCScriptConfig.Multisig.P2WSH_P2SH,
                    )
                    return script_config

            raise BadArgumentError("Input or change script type not recognized.")

        master_fp = self.get_master_fingerprint()

        inputs: List[bitbox02.BTCInputType] = []

        bip44_account = None

        # One pubkey per input. The pubkey identifies the key per input with which we sign. There
        # must be exactly one pubkey per input that belongs to the BitBox02.
        found_pubkeys: List[bytes] = []

        for input_index, psbt_in in builtins.enumerate(psbt.inputs):
            assert psbt_in.prev_txid is not None
            assert psbt_in.prev_out is not None
            assert psbt_in.sequence is not None

            if psbt_in.sighash and psbt_in.sighash != 1:
                raise BadArgumentError(
                    "The BitBox02 only supports SIGHASH_ALL. Found sighash: {}".format(
                        psbt_in.sighash
                    )
                )

            utxo = None
            prevtx = None

            # psbt_in.witness_utxo was originally used for segwit utxo's, but since it was
            # discovered that the amounts are not correctly committed to in the segwit sighash, the
            # full prevtx (non_witness_utxo) is supplied for both segwit and non-segwit inputs.
            # See
            # - https://medium.com/shiftcrypto/bitbox-app-firmware-update-6-2020-c70f733a5330
            # - https://blog.trezor.io/details-of-firmware-updates-for-trezor-one-version-1-9-1-and-trezor-model-t-version-2-3-1-1eba8f60f2dd.
            # - https://github.com/zkSNACKs/WalletWasabi/pull/3822
            # The BitBox02 for now requires the prevtx, at least until Taproot activates.

            if psbt_in.non_witness_utxo:
                assert psbt_in.non_witness_utxo.sha256 is not None
                if psbt_in.prev_txid != ser_uint256(psbt_in.non_witness_utxo.sha256):
                    raise BadArgumentError(
                        "Input {} has a non_witness_utxo with the wrong hash".format(
                            input_index
                        )
                    )
                assert psbt_in.prev_out is not None
                utxo = psbt_in.non_witness_utxo.vout[psbt_in.prev_out]
                prevtx = psbt_in.non_witness_utxo
            elif psbt_in.witness_utxo:
                utxo = psbt_in.witness_utxo
            if utxo is None:
                raise BadArgumentError("No utxo found for input {}".format(input_index))
            if prevtx is None:
                raise BadArgumentError(
                    "Previous transaction missing for input {}".format(input_index)
                )

            found_pubkey, keypath = find_our_key(psbt_in.hd_keypaths)
            if not found_pubkey:
                raise BadArgumentError("No key found for input {}".format(input_index))
            assert keypath is not None
            found_pubkeys.append(found_pubkey)

            if bip44_account is None:
                bip44_account = keypath[2]
            elif bip44_account != keypath[2]:
                raise BadArgumentError(
                    "The bip44 account index must be the same for all inputs and changes"
                )

            script_config_index = add_script_config(
                script_config_from_utxo(
                    utxo, keypath, psbt_in.redeem_script, psbt_in.witness_script
                )
            )
            inputs.append(
                {
                    "prev_out_hash": psbt_in.prev_txid,
                    "prev_out_index": psbt_in.prev_out,
                    "prev_out_value": utxo.nValue,
                    "sequence": psbt_in.sequence,
                    "keypath": keypath,
                    "script_config_index": script_config_index,
                    "prev_tx": {
                        "version": prevtx.nVersion,
                        "locktime": prevtx.nLockTime,
                        "inputs": [
                            {
                                "prev_out_hash": ser_uint256(prev_in.prevout.hash),
                                "prev_out_index": prev_in.prevout.n,
                                "signature_script": prev_in.scriptSig,
                                "sequence": prev_in.nSequence,
                            }
                            for prev_in in prevtx.vin
                        ],
                        "outputs": [
                            {
                                "value": prev_out.nValue,
                                "pubkey_script": prev_out.scriptPubKey,
                            }
                            for prev_out in prevtx.vout
                        ],
                    },
                }
            )

        outputs: List[bitbox02.BTCOutputType] = []

        for output_index, psbt_out in builtins.enumerate(psbt.outputs):
            tx_out = psbt_out.get_txout()

            _, keypath = find_our_key(psbt_out.hd_keypaths)
            is_change = keypath and keypath[-2] == 1
            if is_change:
                assert keypath is not None
                script_config_index = add_script_config(
                    script_config_from_utxo(
                        tx_out, keypath, psbt_out.redeem_script, psbt_out.witness_script
                    )
                )
                outputs.append(
                    bitbox02.BTCOutputInternal(
                        keypath=keypath,
                        value=tx_out.nValue,
                        script_config_index=script_config_index,
                    )
                )
            else:
                if tx_out.is_p2pkh():
                    output_type = bitbox02.btc.P2PKH
                    output_hash = tx_out.scriptPubKey[3:23]
                elif is_p2wpkh(tx_out.scriptPubKey):
                    output_type = bitbox02.btc.P2WPKH
                    output_hash = tx_out.scriptPubKey[2:]
                elif tx_out.is_p2sh():
                    output_type = bitbox02.btc.P2SH
                    output_hash = tx_out.scriptPubKey[2:22]
                elif is_p2wsh(tx_out.scriptPubKey):
                    output_type = bitbox02.btc.P2WSH
                    output_hash = tx_out.scriptPubKey[2:]
                else:
                    raise BadArgumentError(
                        "Output type not recognized of output {}".format(output_index)
                    )

                outputs.append(
                    bitbox02.BTCOutputExternal(
                        output_type=output_type,
                        output_hash=output_hash,
                        value=tx_out.nValue,
                    )
                )

        assert bip44_account is not None
        if (
            len(script_configs) == 1
            and script_configs[0].script_config.WhichOneof("config") == "multisig"
        ):
            self._maybe_register_script_config(
                script_configs[0].script_config, script_configs[0].keypath
            )

        assert psbt.tx_version is not None
        sigs = self.init().btc_sign(
            self._get_coin(),
            script_configs,
            inputs=inputs,
            outputs=outputs,
            locktime=psbt.compute_lock_time(),
            version=psbt.tx_version,
        )

        for (_, sig), pubkey, psbt_in in zip(sigs, found_pubkeys, psbt.inputs):
            r, s = sig[:32], sig[32:64]
            # ser_sig_der() adds SIGHASH_ALL
            psbt_in.partial_sigs[pubkey] = ser_sig_der(r, s)

        return psbt
Beispiel #3
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)
Beispiel #4
0
    def sign_tx(self, psbt: PSBT) -> Dict[str, str]:
        def find_our_key(
            keypaths: Dict[bytes, KeyOriginInfo]
        ) -> Tuple[Optional[bytes], Optional[Sequence[int]]]:
            """
            Keypaths is a map of pubkey to hd keypath, where the first element in the keypath is the master fingerprint. We attempt to find the key which belongs to the BitBox02 by matching the fingerprint, and then matching the pubkey.
            Returns the pubkey and the keypath, without the fingerprint.
            """
            for pubkey, origin in keypaths.items():
                # Cheap check if the key is ours.
                if origin.fingerprint != master_fp:
                    continue

                # Expensive check if the key is ours.
                # TODO: check for fingerprint collision
                # keypath_account = keypath[:-2]

                return pubkey, origin.path
            return None, None

        def get_simple_type(
                output: CTxOut, redeem_script: bytes
        ) -> bitbox02.btc.BTCScriptConfig.SimpleType:
            if is_p2pkh(output.scriptPubKey):
                raise BadArgumentError(
                    "The BitBox02 does not support legacy p2pkh scripts")
            if is_p2wpkh(output.scriptPubKey):
                return bitbox02.btc.BTCScriptConfig.P2WPKH
            if output.is_p2sh() and is_p2wpkh(redeem_script):
                return bitbox02.btc.BTCScriptConfig.P2WPKH_P2SH
            raise BadArgumentError(
                "Input script type not recognized of input {}.".format(
                    input_index))

        master_fp = self.get_master_fingerprint()

        inputs: List[bitbox02.BTCInputType] = []

        bip44_account = None

        # One pubkey per input. The pubkey identifies the key per input with which we sign. There
        # must be exactly one pubkey per input that belongs to the BitBox02.
        found_pubkeys: List[bytes] = []

        for input_index, (psbt_in, tx_in) in builtins.enumerate(
                zip(psbt.inputs, psbt.tx.vin)):
            if psbt_in.sighash and psbt_in.sighash != 1:
                raise BadArgumentError(
                    "The BitBox02 only supports SIGHASH_ALL. Found sighash: {}"
                    .format(psbt_in.sighash))

            utxo = None
            prevtx = None

            # psbt_in.witness_utxo was originally used for segwit utxo's, but since it was
            # discovered that the amounts are not correctly committed to in the segwit sighash, the
            # full prevtx (non_witness_utxo) is supplied for both segwit and non-segwit inputs.
            # See
            # - https://medium.com/shiftcrypto/bitbox-app-firmware-update-6-2020-c70f733a5330
            # - https://blog.trezor.io/details-of-firmware-updates-for-trezor-one-version-1-9-1-and-trezor-model-t-version-2-3-1-1eba8f60f2dd.
            # - https://github.com/zkSNACKs/WalletWasabi/pull/3822
            # The BitBox02 for now requires the prevtx, at least until Taproot activates.

            if psbt_in.non_witness_utxo:
                if tx_in.prevout.hash != psbt_in.non_witness_utxo.sha256:
                    raise BadArgumentError(
                        "Input {} has a non_witness_utxo with the wrong hash".
                        format(input_index))
                utxo = psbt_in.non_witness_utxo.vout[tx_in.prevout.n]
                prevtx = psbt_in.non_witness_utxo
            elif psbt_in.witness_utxo:
                utxo = psbt_in.witness_utxo
            if utxo is None:
                raise BadArgumentError(
                    "No utxo found for input {}".format(input_index))
            if prevtx is None:
                raise BadArgumentError(
                    "Previous transaction missing for input {}".format(
                        input_index))

            found_pubkey, keypath = find_our_key(psbt_in.hd_keypaths)
            if not found_pubkey:
                raise BadArgumentError(
                    "No key found for input {}".format(input_index))
            assert keypath is not None
            found_pubkeys.append(found_pubkey)

            # TOOD: validate keypath

            if bip44_account is None:
                bip44_account = keypath[2]
            elif bip44_account != keypath[2]:
                raise BadArgumentError(
                    "The bip44 account index must be the same for all inputs and changes"
                )

            simple_type = get_simple_type(utxo, psbt_in.redeem_script)

            script_config_index_map = {
                bitbox02.btc.BTCScriptConfig.P2WPKH: 0,
                bitbox02.btc.BTCScriptConfig.P2WPKH_P2SH: 1,
            }

            inputs.append({
                "prev_out_hash":
                ser_uint256(tx_in.prevout.hash),
                "prev_out_index":
                tx_in.prevout.n,
                "prev_out_value":
                utxo.nValue,
                "sequence":
                tx_in.nSequence,
                "keypath":
                keypath,
                "script_config_index":
                script_config_index_map[simple_type],
                "prev_tx": {
                    "version":
                    prevtx.nVersion,
                    "locktime":
                    prevtx.nLockTime,
                    "inputs": [{
                        "prev_out_hash":
                        ser_uint256(prev_in.prevout.hash),
                        "prev_out_index":
                        prev_in.prevout.n,
                        "signature_script":
                        prev_in.scriptSig,
                        "sequence":
                        prev_in.nSequence,
                    } for prev_in in prevtx.vin],
                    "outputs": [{
                        "value": prev_out.nValue,
                        "pubkey_script": prev_out.scriptPubKey,
                    } for prev_out in prevtx.vout],
                },
            })

        outputs: List[bitbox02.BTCOutputType] = []
        for output_index, (psbt_out, tx_out) in builtins.enumerate(
                zip(psbt.outputs, psbt.tx.vout)):
            _, keypath = find_our_key(psbt_out.hd_keypaths)
            is_change = keypath and keypath[-2] == 1
            if is_change:
                assert keypath is not None
                simple_type = get_simple_type(tx_out, psbt_out.redeem_script)
                outputs.append(
                    bitbox02.BTCOutputInternal(
                        keypath=keypath,
                        value=tx_out.nValue,
                        script_config_index=script_config_index_map[
                            simple_type],
                    ))
            else:
                if tx_out.is_p2pkh():
                    output_type = bitbox02.btc.P2PKH
                    output_hash = tx_out.scriptPubKey[3:23]
                elif is_p2wpkh(tx_out.scriptPubKey):
                    output_type = bitbox02.btc.P2WPKH
                    output_hash = tx_out.scriptPubKey[2:]
                elif tx_out.is_p2sh():
                    output_type = bitbox02.btc.P2SH
                    output_hash = tx_out.scriptPubKey[2:22]
                elif is_p2wsh(tx_out.scriptPubKey):
                    output_type = bitbox02.btc.P2WSH
                    output_hash = tx_out.scriptPubKey[2:]
                else:
                    raise BadArgumentError(
                        "Output type not recognized of output {}".format(
                            output_index))

                outputs.append(
                    bitbox02.BTCOutputExternal(
                        output_type=output_type,
                        output_hash=output_hash,
                        value=tx_out.nValue,
                    ))

        assert bip44_account is not None

        bip44_network = 1 + HARDENED if self.is_testnet else 0 + HARDENED
        sigs = self.init().btc_sign(
            bitbox02.btc.TBTC if self.is_testnet else bitbox02.btc.BTC,
            [
                bitbox02.btc.BTCScriptConfigWithKeypath(
                    script_config=bitbox02.btc.BTCScriptConfig(
                        simple_type=bitbox02.btc.BTCScriptConfig.P2WPKH),
                    keypath=[84 + HARDENED, bip44_network, bip44_account],
                ),
                bitbox02.btc.BTCScriptConfigWithKeypath(
                    script_config=bitbox02.btc.BTCScriptConfig(
                        simple_type=bitbox02.btc.BTCScriptConfig.P2WPKH_P2SH),
                    keypath=[49 + HARDENED, bip44_network, bip44_account],
                ),
            ],
            inputs=inputs,
            outputs=outputs,
            locktime=psbt.tx.nLockTime,
            version=psbt.tx.nVersion,
        )

        for (_, sig), pubkey, psbt_in in zip(sigs, found_pubkeys, psbt.inputs):
            r, s = sig[:32], sig[32:64]
            # ser_sig_der() adds SIGHASH_ALL
            psbt_in.partial_sigs[pubkey] = ser_sig_der(r, s)

        return {"psbt": psbt.serialize()}
Beispiel #5
0
def _btc_demo_inputs_outputs(
    bip44_account: int
) -> Tuple[List[bitbox02.BTCInputType], List[bitbox02.BTCOutputType]]:
    """
    Returns a sample btc tx.
    """
    inputs: List[bitbox02.BTCInputType] = [
        {
            "prev_out_hash":
            binascii.unhexlify(
                "c58b7e3f1200e0c0ec9a5e81e925baface2cc1d4715514f2d8205be2508b48ee"
            ),
            "prev_out_index":
            0,
            "prev_out_value":
            int(1e8 * 0.60005),
            "sequence":
            0xFFFFFFFF,
            "keypath": [84 + HARDENED, 0 + HARDENED, bip44_account, 0, 0],
            "script_config_index":
            0,
            "prev_tx": {
                "version":
                1,
                "locktime":
                0,
                "inputs": [{
                    "prev_out_hash": b"11111111111111111111111111111111",
                    "prev_out_index": 0,
                    "signature_script": b"some signature script",
                    "sequence": 0xFFFFFFFF,
                }],
                "outputs": [{
                    "value": int(1e8 * 0.60005),
                    "pubkey_script": b"some pubkey script"
                }],
            },
        },
        {
            "prev_out_hash":
            binascii.unhexlify(
                "c58b7e3f1200e0c0ec9a5e81e925baface2cc1d4715514f2d8205be2508b48ee"
            ),
            "prev_out_index":
            1,
            "prev_out_value":
            int(1e8 * 0.60005),
            "sequence":
            0xFFFFFFFF,
            "keypath": [49 + HARDENED, 0 + HARDENED, bip44_account, 0, 1],
            "script_config_index":
            1,
            "prev_tx": {
                "version":
                1,
                "locktime":
                0,
                "inputs": [{
                    "prev_out_hash": b"11111111111111111111111111111111",
                    "prev_out_index": 0,
                    "signature_script": b"some signature script",
                    "sequence": 0xFFFFFFFF,
                }],
                "outputs": [{
                    "value": int(1e8 * 0.60005),
                    "pubkey_script": b"some pubkey script"
                }],
            },
        },
    ]
    outputs: List[bitbox02.BTCOutputType] = [
        bitbox02.BTCOutputInternal(
            keypath=[84 + HARDENED, 0 + HARDENED, bip44_account, 1, 0],
            value=int(1e8 * 1),
            script_config_index=0,
        ),
        bitbox02.BTCOutputExternal(
            output_type=bitbox02.btc.P2WSH,
            output_hash=b"11111111111111111111111111111111",
            value=int(1e8 * 0.2),
        ),
    ]
    return inputs, outputs
Beispiel #6
0
    def _sign_btc_tx_from_raw(self) -> None:
        """
        Experiment with testnet transactions.
        Uses blockchair.com to convert a testnet transaction to the input required by btc_sign(),
        including the previous transactions.
        """

        # pylint: disable=no-member

        def get(tx_id: str) -> Any:
            return requests.get(
                "https://api.blockchair.com/bitcoin/testnet/dashboards/transaction/{}"
                .format(tx_id)).json()["data"][tx_id]

        tx_id = input("Paste a btc testnet tx ID: ").strip()
        tx = get(tx_id)

        inputs: List[bitbox02.BTCInputType] = []
        outputs: List[bitbox02.BTCOutputType] = []

        bip44_account: int = 0 + HARDENED

        for inp in tx["inputs"]:
            print("Downloading prev tx")
            prev_tx = get(inp["transaction_hash"])
            print("Downloaded prev tx")
            prev_inputs: List[bitbox02.BTCPrevTxInputType] = []
            prev_outputs: List[bitbox02.BTCPrevTxOutputType] = []

            for prev_inp in prev_tx["inputs"]:
                prev_inputs.append({
                    "prev_out_hash":
                    binascii.unhexlify(prev_inp["transaction_hash"])[::-1],
                    "prev_out_index":
                    prev_inp["index"],
                    "signature_script":
                    binascii.unhexlify(prev_inp["spending_signature_hex"]),
                    "sequence":
                    prev_inp["spending_sequence"],
                })
            for prev_outp in prev_tx["outputs"]:
                prev_outputs.append({
                    "value":
                    prev_outp["value"],
                    "pubkey_script":
                    binascii.unhexlify(prev_outp["script_hex"]),
                })

            inputs.append({
                "prev_out_hash":
                binascii.unhexlify(inp["transaction_hash"])[::-1],
                "prev_out_index":
                inp["index"],
                "prev_out_value":
                inp["value"],
                "sequence":
                inp["spending_sequence"],
                "keypath": [84 + HARDENED, 1 + HARDENED, bip44_account, 0, 0],
                "script_config_index":
                0,
                "prev_tx": {
                    "version": prev_tx["transaction"]["version"],
                    "locktime": prev_tx["transaction"]["lock_time"],
                    "inputs": prev_inputs,
                    "outputs": prev_outputs,
                },
            })

        for outp in tx["outputs"]:
            outputs.append(
                bitbox02.BTCOutputExternal(
                    # TODO: parse pubkey script
                    output_type=bitbox02.btc.P2WSH,
                    output_hash=b"11111111111111111111111111111111",
                    value=outp["value"],
                ))

        print("Start signing...")
        self._device.btc_sign(
            bitbox02.btc.TBTC,
            [
                bitbox02.btc.BTCScriptConfigWithKeypath(
                    script_config=bitbox02.btc.BTCScriptConfig(
                        simple_type=bitbox02.btc.BTCScriptConfig.P2WPKH),
                    keypath=[84 + HARDENED, 1 + HARDENED, bip44_account],
                )
            ],
            inputs=inputs,
            outputs=outputs,
        )