diff --git a/tests/device_tests/bitcoin/test_getaddress.py b/tests/device_tests/bitcoin/test_getaddress.py index f668a22bf5..9977b5598d 100644 --- a/tests/device_tests/bitcoin/test_getaddress.py +++ b/tests/device_tests/bitcoin/test_getaddress.py @@ -19,7 +19,7 @@ import pytest from trezorlib import btc, device, messages from trezorlib.debuglink import TrezorClientDebugLink as Client from trezorlib.exceptions import TrezorFailure -from trezorlib.messages import SafetyCheckLevel +from trezorlib.messages import MultisigPubkeysOrder, SafetyCheckLevel from trezorlib.tools import parse_path from ... import bip32 @@ -196,6 +196,73 @@ def test_altcoin_address_mac(client: Client): ) +@pytest.mark.multisig +@pytest.mark.models(skip="legacy", reason="Sortedmulti is not supported") +def test_multisig_pubkeys_order(client: Client): + xpub_internal = btc.get_public_node(client, parse_path("m/45h/0")).xpub + xpub_external = btc.get_public_node(client, parse_path("m/44h/1")).xpub + + multisig_unsorted_1 = messages.MultisigRedeemScriptType( + nodes=[bip32.deserialize(xpub) for xpub in [xpub_internal, xpub_internal]], + address_n=[0, 0], + signatures=[b"", b"", b""], + m=2, + pubkeys_order=MultisigPubkeysOrder.PRESERVED, + ) + + multisig_unsorted_2 = messages.MultisigRedeemScriptType( + nodes=[bip32.deserialize(xpub) for xpub in [xpub_internal, xpub_external]], + address_n=[0, 0], + signatures=[b"", b"", b""], + m=2, + pubkeys_order=MultisigPubkeysOrder.PRESERVED, + ) + + multisig_sorted_1 = messages.MultisigRedeemScriptType( + nodes=[bip32.deserialize(xpub) for xpub in [xpub_internal, xpub_external]], + address_n=[0, 0], + signatures=[b"", b"", b""], + m=2, + pubkeys_order=MultisigPubkeysOrder.LEXICOGRAPHIC, + ) + + multisig_sorted_2 = messages.MultisigRedeemScriptType( + nodes=[bip32.deserialize(xpub) for xpub in [xpub_external, xpub_internal]], + address_n=[0, 0], + signatures=[b"", b"", b""], + m=2, + pubkeys_order=MultisigPubkeysOrder.LEXICOGRAPHIC, + ) + + address_unsorted_1 = "3JpFrKHq9F2R3Kr65pXMe8M8vy4dwJj7Ci" + address_unsorted_2 = "3HnEADzLm88XUugzXmfkfC5Ed6PXK9AXQh" + + assert ( + btc.get_address( + client, "Bitcoin", parse_path("m/45h/0/0/0"), multisig=multisig_unsorted_1 + ) + == address_unsorted_1 + ) + assert ( + btc.get_address( + client, "Bitcoin", parse_path("m/45h/0/0/0"), multisig=multisig_unsorted_2 + ) + == address_unsorted_2 + ) + assert ( + btc.get_address( + client, "Bitcoin", parse_path("m/45h/0/0/0"), multisig=multisig_sorted_1 + ) + == address_unsorted_2 + ) + assert ( + btc.get_address( + client, "Bitcoin", parse_path("m/45h/0/0/0"), multisig=multisig_sorted_2 + ) + == address_unsorted_2 + ) + + @pytest.mark.multisig def test_multisig(client: Client): xpubs = [] diff --git a/tests/device_tests/bitcoin/test_multisig.py b/tests/device_tests/bitcoin/test_multisig.py index 927720bfb2..7a3da905d6 100644 --- a/tests/device_tests/bitcoin/test_multisig.py +++ b/tests/device_tests/bitcoin/test_multisig.py @@ -26,6 +26,7 @@ from ...input_flows import InputFlowConfirmAllWarnings from ...tx_cache import TxCache from .signtx import ( assert_tx_matches, + forge_prevtx, request_finished, request_input, request_meta, @@ -160,6 +161,164 @@ def test_2_of_3(client: Client, chunkify: bool): ) +@pytest.mark.multisig +@pytest.mark.models(skip="legacy", reason="Sortedmulti is not supported") +def test_pubkeys_order(client: Client): + node_internal = btc.get_public_node( + client, parse_path("m/45h/0"), coin_name="Bitcoin" + ).node + node_external = btc.get_public_node( + client, parse_path("m/45h/1"), coin_name="Bitcoin" + ).node + + multisig_unsorted_1 = messages.MultisigRedeemScriptType( + nodes=[node_internal, node_external], + address_n=[0, 0], + signatures=[b"", b"", b""], + m=1, + pubkeys_order=messages.MultisigPubkeysOrder.PRESERVED, + ) + + multisig_unsorted_2 = messages.MultisigRedeemScriptType( + nodes=[node_external, node_internal], + address_n=[0, 0], + signatures=[b"", b"", b""], + m=1, + pubkeys_order=messages.MultisigPubkeysOrder.PRESERVED, + ) + + multisig_sorted_1 = messages.MultisigRedeemScriptType( + nodes=[node_internal, node_external], + address_n=[0, 0], + signatures=[b"", b"", b""], + m=1, + pubkeys_order=messages.MultisigPubkeysOrder.LEXICOGRAPHIC, + ) + + multisig_sorted_2 = messages.MultisigRedeemScriptType( + nodes=[node_external, node_internal], + address_n=[0, 0], + signatures=[b"", b"", b""], + m=1, + pubkeys_order=messages.MultisigPubkeysOrder.LEXICOGRAPHIC, + ) + + address_unsorted_1 = btc.get_address( + client, "Bitcoin", parse_path("m/45h/0/0/0"), multisig=multisig_unsorted_1 + ) + address_unsorted_2 = btc.get_address( + client, "Bitcoin", parse_path("m/45h/0/0/0"), multisig=multisig_unsorted_2 + ) + + prev_hash, prev_tx = forge_prevtx( + [(address_unsorted_1, 1_000_000), (address_unsorted_2, 1_000_000)] + ) + + input_unsorted_1 = messages.TxInputType( + address_n=parse_path("m/45h/0/0/0"), + amount=1_000_000, + prev_hash=prev_hash, + prev_index=0, # multisig_unsorted_1 + script_type=messages.InputScriptType.SPENDMULTISIG, + multisig=multisig_unsorted_1, + ) + + input_unsorted_2 = messages.TxInputType( + address_n=parse_path("m/45h/0/0/0"), + amount=1_000_000, + prev_hash=prev_hash, + prev_index=1, # multisig_unsorted_2 + script_type=messages.InputScriptType.SPENDMULTISIG, + multisig=multisig_unsorted_2, + ) + + input_sorted_1 = messages.TxInputType( + address_n=parse_path("m/45h/0/0/0"), + amount=1_000_000, + prev_hash=prev_hash, + prev_index=0, # multisig_unsorted_1 + script_type=messages.InputScriptType.SPENDMULTISIG, + multisig=multisig_sorted_1, + ) + + input_sorted_2 = messages.TxInputType( + address_n=parse_path("m/45h/0/0/0"), + amount=1_000_000, + prev_hash=prev_hash, + prev_index=0, # multisig_unsorted_1 + script_type=messages.InputScriptType.SPENDMULTISIG, + multisig=multisig_sorted_2, + ) + + output_unsorted_1 = messages.TxOutputType( + address_n=parse_path("m/45h/0/0/0"), + amount=1_000_000 - 10_000, + script_type=messages.OutputScriptType.PAYTOMULTISIG, + multisig=multisig_unsorted_1, + ) + + output_unsorted_2 = messages.TxOutputType( + address_n=parse_path("m/45h/0/0/0"), + amount=1_000_000 - 10_000, + script_type=messages.OutputScriptType.PAYTOMULTISIG, + multisig=multisig_unsorted_2, + ) + + output_sorted_1 = messages.TxOutputType( + address_n=parse_path("m/45h/0/0/0"), + amount=1_000_000 - 10_000, + script_type=messages.OutputScriptType.PAYTOMULTISIG, + multisig=multisig_sorted_1, + ) + + output_sorted_2 = messages.TxOutputType( + address_n=parse_path("m/45h/0/0/0"), + amount=1_000_000 - 10_000, + script_type=messages.OutputScriptType.PAYTOMULTISIG, + multisig=multisig_sorted_2, + ) + + tx_unsorted_1 = "0100000001637ffac0d4fbd8a6c02b114e36b079615ec3e4bdf09b769c7bf8b5fd6f8e7817000000009200483045022100f062d71445509d84a5769f219f4f0158dc7ff4351d671e6fa6bfdb171435064802206e1104f1d14f010bcc166cca6a9bd24b4a497475f8e23167858b4a3e92ac6a67014751210262e9ac5bea4c84c7dea650424ed768cf123af9e447eef3c63d37c41d1f825e49210369b79f2094a6eb89e7aff0e012a5699f7272968a341e48e99e64a54312f2932b52aeffffffff01301b0f000000000017a91440bfd8ae9d806d5bd7cf475ce6a80535836285148700000000" + + tx_unsorted_2 = "0100000001637ffac0d4fbd8a6c02b114e36b079615ec3e4bdf09b769c7bf8b5fd6f8e781701000000910047304402204914036468434698e2d87985007a66691f170195e4a16507bbb86b4c00da5fde02200a788312d447b3796ee5288ce9e9c0247896debfa473339302bc928da6dd78cb014751210369b79f2094a6eb89e7aff0e012a5699f7272968a341e48e99e64a54312f2932b210262e9ac5bea4c84c7dea650424ed768cf123af9e447eef3c63d37c41d1f825e4952aeffffffff01301b0f000000000017a914320ad0ff0f1b605ab1fa8e29b70d22827cf45a9f8700000000" + + _, tx = btc.sign_tx( + client, + "Bitcoin", + [input_unsorted_1], + [output_unsorted_1], + prev_txes={prev_hash: prev_tx}, + ) + assert tx.hex() == tx_unsorted_1 + + _, tx = btc.sign_tx( + client, + "Bitcoin", + [input_unsorted_2], + [output_unsorted_2], + prev_txes={prev_hash: prev_tx}, + ) + assert tx.hex() == tx_unsorted_2 + + _, tx = btc.sign_tx( + client, + "Bitcoin", + [input_sorted_1], + [output_sorted_1], + prev_txes={prev_hash: prev_tx}, + ) + assert tx.hex() == tx_unsorted_1 + + _, tx = btc.sign_tx( + client, + "Bitcoin", + [input_sorted_2], + [output_sorted_2], + prev_txes={prev_hash: prev_tx}, + ) + assert tx.hex() == tx_unsorted_1 + + @pytest.mark.multisig def test_15_of_15(client: Client): # input tx: 0d5b5648d47b5650edea1af3d47bbe5624213abb577cf1b1c96f98321f75cdbc