Beispiel #1
0
def sanitize_tx_input(txi: TxInput, coin: CoinInfo) -> TxInput:
    if len(txi.prev_hash) != TX_HASH_SIZE:
        raise wire.DataError("Provided prev_hash is invalid.")

    if txi.multisig and txi.script_type not in common.MULTISIG_INPUT_SCRIPT_TYPES:
        raise wire.DataError("Multisig field provided but not expected.")

    if not txi.multisig and txi.script_type == InputScriptType.SPENDMULTISIG:
        raise wire.DataError("Multisig details required.")

    if txi.script_type in common.INTERNAL_INPUT_SCRIPT_TYPES:
        if not txi.address_n:
            raise wire.DataError("Missing address_n field.")

        if txi.script_pubkey:
            raise wire.DataError(
                "Input's script_pubkey provided but not expected.")
    else:
        if txi.address_n:
            raise wire.DataError(
                "Input's address_n provided but not expected.")

        if not txi.script_pubkey:
            raise wire.DataError("Missing script_pubkey field.")

    if not coin.decred and txi.decred_tree is not None:
        raise wire.DataError(
            "Decred details provided but Decred coin not specified.")

    if txi.script_type in common.SEGWIT_INPUT_SCRIPT_TYPES or txi.witness is not None:
        if not coin.segwit:
            raise wire.DataError("Segwit not enabled on this coin.")

    if txi.script_type == InputScriptType.SPENDTAPROOT and not coin.taproot:
        raise wire.DataError("Taproot not enabled on this coin")

    if txi.commitment_data and not txi.ownership_proof:
        raise wire.DataError(
            "commitment_data field provided but not expected.")

    if txi.orig_hash and txi.orig_index is None:
        raise wire.DataError("Missing orig_index field.")

    return txi
Beispiel #2
0
async def sign_tx(ctx, msg: NEMSignTx, keychain):
    validate(msg)

    await validate_path(
        ctx,
        keychain,
        msg.transaction.address_n,
        check_path(msg.transaction.address_n, msg.transaction.network),
    )

    node = keychain.derive(msg.transaction.address_n)

    if msg.multisig:
        public_key = msg.multisig.signer
        common = msg.multisig
        await multisig.ask(ctx, msg)
    else:
        public_key = seed.remove_ed25519_prefix(node.public_key())
        common = msg.transaction

    if msg.transfer:
        tx = await transfer.transfer(ctx, public_key, common, msg.transfer,
                                     node)
    elif msg.provision_namespace:
        tx = await namespace.namespace(ctx, public_key, common,
                                       msg.provision_namespace)
    elif msg.mosaic_creation:
        tx = await mosaic.mosaic_creation(ctx, public_key, common,
                                          msg.mosaic_creation)
    elif msg.supply_change:
        tx = await mosaic.supply_change(ctx, public_key, common,
                                        msg.supply_change)
    elif msg.aggregate_modification:
        tx = await multisig.aggregate_modification(
            ctx,
            public_key,
            common,
            msg.aggregate_modification,
            msg.multisig is not None,
        )
    elif msg.importance_transfer:
        tx = await transfer.importance_transfer(ctx, public_key, common,
                                                msg.importance_transfer)
    else:
        raise wire.DataError("No transaction provided")

    if msg.multisig:
        # wrap transaction in multisig wrapper
        if msg.cosigning:
            tx = multisig.cosign(
                seed.remove_ed25519_prefix(node.public_key()),
                msg.transaction,
                tx,
                msg.multisig.signer,
            )
        else:
            tx = multisig.initiate(
                seed.remove_ed25519_prefix(node.public_key()), msg.transaction,
                tx)

    signature = ed25519.sign(node.private_key(), tx, NEM_HASH_ALG)

    return NEMSignedTx(
        data=tx,
        signature=signature,
    )
Beispiel #3
0
async def sign_tx(ctx, msg, keychain):
    await paths.validate_path(ctx, keychain, msg.address_n)

    node = keychain.derive(msg.address_n)

    if msg.transaction is not None:
        # if the tranasction oprtation is used to execute code on a smart contract
        if msg.transaction.parameters_manager is not None:
            parameters_manager = msg.transaction.parameters_manager

            # operation to delegate from a smart contract with manager.tz
            if parameters_manager.set_delegate is not None:
                delegate = _get_address_by_tag(parameters_manager.set_delegate)
                await layout.require_confirm_delegation_baker(ctx, delegate)
                await layout.require_confirm_set_delegate(ctx, msg.transaction.fee)

            # operation to remove delegate from the smart contract with manager.tz
            elif parameters_manager.cancel_delegate is not None:
                address = _get_address_from_contract(msg.transaction.destination)
                await layout.require_confirm_delegation_manager_withdraw(ctx, address)
                await layout.require_confirm_manager_remove_delegate(
                    ctx, msg.transaction.fee
                )

            # operation to transfer tokens from a smart contract to an implicit account or a smart contract
            elif parameters_manager.transfer is not None:
                to = _get_address_from_contract(parameters_manager.transfer.destination)
                await layout.require_confirm_tx(
                    ctx, to, parameters_manager.transfer.amount
                )
                await layout.require_confirm_fee(
                    ctx, parameters_manager.transfer.amount, msg.transaction.fee
                )
        else:
            # transactions from an implicit account
            to = _get_address_from_contract(msg.transaction.destination)
            await layout.require_confirm_tx(ctx, to, msg.transaction.amount)
            await layout.require_confirm_fee(
                ctx, msg.transaction.amount, msg.transaction.fee
            )

    elif msg.origination is not None:
        source = _get_address_by_tag(msg.origination.source)
        await layout.require_confirm_origination(ctx, source)

        # if we are immediately delegating contract
        if msg.origination.delegate is not None:
            delegate = _get_address_by_tag(msg.origination.delegate)
            await layout.require_confirm_delegation_baker(ctx, delegate)

        await layout.require_confirm_origination_fee(
            ctx, msg.origination.balance, msg.origination.fee
        )

    elif msg.delegation is not None:
        source = _get_address_by_tag(msg.delegation.source)

        delegate = None
        if msg.delegation.delegate is not None:
            delegate = _get_address_by_tag(msg.delegation.delegate)

        if delegate is not None and source != delegate:
            await layout.require_confirm_delegation_baker(ctx, delegate)
            await layout.require_confirm_set_delegate(ctx, msg.delegation.fee)
        # if account registers itself as a delegate
        else:
            await layout.require_confirm_register_delegate(
                ctx, source, msg.delegation.fee
            )

    elif msg.proposal is not None:
        proposed_protocols = [_get_protocol_hash(p) for p in msg.proposal.proposals]
        await layout.require_confirm_proposals(ctx, proposed_protocols)

    elif msg.ballot is not None:
        proposed_protocol = _get_protocol_hash(msg.ballot.proposal)
        submitted_ballot = _get_ballot(msg.ballot.ballot)
        await layout.require_confirm_ballot(ctx, proposed_protocol, submitted_ballot)

    else:
        raise wire.DataError("Invalid operation")

    w = bytearray()
    _get_operation_bytes(w, msg)

    opbytes = bytes(w)

    # watermark 0x03 is prefix for transactions, delegations, originations, reveals...
    watermark = bytes([3])
    wm_opbytes = watermark + opbytes
    wm_opbytes_hash = hashlib.blake2b(wm_opbytes, outlen=32).digest()

    signature = ed25519.sign(node.private_key(), wm_opbytes_hash)

    sig_op_contents = opbytes + signature
    sig_op_contents_hash = hashlib.blake2b(sig_op_contents, outlen=32).digest()
    ophash = helpers.base58_encode_check(sig_op_contents_hash, prefix="o")

    sig_prefixed = helpers.base58_encode_check(
        signature, prefix=helpers.TEZOS_SIGNATURE_PREFIX
    )

    return TezosSignedTx(
        signature=sig_prefixed, sig_op_contents=sig_op_contents, operation_hash=ophash
    )
