diff --git a/core/src/apps/wallet/sign_tx/signing.py b/core/src/apps/wallet/sign_tx/signing.py index fca7fe77b..6be0cffb7 100644 --- a/core/src/apps/wallet/sign_tx/signing.py +++ b/core/src/apps/wallet/sign_tx/signing.py @@ -111,6 +111,8 @@ async def check_tx_fee(tx: SignTx, keychain: seed.Keychain): if txi.multisig: multifp.add(txi.multisig) + else: + multifp.mismatch = True if txi.script_type in ( InputScriptType.SPENDWITNESS, diff --git a/tests/device_tests/test_msg_signtx_segwit_native.py b/tests/device_tests/test_msg_signtx_segwit_native.py index 3426de8f8..d13a3767a 100644 --- a/tests/device_tests/test_msg_signtx_segwit_native.py +++ b/tests/device_tests/test_msg_signtx_segwit_native.py @@ -804,3 +804,119 @@ class TestMsgSigntxSegwitNative: serialized_tx.hex() == "01000000000101e5918f661488bb7f0a7d04fc1dad61b5d0bad5167a05b3a637e36ace881cbc310000000023220020fa6c73de618ec134eeec0c16f6dd04d46d4347e9a4fd0a95fd7938403a4949f9ffffffff01d071180000000000220020bcea2324dacbcde5a9db90cc26b8df9cbc72010e05cb68cf034df6f0e05239a2040047304402206bbddb45f12e31e77610fd85b50a83bad4426433b1c4860b1c5ddc0a69f803720220087b0607daab14830f4b4941f16b953b38e606ad70029bac24af7267f93c4242014730440220551a0cb6b0d5b3fa0cfd0b07bb5d751494b827b1c6a08702186696cfbc18278302204f37c382876c4117cca656654599b508f2d55fc3b083dc938e3cd8491b29719601695221036a5ec3abd10501409092246fe59c6d7a15fff1a933479483c3ba98b866c5b9742103559be875179d44e438db2c74de26e0bc9842cbdefd16018eae8a2ed989e474722103067b56aad037cd8b5f569b21f9025b76470a72dc69457813d2b76e98dc0cd01a53ae00000000" ) + + # Ensure that if there is a non-multisig input, then a multisig output + # will not be identified as a change output. + def test_multisig_mismatch_inputs_single(self, client): + # m/84'/1'/0' for "alcohol woman abuse ..." seed. + node_int = deserialize( + "Vpub5kFDCYhiYuAzjk7TBQPNFffbexHF7iAd8AVVgHQKUany7e6NQvthgk86d7DfH57DY2dwBK4PyVTDDaS1r2gjkdyJyUYGoV9qNujGSrW9Dpe" + ) + + # m/84'/1'/0' for "all all ... all" seed. + node_ext = deserialize( + "Vpub5jR76XyyhBaQXPSRf3PBeY3gF914d9sf7DWFVhMESEQMCdNv35XiVvp8gZsFXAv222VPHLNnAEXxMPG8DPiSuhAXfEydBf55LTLBGHCDzH2" + ) + + # tb1qpzmgzpcumztvmpu3q27wwdggqav26j9dgks92pvnne2lz9ferxgssmhzlq + multisig_in = proto.MultisigRedeemScriptType( + nodes=[node_int, node_ext], address_n=[0, 0], signatures=[b"", b""], m=1 + ) + + multisig_out = proto.MultisigRedeemScriptType( + nodes=[node_int, node_ext], address_n=[1, 0], signatures=[b"", b""], m=1 + ) + + inp1 = proto.TxInputType( + address_n=parse_path("84'/1'/0'/0/0"), + amount=12300000, + prev_hash=bytes.fromhex( + "09144602765ce3dd8f4329445b20e3684e948709c5cdcaf12da3bb079c99448a" + ), + prev_index=0, + script_type=proto.InputScriptType.SPENDWITNESS, + ) + + inp2 = proto.TxInputType( + address_n=parse_path("84'/1'/0'/0/0"), + prev_hash=bytes.fromhex( + "a345b85759b385c6446055e4c3baa77e8161a65009dc009489b48aa6587ce348" + ), + prev_index=0, + script_type=proto.InputScriptType.SPENDWITNESS, + multisig=multisig_in, + amount=100, + ) + + out1 = proto.TxOutputType( + address="2N4Q5FhU2497BryFfUgbqkAJE87aKHUhXMp", + amount=5000000, + script_type=proto.OutputScriptType.PAYTOADDRESS, + ) + + out2 = proto.TxOutputType( + address_n=parse_path("84'/1'/0'/1/0"), + script_type=proto.OutputScriptType.PAYTOWITNESS, + multisig=multisig_out, + amount=12300000 + 100 - 5000000 - 10000, + ) + + with client: + client.set_expected_responses( + [ + proto.TxRequest( + request_type=proto.RequestType.TXINPUT, + details=proto.TxRequestDetailsType(request_index=0), + ), + proto.TxRequest( + request_type=proto.RequestType.TXINPUT, + details=proto.TxRequestDetailsType(request_index=1), + ), + proto.TxRequest( + request_type=proto.RequestType.TXOUTPUT, + details=proto.TxRequestDetailsType(request_index=0), + ), + proto.ButtonRequest(code=proto.ButtonRequestType.ConfirmOutput), + proto.TxRequest( + request_type=proto.RequestType.TXOUTPUT, + details=proto.TxRequestDetailsType(request_index=1), + ), + # Ensure that the multisig output is not identified as a change output. + proto.ButtonRequest(code=proto.ButtonRequestType.ConfirmOutput), + proto.ButtonRequest(code=proto.ButtonRequestType.SignTx), + proto.TxRequest( + request_type=proto.RequestType.TXINPUT, + details=proto.TxRequestDetailsType(request_index=0), + ), + proto.TxRequest( + request_type=proto.RequestType.TXINPUT, + details=proto.TxRequestDetailsType(request_index=1), + ), + proto.TxRequest( + request_type=proto.RequestType.TXOUTPUT, + details=proto.TxRequestDetailsType(request_index=0), + ), + proto.TxRequest( + request_type=proto.RequestType.TXOUTPUT, + details=proto.TxRequestDetailsType(request_index=1), + ), + proto.TxRequest( + request_type=proto.RequestType.TXINPUT, + details=proto.TxRequestDetailsType(request_index=0), + ), + proto.TxRequest( + request_type=proto.RequestType.TXINPUT, + details=proto.TxRequestDetailsType(request_index=1), + ), + proto.TxRequest(request_type=proto.RequestType.TXFINISHED), + ] + ) + + _, serialized_tx = btc.sign_tx( + client, "Testnet", [inp1, inp2], [out1, out2], prev_txes=TX_API + ) + + assert ( + serialized_tx.hex() + == "010000000001028a44999c07bba32df1cacdc50987944e68e3205b4429438fdde35c76024614090000000000ffffffff48e37c58a68ab4899400dc0950a661817ea7bac3e4556044c685b35957b845a30000000000ffffffff02404b4c000000000017a9147a55d61848e77ca266e79a39bfc85c580a6426c987f43c6f0000000000220020733ecfbbe7e47a74dde6c7645b60cdf627e90a585cde7733bc7fdaf9fe30b37402473044022037dc98b16be542a6e3e1ab32007a74192c43f2498170cc5e1dffb6847e3663e402206715102d0eb59e6461a97c78eb40a8679a04a8921fdafef25f0d3d16cc65de39012103adc58245cf28406af0ef5cc24b8afba7f1be6c72f279b642d85c48798685f8620300473044022070a24bcb00041cbed465f1f546bc59e1e353a6e182393932d5ba96e20bc32ef702202ddc76a97c01465692d5b0a0a61d653f64b9ea833af1810022110fd4d505ff950147512103505f0d82bbdd251511591b34f36ad5eea37d3220c2b81a1189084431ddb3aa3d2103adc58245cf28406af0ef5cc24b8afba7f1be6c72f279b642d85c48798685f86252ae00000000" + )