You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/tests/device_tests/zcash/test_sign_tx.py

554 lines
20 KiB

# 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 <https://www.gnu.org/licenses/lgpl-3.0.html>.
import pytest
from trezorlib import btc, messages
from trezorlib.debuglink import TrezorClientDebugLink as Client
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(client: Client):
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(
client,
"Zcash Testnet",
[inp1],
[out1],
version=5,
)
def test_spend_v4_input(client: Client):
# 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 client:
client.set_expected_responses(
[
request_input(0),
request_output(0),
messages.ButtonRequest(code=B.ConfirmOutput),
(is_core(client), messages.ButtonRequest(code=B.ConfirmOutput)),
messages.ButtonRequest(code=B.SignTx),
request_input(0),
request_output(0),
request_finished(),
]
)
_, serialized_tx = btc.sign_tx(
client,
"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(client: Client):
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 client:
client.set_expected_responses(
[
request_input(0),
request_output(0),
messages.ButtonRequest(code=B.ConfirmOutput),
(is_core(client), messages.ButtonRequest(code=B.ConfirmOutput)),
messages.ButtonRequest(code=B.SignTx),
request_input(0),
request_output(0),
request_finished(),
]
)
_, serialized_tx = btc.sign_tx(
client,
"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(client: Client):
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 client:
client.set_expected_responses(
[
request_input(0),
request_output(0),
messages.ButtonRequest(code=B.ConfirmOutput),
(is_core(client), messages.ButtonRequest(code=B.ConfirmOutput)),
messages.ButtonRequest(code=B.SignTx),
request_input(0),
request_output(0),
request_finished(),
]
)
_, serialized_tx = btc.sign_tx(
client,
"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(client: Client):
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 client:
client.set_expected_responses(
[
request_input(0),
request_output(0),
messages.ButtonRequest(code=B.ConfirmOutput),
(is_core(client), 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(
client,
"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.skip_t1
def test_unified_address(client: Client):
# 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 client:
client.set_expected_responses(
[
request_input(0),
request_output(0),
messages.ButtonRequest(code=B.ConfirmOutput),
(is_core(client), 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(
client,
"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.skip_t1
def test_external_presigned(client: Client):
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 client:
client.set_expected_responses(
[
request_input(0),
request_input(1),
request_output(0),
messages.ButtonRequest(code=B.ConfirmOutput),
(is_core(client), 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(
client,
"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(client: Client):
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(
client,
"Zcash Testnet",
[inp1],
[out1, out2],
version=5,
version_group_id=VERSION_GROUP_ID,
branch_id=BRANCH_ID,
)
def test_spend_multisig(client: Client):
# Cloned from tests/device_tests/bitcoin/test_multisig.py::test_2_of_3
nodes = [
btc.get_public_node(
client, 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(client), messages.ButtonRequest(code=B.ConfirmOutput)),
messages.ButtonRequest(code=B.SignTx),
request_input(0),
request_output(0),
request_finished(),
]
with client:
client.set_expected_responses(expected_responses)
signatures1, _ = btc.sign_tx(
client,
"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 client:
client.set_expected_responses(expected_responses)
signatures2, serialized_tx = btc.sign_tx(
client,
"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"
)