Beispiel #4
0
async def sign_tx(ctx, msg, keychain):
    msg = sanitize(msg)  #TODO refine `sanitize` to support more fields
    check(msg)  #TODO refine check to support ~
    await paths.validate_path(ctx, validate_full_path, keychain, msg.address_n,
                              CURVE)

    node = keychain.derive(msg.address_n)
    seckey = node.private_key()
    public_key = secp256k1.publickey(seckey, False)  # uncompressed
    sender_address = sha3_256(public_key[1:], keccak=True).digest()[12:]

    recipient = address.bytes_from_address(msg.to)
    # TODO- check sender and real sender addr

    value = int.from_bytes(msg.value, "big")
    await require_confirm_tx(ctx, recipient, value, msg.chain_id, None,
                             msg.tx_type)

    # TODO if fee delegation tx? -> require_confirm_fee_delegation
    await require_confirm_fee(
        ctx,
        value,
        int.from_bytes(msg.gas_price, "big"),
        int.from_bytes(msg.gas_limit, "big"),
        msg.chain_id,
        msg.fee_ratio,
        None,
        msg.tx_type,
    )

    data_total = msg.data_length

    data = bytearray()
    data += msg.data_initial_chunk
    data_left = data_total - len(msg.data_initial_chunk)

    total_length = get_total_length(msg, data_total)
    print("total length: ", total_length)

    sha = HashWriter(sha3_256(keccak=True))

    if msg.tx_type is None:
        sha.extend(rlp.encode_length(total_length, True))  # total length

        for field in (msg.nonce, msg.gas_price, msg.gas_limit, recipient,
                      msg.value):
            sha.extend(rlp.encode(field))

        if data_left == 0:
            sha.extend(rlp.encode(data))
        else:
            sha.extend(rlp.encode_length(data_total, False))
            sha.extend(rlp.encode(data, False))

        if msg.chain_id:
            sha.extend(rlp.encode(msg.chain_id))
            sha.extend(rlp.encode(0))
            sha.extend(rlp.encode(0))

    else:
        basic_type = to_basic_type(msg.tx_type)
        attributes = [msg.tx_type, msg.nonce, msg.gas_price, msg.gas_limit]

        # TxTypeValueTransfer(0x08)
        if basic_type == 0x08:
            attributes += [recipient, msg.value, sender_address]
            if to_fee_type(msg.tx_type) == TX_TYPE_PARTIAL_FEE_DELEGATION:
                attributes.append(msg.fee_ratio)

        # TxTypeValueTransferMemo(0x10), TxTypeSmartContractExecution(0x30)
        elif basic_type == 0x10 or basic_type == 0x30:
            attributes += [recipient, msg.value, sender_address, data]
            if to_fee_type(msg.tx_type) == TX_TYPE_PARTIAL_FEE_DELEGATION:
                attributes.append(msg.fee_ratio)

        # TxTypeSmartContractDeploy(0x28)
        elif basic_type == 0x28:
            human_readable = 0x00
            if msg.human_readable:
                human_readable = 0x01
            attributes += [
                recipient, msg.value, sender_address, data, human_readable
            ]
            if to_fee_type(msg.tx_type) == TX_TYPE_PARTIAL_FEE_DELEGATION:
                attributes.append(msg.fee_ratio)
            attributes.append(msg.code_format)

        # TxTypeCancel(0x38)
        elif basic_type == 0x38:
            attributes.append(sender_address)
            if to_fee_type(msg.tx_type) == TX_TYPE_PARTIAL_FEE_DELEGATION:
                attributes.append(msg.fee_ratio)

        # not supported tx type
        else:
            raise wire.DataError("Not supported transaction type")

        encoded_out = rlp.encode(attributes)
        sha.extend(rlp.encode([encoded_out, msg.chain_id, 0, 0], True))

    digest = sha.get_digest()
    result = sign_digest(msg, keychain, digest)

    return result
