# This file is part of the Trezor project. # # Copyright (C) 2012-2019 SatoshiLabs and contributors # # This library is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the License along with this library. # If not, see . import pytest from trezorlib import btc, messages from trezorlib.debuglink import SessionDebugWrapper as Session from trezorlib.exceptions import TrezorFailure from trezorlib.tools import parse_path from ...common import is_core from ..bitcoin.signtx import request_finished, request_input, request_output B = messages.ButtonRequestType TXHASH_aaf51e = bytes.fromhex( "aaf51e4606c264e47e5c42c958fe4cf1539c5172684721e38e69f4ef634d75dc" ) TXHASH_e38206 = bytes.fromhex( # FAKE TX "e3820602226974b1dd87b7113cc8aea8c63e5ae29293991e7bfa80c126930368" ) TXHASH_f9231f = bytes.fromhex( "f9231f2d6cdcd86b4892c95a5d2045bacd81f4060e8127073456fbb7b7b51568" ) TXHASH_c5309b = bytes.fromhex( "c5309bd6a18f6bf374918b1c96e872af02e80d678c53d37547de03048ace79bf" ) TXHASH_431b68 = bytes.fromhex( "431b68c170799a1ba9a936f9bde4ba1fb5606b0ab0a770012875a23d23ba72a3" ) TXHASH_4b6cec = bytes.fromhex( "4b6cecb81c825180786ebe07b65bcc76078afc5be0f1c64e08d764005012380d" ) # This exact VERSION_GROUP_ID is absolutely necessary for a valid v5 transaction # BRANCH_ID could maybe change VERSION_GROUP_ID = 0x26A7270A BRANCH_ID = 0xC2D6D0B4 pytestmark = [pytest.mark.altcoin, pytest.mark.zcash] def test_version_group_id_missing(session: Session): inp1 = messages.TxInputType( # tmQoJ3PTXgQLaRRZZYT6xk8XtjRbr2kCqwu address_n=parse_path("m/44h/1h/0h/0/0"), amount=300000000, prev_hash=TXHASH_e38206, prev_index=0, ) out1 = messages.TxOutputType( address="tmJ1xYxP8XNTtCoDgvdmQPSrxh5qZJgy65Z", amount=300000000 - 1940, script_type=messages.OutputScriptType.PAYTOADDRESS, ) with pytest.raises(TrezorFailure, match="Version group ID must be set."): btc.sign_tx( session, "Zcash Testnet", [inp1], [out1], version=5, ) def test_spend_v4_input(session: Session): # 4b6cecb81c825180786ebe07b65bcc76078afc5be0f1c64e08d764005012380d is a v4 tx inp1 = messages.TxInputType( # tmAgYbANTzZp7YoMkRbbaemQETgV5GkBEjF address_n=parse_path("m/44h/1h/0h/0/7"), amount=989_680, prev_hash=TXHASH_4b6cec, prev_index=0, ) out1 = messages.TxOutputType( # m/44h/1h/0h/0/9 address="tmBMyeJebzkP5naji8XUKqLyL1NDwNkgJFt", amount=989_680 - 10_000, script_type=messages.OutputScriptType.PAYTOADDRESS, ) with session: session.set_expected_responses( [ request_input(0), request_output(0), messages.ButtonRequest(code=B.ConfirmOutput), (is_core(session), messages.ButtonRequest(code=B.ConfirmOutput)), messages.ButtonRequest(code=B.SignTx), request_input(0), request_output(0), request_finished(), ] ) _, serialized_tx = btc.sign_tx( session, "Zcash Testnet", [inp1], [out1], version=5, version_group_id=VERSION_GROUP_ID, branch_id=BRANCH_ID, ) # Accepted by network: txid = b29b1f27763e8caf9fe51f33a6a7daf138438b5278efcd60941782244e35b19e assert ( serialized_tx.hex() == "050000800a27a726b4d0d6c20000000000000000010d3812500064d7084ec6f1e05bfc8a0776cc5bb607be6e788051821cb8ec6c4b000000006b483045022100cc6efc5678eefec9dd95a890e5961be3e8fc64ea6654959873316fcd2d523d36022036036e2e23071812319d170484926bc641d54028613acaa28b1fd2530013a3400121035169c4d6a36b6c4f3e210f46d329efa1cb7a67ffce7d62062d4a8a17c23756e1ffffffff01e0f20e00000000001976a9141215d421cb8cec1dea62cbd9e4e07c01520d873f88ac000000" ) def test_send_to_multisig(session: Session): inp1 = messages.TxInputType( # tmQoJ3PTXgQLaRRZZYT6xk8XtjRbr2kCqwu address_n=parse_path("m/44h/1h/0h/0/8"), amount=1_000_000, prev_hash=bytes.fromhex( "d8cfa377012ca0b8d856586693b530835bf2fa14add0380e24ec6755bed5b931" ), prev_index=0, ) out1 = messages.TxOutputType( address="t2PQpjcfpYHK1bcXZcSaTbg5cQRV93B2NRY", amount=1_000_000 - 19_400, script_type=messages.OutputScriptType.PAYTOSCRIPTHASH, ) with session: session.set_expected_responses( [ request_input(0), request_output(0), messages.ButtonRequest(code=B.ConfirmOutput), (is_core(session), messages.ButtonRequest(code=B.ConfirmOutput)), messages.ButtonRequest(code=B.SignTx), request_input(0), request_output(0), request_finished(), ] ) _, serialized_tx = btc.sign_tx( session, "Zcash Testnet", [inp1], [out1], version=5, version_group_id=VERSION_GROUP_ID, branch_id=BRANCH_ID, ) # Accepted by network: txid = 431b68c170799a1ba9a936f9bde4ba1fb5606b0ab0a770012875a23d23ba72a3 assert ( serialized_tx.hex() == "050000800a27a726b4d0d6c200000000000000000131b9d5be5567ec240e38d0ad14faf25b8330b593665856d8b8a02c0177a3cfd8000000006a4730440220578ca11522b12ab2048b5c81a2ef788cccfacdb42d08efb31a802ee75909c38c02202fd12addfae4822141821b14a012828c7a4a46cc02de777101d1b8ee14128070012103260dc4925b14addb52b4e62c698b99d2318f3d909477a081ae8e5d94dc3c66d8ffffffff0178f60e000000000017a914b8f771de8bbdcfee76e0dbf76f1005f2028bf3e787000000" ) def test_spend_v5_input(session: Session): inp1 = messages.TxInputType( # tmBMyeJebzkP5naji8XUKqLyL1NDwNkgJFt address_n=parse_path("m/44h/1h/0h/0/9"), amount=4_154_120, prev_hash=TXHASH_f9231f, prev_index=0, ) out1 = messages.TxOutputType( # m/44h/1h/0h/0/0 address="tmQoJ3PTXgQLaRRZZYT6xk8XtjRbr2kCqwu", amount=4_154_120 - 19_400, script_type=messages.OutputScriptType.PAYTOADDRESS, ) with session: session.set_expected_responses( [ request_input(0), request_output(0), messages.ButtonRequest(code=B.ConfirmOutput), (is_core(session), messages.ButtonRequest(code=B.ConfirmOutput)), messages.ButtonRequest(code=B.SignTx), request_input(0), request_output(0), request_finished(), ] ) _, serialized_tx = btc.sign_tx( session, "Zcash Testnet", [inp1], [out1], version=5, version_group_id=VERSION_GROUP_ID, branch_id=BRANCH_ID, ) # Accepted by network: txid = c5309bd6a18f6bf374918b1c96e872af02e80d678c53d37547de03048ace79bf assert ( serialized_tx.hex() == "050000800a27a726b4d0d6c20000000000000000016815b5b7b7fb56340727810e06f481cdba45205d5ac992486bd8dc6c2d1f23f9000000006a47304402201fc2effdaa338d4fd42a018debed2c8a170c57c7763faabf9596ea408961cc5b02200dd35764d2797723c73f2984c5ea49522d4558ca3c5143e95235f522f65c84b5012102b3397d76b093624981b3c3a279c79496d16820f821528b9e403bdfc162b34c3cffffffff0140173f00000000001976a914a579388225827d9f2fe9014add644487808c695d88ac000000" ) def test_one_two(session: Session): inp1 = messages.TxInputType( # tmQoJ3PTXgQLaRRZZYT6xk8XtjRbr2kCqwu address_n=parse_path("m/44h/1h/0h/0/0"), amount=4_134_720, prev_hash=TXHASH_c5309b, prev_index=0, ) out1 = messages.TxOutputType( # m/44h/1h/0h/0/8 address="tmCYEhUmZGpzyFrhUdKqwt64DrPqkFNChxx", amount=1_000_000, script_type=messages.OutputScriptType.PAYTOADDRESS, ) out2 = messages.TxOutputType( address_n=parse_path("m/44h/1h/0h/0/0"), amount=4_134_720 - 1_000_000 - 2_000, script_type=messages.OutputScriptType.PAYTOADDRESS, ) with session: session.set_expected_responses( [ request_input(0), request_output(0), messages.ButtonRequest(code=B.ConfirmOutput), (is_core(session), messages.ButtonRequest(code=B.ConfirmOutput)), request_output(1), messages.ButtonRequest(code=B.SignTx), request_input(0), request_output(0), request_output(1), request_finished(), ] ) _, serialized_tx = btc.sign_tx( session, "Zcash Testnet", [inp1], [out1, out2], version=5, version_group_id=VERSION_GROUP_ID, branch_id=BRANCH_ID, ) # Accepted by network: txid = d8cfa377012ca0b8d856586693b530835bf2fa14add0380e24ec6755bed5b931 assert ( serialized_tx.hex() == "050000800a27a726b4d0d6c2000000000000000001bf79ce8a0403de4775d3538c670de802af72e8961c8b9174f36b8fa1d69b30c5000000006b483045022100be78eccf801dda4dd33f9d4e04c2aae01022869d1d506d51669204ec269d71a90220394a51838faf40176058cf45fe7032be9c5c942e21aff35d7dbe4b96ab5e0a500121030e669acac1f280d1ddf441cd2ba5e97417bf2689e4bbec86df4f831bf9f7ffd0ffffffff0240420f00000000001976a9141efeae5c937bfc7f095a06aabdb5476a5d6d19db88ac30cd2f00000000001976a914a579388225827d9f2fe9014add644487808c695d88ac000000" ) @pytest.mark.models("core") def test_unified_address(session: Session): # identical to the test_one_two # but receiver address is unified with an orchard address inp1 = messages.TxInputType( # tmQoJ3PTXgQLaRRZZYT6xk8XtjRbr2kCqwu address_n=parse_path("m/44h/1h/0h/0/0"), amount=4_134_720, prev_hash=TXHASH_c5309b, prev_index=0, ) out1 = messages.TxOutputType( # p2pkh: m/44h/1h/0h/0/8 + orchard: m/32h/1h/0h address="utest1xt8k2akcdnncjfz8sfxkm49quc4w627skp3qpggkwp8c8ay3htftjf7tur9kftcw0w4vu4scwfg93ckfag84khy9k40yanl5k0qkanh9cyhddgws786qeqn37rtyf6rx4eflz09zk06", amount=1_000_000, script_type=messages.OutputScriptType.PAYTOADDRESS, ) out2 = messages.TxOutputType( address_n=parse_path("m/44h/1h/0h/0/0"), amount=4_134_720 - 1_000_000 - 2_000, script_type=messages.OutputScriptType.PAYTOADDRESS, ) with session: session.set_expected_responses( [ request_input(0), request_output(0), messages.ButtonRequest(code=B.ConfirmOutput), (is_core(session), messages.ButtonRequest(code=B.ConfirmOutput)), request_output(1), messages.ButtonRequest(code=B.SignTx), request_input(0), request_output(0), request_output(1), request_finished(), ] ) _, serialized_tx = btc.sign_tx( session, "Zcash Testnet", [inp1], [out1, out2], version=5, version_group_id=VERSION_GROUP_ID, branch_id=BRANCH_ID, ) # Accepted by network: txid = d8cfa377012ca0b8d856586693b530835bf2fa14add0380e24ec6755bed5b931 assert ( serialized_tx.hex() == "050000800a27a726b4d0d6c2000000000000000001bf79ce8a0403de4775d3538c670de802af72e8961c8b9174f36b8fa1d69b30c5000000006b483045022100be78eccf801dda4dd33f9d4e04c2aae01022869d1d506d51669204ec269d71a90220394a51838faf40176058cf45fe7032be9c5c942e21aff35d7dbe4b96ab5e0a500121030e669acac1f280d1ddf441cd2ba5e97417bf2689e4bbec86df4f831bf9f7ffd0ffffffff0240420f00000000001976a9141efeae5c937bfc7f095a06aabdb5476a5d6d19db88ac30cd2f00000000001976a914a579388225827d9f2fe9014add644487808c695d88ac000000" ) @pytest.mark.models("core") def test_external_presigned(session: Session): inp1 = messages.TxInputType( # tmQoJ3PTXgQLaRRZZYT6xk8XtjRbr2kCqwu address_n=parse_path("m/44h/1h/0h/0/0"), amount=300000000, prev_hash=TXHASH_e38206, prev_index=0, ) inp2 = messages.TxInputType( # tmQoJ3PTXgQLaRRZZYT6xk8XtjRbr2kCqwu # address_n=parse_path("m/44h/1h/0h/0/0"), amount=300000000, prev_hash=TXHASH_aaf51e, prev_index=1, script_type=messages.InputScriptType.EXTERNAL, script_pubkey=bytes.fromhex( "76a914a579388225827d9f2fe9014add644487808c695d88ac" ), script_sig=bytes.fromhex( "47304402207635614c690bfe8701ebe822a7322273feaa8d664a82780901628fd4c907879e022011541c320b9d994b16ee7251b41a76e5edb5842cf2fa77db2c9381f32f921b0f0121030e669acac1f280d1ddf441cd2ba5e97417bf2689e4bbec86df4f831bf9f7ffd0" ), ) out1 = messages.TxOutputType( address="tmJ1xYxP8XNTtCoDgvdmQPSrxh5qZJgy65Z", amount=300000000 + 300000000 - 1940, script_type=messages.OutputScriptType.PAYTOADDRESS, ) with session: session.set_expected_responses( [ request_input(0), request_input(1), request_output(0), messages.ButtonRequest(code=B.ConfirmOutput), (is_core(session), messages.ButtonRequest(code=B.ConfirmOutput)), messages.ButtonRequest(code=B.SignTx), request_input(1), request_input(0), request_input(1), request_output(0), request_finished(), ] ) _, serialized_tx = btc.sign_tx( session, "Zcash Testnet", [inp1, inp2], [out1], version=5, version_group_id=VERSION_GROUP_ID, branch_id=BRANCH_ID, ) # FAKE tx assert ( serialized_tx.hex() == "050000800a27a726b4d0d6c200000000000000000268039326c180fa7b1e999392e25a3ec6a8aec83c11b787ddb1746922020682e3000000006b48304502210083493f0a49e80b95469ea933e369500b69e73871d3e6d6c404f4bc8fc98701a80220326f5159a3fa17abc001cc6126ba5268ec78f34cccd559821fb7b57cbe0697080121030e669acac1f280d1ddf441cd2ba5e97417bf2689e4bbec86df4f831bf9f7ffd0ffffffffdc754d63eff4698ee321476872519c53f14cfe58c9425c7ee464c206461ef5aa010000006a47304402207635614c690bfe8701ebe822a7322273feaa8d664a82780901628fd4c907879e022011541c320b9d994b16ee7251b41a76e5edb5842cf2fa77db2c9381f32f921b0f0121030e669acac1f280d1ddf441cd2ba5e97417bf2689e4bbec86df4f831bf9f7ffd0ffffffff016c3ec323000000001976a9145b157a678a10021243307e4bb58f36375aa80e1088ac000000" ) def test_refuse_replacement_tx(session: Session): inp1 = messages.TxInputType( address_n=parse_path("m/44h/1h/0h/0/4"), amount=174998, prev_hash=bytes.fromhex( "beafc7cbd873d06dbee88a7002768ad5864228639db514c81cfb29f108bb1e7a" ), prev_index=0, orig_hash=bytes.fromhex( "50f6f1209ca92d7359564be803cb2c932cde7d370f7cee50fd1fad6790f6206d" ), orig_index=0, ) out1 = messages.TxOutputType( address_n=parse_path("m/44h/1h/0h/1/2"), amount=174998 - 50000 - 1111, script_type=messages.OutputScriptType.PAYTOADDRESS, orig_hash=bytes.fromhex( "50f6f1209ca92d7359564be803cb2c932cde7d370f7cee50fd1fad6790f6206d" ), orig_index=0, ) out2 = messages.TxOutputType( address="tmJ1xYxP8XNTtCoDgvdmQPSrxh5qZJgy65Z", amount=50000, script_type=messages.OutputScriptType.PAYTOADDRESS, orig_hash=bytes.fromhex( "50f6f1209ca92d7359564be803cb2c932cde7d370f7cee50fd1fad6790f6206d" ), orig_index=1, ) with pytest.raises( TrezorFailure, match="Replacement transactions are not supported." ): btc.sign_tx( session, "Zcash Testnet", [inp1], [out1, out2], version=5, version_group_id=VERSION_GROUP_ID, branch_id=BRANCH_ID, ) def test_spend_multisig(session: Session): # Cloned from tests/device_tests/bitcoin/test_multisig.py::test_2_of_3 nodes = [ btc.get_public_node( session, parse_path(f"m/48h/1h/{index}h/0h"), coin_name="Zcash Testnet" ).node for index in range(1, 4) ] multisig = messages.MultisigRedeemScriptType( nodes=nodes, address_n=[0, 0], signatures=[b"", b"", b""], m=2 ) # Let's go to sign with key 1 inp1 = messages.TxInputType( address_n=parse_path("m/48h/1h/1h/0h/0/0"), amount=980_600, prev_hash=TXHASH_431b68, prev_index=0, script_type=messages.InputScriptType.SPENDMULTISIG, multisig=multisig, ) out1 = messages.TxOutputType( # m/44h/1h/0h/0/4 address="tmHRQfcNVCZnjY8g6X7Yp6Tcpx8M5gy4Joj", amount=980_600 - 10_000, script_type=messages.OutputScriptType.PAYTOADDRESS, ) # Expected responses are the same for both two signings expected_responses = [ request_input(0), request_output(0), messages.ButtonRequest(code=B.ConfirmOutput), (is_core(session), messages.ButtonRequest(code=B.ConfirmOutput)), messages.ButtonRequest(code=B.SignTx), request_input(0), request_output(0), request_finished(), ] with session: session.set_expected_responses(expected_responses) signatures1, _ = btc.sign_tx( session, "Zcash Testnet", [inp1], [out1], version=5, version_group_id=VERSION_GROUP_ID, branch_id=BRANCH_ID, ) assert ( signatures1[0].hex() == "3045022100d1f91921391ca4a985cbe080ce8be71f1b8ceba6049151bffe7dc6cc27a4a4d80220082fb171f7536779cd216f0508e0205039b2f20988d05455dac9bc22bc713005" ) # Now we have first signature multisig = messages.MultisigRedeemScriptType( nodes=nodes, address_n=[0, 0], signatures=[ signatures1[0], b"", b"", ], # Fill signature from previous signing process m=2, ) # Let's do a second signature with key 3 inp3 = messages.TxInputType( address_n=parse_path("m/48h/1h/3h/0h/0/0"), amount=980_600, prev_hash=TXHASH_431b68, prev_index=0, script_type=messages.InputScriptType.SPENDMULTISIG, multisig=multisig, ) with session: session.set_expected_responses(expected_responses) signatures2, serialized_tx = btc.sign_tx( session, "Zcash Testnet", [inp3], [out1], version=5, version_group_id=VERSION_GROUP_ID, branch_id=BRANCH_ID, ) assert ( signatures2[0].hex() == "3044022058bb0b1ac0d6b62b6f86bdb32879e9240369387282e73a96cb6fbeba56f5493e02206dabb42fc4ce4f5d97bc641f353e7351949695d8e6383764e76eebe572cc33fc" ) # Accepted by network: txid = 38f6771c8deabc3a8b960e0c8b6aa464ddfab469e7298e91a7900101f7b60880 assert ( serialized_tx.hex() == "050000800a27a726b4d0d6c2000000000000000001a372ba233da275280170a7b00a6b60b51fbae4bdf936a9a91b9a7970c1681b4300000000fdfd0000483045022100d1f91921391ca4a985cbe080ce8be71f1b8ceba6049151bffe7dc6cc27a4a4d80220082fb171f7536779cd216f0508e0205039b2f20988d05455dac9bc22bc71300501473044022058bb0b1ac0d6b62b6f86bdb32879e9240369387282e73a96cb6fbeba56f5493e02206dabb42fc4ce4f5d97bc641f353e7351949695d8e6383764e76eebe572cc33fc014c69522103725d6c5253f2040a9a73af24bcc196bf302d6cc94374dd7197b138e10912670121038924e94fff15302a3fb45ad4fc0ed17178800f0f1c2bdacb1017f4db951aa9f12102aae8affd0eb8e1181d665daef4de1828f23053c548ec9bafc3a787f558aa014153aeffffffff0168cf0e00000000001976a914548cb80e45b1d36312fe0cb075e5e337e3c54cef88ac000000" )