diff --git a/CHANGELOG.md b/CHANGELOG.md index b7e00c3c6..5135a28a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ customers cannot upgrade their bootloader, its changes are recorded separately. - simulator: simulate a Nova device - Add API call to fetch multiple xpubs at once - Add the option for the simulator to write its memory to file. +- Bitcoin: do not request previous transactions if all inputs are Taproot, even if an output paying + to the same account is not Taproot ### 9.23.1 - EVM: add HyperEVM (HYPE) and SONIC (S) to known networks diff --git a/messages/btc.proto b/messages/btc.proto index 24669fff7..51e521eb8 100644 --- a/messages/btc.proto +++ b/messages/btc.proto @@ -129,6 +129,19 @@ message BTCSignInitRequest { // used script configs for outputs that send to an address of the same keystore, but not // necessarily the same account (as defined by `script_configs` above). repeated BTCScriptConfigWithKeypath output_script_configs = 10; + + enum PrevTxs { + PREV_TXS_DEFAULT = 0; + PREV_TXS_REQUIRED = 1; + PREV_TXS_NOT_REQUIRED = 2; + } + // Set this to: + // - `PREV_TXS_DEFAULT` for compatibility mode, the value will be determined using `script_configs`. + // This is not robust, as non-Taproot change outputs are included there and falsely leads to + // previous transactions being required. + // - `PREV_TXS_REQUIRED` if not all transaction inputs are Taproot. + // - `PREV_TXS_NOT_REQUIRED` if all transaction inputs are Taproot. + PrevTxs prev_txs = 11; } message BTCSignNextResponse { diff --git a/py/bitbox02/bitbox02/bitbox02/bitbox02.py b/py/bitbox02/bitbox02/bitbox02/bitbox02.py index 753cde511..dc4b52a20 100644 --- a/py/bitbox02/bitbox02/bitbox02/bitbox02.py +++ b/py/bitbox02/bitbox02/bitbox02/bitbox02.py @@ -471,6 +471,9 @@ def btc_sign( sigs: List[Tuple[int, bytes]] = [] + all_inputs_are_taproot = all( + is_taproot(script_configs[inp["script_config_index"]]) for inp in inputs + ) # Init request request = hww.Request() request.btc_sign_init.CopyFrom( @@ -483,6 +486,11 @@ def btc_sign( locktime=locktime, format_unit=format_unit, output_script_configs=output_script_configs, + prev_txs=( + btc.BTCSignInitRequest.PrevTxs.PREV_TXS_NOT_REQUIRED + if all_inputs_are_taproot + else btc.BTCSignInitRequest.PrevTxs.PREV_TXS_REQUIRED + ), ) ) next_response = self._msg_query(request, expected_response="btc_sign_next").btc_sign_next diff --git a/py/bitbox02/bitbox02/communication/generated/btc_pb2.py b/py/bitbox02/bitbox02/communication/generated/btc_pb2.py index 45148fdd3..158ad7d7b 100644 --- a/py/bitbox02/bitbox02/communication/generated/btc_pb2.py +++ b/py/bitbox02/bitbox02/communication/generated/btc_pb2.py @@ -15,17 +15,17 @@ from . import antiklepto_pb2 as antiklepto__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tbtc.proto\x12\x14shiftcrypto.bitbox02\x1a\x0c\x63ommon.proto\x1a\x10\x61ntiklepto.proto\"\xc6\x04\n\x0f\x42TCScriptConfig\x12G\n\x0bsimple_type\x18\x01 \x01(\x0e\x32\x30.shiftcrypto.bitbox02.BTCScriptConfig.SimpleTypeH\x00\x12\x42\n\x08multisig\x18\x02 \x01(\x0b\x32..shiftcrypto.bitbox02.BTCScriptConfig.MultisigH\x00\x12>\n\x06policy\x18\x03 \x01(\x0b\x32,.shiftcrypto.bitbox02.BTCScriptConfig.PolicyH\x00\x1a\xd9\x01\n\x08Multisig\x12\x11\n\tthreshold\x18\x01 \x01(\r\x12)\n\x05xpubs\x18\x02 \x03(\x0b\x32\x1a.shiftcrypto.bitbox02.XPub\x12\x16\n\x0eour_xpub_index\x18\x03 \x01(\r\x12N\n\x0bscript_type\x18\x04 \x01(\x0e\x32\x39.shiftcrypto.bitbox02.BTCScriptConfig.Multisig.ScriptType\"\'\n\nScriptType\x12\t\n\x05P2WSH\x10\x00\x12\x0e\n\nP2WSH_P2SH\x10\x01\x1aK\n\x06Policy\x12\x0e\n\x06policy\x18\x01 \x01(\t\x12\x31\n\x04keys\x18\x02 \x03(\x0b\x32#.shiftcrypto.bitbox02.KeyOriginInfo\"3\n\nSimpleType\x12\x0f\n\x0bP2WPKH_P2SH\x10\x00\x12\n\n\x06P2WPKH\x10\x01\x12\x08\n\x04P2TR\x10\x02\x42\x08\n\x06\x63onfig\"\xfc\x02\n\rBTCPubRequest\x12+\n\x04\x63oin\x18\x01 \x01(\x0e\x32\x1d.shiftcrypto.bitbox02.BTCCoin\x12\x0f\n\x07keypath\x18\x02 \x03(\r\x12\x41\n\txpub_type\x18\x03 \x01(\x0e\x32,.shiftcrypto.bitbox02.BTCPubRequest.XPubTypeH\x00\x12>\n\rscript_config\x18\x04 \x01(\x0b\x32%.shiftcrypto.bitbox02.BTCScriptConfigH\x00\x12\x0f\n\x07\x64isplay\x18\x05 \x01(\x08\"\x8e\x01\n\x08XPubType\x12\x08\n\x04TPUB\x10\x00\x12\x08\n\x04XPUB\x10\x01\x12\x08\n\x04YPUB\x10\x02\x12\x08\n\x04ZPUB\x10\x03\x12\x08\n\x04VPUB\x10\x04\x12\x08\n\x04UPUB\x10\x05\x12\x10\n\x0c\x43\x41PITAL_VPUB\x10\x06\x12\x10\n\x0c\x43\x41PITAL_ZPUB\x10\x07\x12\x10\n\x0c\x43\x41PITAL_UPUB\x10\x08\x12\x10\n\x0c\x43\x41PITAL_YPUB\x10\tB\x08\n\x06output\"\xdf\x01\n\x0f\x42TCXpubsRequest\x12+\n\x04\x63oin\x18\x01 \x01(\x0e\x32\x1d.shiftcrypto.bitbox02.BTCCoin\x12\x41\n\txpub_type\x18\x02 \x01(\x0e\x32..shiftcrypto.bitbox02.BTCXpubsRequest.XPubType\x12/\n\x08keypaths\x18\x03 \x03(\x0b\x32\x1d.shiftcrypto.bitbox02.Keypath\"+\n\x08XPubType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x08\n\x04XPUB\x10\x01\x12\x08\n\x04TPUB\x10\x02\"k\n\x1a\x42TCScriptConfigWithKeypath\x12<\n\rscript_config\x18\x02 \x01(\x0b\x32%.shiftcrypto.bitbox02.BTCScriptConfig\x12\x0f\n\x07keypath\x18\x03 \x03(\r\"\xbf\x03\n\x12\x42TCSignInitRequest\x12+\n\x04\x63oin\x18\x01 \x01(\x0e\x32\x1d.shiftcrypto.bitbox02.BTCCoin\x12H\n\x0escript_configs\x18\x02 \x03(\x0b\x32\x30.shiftcrypto.bitbox02.BTCScriptConfigWithKeypath\x12\x0f\n\x07version\x18\x04 \x01(\r\x12\x12\n\nnum_inputs\x18\x05 \x01(\r\x12\x13\n\x0bnum_outputs\x18\x06 \x01(\r\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12H\n\x0b\x66ormat_unit\x18\x08 \x01(\x0e\x32\x33.shiftcrypto.bitbox02.BTCSignInitRequest.FormatUnit\x12\'\n\x1f\x63ontains_silent_payment_outputs\x18\t \x01(\x08\x12O\n\x15output_script_configs\x18\n \x03(\x0b\x32\x30.shiftcrypto.bitbox02.BTCScriptConfigWithKeypath\"\"\n\nFormatUnit\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x00\x12\x07\n\x03SAT\x10\x01\"\xc4\x03\n\x13\x42TCSignNextResponse\x12<\n\x04type\x18\x01 \x01(\x0e\x32..shiftcrypto.bitbox02.BTCSignNextResponse.Type\x12\r\n\x05index\x18\x02 \x01(\r\x12\x15\n\rhas_signature\x18\x03 \x01(\x08\x12\x11\n\tsignature\x18\x04 \x01(\x0c\x12\x12\n\nprev_index\x18\x05 \x01(\r\x12W\n\x1d\x61nti_klepto_signer_commitment\x18\x06 \x01(\x0b\x32\x30.shiftcrypto.bitbox02.AntiKleptoSignerCommitment\x12!\n\x19generated_output_pkscript\x18\x07 \x01(\x0c\x12!\n\x19silent_payment_dleq_proof\x18\x08 \x01(\x0c\"\x82\x01\n\x04Type\x12\t\n\x05INPUT\x10\x00\x12\n\n\x06OUTPUT\x10\x01\x12\x08\n\x04\x44ONE\x10\x02\x12\x0f\n\x0bPREVTX_INIT\x10\x03\x12\x10\n\x0cPREVTX_INPUT\x10\x04\x12\x11\n\rPREVTX_OUTPUT\x10\x05\x12\x0e\n\nHOST_NONCE\x10\x06\x12\x13\n\x0fPAYMENT_REQUEST\x10\x07\"\xea\x01\n\x13\x42TCSignInputRequest\x12\x13\n\x0bprevOutHash\x18\x01 \x01(\x0c\x12\x14\n\x0cprevOutIndex\x18\x02 \x01(\r\x12\x14\n\x0cprevOutValue\x18\x03 \x01(\x04\x12\x10\n\x08sequence\x18\x04 \x01(\r\x12\x0f\n\x07keypath\x18\x06 \x03(\r\x12\x1b\n\x13script_config_index\x18\x07 \x01(\r\x12R\n\x15host_nonce_commitment\x18\x08 \x01(\x0b\x32\x33.shiftcrypto.bitbox02.AntiKleptoHostNonceCommitment\"\x9f\x03\n\x14\x42TCSignOutputRequest\x12\x0c\n\x04ours\x18\x01 \x01(\x08\x12\x31\n\x04type\x18\x02 \x01(\x0e\x32#.shiftcrypto.bitbox02.BTCOutputType\x12\r\n\x05value\x18\x03 \x01(\x04\x12\x0f\n\x07payload\x18\x04 \x01(\x0c\x12\x0f\n\x07keypath\x18\x05 \x03(\r\x12\x1b\n\x13script_config_index\x18\x06 \x01(\r\x12\"\n\x15payment_request_index\x18\x07 \x01(\rH\x00\x88\x01\x01\x12P\n\x0esilent_payment\x18\x08 \x01(\x0b\x32\x38.shiftcrypto.bitbox02.BTCSignOutputRequest.SilentPayment\x12\'\n\x1aoutput_script_config_index\x18\t \x01(\rH\x01\x88\x01\x01\x1a \n\rSilentPayment\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\tB\x18\n\x16_payment_request_indexB\x1d\n\x1b_output_script_config_index\"\x99\x01\n\x1b\x42TCScriptConfigRegistration\x12+\n\x04\x63oin\x18\x01 \x01(\x0e\x32\x1d.shiftcrypto.bitbox02.BTCCoin\x12<\n\rscript_config\x18\x02 \x01(\x0b\x32%.shiftcrypto.bitbox02.BTCScriptConfig\x12\x0f\n\x07keypath\x18\x03 \x03(\r\"\x0c\n\nBTCSuccess\"m\n\"BTCIsScriptConfigRegisteredRequest\x12G\n\x0cregistration\x18\x01 \x01(\x0b\x32\x31.shiftcrypto.bitbox02.BTCScriptConfigRegistration\"<\n#BTCIsScriptConfigRegisteredResponse\x12\x15\n\ris_registered\x18\x01 \x01(\x08\"\xfc\x01\n\x1e\x42TCRegisterScriptConfigRequest\x12G\n\x0cregistration\x18\x01 \x01(\x0b\x32\x31.shiftcrypto.bitbox02.BTCScriptConfigRegistration\x12\x0c\n\x04name\x18\x02 \x01(\t\x12P\n\txpub_type\x18\x03 \x01(\x0e\x32=.shiftcrypto.bitbox02.BTCRegisterScriptConfigRequest.XPubType\"1\n\x08XPubType\x12\x11\n\rAUTO_ELECTRUM\x10\x00\x12\x12\n\x0e\x41UTO_XPUB_TPUB\x10\x01\"b\n\x14\x42TCPrevTxInitRequest\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x12\n\nnum_inputs\x18\x02 \x01(\r\x12\x13\n\x0bnum_outputs\x18\x03 \x01(\r\x12\x10\n\x08locktime\x18\x04 \x01(\r\"r\n\x15\x42TCPrevTxInputRequest\x12\x15\n\rprev_out_hash\x18\x01 \x01(\x0c\x12\x16\n\x0eprev_out_index\x18\x02 \x01(\r\x12\x18\n\x10signature_script\x18\x03 \x01(\x0c\x12\x10\n\x08sequence\x18\x04 \x01(\r\">\n\x16\x42TCPrevTxOutputRequest\x12\r\n\x05value\x18\x01 \x01(\x04\x12\x15\n\rpubkey_script\x18\x02 \x01(\x0c\"\xab\x02\n\x18\x42TCPaymentRequestRequest\x12\x16\n\x0erecipient_name\x18\x01 \x01(\t\x12\x42\n\x05memos\x18\x02 \x03(\x0b\x32\x33.shiftcrypto.bitbox02.BTCPaymentRequestRequest.Memo\x12\r\n\x05nonce\x18\x03 \x01(\x0c\x12\x14\n\x0ctotal_amount\x18\x04 \x01(\x04\x12\x11\n\tsignature\x18\x05 \x01(\x0c\x1a{\n\x04Memo\x12Q\n\ttext_memo\x18\x01 \x01(\x0b\x32<.shiftcrypto.bitbox02.BTCPaymentRequestRequest.Memo.TextMemoH\x00\x1a\x18\n\x08TextMemo\x12\x0c\n\x04note\x18\x01 \x01(\tB\x06\n\x04memo\"\xee\x01\n\x15\x42TCSignMessageRequest\x12+\n\x04\x63oin\x18\x01 \x01(\x0e\x32\x1d.shiftcrypto.bitbox02.BTCCoin\x12G\n\rscript_config\x18\x02 \x01(\x0b\x32\x30.shiftcrypto.bitbox02.BTCScriptConfigWithKeypath\x12\x0b\n\x03msg\x18\x03 \x01(\x0c\x12R\n\x15host_nonce_commitment\x18\x04 \x01(\x0b\x32\x33.shiftcrypto.bitbox02.AntiKleptoHostNonceCommitment\"+\n\x16\x42TCSignMessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\"\xb9\x05\n\nBTCRequest\x12_\n\x1bis_script_config_registered\x18\x01 \x01(\x0b\x32\x38.shiftcrypto.bitbox02.BTCIsScriptConfigRegisteredRequestH\x00\x12V\n\x16register_script_config\x18\x02 \x01(\x0b\x32\x34.shiftcrypto.bitbox02.BTCRegisterScriptConfigRequestH\x00\x12\x41\n\x0bprevtx_init\x18\x03 \x01(\x0b\x32*.shiftcrypto.bitbox02.BTCPrevTxInitRequestH\x00\x12\x43\n\x0cprevtx_input\x18\x04 \x01(\x0b\x32+.shiftcrypto.bitbox02.BTCPrevTxInputRequestH\x00\x12\x45\n\rprevtx_output\x18\x05 \x01(\x0b\x32,.shiftcrypto.bitbox02.BTCPrevTxOutputRequestH\x00\x12\x43\n\x0csign_message\x18\x06 \x01(\x0b\x32+.shiftcrypto.bitbox02.BTCSignMessageRequestH\x00\x12P\n\x14\x61ntiklepto_signature\x18\x07 \x01(\x0b\x32\x30.shiftcrypto.bitbox02.AntiKleptoSignatureRequestH\x00\x12I\n\x0fpayment_request\x18\x08 \x01(\x0b\x32..shiftcrypto.bitbox02.BTCPaymentRequestRequestH\x00\x12\x36\n\x05xpubs\x18\t \x01(\x0b\x32%.shiftcrypto.bitbox02.BTCXpubsRequestH\x00\x42\t\n\x07request\"\xc4\x03\n\x0b\x42TCResponse\x12\x33\n\x07success\x18\x01 \x01(\x0b\x32 .shiftcrypto.bitbox02.BTCSuccessH\x00\x12`\n\x1bis_script_config_registered\x18\x02 \x01(\x0b\x32\x39.shiftcrypto.bitbox02.BTCIsScriptConfigRegisteredResponseH\x00\x12>\n\tsign_next\x18\x03 \x01(\x0b\x32).shiftcrypto.bitbox02.BTCSignNextResponseH\x00\x12\x44\n\x0csign_message\x18\x04 \x01(\x0b\x32,.shiftcrypto.bitbox02.BTCSignMessageResponseH\x00\x12X\n\x1c\x61ntiklepto_signer_commitment\x18\x05 \x01(\x0b\x32\x30.shiftcrypto.bitbox02.AntiKleptoSignerCommitmentH\x00\x12\x32\n\x04pubs\x18\x06 \x01(\x0b\x32\".shiftcrypto.bitbox02.PubsResponseH\x00\x42\n\n\x08response*9\n\x07\x42TCCoin\x12\x07\n\x03\x42TC\x10\x00\x12\x08\n\x04TBTC\x10\x01\x12\x07\n\x03LTC\x10\x02\x12\x08\n\x04TLTC\x10\x03\x12\x08\n\x04RBTC\x10\x04*R\n\rBTCOutputType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\t\n\x05P2PKH\x10\x01\x12\x08\n\x04P2SH\x10\x02\x12\n\n\x06P2WPKH\x10\x03\x12\t\n\x05P2WSH\x10\x04\x12\x08\n\x04P2TR\x10\x05\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tbtc.proto\x12\x14shiftcrypto.bitbox02\x1a\x0c\x63ommon.proto\x1a\x10\x61ntiklepto.proto\"\xc6\x04\n\x0f\x42TCScriptConfig\x12G\n\x0bsimple_type\x18\x01 \x01(\x0e\x32\x30.shiftcrypto.bitbox02.BTCScriptConfig.SimpleTypeH\x00\x12\x42\n\x08multisig\x18\x02 \x01(\x0b\x32..shiftcrypto.bitbox02.BTCScriptConfig.MultisigH\x00\x12>\n\x06policy\x18\x03 \x01(\x0b\x32,.shiftcrypto.bitbox02.BTCScriptConfig.PolicyH\x00\x1a\xd9\x01\n\x08Multisig\x12\x11\n\tthreshold\x18\x01 \x01(\r\x12)\n\x05xpubs\x18\x02 \x03(\x0b\x32\x1a.shiftcrypto.bitbox02.XPub\x12\x16\n\x0eour_xpub_index\x18\x03 \x01(\r\x12N\n\x0bscript_type\x18\x04 \x01(\x0e\x32\x39.shiftcrypto.bitbox02.BTCScriptConfig.Multisig.ScriptType\"\'\n\nScriptType\x12\t\n\x05P2WSH\x10\x00\x12\x0e\n\nP2WSH_P2SH\x10\x01\x1aK\n\x06Policy\x12\x0e\n\x06policy\x18\x01 \x01(\t\x12\x31\n\x04keys\x18\x02 \x03(\x0b\x32#.shiftcrypto.bitbox02.KeyOriginInfo\"3\n\nSimpleType\x12\x0f\n\x0bP2WPKH_P2SH\x10\x00\x12\n\n\x06P2WPKH\x10\x01\x12\x08\n\x04P2TR\x10\x02\x42\x08\n\x06\x63onfig\"\xfc\x02\n\rBTCPubRequest\x12+\n\x04\x63oin\x18\x01 \x01(\x0e\x32\x1d.shiftcrypto.bitbox02.BTCCoin\x12\x0f\n\x07keypath\x18\x02 \x03(\r\x12\x41\n\txpub_type\x18\x03 \x01(\x0e\x32,.shiftcrypto.bitbox02.BTCPubRequest.XPubTypeH\x00\x12>\n\rscript_config\x18\x04 \x01(\x0b\x32%.shiftcrypto.bitbox02.BTCScriptConfigH\x00\x12\x0f\n\x07\x64isplay\x18\x05 \x01(\x08\"\x8e\x01\n\x08XPubType\x12\x08\n\x04TPUB\x10\x00\x12\x08\n\x04XPUB\x10\x01\x12\x08\n\x04YPUB\x10\x02\x12\x08\n\x04ZPUB\x10\x03\x12\x08\n\x04VPUB\x10\x04\x12\x08\n\x04UPUB\x10\x05\x12\x10\n\x0c\x43\x41PITAL_VPUB\x10\x06\x12\x10\n\x0c\x43\x41PITAL_ZPUB\x10\x07\x12\x10\n\x0c\x43\x41PITAL_UPUB\x10\x08\x12\x10\n\x0c\x43\x41PITAL_YPUB\x10\tB\x08\n\x06output\"\xdf\x01\n\x0f\x42TCXpubsRequest\x12+\n\x04\x63oin\x18\x01 \x01(\x0e\x32\x1d.shiftcrypto.bitbox02.BTCCoin\x12\x41\n\txpub_type\x18\x02 \x01(\x0e\x32..shiftcrypto.bitbox02.BTCXpubsRequest.XPubType\x12/\n\x08keypaths\x18\x03 \x03(\x0b\x32\x1d.shiftcrypto.bitbox02.Keypath\"+\n\x08XPubType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x08\n\x04XPUB\x10\x01\x12\x08\n\x04TPUB\x10\x02\"k\n\x1a\x42TCScriptConfigWithKeypath\x12<\n\rscript_config\x18\x02 \x01(\x0b\x32%.shiftcrypto.bitbox02.BTCScriptConfig\x12\x0f\n\x07keypath\x18\x03 \x03(\r\"\xd6\x04\n\x12\x42TCSignInitRequest\x12+\n\x04\x63oin\x18\x01 \x01(\x0e\x32\x1d.shiftcrypto.bitbox02.BTCCoin\x12H\n\x0escript_configs\x18\x02 \x03(\x0b\x32\x30.shiftcrypto.bitbox02.BTCScriptConfigWithKeypath\x12\x0f\n\x07version\x18\x04 \x01(\r\x12\x12\n\nnum_inputs\x18\x05 \x01(\r\x12\x13\n\x0bnum_outputs\x18\x06 \x01(\r\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12H\n\x0b\x66ormat_unit\x18\x08 \x01(\x0e\x32\x33.shiftcrypto.bitbox02.BTCSignInitRequest.FormatUnit\x12\'\n\x1f\x63ontains_silent_payment_outputs\x18\t \x01(\x08\x12O\n\x15output_script_configs\x18\n \x03(\x0b\x32\x30.shiftcrypto.bitbox02.BTCScriptConfigWithKeypath\x12\x42\n\x08prev_txs\x18\x0b \x01(\x0e\x32\x30.shiftcrypto.bitbox02.BTCSignInitRequest.PrevTxs\"\"\n\nFormatUnit\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x00\x12\x07\n\x03SAT\x10\x01\"Q\n\x07PrevTxs\x12\x14\n\x10PREV_TXS_DEFAULT\x10\x00\x12\x15\n\x11PREV_TXS_REQUIRED\x10\x01\x12\x19\n\x15PREV_TXS_NOT_REQUIRED\x10\x02\"\xc4\x03\n\x13\x42TCSignNextResponse\x12<\n\x04type\x18\x01 \x01(\x0e\x32..shiftcrypto.bitbox02.BTCSignNextResponse.Type\x12\r\n\x05index\x18\x02 \x01(\r\x12\x15\n\rhas_signature\x18\x03 \x01(\x08\x12\x11\n\tsignature\x18\x04 \x01(\x0c\x12\x12\n\nprev_index\x18\x05 \x01(\r\x12W\n\x1d\x61nti_klepto_signer_commitment\x18\x06 \x01(\x0b\x32\x30.shiftcrypto.bitbox02.AntiKleptoSignerCommitment\x12!\n\x19generated_output_pkscript\x18\x07 \x01(\x0c\x12!\n\x19silent_payment_dleq_proof\x18\x08 \x01(\x0c\"\x82\x01\n\x04Type\x12\t\n\x05INPUT\x10\x00\x12\n\n\x06OUTPUT\x10\x01\x12\x08\n\x04\x44ONE\x10\x02\x12\x0f\n\x0bPREVTX_INIT\x10\x03\x12\x10\n\x0cPREVTX_INPUT\x10\x04\x12\x11\n\rPREVTX_OUTPUT\x10\x05\x12\x0e\n\nHOST_NONCE\x10\x06\x12\x13\n\x0fPAYMENT_REQUEST\x10\x07\"\xea\x01\n\x13\x42TCSignInputRequest\x12\x13\n\x0bprevOutHash\x18\x01 \x01(\x0c\x12\x14\n\x0cprevOutIndex\x18\x02 \x01(\r\x12\x14\n\x0cprevOutValue\x18\x03 \x01(\x04\x12\x10\n\x08sequence\x18\x04 \x01(\r\x12\x0f\n\x07keypath\x18\x06 \x03(\r\x12\x1b\n\x13script_config_index\x18\x07 \x01(\r\x12R\n\x15host_nonce_commitment\x18\x08 \x01(\x0b\x32\x33.shiftcrypto.bitbox02.AntiKleptoHostNonceCommitment\"\x9f\x03\n\x14\x42TCSignOutputRequest\x12\x0c\n\x04ours\x18\x01 \x01(\x08\x12\x31\n\x04type\x18\x02 \x01(\x0e\x32#.shiftcrypto.bitbox02.BTCOutputType\x12\r\n\x05value\x18\x03 \x01(\x04\x12\x0f\n\x07payload\x18\x04 \x01(\x0c\x12\x0f\n\x07keypath\x18\x05 \x03(\r\x12\x1b\n\x13script_config_index\x18\x06 \x01(\r\x12\"\n\x15payment_request_index\x18\x07 \x01(\rH\x00\x88\x01\x01\x12P\n\x0esilent_payment\x18\x08 \x01(\x0b\x32\x38.shiftcrypto.bitbox02.BTCSignOutputRequest.SilentPayment\x12\'\n\x1aoutput_script_config_index\x18\t \x01(\rH\x01\x88\x01\x01\x1a \n\rSilentPayment\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\tB\x18\n\x16_payment_request_indexB\x1d\n\x1b_output_script_config_index\"\x99\x01\n\x1b\x42TCScriptConfigRegistration\x12+\n\x04\x63oin\x18\x01 \x01(\x0e\x32\x1d.shiftcrypto.bitbox02.BTCCoin\x12<\n\rscript_config\x18\x02 \x01(\x0b\x32%.shiftcrypto.bitbox02.BTCScriptConfig\x12\x0f\n\x07keypath\x18\x03 \x03(\r\"\x0c\n\nBTCSuccess\"m\n\"BTCIsScriptConfigRegisteredRequest\x12G\n\x0cregistration\x18\x01 \x01(\x0b\x32\x31.shiftcrypto.bitbox02.BTCScriptConfigRegistration\"<\n#BTCIsScriptConfigRegisteredResponse\x12\x15\n\ris_registered\x18\x01 \x01(\x08\"\xfc\x01\n\x1e\x42TCRegisterScriptConfigRequest\x12G\n\x0cregistration\x18\x01 \x01(\x0b\x32\x31.shiftcrypto.bitbox02.BTCScriptConfigRegistration\x12\x0c\n\x04name\x18\x02 \x01(\t\x12P\n\txpub_type\x18\x03 \x01(\x0e\x32=.shiftcrypto.bitbox02.BTCRegisterScriptConfigRequest.XPubType\"1\n\x08XPubType\x12\x11\n\rAUTO_ELECTRUM\x10\x00\x12\x12\n\x0e\x41UTO_XPUB_TPUB\x10\x01\"b\n\x14\x42TCPrevTxInitRequest\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x12\n\nnum_inputs\x18\x02 \x01(\r\x12\x13\n\x0bnum_outputs\x18\x03 \x01(\r\x12\x10\n\x08locktime\x18\x04 \x01(\r\"r\n\x15\x42TCPrevTxInputRequest\x12\x15\n\rprev_out_hash\x18\x01 \x01(\x0c\x12\x16\n\x0eprev_out_index\x18\x02 \x01(\r\x12\x18\n\x10signature_script\x18\x03 \x01(\x0c\x12\x10\n\x08sequence\x18\x04 \x01(\r\">\n\x16\x42TCPrevTxOutputRequest\x12\r\n\x05value\x18\x01 \x01(\x04\x12\x15\n\rpubkey_script\x18\x02 \x01(\x0c\"\xab\x02\n\x18\x42TCPaymentRequestRequest\x12\x16\n\x0erecipient_name\x18\x01 \x01(\t\x12\x42\n\x05memos\x18\x02 \x03(\x0b\x32\x33.shiftcrypto.bitbox02.BTCPaymentRequestRequest.Memo\x12\r\n\x05nonce\x18\x03 \x01(\x0c\x12\x14\n\x0ctotal_amount\x18\x04 \x01(\x04\x12\x11\n\tsignature\x18\x05 \x01(\x0c\x1a{\n\x04Memo\x12Q\n\ttext_memo\x18\x01 \x01(\x0b\x32<.shiftcrypto.bitbox02.BTCPaymentRequestRequest.Memo.TextMemoH\x00\x1a\x18\n\x08TextMemo\x12\x0c\n\x04note\x18\x01 \x01(\tB\x06\n\x04memo\"\xee\x01\n\x15\x42TCSignMessageRequest\x12+\n\x04\x63oin\x18\x01 \x01(\x0e\x32\x1d.shiftcrypto.bitbox02.BTCCoin\x12G\n\rscript_config\x18\x02 \x01(\x0b\x32\x30.shiftcrypto.bitbox02.BTCScriptConfigWithKeypath\x12\x0b\n\x03msg\x18\x03 \x01(\x0c\x12R\n\x15host_nonce_commitment\x18\x04 \x01(\x0b\x32\x33.shiftcrypto.bitbox02.AntiKleptoHostNonceCommitment\"+\n\x16\x42TCSignMessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\"\xb9\x05\n\nBTCRequest\x12_\n\x1bis_script_config_registered\x18\x01 \x01(\x0b\x32\x38.shiftcrypto.bitbox02.BTCIsScriptConfigRegisteredRequestH\x00\x12V\n\x16register_script_config\x18\x02 \x01(\x0b\x32\x34.shiftcrypto.bitbox02.BTCRegisterScriptConfigRequestH\x00\x12\x41\n\x0bprevtx_init\x18\x03 \x01(\x0b\x32*.shiftcrypto.bitbox02.BTCPrevTxInitRequestH\x00\x12\x43\n\x0cprevtx_input\x18\x04 \x01(\x0b\x32+.shiftcrypto.bitbox02.BTCPrevTxInputRequestH\x00\x12\x45\n\rprevtx_output\x18\x05 \x01(\x0b\x32,.shiftcrypto.bitbox02.BTCPrevTxOutputRequestH\x00\x12\x43\n\x0csign_message\x18\x06 \x01(\x0b\x32+.shiftcrypto.bitbox02.BTCSignMessageRequestH\x00\x12P\n\x14\x61ntiklepto_signature\x18\x07 \x01(\x0b\x32\x30.shiftcrypto.bitbox02.AntiKleptoSignatureRequestH\x00\x12I\n\x0fpayment_request\x18\x08 \x01(\x0b\x32..shiftcrypto.bitbox02.BTCPaymentRequestRequestH\x00\x12\x36\n\x05xpubs\x18\t \x01(\x0b\x32%.shiftcrypto.bitbox02.BTCXpubsRequestH\x00\x42\t\n\x07request\"\xc4\x03\n\x0b\x42TCResponse\x12\x33\n\x07success\x18\x01 \x01(\x0b\x32 .shiftcrypto.bitbox02.BTCSuccessH\x00\x12`\n\x1bis_script_config_registered\x18\x02 \x01(\x0b\x32\x39.shiftcrypto.bitbox02.BTCIsScriptConfigRegisteredResponseH\x00\x12>\n\tsign_next\x18\x03 \x01(\x0b\x32).shiftcrypto.bitbox02.BTCSignNextResponseH\x00\x12\x44\n\x0csign_message\x18\x04 \x01(\x0b\x32,.shiftcrypto.bitbox02.BTCSignMessageResponseH\x00\x12X\n\x1c\x61ntiklepto_signer_commitment\x18\x05 \x01(\x0b\x32\x30.shiftcrypto.bitbox02.AntiKleptoSignerCommitmentH\x00\x12\x32\n\x04pubs\x18\x06 \x01(\x0b\x32\".shiftcrypto.bitbox02.PubsResponseH\x00\x42\n\n\x08response*9\n\x07\x42TCCoin\x12\x07\n\x03\x42TC\x10\x00\x12\x08\n\x04TBTC\x10\x01\x12\x07\n\x03LTC\x10\x02\x12\x08\n\x04TLTC\x10\x03\x12\x08\n\x04RBTC\x10\x04*R\n\rBTCOutputType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\t\n\x05P2PKH\x10\x01\x12\x08\n\x04P2SH\x10\x02\x12\n\n\x06P2WPKH\x10\x03\x12\t\n\x05P2WSH\x10\x04\x12\x08\n\x04P2TR\x10\x05\x62\x06proto3') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'btc_pb2', globals()) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None - _BTCCOIN._serialized_start=5551 - _BTCCOIN._serialized_end=5608 - _BTCOUTPUTTYPE._serialized_start=5610 - _BTCOUTPUTTYPE._serialized_end=5692 + _BTCCOIN._serialized_start=5702 + _BTCCOIN._serialized_end=5759 + _BTCOUTPUTTYPE._serialized_start=5761 + _BTCOUTPUTTYPE._serialized_end=5843 _BTCSCRIPTCONFIG._serialized_start=68 _BTCSCRIPTCONFIG._serialized_end=650 _BTCSCRIPTCONFIG_MULTISIG._serialized_start=293 @@ -47,49 +47,51 @@ _BTCSCRIPTCONFIGWITHKEYPATH._serialized_start=1261 _BTCSCRIPTCONFIGWITHKEYPATH._serialized_end=1368 _BTCSIGNINITREQUEST._serialized_start=1371 - _BTCSIGNINITREQUEST._serialized_end=1818 - _BTCSIGNINITREQUEST_FORMATUNIT._serialized_start=1784 - _BTCSIGNINITREQUEST_FORMATUNIT._serialized_end=1818 - _BTCSIGNNEXTRESPONSE._serialized_start=1821 - _BTCSIGNNEXTRESPONSE._serialized_end=2273 - _BTCSIGNNEXTRESPONSE_TYPE._serialized_start=2143 - _BTCSIGNNEXTRESPONSE_TYPE._serialized_end=2273 - _BTCSIGNINPUTREQUEST._serialized_start=2276 - _BTCSIGNINPUTREQUEST._serialized_end=2510 - _BTCSIGNOUTPUTREQUEST._serialized_start=2513 - _BTCSIGNOUTPUTREQUEST._serialized_end=2928 - _BTCSIGNOUTPUTREQUEST_SILENTPAYMENT._serialized_start=2839 - _BTCSIGNOUTPUTREQUEST_SILENTPAYMENT._serialized_end=2871 - _BTCSCRIPTCONFIGREGISTRATION._serialized_start=2931 - _BTCSCRIPTCONFIGREGISTRATION._serialized_end=3084 - _BTCSUCCESS._serialized_start=3086 - _BTCSUCCESS._serialized_end=3098 - _BTCISSCRIPTCONFIGREGISTEREDREQUEST._serialized_start=3100 - _BTCISSCRIPTCONFIGREGISTEREDREQUEST._serialized_end=3209 - _BTCISSCRIPTCONFIGREGISTEREDRESPONSE._serialized_start=3211 - _BTCISSCRIPTCONFIGREGISTEREDRESPONSE._serialized_end=3271 - _BTCREGISTERSCRIPTCONFIGREQUEST._serialized_start=3274 - _BTCREGISTERSCRIPTCONFIGREQUEST._serialized_end=3526 - _BTCREGISTERSCRIPTCONFIGREQUEST_XPUBTYPE._serialized_start=3477 - _BTCREGISTERSCRIPTCONFIGREQUEST_XPUBTYPE._serialized_end=3526 - _BTCPREVTXINITREQUEST._serialized_start=3528 - _BTCPREVTXINITREQUEST._serialized_end=3626 - _BTCPREVTXINPUTREQUEST._serialized_start=3628 - _BTCPREVTXINPUTREQUEST._serialized_end=3742 - _BTCPREVTXOUTPUTREQUEST._serialized_start=3744 - _BTCPREVTXOUTPUTREQUEST._serialized_end=3806 - _BTCPAYMENTREQUESTREQUEST._serialized_start=3809 - _BTCPAYMENTREQUESTREQUEST._serialized_end=4108 - _BTCPAYMENTREQUESTREQUEST_MEMO._serialized_start=3985 - _BTCPAYMENTREQUESTREQUEST_MEMO._serialized_end=4108 - _BTCPAYMENTREQUESTREQUEST_MEMO_TEXTMEMO._serialized_start=4076 - _BTCPAYMENTREQUESTREQUEST_MEMO_TEXTMEMO._serialized_end=4100 - _BTCSIGNMESSAGEREQUEST._serialized_start=4111 - _BTCSIGNMESSAGEREQUEST._serialized_end=4349 - _BTCSIGNMESSAGERESPONSE._serialized_start=4351 - _BTCSIGNMESSAGERESPONSE._serialized_end=4394 - _BTCREQUEST._serialized_start=4397 - _BTCREQUEST._serialized_end=5094 - _BTCRESPONSE._serialized_start=5097 - _BTCRESPONSE._serialized_end=5549 + _BTCSIGNINITREQUEST._serialized_end=1969 + _BTCSIGNINITREQUEST_FORMATUNIT._serialized_start=1852 + _BTCSIGNINITREQUEST_FORMATUNIT._serialized_end=1886 + _BTCSIGNINITREQUEST_PREVTXS._serialized_start=1888 + _BTCSIGNINITREQUEST_PREVTXS._serialized_end=1969 + _BTCSIGNNEXTRESPONSE._serialized_start=1972 + _BTCSIGNNEXTRESPONSE._serialized_end=2424 + _BTCSIGNNEXTRESPONSE_TYPE._serialized_start=2294 + _BTCSIGNNEXTRESPONSE_TYPE._serialized_end=2424 + _BTCSIGNINPUTREQUEST._serialized_start=2427 + _BTCSIGNINPUTREQUEST._serialized_end=2661 + _BTCSIGNOUTPUTREQUEST._serialized_start=2664 + _BTCSIGNOUTPUTREQUEST._serialized_end=3079 + _BTCSIGNOUTPUTREQUEST_SILENTPAYMENT._serialized_start=2990 + _BTCSIGNOUTPUTREQUEST_SILENTPAYMENT._serialized_end=3022 + _BTCSCRIPTCONFIGREGISTRATION._serialized_start=3082 + _BTCSCRIPTCONFIGREGISTRATION._serialized_end=3235 + _BTCSUCCESS._serialized_start=3237 + _BTCSUCCESS._serialized_end=3249 + _BTCISSCRIPTCONFIGREGISTEREDREQUEST._serialized_start=3251 + _BTCISSCRIPTCONFIGREGISTEREDREQUEST._serialized_end=3360 + _BTCISSCRIPTCONFIGREGISTEREDRESPONSE._serialized_start=3362 + _BTCISSCRIPTCONFIGREGISTEREDRESPONSE._serialized_end=3422 + _BTCREGISTERSCRIPTCONFIGREQUEST._serialized_start=3425 + _BTCREGISTERSCRIPTCONFIGREQUEST._serialized_end=3677 + _BTCREGISTERSCRIPTCONFIGREQUEST_XPUBTYPE._serialized_start=3628 + _BTCREGISTERSCRIPTCONFIGREQUEST_XPUBTYPE._serialized_end=3677 + _BTCPREVTXINITREQUEST._serialized_start=3679 + _BTCPREVTXINITREQUEST._serialized_end=3777 + _BTCPREVTXINPUTREQUEST._serialized_start=3779 + _BTCPREVTXINPUTREQUEST._serialized_end=3893 + _BTCPREVTXOUTPUTREQUEST._serialized_start=3895 + _BTCPREVTXOUTPUTREQUEST._serialized_end=3957 + _BTCPAYMENTREQUESTREQUEST._serialized_start=3960 + _BTCPAYMENTREQUESTREQUEST._serialized_end=4259 + _BTCPAYMENTREQUESTREQUEST_MEMO._serialized_start=4136 + _BTCPAYMENTREQUESTREQUEST_MEMO._serialized_end=4259 + _BTCPAYMENTREQUESTREQUEST_MEMO_TEXTMEMO._serialized_start=4227 + _BTCPAYMENTREQUESTREQUEST_MEMO_TEXTMEMO._serialized_end=4251 + _BTCSIGNMESSAGEREQUEST._serialized_start=4262 + _BTCSIGNMESSAGEREQUEST._serialized_end=4500 + _BTCSIGNMESSAGERESPONSE._serialized_start=4502 + _BTCSIGNMESSAGERESPONSE._serialized_end=4545 + _BTCREQUEST._serialized_start=4548 + _BTCREQUEST._serialized_end=5245 + _BTCRESPONSE._serialized_start=5248 + _BTCRESPONSE._serialized_end=5700 # @@protoc_insertion_point(module_scope) diff --git a/py/bitbox02/bitbox02/communication/generated/btc_pb2.pyi b/py/bitbox02/bitbox02/communication/generated/btc_pb2.pyi index 05022bdd1..3d86c54bc 100644 --- a/py/bitbox02/bitbox02/communication/generated/btc_pb2.pyi +++ b/py/bitbox02/bitbox02/communication/generated/btc_pb2.pyi @@ -342,6 +342,21 @@ class BTCSignInitRequest(google.protobuf.message.Message): SAT: BTCSignInitRequest.FormatUnit.ValueType # 1 """Only valid for BTC/TBTC, formats as "sat"/"tsat".""" + class _PrevTxs: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _PrevTxsEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[BTCSignInitRequest._PrevTxs.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PREV_TXS_DEFAULT: BTCSignInitRequest._PrevTxs.ValueType # 0 + PREV_TXS_REQUIRED: BTCSignInitRequest._PrevTxs.ValueType # 1 + PREV_TXS_NOT_REQUIRED: BTCSignInitRequest._PrevTxs.ValueType # 2 + + class PrevTxs(_PrevTxs, metaclass=_PrevTxsEnumTypeWrapper): ... + PREV_TXS_DEFAULT: BTCSignInitRequest.PrevTxs.ValueType # 0 + PREV_TXS_REQUIRED: BTCSignInitRequest.PrevTxs.ValueType # 1 + PREV_TXS_NOT_REQUIRED: BTCSignInitRequest.PrevTxs.ValueType # 2 + COIN_FIELD_NUMBER: builtins.int SCRIPT_CONFIGS_FIELD_NUMBER: builtins.int VERSION_FIELD_NUMBER: builtins.int @@ -351,6 +366,7 @@ class BTCSignInitRequest(google.protobuf.message.Message): FORMAT_UNIT_FIELD_NUMBER: builtins.int CONTAINS_SILENT_PAYMENT_OUTPUTS_FIELD_NUMBER: builtins.int OUTPUT_SCRIPT_CONFIGS_FIELD_NUMBER: builtins.int + PREV_TXS_FIELD_NUMBER: builtins.int coin: global___BTCCoin.ValueType version: builtins.int """must be 1 or 2""" @@ -360,6 +376,14 @@ class BTCSignInitRequest(google.protobuf.message.Message): """must be <500000000""" format_unit: global___BTCSignInitRequest.FormatUnit.ValueType contains_silent_payment_outputs: builtins.bool + prev_txs: global___BTCSignInitRequest.PrevTxs.ValueType + """Set this to: + - `PREV_TXS_DEFAULT` for compatibility mode, the value will be determined using `script_configs`. + This is not robust, as non-Taproot change outputs are included there and falsely leads to + previous transactions being required. + - `PREV_TXS_REQUIRED` if not all transaction inputs are Taproot. + - `PREV_TXS_NOT_REQUIRED` if all transaction inputs are Taproot. + """ @property def script_configs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___BTCScriptConfigWithKeypath]: """used script configs in inputs and changes""" @@ -382,8 +406,9 @@ class BTCSignInitRequest(google.protobuf.message.Message): format_unit: global___BTCSignInitRequest.FormatUnit.ValueType = ..., contains_silent_payment_outputs: builtins.bool = ..., output_script_configs: collections.abc.Iterable[global___BTCScriptConfigWithKeypath] | None = ..., + prev_txs: global___BTCSignInitRequest.PrevTxs.ValueType = ..., ) -> None: ... - def ClearField(self, field_name: typing.Literal["coin", b"coin", "contains_silent_payment_outputs", b"contains_silent_payment_outputs", "format_unit", b"format_unit", "locktime", b"locktime", "num_inputs", b"num_inputs", "num_outputs", b"num_outputs", "output_script_configs", b"output_script_configs", "script_configs", b"script_configs", "version", b"version"]) -> None: ... + def ClearField(self, field_name: typing.Literal["coin", b"coin", "contains_silent_payment_outputs", b"contains_silent_payment_outputs", "format_unit", b"format_unit", "locktime", b"locktime", "num_inputs", b"num_inputs", "num_outputs", b"num_outputs", "output_script_configs", b"output_script_configs", "prev_txs", b"prev_txs", "script_configs", b"script_configs", "version", b"version"]) -> None: ... global___BTCSignInitRequest = BTCSignInitRequest diff --git a/src/rust/bitbox02-rust/src/hww/api/bitcoin/signtx.rs b/src/rust/bitbox02-rust/src/hww/api/bitcoin/signtx.rs index c7f8d37ae..eb4f1f881 100644 --- a/src/rust/bitbox02-rust/src/hww/api/bitcoin/signtx.rs +++ b/src/rust/bitbox02-rust/src/hww/api/bitcoin/signtx.rs @@ -717,8 +717,20 @@ async fn _process( let mut hasher_amounts = Sha256::new(); let mut hasher_scriptpubkeys = Sha256::new(); - // Are all inputs taproot? - let taproot_only = validated_script_configs.iter().all(is_taproot); + let prev_txs = pb::btc_sign_init_request::PrevTxs::try_from(request.prev_txs)?; + + // We will request to stream previous transactions if not all inputs are Taproot. + let prevtxs_required: bool = match prev_txs { + // For backwards compatibility for client's that don't provide the `prev_txs` field: handle + // it like before `prev_txs` was introduced by inspecting the script configs and seeing if + // all of them are taproot. This is not robust, as non-Taproot change outputs are included + // there and falsely leads to previous transactions being required. + pb::btc_sign_init_request::PrevTxs::Default => { + !validated_script_configs.iter().all(is_taproot) + } + pb::btc_sign_init_request::PrevTxs::Required => true, + pb::btc_sign_init_request::PrevTxs::NotRequired => false, + }; let mut silent_payment = if request.contains_silent_payment_outputs { Some(SilentPayment::new(SECP256K1, coin.try_into()?)) @@ -775,7 +787,7 @@ async fn _process( hasher_scriptpubkeys.update(serialize_varint(pk_script.len() as u64).as_slice()); hasher_scriptpubkeys.update(pk_script.as_slice()); - if !taproot_only { + if prevtxs_required { handle_prevtx( input_index, &tx_input, @@ -784,6 +796,8 @@ async fn _process( &mut next_response, ) .await?; + } else if !is_taproot(script_config_account) { + return Err(Error::InvalidInput); } if let Some(ref mut silent_payment) = silent_payment { @@ -1572,6 +1586,7 @@ mod tests { .outputs .iter() .any(|output| output.silent_payment.is_some()), + prev_txs: pb::btc_sign_init_request::PrevTxs::Default as _, } } @@ -1595,6 +1610,7 @@ mod tests { locktime: self.locktime, format_unit: FormatUnit::Default as _, contains_silent_payment_outputs: false, + prev_txs: pb::btc_sign_init_request::PrevTxs::Default as _, } } @@ -1659,7 +1675,7 @@ mod tests { } #[test] - pub fn test_sign_init_fail() { + fn test_sign_init_fail() { *crate::hww::MOCK_NEXT_REQUEST.0.borrow_mut() = None; let init_req_valid = pb::BtcSignInitRequest { @@ -1679,6 +1695,7 @@ mod tests { locktime: 0, format_unit: FormatUnit::Default as _, contains_silent_payment_outputs: false, + prev_txs: pb::btc_sign_init_request::PrevTxs::Default as _, }; { @@ -1871,6 +1888,7 @@ mod tests { locktime: 0, format_unit: FormatUnit::Default as _, contains_silent_payment_outputs: false, + prev_txs: pb::btc_sign_init_request::PrevTxs::Default as _, } )), Err(Error::InvalidInput) @@ -1895,7 +1913,7 @@ mod tests { } #[test] - pub fn test_process() { + fn test_process() { static mut UI_COUNTER: u32 = 0; static mut PREVTX_REQUESTED: u32 = 0; @@ -2042,7 +2060,7 @@ mod tests { /// Test that receiving an unexpected message from the host results in an invalid state error. #[test] - pub fn test_invalid_state() { + fn test_invalid_state() { let transaction = alloc::rc::Rc::new(core::cell::RefCell::new(Transaction::new(pb::BtcCoin::Btc))); mock_unlocked(); @@ -2066,7 +2084,7 @@ mod tests { /// Test signing if all inputs are of type P2WPKH-P2SH. #[test] - pub fn test_script_type_p2wpkh_p2sh() { + fn test_script_type_p2wpkh_p2sh() { let transaction = alloc::rc::Rc::new(core::cell::RefCell::new(Transaction::new(pb::BtcCoin::Btc))); for input in transaction.borrow_mut().inputs.iter_mut() { @@ -2101,7 +2119,7 @@ mod tests { /// Test signing if all inputs are of type P2TR. #[test] - pub fn test_script_type_p2tr() { + fn test_script_type_p2tr() { let transaction = alloc::rc::Rc::new(core::cell::RefCell::new(Transaction::new(pb::BtcCoin::Btc))); for input in transaction.borrow_mut().inputs.iter_mut() { @@ -2147,10 +2165,61 @@ mod tests { assert!(unsafe { !PREVTX_REQUESTED }); } + /// Test signing if all inputs are of type P2TR, but change is not of type P2TR. + /// Previous transactions are requested for backwards compatibility when + #[test] + fn test_script_type_p2tr_different_change() { + let transaction = + alloc::rc::Rc::new(core::cell::RefCell::new(Transaction::new(pb::BtcCoin::Btc))); + for input in transaction.borrow_mut().inputs.iter_mut() { + input.input.keypath[0] = 86 + HARDENED; + input.input.script_config_index = 1; + } + + let tx = transaction.clone(); + // Check that previous transactions are not streamed, as all inputs are taproot. + static mut PREVTX_REQUESTED: bool = false; + *crate::hww::MOCK_NEXT_REQUEST.0.borrow_mut() = + Some(Box::new(move |response: Response| { + let next = extract_next(&response); + if NextType::try_from(next.r#type).unwrap() == NextType::PrevtxInit { + unsafe { PREVTX_REQUESTED = true } + } + Ok(tx.borrow().make_host_request(response)) + })); + + mock_unlocked(); + bitbox02::random::fake_reset(); + let mut init_request = transaction.borrow().init_request(); + init_request + .script_configs + .push(pb::BtcScriptConfigWithKeypath { + script_config: Some(pb::BtcScriptConfig { + config: Some(pb::btc_script_config::Config::SimpleType( + SimpleType::P2tr as _, + )), + }), + keypath: vec![86 + HARDENED, 0 + HARDENED, 10 + HARDENED], + }); + + init_request.prev_txs = pb::btc_sign_init_request::PrevTxs::NotRequired as _; + assert!(block_on(process(&mut TestingHal::new(), &init_request)).is_ok()); + assert!(unsafe { !PREVTX_REQUESTED }); + + // Also test compatibility mode - before the introduction of `prev_txs`, the previous + // transactions were wrongly requested in this case. + unsafe { + PREVTX_REQUESTED = false; + } + init_request.prev_txs = pb::btc_sign_init_request::PrevTxs::Default as _; + assert!(block_on(process(&mut TestingHal::new(), &init_request)).is_ok()); + assert!(unsafe { PREVTX_REQUESTED }); + } + /// Test signing if with mixed inputs, one of them being taproot. Previous transactions of all /// inputs should be streamed in this case. #[test] - pub fn test_script_type_p2tr_mixed() { + fn test_script_type_p2tr_mixed() { let transaction = alloc::rc::Rc::new(core::cell::RefCell::new(Transaction::new(pb::BtcCoin::Btc))); transaction.borrow_mut().inputs[0].input.script_config_index = 1; @@ -2180,18 +2249,38 @@ mod tests { }), keypath: vec![86 + HARDENED, 0 + HARDENED, 10 + HARDENED], }); + + // In compatibility mode, prevtxs are correctly requested. + init_request.prev_txs = pb::btc_sign_init_request::PrevTxs::Default as _; + unsafe { PREVTX_REQUESTED = 0 }; assert!(block_on(process(&mut TestingHal::new(), &init_request)).is_ok()); assert_eq!( unsafe { PREVTX_REQUESTED }, transaction.borrow().inputs.len() as _ ); + + // Same as when the client explicitly states it. + init_request.prev_txs = pb::btc_sign_init_request::PrevTxs::Required as _; + unsafe { PREVTX_REQUESTED = 0 }; + assert!(block_on(process(&mut TestingHal::new(), &init_request)).is_ok()); + assert_eq!( + unsafe { PREVTX_REQUESTED }, + transaction.borrow().inputs.len() as _ + ); + + // Client can't lie about it. + init_request.prev_txs = pb::btc_sign_init_request::PrevTxs::NotRequired as _; + assert_eq!( + block_on(process(&mut TestingHal::new(), &init_request)), + Err(Error::InvalidInput) + ); } /// Test signing UTXOs with high keypath address indices. Even though we don't support verifying /// receive addresses at these indices (to mitigate ransom attacks), we should still be able to /// spend them. #[test] - pub fn test_spend_high_address_index() { + fn test_spend_high_address_index() { let transaction = alloc::rc::Rc::new(core::cell::RefCell::new(Transaction::new(pb::BtcCoin::Btc))); transaction.borrow_mut().inputs[0].input.keypath[4] = 100000; @@ -2208,7 +2297,7 @@ mod tests { /// Test invalid input cases. #[test] - pub fn test_invalid_input() { + fn test_invalid_input() { enum TestCase { // all inputs should be the same coin type. WrongCoinInput, @@ -2319,7 +2408,7 @@ mod tests { /// Test signing with mixed input types. #[test] - pub fn test_mixed_inputs() { + fn test_mixed_inputs() { let transaction = alloc::rc::Rc::new(core::cell::RefCell::new(Transaction::new(pb::BtcCoin::Btc))); transaction.borrow_mut().inputs[0].input.script_config_index = 1; @@ -2881,6 +2970,7 @@ mod tests { locktime: tx.locktime, format_unit: FormatUnit::Default as _, contains_silent_payment_outputs: false, + prev_txs: pb::btc_sign_init_request::PrevTxs::Default as _, } }; @@ -2976,6 +3066,7 @@ mod tests { locktime: tx.locktime, format_unit: FormatUnit::Default as _, contains_silent_payment_outputs: false, + prev_txs: pb::btc_sign_init_request::PrevTxs::Default as _, } }; assert_eq!( @@ -3047,6 +3138,7 @@ mod tests { locktime: tx.locktime, format_unit: FormatUnit::Default as _, contains_silent_payment_outputs: false, + prev_txs: pb::btc_sign_init_request::PrevTxs::Default as _, } }; let result = block_on(process(&mut TestingHal::new(), &init_request)); @@ -3124,6 +3216,7 @@ mod tests { locktime: tx.locktime, format_unit: FormatUnit::Default as _, contains_silent_payment_outputs: false, + prev_txs: pb::btc_sign_init_request::PrevTxs::Default as _, } }; let result = block_on(process(&mut TestingHal::new(), &init_request)); @@ -3588,7 +3681,7 @@ mod tests { } #[test] - pub fn test_payment_request() { + fn test_payment_request() { let transaction = alloc::rc::Rc::new(core::cell::RefCell::new(Transaction::new(pb::BtcCoin::Btc))); diff --git a/src/rust/bitbox02-rust/src/shiftcrypto.bitbox02.rs b/src/rust/bitbox02-rust/src/shiftcrypto.bitbox02.rs index 018adf959..75cfeb5ea 100644 --- a/src/rust/bitbox02-rust/src/shiftcrypto.bitbox02.rs +++ b/src/rust/bitbox02-rust/src/shiftcrypto.bitbox02.rs @@ -599,6 +599,14 @@ pub struct BtcSignInitRequest { /// necessarily the same account (as defined by `script_configs` above). #[prost(message, repeated, tag = "10")] pub output_script_configs: ::prost::alloc::vec::Vec, + /// Set this to: + /// - `PREV_TXS_DEFAULT` for compatibility mode, the value will be determined using `script_configs`. + /// This is not robust, as non-Taproot change outputs are included there and falsely leads to + /// previous transactions being required. + /// - `PREV_TXS_REQUIRED` if not all transaction inputs are Taproot. + /// - `PREV_TXS_NOT_REQUIRED` if all transaction inputs are Taproot. + #[prost(enumeration = "btc_sign_init_request::PrevTxs", tag = "11")] + pub prev_txs: i32, } /// Nested message and enum types in `BTCSignInitRequest`. pub mod btc_sign_init_request { @@ -640,6 +648,45 @@ pub mod btc_sign_init_request { } } } + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum PrevTxs { + Default = 0, + Required = 1, + NotRequired = 2, + } + impl PrevTxs { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + PrevTxs::Default => "PREV_TXS_DEFAULT", + PrevTxs::Required => "PREV_TXS_REQUIRED", + PrevTxs::NotRequired => "PREV_TXS_NOT_REQUIRED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "PREV_TXS_DEFAULT" => Some(Self::Default), + "PREV_TXS_REQUIRED" => Some(Self::Required), + "PREV_TXS_NOT_REQUIRED" => Some(Self::NotRequired), + _ => None, + } + } + } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)]