Beispiel #5
0
async def sign_tx_dispatch(state, msg, keychain):
    if msg.MESSAGE_WIRE_TYPE == MessageType.MoneroTransactionInitRequest:
        from apps.monero.signing import step_01_init_transaction

        return (
            await step_01_init_transaction.init_transaction(
                state, msg.address_n, msg.network_type, msg.tsx_data, keychain
            ),
            (MessageType.MoneroTransactionSetInputRequest,),
        )

    elif msg.MESSAGE_WIRE_TYPE == MessageType.MoneroTransactionSetInputRequest:
        from apps.monero.signing import step_02_set_input

        return (
            await step_02_set_input.set_input(state, msg.src_entr),
            (
                MessageType.MoneroTransactionSetInputRequest,
                MessageType.MoneroTransactionInputsPermutationRequest,
                MessageType.MoneroTransactionInputViniRequest,
            ),
        )

    elif msg.MESSAGE_WIRE_TYPE == MessageType.MoneroTransactionInputsPermutationRequest:
        from apps.monero.signing import step_03_inputs_permutation

        return (
            await step_03_inputs_permutation.tsx_inputs_permutation(state, msg.perm),
            (MessageType.MoneroTransactionInputViniRequest,),
        )

    elif msg.MESSAGE_WIRE_TYPE == MessageType.MoneroTransactionInputViniRequest:
        from apps.monero.signing import step_04_input_vini

        return (
            await step_04_input_vini.input_vini(
                state, msg.src_entr, msg.vini, msg.vini_hmac, msg.orig_idx
            ),
            (
                MessageType.MoneroTransactionInputViniRequest,
                MessageType.MoneroTransactionAllInputsSetRequest,
            ),
        )

    elif msg.MESSAGE_WIRE_TYPE == MessageType.MoneroTransactionAllInputsSetRequest:
        from apps.monero.signing import step_05_all_inputs_set

        return (
            await step_05_all_inputs_set.all_inputs_set(state),
            (MessageType.MoneroTransactionSetOutputRequest,),
        )

    elif msg.MESSAGE_WIRE_TYPE == MessageType.MoneroTransactionSetOutputRequest:
        from apps.monero.signing import step_06_set_output

        is_offloaded_bp = bool(msg.is_offloaded_bp)
        dst, dst_hmac, rsig_data = msg.dst_entr, msg.dst_entr_hmac, msg.rsig_data
        del msg

        return (
            await step_06_set_output.set_output(
                state, dst, dst_hmac, rsig_data, is_offloaded_bp
            ),
            (
                MessageType.MoneroTransactionSetOutputRequest,
                MessageType.MoneroTransactionAllOutSetRequest,
            ),
        )

    elif msg.MESSAGE_WIRE_TYPE == MessageType.MoneroTransactionAllOutSetRequest:
        from apps.monero.signing import step_07_all_outputs_set

        return (
            await step_07_all_outputs_set.all_outputs_set(state),
            (MessageType.MoneroTransactionSignInputRequest,),
        )

    elif msg.MESSAGE_WIRE_TYPE == MessageType.MoneroTransactionSignInputRequest:
        from apps.monero.signing import step_09_sign_input

        return (
            await step_09_sign_input.sign_input(
                state,
                msg.src_entr,
                msg.vini,
                msg.vini_hmac,
                msg.pseudo_out,
                msg.pseudo_out_hmac,
                msg.pseudo_out_alpha,
                msg.spend_key,
                msg.orig_idx,
            ),
            (
                MessageType.MoneroTransactionSignInputRequest,
                MessageType.MoneroTransactionFinalRequest,
            ),
        )

    elif msg.MESSAGE_WIRE_TYPE == MessageType.MoneroTransactionFinalRequest:
        from apps.monero.signing import step_10_sign_final

        return await step_10_sign_final.final_msg(state), None

    else:
        raise wire.DataError("Unknown message")
Beispiel #6
0
    def __init__(
        self,
        script_pubkey: bytes,
        script_sig: bytes | None,
        witness: bytes | None,
        coin: CoinInfo,
    ):
        self.threshold = 1
        self.public_keys: list[bytes] = []
        self.signatures: list[tuple[bytes, int]] = []

        if not script_sig:
            if not witness:
                raise wire.DataError("Signature data not provided")

            if len(script_pubkey) == 22:  # P2WPKH
                public_key, signature, hash_type = parse_witness_p2wpkh(
                    witness)
                pubkey_hash = ecdsa_hash_pubkey(public_key, coin)
                if output_script_native_p2wpkh_or_p2wsh(
                        pubkey_hash) != script_pubkey:
                    raise wire.DataError("Invalid public key hash")
                self.public_keys = [public_key]
                self.signatures = [(signature, hash_type)]
            elif len(script_pubkey) == 34:  # P2WSH
                script, self.signatures = parse_witness_multisig(witness)
                script_hash = sha256(script).digest()
                if output_script_native_p2wpkh_or_p2wsh(
                        script_hash) != script_pubkey:
                    raise wire.DataError("Invalid script hash")
                self.public_keys, self.threshold = parse_output_script_multisig(
                    script)
            else:
                raise wire.DataError("Unsupported signature script")
        elif witness and witness != b"\x00":
            if len(script_sig) == 23:  # P2WPKH nested in BIP16 P2SH
                public_key, signature, hash_type = parse_witness_p2wpkh(
                    witness)
                pubkey_hash = ecdsa_hash_pubkey(public_key, coin)
                if input_script_p2wpkh_in_p2sh(pubkey_hash) != script_sig:
                    raise wire.DataError("Invalid public key hash")
                script_hash = coin.script_hash(script_sig[1:])
                if output_script_p2sh(script_hash) != script_pubkey:
                    raise wire.DataError("Invalid script hash")
                self.public_keys = [public_key]
                self.signatures = [(signature, hash_type)]
            elif len(script_sig) == 35:  # P2WSH nested in BIP16 P2SH
                script, self.signatures = parse_witness_multisig(witness)
                script_hash = sha256(script).digest()
                if input_script_p2wsh_in_p2sh(script_hash) != script_sig:
                    raise wire.DataError("Invalid script hash")
                script_hash = coin.script_hash(script_sig[1:])
                if output_script_p2sh(script_hash) != script_pubkey:
                    raise wire.DataError("Invalid script hash")
                self.public_keys, self.threshold = parse_output_script_multisig(
                    script)
            else:
                raise wire.DataError("Unsupported signature script")
        else:
            if len(script_pubkey) == 25:  # P2PKH
                public_key, signature, hash_type = parse_input_script_p2pkh(
                    script_sig)
                pubkey_hash = ecdsa_hash_pubkey(public_key, coin)
                if output_script_p2pkh(pubkey_hash) != script_pubkey:
                    raise wire.DataError("Invalid public key hash")
                self.public_keys = [public_key]
                self.signatures = [(signature, hash_type)]
            elif len(script_pubkey) == 23:  # P2SH
                script, self.signatures = parse_input_script_multisig(
                    script_sig)
                script_hash = coin.script_hash(script)
                if output_script_p2sh(script_hash) != script_pubkey:
                    raise wire.DataError("Invalid script hash")
                self.public_keys, self.threshold = parse_output_script_multisig(
                    script)
            else:
                raise wire.DataError("Unsupported signature script")

        if self.threshold != len(self.signatures):
            raise wire.DataError("Invalid signature")
async def get_ownership_proof(
    ctx: wire.Context,
    msg: GetOwnershipProof,
    keychain: Keychain,
    coin: CoinInfo,
    authorization: CoinJoinAuthorization | None = None,
) -> OwnershipProof:
    if authorization:
        if not authorization.check_get_ownership_proof(msg):
            raise wire.ProcessError("Unauthorized operation")
    else:
        await validate_path(
            ctx,
            keychain,
            msg.address_n,
            validate_path_against_script_type(coin, msg),
        )

    if msg.script_type not in common.INTERNAL_INPUT_SCRIPT_TYPES:
        raise wire.DataError("Invalid script type")

    if msg.script_type in common.SEGWIT_INPUT_SCRIPT_TYPES and not coin.segwit:
        raise wire.DataError("Segwit not enabled on this coin")

    node = keychain.derive(msg.address_n)
    address = addresses.get_address(msg.script_type, coin, node, msg.multisig)
    script_pubkey = scripts.output_derive_script(address, coin)
    ownership_id = get_identifier(script_pubkey, keychain)

    # If the scriptPubKey is multisig, then the caller has to provide
    # ownership IDs, otherwise providing an ID is optional.
    if msg.multisig:
        if ownership_id not in msg.ownership_ids:
            raise wire.DataError("Missing ownership identifier")
    elif msg.ownership_ids:
        if msg.ownership_ids != [ownership_id]:
            raise wire.DataError("Invalid ownership identifier")
    else:
        msg.ownership_ids = [ownership_id]

    # In order to set the "user confirmation" bit in the proof, the user must actually confirm.
    if msg.user_confirmation and not authorization:
        await confirm_action(
            ctx,
            "confirm_ownership_proof",
            title="Proof of ownership",
            description="Do you want to create a proof of ownership?",
        )
        if msg.commitment_data:
            await confirm_blob(
                ctx,
                "confirm_ownership_proof",
                title="Proof of ownership",
                description="Commitment data:",
                data=msg.commitment_data,
                icon=ui.ICON_CONFIG,
                icon_color=ui.ORANGE_ICON,
            )

    ownership_proof, signature = generate_proof(
        node,
        msg.script_type,
        msg.multisig,
        coin,
        msg.user_confirmation,
        msg.ownership_ids,
        script_pubkey,
        msg.commitment_data,
    )

    return OwnershipProof(ownership_proof=ownership_proof, signature=signature)
Beispiel #8
0
def encode_address(tag: int, S: bcncrypto.BcnPoint, Sv: bcncrypto.BcnPoint):
    if tag == 0:
        return bcncrypto.encode_address(6, S, Sv)
    if tag == 1:
        return bcncrypto.encode_address(572238, S, Sv)
    raise wire.DataError("Unknown address type")
Beispiel #9
0
def add_amount(sum: int, amount: int):
    if amount > 0xFFFFFFFFFFFFFFFF - sum:  # sum is safe, amount itself can be > 2^64-1
        raise wire.DataError("Amount overflow")
    sum += amount
    return sum
Beispiel #10
0
    def _add_output(self, txo: TxOutput, script_pubkey: bytes) -> None:
        super()._add_output(txo, script_pubkey)

        # All CoinJoin outputs must be accompanied by a signed payment request.
        if txo.payment_req_index is None:
            raise wire.DataError("Missing payment request.")
async def get_ownership_proof(
    ctx, msg: GetOwnershipProof, keychain: Keychain, coin: coininfo.CoinInfo
) -> OwnershipProof:
    await validate_path(
        ctx,
        addresses.validate_full_path,
        keychain,
        msg.address_n,
        coin.curve_name,
        coin=coin,
        script_type=msg.script_type,
    )

    if msg.script_type not in common.INTERNAL_INPUT_SCRIPT_TYPES:
        raise wire.DataError("Invalid script type")

    if msg.script_type in common.SEGWIT_INPUT_SCRIPT_TYPES and not coin.segwit:
        raise wire.DataError("Segwit not enabled on this coin")

    node = keychain.derive(msg.address_n)
    address = addresses.get_address(msg.script_type, coin, node, msg.multisig)
    script_pubkey = scripts.output_derive_script(address, coin)
    ownership_id = get_identifier(script_pubkey, keychain)

    # If the scriptPubKey is multisig, then the caller has to provide
    # ownership IDs, otherwise providing an ID is optional.
    if msg.multisig:
        if ownership_id not in msg.ownership_ids:
            raise wire.DataError("Missing ownership identifier")
    elif msg.ownership_ids:
        if msg.ownership_ids != [ownership_id]:
            raise wire.DataError("Invalid ownership identifier")
    else:
        msg.ownership_ids = [ownership_id]

    # In order to set the "user confirmation" bit in the proof, the user must actually confirm.
    if msg.user_confirmation:
        text = Text("Proof of ownership", ui.ICON_CONFIG)
        text.normal("Do you want to create a")
        if not msg.commitment_data:
            text.normal("proof of ownership?")
        else:
            hex_data = hexlify(msg.commitment_data).decode()
            text.normal("proof of ownership for:")
            if len(hex_data) > 3 * _MAX_MONO_LINE:
                text.mono(hex_data[0:_MAX_MONO_LINE])
                text.mono(
                    hex_data[_MAX_MONO_LINE : 3 * _MAX_MONO_LINE // 2 - 1]
                    + "..."
                    + hex_data[-3 * _MAX_MONO_LINE // 2 + 2 : -_MAX_MONO_LINE]
                )
                text.mono(hex_data[-_MAX_MONO_LINE:])
            else:
                text.mono(hex_data)

        await require_confirm(ctx, text)

    ownership_proof, signature = generate_proof(
        node,
        msg.script_type,
        msg.multisig,
        coin,
        msg.user_confirmation,
        msg.ownership_ids,
        script_pubkey,
        msg.commitment_data,
    )

    return OwnershipProof(ownership_proof=ownership_proof, signature=signature)
Beispiel #12
0
 async def process_external_input(self, txi: TxInput) -> None:
     raise wire.DataError("External inputs not supported")
Beispiel #13
0
    def hash143_preimage_hash(self, txi: TxInputType, public_keys: List[bytes],
                              threshold: int) -> bytes:
        h_preimage = HashWriter(
            blake2b(
                outlen=32,
                personal=b"ZcashSigHash" +
                struct.pack("<I", self.tx.branch_id),
            ))

        # 1. nVersion | fOverwintered
        write_uint32(h_preimage, self.tx.version | OVERWINTERED)
        # 2. nVersionGroupId
        write_uint32(h_preimage, self.tx.version_group_id)
        # 3. hashPrevouts
        write_bytes_fixed(h_preimage, get_tx_hash(self.h_prevouts),
                          TX_HASH_SIZE)
        # 4. hashSequence
        write_bytes_fixed(h_preimage, get_tx_hash(self.h_sequence),
                          TX_HASH_SIZE)
        # 5. hashOutputs
        write_bytes_fixed(h_preimage, get_tx_hash(self.h_outputs),
                          TX_HASH_SIZE)

        if self.tx.version == 3:
            # 6. hashJoinSplits
            write_bytes_fixed(h_preimage, b"\x00" * TX_HASH_SIZE, TX_HASH_SIZE)
            # 7. nLockTime
            write_uint32(h_preimage, self.tx.lock_time)
            # 8. expiryHeight
            write_uint32(h_preimage, self.tx.expiry)
            # 9. nHashType
            write_uint32(h_preimage, self.get_sighash_type(txi))
        elif self.tx.version == 4:
            zero_hash = b"\x00" * TX_HASH_SIZE
            # 6. hashJoinSplits
            write_bytes_fixed(h_preimage, zero_hash, TX_HASH_SIZE)
            # 7. hashShieldedSpends
            write_bytes_fixed(h_preimage, zero_hash, TX_HASH_SIZE)
            # 8. hashShieldedOutputs
            write_bytes_fixed(h_preimage, zero_hash, TX_HASH_SIZE)
            # 9. nLockTime
            write_uint32(h_preimage, self.tx.lock_time)
            # 10. expiryHeight
            write_uint32(h_preimage, self.tx.expiry)
            # 11. valueBalance
            write_uint64(h_preimage, 0)
            # 12. nHashType
            write_uint32(h_preimage, self.get_sighash_type(txi))
        else:
            raise wire.DataError(
                "Unsupported version for overwintered transaction")

        # 10a /13a. outpoint
        write_bytes_reversed(h_preimage, txi.prev_hash, TX_HASH_SIZE)
        write_uint32(h_preimage, txi.prev_index)

        # 10b / 13b. scriptCode
        script_code = derive_script_code(txi, public_keys, threshold,
                                         self.coin)
        write_bytes_prefixed(h_preimage, script_code)

        # 10c / 13c. value
        write_uint64(h_preimage, txi.amount)

        # 10d / 13d. nSequence
        write_uint32(h_preimage, txi.sequence)

        return get_tx_hash(h_preimage)
Beispiel #14
0
def sanitize_tx_output(txo: TxOutput, coin: CoinInfo) -> TxOutput:
    if txo.multisig and txo.script_type not in common.MULTISIG_OUTPUT_SCRIPT_TYPES:
        raise wire.DataError("Multisig field provided but not expected.")

    if not txo.multisig and txo.script_type == OutputScriptType.PAYTOMULTISIG:
        raise wire.DataError("Multisig details required.")

    if txo.address_n and txo.script_type not in common.CHANGE_OUTPUT_SCRIPT_TYPES:
        raise wire.DataError("Output's address_n provided but not expected.")

    if txo.amount is None:
        raise wire.DataError("Missing amount field.")

    if txo.script_type in common.SEGWIT_OUTPUT_SCRIPT_TYPES:
        if not coin.segwit:
            raise wire.DataError("Segwit not enabled on this coin.")

    if txo.script_type == OutputScriptType.PAYTOTAPROOT and not coin.taproot:
        raise wire.DataError("Taproot not enabled on this coin")

    if txo.script_type == OutputScriptType.PAYTOOPRETURN:
        # op_return output
        if txo.op_return_data is None:
            raise wire.DataError("OP_RETURN output without op_return_data")
        if txo.amount != 0:
            raise wire.DataError("OP_RETURN output with non-zero amount")
        if txo.address or txo.address_n or txo.multisig:
            raise wire.DataError("OP_RETURN output with address or multisig")
    else:
        if txo.op_return_data:
            raise wire.DataError(
                "OP RETURN data provided but not OP RETURN script type.")
        if txo.address_n and txo.address:
            raise wire.DataError("Both address and address_n provided.")
        if not txo.address_n and not txo.address:
            raise wire.DataError("Missing address")

    if txo.orig_hash and txo.orig_index is None:
        raise wire.DataError("Missing orig_index field.")

    return txo
Beispiel #15
0
 def verify_path(self, path: Bip32Path) -> None:
     if not self.is_in_keychain(path):
         raise wire.DataError("Forbidden key path")
Beispiel #16
0
async def sign_tx(ctx, msg, keychain):
    await paths.validate_path(ctx,
                              helpers.validate_full_path,
                              path=msg.address_n)

    node = keychain.derive(msg.address_n, helpers.TEZOS_CURVE)

    if msg.transaction is not None:
        to = _get_address_from_contract(msg.transaction.destination)
        await layout.require_confirm_tx(ctx, to, msg.transaction.amount)
        await layout.require_confirm_fee(ctx, msg.transaction.amount,
                                         msg.transaction.fee)

    elif msg.origination is not None:
        source = _get_address_from_contract(msg.origination.source)
        await layout.require_confirm_origination(ctx, source)

        # if we are immediately delegating contract
        if msg.origination.delegate is not None:
            delegate = _get_address_by_tag(msg.origination.delegate)
            await layout.require_confirm_delegation_baker(ctx, delegate)

        await layout.require_confirm_origination_fee(ctx,
                                                     msg.origination.balance,
                                                     msg.origination.fee)

    elif msg.delegation is not None:
        source = _get_address_from_contract(msg.delegation.source)

        delegate = None
        if msg.delegation.delegate is not None:
            delegate = _get_address_by_tag(msg.delegation.delegate)

        if delegate is not None and source != delegate:
            await layout.require_confirm_delegation_baker(ctx, delegate)
            await layout.require_confirm_set_delegate(ctx, msg.delegation.fee)
        # if account registers itself as a delegate
        else:
            await layout.require_confirm_register_delegate(
                ctx, source, msg.delegation.fee)

    else:
        raise wire.DataError("Invalid operation")

    w = bytearray()
    _get_operation_bytes(w, msg)

    opbytes = bytes(w)

    # watermark 0x03 is prefix for transactions, delegations, originations, reveals...
    watermark = bytes([3])
    wm_opbytes = watermark + opbytes
    wm_opbytes_hash = hashlib.blake2b(wm_opbytes, outlen=32).digest()

    signature = ed25519.sign(node.private_key(), wm_opbytes_hash)

    sig_op_contents = opbytes + signature
    sig_op_contents_hash = hashlib.blake2b(sig_op_contents, outlen=32).digest()
    ophash = helpers.base58_encode_check(sig_op_contents_hash, prefix="o")

    sig_prefixed = helpers.base58_encode_check(
        signature, prefix=helpers.TEZOS_SIGNATURE_PREFIX)

    return TezosSignedTx(signature=sig_prefixed,
                         sig_op_contents=sig_op_contents,
                         operation_hash=ophash)
Beispiel #17
0
 def ensure_hash_type(self, hash_type: int) -> None:
     if any(h != hash_type for _, h in self.signatures):
         raise wire.DataError("Unsupported sighash type")
Beispiel #18
0
async def _fail_or_warn_path(ctx: wire.Context, path: list[int],
                             path_name: str) -> None:
    if safety_checks.is_strict():
        raise wire.DataError(f"Invalid {path_name.lower()}")
    else:
        await show_warning_path(ctx, path, path_name)
Beispiel #19
0
    async def approve_tx(self, tx_info: TxInfo, orig_txs: list[OriginalTxInfo]) -> None:
        fee = self.total_in - self.total_out

        # some coins require negative fees for reward TX
        if fee < 0 and not self.coin.negative_fee:
            raise wire.NotEnoughFunds("Not enough funds")

        total = self.total_in - self.change_out
        spending = total - self.external_in
        # fee_threshold = (coin.maxfee per byte * tx size)
        fee_threshold = (self.coin.maxfee_kb / 1000) * (self.weight.get_total() / 4)

        # fee > (coin.maxfee per byte * tx size)
        if fee > fee_threshold:
            if fee > 10 * fee_threshold and safety_checks.is_strict():
                raise wire.DataError("The fee is unexpectedly large")
            await helpers.confirm_feeoverthreshold(fee, self.coin, self.amount_unit)

        if self.change_count > self.MAX_SILENT_CHANGE_COUNT:
            await helpers.confirm_change_count_over_threshold(self.change_count)

        if orig_txs:
            # Replacement transaction.
            orig_spending = (
                self.orig_total_in - self.orig_change_out - self.orig_external_in
            )
            orig_fee = self.orig_total_in - self.orig_total_out

            if fee < 0 or orig_fee < 0:
                raise wire.ProcessError(
                    "Negative fees not supported in transaction replacement."
                )

            # Replacement transactions are only allowed to make amendments which
            # do not increase the amount that we are spending on external outputs.
            # In other words, the total amount being sent out of the wallet must
            # not increase by more than the fee difference (so additional funds
            # can only go towards the fee, which is confirmed by the user).
            if spending - orig_spending > fee - orig_fee:
                raise wire.ProcessError("Invalid replacement transaction.")

            # Replacement transactions must not change the effective nLockTime.
            lock_time = 0 if tx_info.lock_time_disabled() else tx_info.tx.lock_time
            for orig in orig_txs:
                orig_lock_time = 0 if orig.lock_time_disabled() else orig.tx.lock_time
                if lock_time != orig_lock_time:
                    raise wire.ProcessError(
                        "Original transactions must have same effective nLockTime as replacement transaction."
                    )

            if not self.is_payjoin():
                # Not a PayJoin: Show the actual fee difference, since any difference in the fee is
                # coming entirely from the user's own funds and from decreases of external outputs.
                # We consider the decreases as belonging to the user.
                await helpers.confirm_modify_fee(
                    fee - orig_fee, fee, self.coin, self.amount_unit
                )
            elif spending > orig_spending:
                # PayJoin and user is spending more: Show the increase in the user's contribution
                # to the fee, ignoring any contribution from external inputs. Decreasing of
                # external outputs is not allowed in PayJoin, so there is no need to handle those.
                await helpers.confirm_modify_fee(
                    spending - orig_spending, fee, self.coin, self.amount_unit
                )
            else:
                # PayJoin and user is not spending more: When new external inputs are involved and
                # the user is paying less, the scenario can be open to multiple interpretations and
                # the dialog would likely cause more confusion than what it's worth, see PR #1292.
                pass
        else:
            # Standard transaction.
            if tx_info.tx.lock_time > 0:
                await helpers.confirm_nondefault_locktime(
                    tx_info.tx.lock_time, tx_info.lock_time_disabled()
                )

            if not self.external_in:
                await helpers.confirm_total(total, fee, self.coin, self.amount_unit)
            else:
                await helpers.confirm_joint_total(
                    spending, total, self.coin, self.amount_unit
                )
Beispiel #20
0
    async def process_internal_input(self, txi: TxInput) -> None:
        if txi.script_type not in common.INTERNAL_INPUT_SCRIPT_TYPES:
            raise wire.DataError("Wrong input script type")

        await self.approver.add_internal_input(txi)
Beispiel #21
0
async def apply_settings(ctx: wire.Context, msg: ApplySettings) -> Success:
    if not storage.device.is_initialized():
        raise wire.NotInitialized("Device is not initialized")
    if (msg.homescreen is None and msg.label is None
            and msg.use_passphrase is None
            and msg.passphrase_always_on_device is None
            and msg.display_rotation is None and msg.auto_lock_delay_ms is None
            and msg.safety_checks is None
            and msg.experimental_features is None):
        raise wire.ProcessError("No setting provided")

    if msg.homescreen is not None:
        validate_homescreen(msg.homescreen)
        await require_confirm_change_homescreen(ctx)
        try:
            storage.device.set_homescreen(msg.homescreen)
        except ValueError:
            raise wire.DataError("Invalid homescreen")

    if msg.label is not None:
        if len(msg.label) > storage.device.LABEL_MAXLENGTH:
            raise wire.DataError("Label too long")
        await require_confirm_change_label(ctx, msg.label)
        storage.device.set_label(msg.label)

    if msg.use_passphrase is not None:
        await require_confirm_change_passphrase(ctx, msg.use_passphrase)
        storage.device.set_passphrase_enabled(msg.use_passphrase)

    if msg.passphrase_always_on_device is not None:
        if not storage.device.is_passphrase_enabled():
            raise wire.DataError("Passphrase is not enabled")
        await require_confirm_change_passphrase_source(
            ctx, msg.passphrase_always_on_device)
        storage.device.set_passphrase_always_on_device(
            msg.passphrase_always_on_device)

    if msg.auto_lock_delay_ms is not None:
        if msg.auto_lock_delay_ms < storage.device.AUTOLOCK_DELAY_MINIMUM:
            raise wire.ProcessError("Auto-lock delay too short")
        if msg.auto_lock_delay_ms > storage.device.AUTOLOCK_DELAY_MAXIMUM:
            raise wire.ProcessError("Auto-lock delay too long")
        await require_confirm_change_autolock_delay(ctx,
                                                    msg.auto_lock_delay_ms)
        storage.device.set_autolock_delay_ms(msg.auto_lock_delay_ms)

    if msg.safety_checks is not None:
        await require_confirm_safety_checks(ctx, msg.safety_checks)
        safety_checks.apply_setting(msg.safety_checks)

    if msg.display_rotation is not None:
        await require_confirm_change_display_rotation(ctx,
                                                      msg.display_rotation)
        storage.device.set_rotation(msg.display_rotation)

    if msg.experimental_features is not None:
        await require_confirm_experimental_features(ctx,
                                                    msg.experimental_features)
        storage.device.set_experimental_features(msg.experimental_features)

    reload_settings_from_storage()

    return Success(message="Settings applied")
 def verify_path(self, path: Bip32Path) -> None:
     if not is_byron_path(path) and not is_shelley_path(path):
         raise wire.DataError("Forbidden key path")
Beispiel #23
0
 def validate_path(self, checked_path: list, checked_curve: str):
     if checked_curve != CURVE or checked_path[:2] != SEED_NAMESPACE:
         raise wire.DataError("Forbidden key path")
 def ensure_hash_type(self, sighash_types: Sequence[SigHashType]) -> None:
     if any(h not in sighash_types for _, h in self.signatures):
         raise wire.DataError("Unsupported sighash type")
Beispiel #25
0
    async def approve_tx(self, tx_info: TxInfo, orig_txs: List[OriginalTxInfo]) -> None:
        fee = self.total_in - self.total_out

        # some coins require negative fees for reward TX
        if fee < 0 and not self.coin.negative_fee:
            raise wire.NotEnoughFunds("Not enough funds")

        total = self.total_in - self.change_out
        spending = total - self.external_in
        # fee_threshold = (coin.maxfee per byte * tx size)
        fee_threshold = (self.coin.maxfee_kb / 1000) * (self.weight.get_total() / 4)

        # fee > (coin.maxfee per byte * tx size)
        if fee > fee_threshold:
            if fee > 10 * fee_threshold and safety_checks.is_strict():
                raise wire.DataError("The fee is unexpectedly large")
            await helpers.confirm_feeoverthreshold(fee, self.coin)

        if self.change_count > self.MAX_SILENT_CHANGE_COUNT:
            await helpers.confirm_change_count_over_threshold(self.change_count)

        if orig_txs:
            # Replacement transaction.
            orig_spending = (
                self.orig_total_in - self.orig_change_out - self.orig_external_in
            )
            orig_fee = self.orig_total_in - self.orig_total_out

            # Replacement transactions are only allowed to make amendments which
            # do not increase the amount that we are spending on external outputs.
            # In other words, the total amount being sent out of the wallet must
            # not increase by more than the fee difference (so additional funds
            # can only go towards the fee, which is confirmed by the user).
            if spending - orig_spending > fee - orig_fee:
                raise wire.ProcessError("Invalid replacement transaction.")

            # Replacement transactions must not change the effective nLockTime.
            lock_time = 0 if tx_info.lock_time_disabled() else tx_info.tx.lock_time
            for orig in orig_txs:
                orig_lock_time = 0 if orig.lock_time_disabled() else orig.tx.lock_time
                if lock_time != orig_lock_time:
                    raise wire.ProcessError(
                        "Original transactions must have same effective nLockTime as replacement transaction."
                    )

            if self.external_in > self.orig_external_in:
                description = "PayJoin"
            elif len(orig_txs) > 1:
                description = "Transaction meld"
            else:
                description = "Fee modification"

            for orig in orig_txs:
                await helpers.confirm_replacement(description, orig.orig_hash)

            # Always ask the user to confirm when they are paying more towards the fee.
            # If they are not spending more, then ask for confirmation only if it's not
            # a PayJoin. In complex scenarios where the user is not spending more and
            # there are new external inputs the scenario can be open to multiple
            # interpretations and the dialog would likely cause more confusion than
            # what it's worth, see PR #1292.
            if spending > orig_spending or self.external_in == self.orig_external_in:
                await helpers.confirm_modify_fee(
                    spending - orig_spending, fee, self.coin
                )
        else:
            # Standard transaction.
            if tx_info.tx.lock_time > 0:
                await helpers.confirm_nondefault_locktime(
                    tx_info.tx.lock_time, tx_info.lock_time_disabled()
                )

            if not self.external_in:
                await helpers.confirm_total(total, fee, self.coin)
            else:
                await helpers.confirm_joint_total(spending, total, self.coin)
 def verify_bip340(self, digest: bytes) -> None:
     if not bip340.verify(self.public_keys[0], self.signatures[0][0],
                          digest):
         raise wire.DataError("Invalid signature")
Beispiel #27
0
async def get_public_key(ctx: wire.Context, msg: GetPublicKey) -> PublicKey:
    coin_name = msg.coin_name or "Bitcoin"
    script_type = msg.script_type or InputScriptType.SPENDADDRESS
    coin = coininfo.by_name(coin_name)
    curve_name = msg.ecdsa_curve_name or coin.curve_name

    keychain = await get_keychain(ctx, curve_name, [paths.AlwaysMatchingSchema])

    node = keychain.derive(msg.address_n)

    if (
        script_type
        in (
            InputScriptType.SPENDADDRESS,
            InputScriptType.SPENDMULTISIG,
            InputScriptType.SPENDTAPROOT,
        )
        and coin.xpub_magic is not None
    ):
        node_xpub = node.serialize_public(coin.xpub_magic)
    elif (
        coin.segwit
        and script_type == InputScriptType.SPENDP2SHWITNESS
        and (msg.ignore_xpub_magic or coin.xpub_magic_segwit_p2sh is not None)
    ):
        # TODO: resolve type: ignore below
        node_xpub = node.serialize_public(
            coin.xpub_magic if msg.ignore_xpub_magic else coin.xpub_magic_segwit_p2sh  # type: ignore
        )
    elif (
        coin.segwit
        and script_type == InputScriptType.SPENDWITNESS
        and (msg.ignore_xpub_magic or coin.xpub_magic_segwit_native is not None)
    ):
        # TODO: resolve type: ignore below
        node_xpub = node.serialize_public(
            coin.xpub_magic if msg.ignore_xpub_magic else coin.xpub_magic_segwit_native  # type: ignore
        )
    else:
        raise wire.DataError("Invalid combination of coin and script_type")

    pubkey = node.public_key()
    if pubkey[0] == 1:
        pubkey = b"\x00" + pubkey[1:]
    node_type = HDNodeType(
        depth=node.depth(),
        child_num=node.child_num(),
        fingerprint=node.fingerprint(),
        chain_code=node.chain_code(),
        public_key=pubkey,
    )

    if msg.show_display:
        from trezor.ui.layouts import show_xpub

        await show_xpub(ctx, node_xpub, "XPUB", "Cancel")

    return PublicKey(
        node=node_type,
        xpub=node_xpub,
        root_fingerprint=keychain.root_fingerprint(),
    )
    def __init__(
        self,
        script_pubkey: bytes,
        script_sig: bytes | None,
        witness: bytes | None,
        coin: CoinInfo,
    ):
        self.threshold = 1
        self.public_keys: list[memoryview] = []
        self.signatures: list[tuple[memoryview, SigHashType]] = []
        self.is_taproot = False

        if not script_sig:
            if not witness:
                raise wire.DataError("Signature data not provided")

            if len(script_pubkey) == 22:  # P2WPKH
                public_key, signature, hash_type = parse_witness_p2wpkh(
                    witness)
                pubkey_hash = ecdsa_hash_pubkey(public_key, coin)
                if output_script_native_segwit(0,
                                               pubkey_hash) != script_pubkey:
                    raise wire.DataError("Invalid public key hash")
                self.public_keys = [public_key]
                self.signatures = [(signature, hash_type)]
            elif len(
                    script_pubkey) == 34 and script_pubkey[0] == OP_0:  # P2WSH
                script, self.signatures = parse_witness_multisig(witness)
                script_hash = sha256(script).digest()
                if output_script_native_segwit(0,
                                               script_hash) != script_pubkey:
                    raise wire.DataError("Invalid script hash")
                self.public_keys, self.threshold = parse_output_script_multisig(
                    script)
            elif len(script_pubkey) == 34 and script_pubkey[0] == OP_1:  # P2TR
                self.is_taproot = True
                self.public_keys = [parse_output_script_p2tr(script_pubkey)]
                self.signatures = [parse_witness_p2tr(witness)]
            else:
                raise wire.DataError("Unsupported signature script")
        elif witness and witness != b"\x00":
            if len(script_sig) == 23:  # P2WPKH nested in BIP16 P2SH
                public_key, signature, hash_type = parse_witness_p2wpkh(
                    witness)
                pubkey_hash = ecdsa_hash_pubkey(public_key, coin)
                w = utils.empty_bytearray(23)
                write_input_script_p2wpkh_in_p2sh(w, pubkey_hash)
                if w != script_sig:
                    raise wire.DataError("Invalid public key hash")
                script_hash = coin.script_hash(script_sig[1:]).digest()
                if output_script_p2sh(script_hash) != script_pubkey:
                    raise wire.DataError("Invalid script hash")
                self.public_keys = [public_key]
                self.signatures = [(signature, hash_type)]
            elif len(script_sig) == 35:  # P2WSH nested in BIP16 P2SH
                script, self.signatures = parse_witness_multisig(witness)
                script_hash = sha256(script).digest()
                w = utils.empty_bytearray(35)
                write_input_script_p2wsh_in_p2sh(w, script_hash)
                if w != script_sig:
                    raise wire.DataError("Invalid script hash")
                script_hash = coin.script_hash(script_sig[1:]).digest()
                if output_script_p2sh(script_hash) != script_pubkey:
                    raise wire.DataError("Invalid script hash")
                self.public_keys, self.threshold = parse_output_script_multisig(
                    script)
            else:
                raise wire.DataError("Unsupported signature script")
        else:
            if len(script_pubkey) == 25:  # P2PKH
                public_key, signature, hash_type = parse_input_script_p2pkh(
                    script_sig)
                pubkey_hash = ecdsa_hash_pubkey(public_key, coin)
                if output_script_p2pkh(pubkey_hash) != script_pubkey:
                    raise wire.DataError("Invalid public key hash")
                self.public_keys = [public_key]
                self.signatures = [(signature, hash_type)]
            elif len(script_pubkey) == 23:  # P2SH
                script, self.signatures = parse_input_script_multisig(
                    script_sig)
                script_hash = coin.script_hash(script).digest()
                if output_script_p2sh(script_hash) != script_pubkey:
                    raise wire.DataError("Invalid script hash")
                self.public_keys, self.threshold = parse_output_script_multisig(
                    script)
            else:
                raise wire.DataError("Unsupported signature script")

        if self.threshold != len(self.signatures):
            raise wire.DataError("Invalid signature")
Beispiel #29
0
    NodeType = TypeVar("NodeType", bound=NodeProtocol)

    MsgIn = TypeVar("MsgIn", bound=MessageType)
    MsgOut = TypeVar("MsgOut", bound=MessageType)

    Handler = Callable[[wire.Context, MsgIn], Awaitable[MsgOut]]
    HandlerWithKeychain = Callable[[wire.Context, MsgIn, "Keychain"],
                                   Awaitable[MsgOut]]

    class Deletable(Protocol):
        def __del__(self) -> None:
            ...


FORBIDDEN_KEY_PATH = wire.DataError("Forbidden key path")


class LRUCache:
    def __init__(self, size: int) -> None:
        self.size = size
        self.cache_keys: list[Any] = []
        self.cache: dict[Any, Deletable] = {}

    def insert(self, key: Any, value: Deletable) -> None:
        if key in self.cache_keys:
            self.cache_keys.remove(key)
        self.cache_keys.insert(0, key)
        self.cache[key] = value

        if len(self.cache_keys) > self.size:
Beispiel #30
0
 async def process_original_input(self, txi: TxInput) -> None:
     raise wire.DataError("Replacement transactions not supported")