From b9486c0b33ca1ec40c90c0f698132658d9610720 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Tue, 18 Feb 2020 22:39:55 +0000 Subject: [PATCH 1/2] core: show yours/others in get_address for multisig --- core/src/apps/wallet/get_address.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/core/src/apps/wallet/get_address.py b/core/src/apps/wallet/get_address.py index 85d822e49..c6c13f40e 100644 --- a/core/src/apps/wallet/get_address.py +++ b/core/src/apps/wallet/get_address.py @@ -6,6 +6,7 @@ from apps.common import coins from apps.common.layout import address_n_to_str, show_address, show_qr, show_xpub from apps.common.paths import validate_path from apps.wallet.sign_tx import addresses +from apps.wallet.sign_tx.multisig import multisig_pubkey_index if False: from typing import List @@ -15,7 +16,7 @@ if False: async def show_xpubs( - ctx: wire.Context, coin: CoinInfo, pubnodes: List[HDNodeType] + ctx: wire.Context, coin: CoinInfo, pubnodes: List[HDNodeType], multisig_index: int ) -> bool: for i, pubnode in enumerate(pubnodes): cancel = "Next" if i < len(pubnodes) - 1 else "Address" @@ -28,7 +29,9 @@ async def show_xpubs( curve_name=coin.curve_name, ) xpub = node.serialize_public(coin.xpub_magic) - if await show_xpub(ctx, xpub, desc="XPUB #%d" % (i + 1), cancel=cancel): + desc = "XPUB #%d" % (i + 1) + desc += " (yours)" if i == multisig_index else " (others)" + if await show_xpub(ctx, xpub, desc=desc, cancel=cancel): return True return False @@ -63,13 +66,14 @@ async def get_address(ctx, msg, keychain): pubnodes = msg.multisig.nodes else: pubnodes = [hd.node for hd in msg.multisig.pubkeys] + multisig_index = multisig_pubkey_index(msg.multisig, node.public_key()) desc = "Multisig %d of %d" % (msg.multisig.m, len(pubnodes)) while True: if await show_address(ctx, address_short, desc=desc): break if await show_qr(ctx, address_qr, desc=desc, cancel="XPUBs"): break - if await show_xpubs(ctx, coin, pubnodes): + if await show_xpubs(ctx, coin, pubnodes, multisig_index): break else: desc = address_n_to_str(msg.address_n) From d98bbebf5e3f639fb42e3597554562cf8f5e3c6f Mon Sep 17 00:00:00 2001 From: matejcik Date: Thu, 20 Feb 2020 12:47:29 +0100 Subject: [PATCH 2/2] tests: add UI test for multisig+xpub feature --- .../device_tests/test_msg_getaddress_show.py | 83 +++++++++++++++++-- tests/ui_tests/fixtures.json | 1 + 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/tests/device_tests/test_msg_getaddress_show.py b/tests/device_tests/test_msg_getaddress_show.py index 9e344cb15..1719e526f 100644 --- a/tests/device_tests/test_msg_getaddress_show.py +++ b/tests/device_tests/test_msg_getaddress_show.py @@ -16,7 +16,7 @@ import pytest -from trezorlib import btc, ckd_public as bip32, messages as proto +from trezorlib import btc, ckd_public as bip32, messages, tools from ..common import MNEMONIC12 @@ -43,11 +43,11 @@ class TestMsgGetaddressShow: node = bip32.deserialize( "xpub661MyMwAqRbcF1zGijBb2K6x9YiJPh58xpcCeLvTxMX6spkY3PcpJ4ABcCyWfskq5DDxM3e6Ez5ePCqG5bnPUXR4wL8TZWyoDaUdiWW7bKy" ) - multisig = proto.MultisigRedeemScriptType( + multisig = messages.MultisigRedeemScriptType( pubkeys=[ - proto.HDNodePathType(node=node, address_n=[1]), - proto.HDNodePathType(node=node, address_n=[2]), - proto.HDNodePathType(node=node, address_n=[3]), + messages.HDNodePathType(node=node, address_n=[1]), + messages.HDNodePathType(node=node, address_n=[2]), + messages.HDNodePathType(node=node, address_n=[3]), ], signatures=[b"", b"", b""], m=2, @@ -61,6 +61,75 @@ class TestMsgGetaddressShow: == "3E7GDtuHqnqPmDgwH59pVC7AvySiSkbibz" ) + @pytest.mark.skip_t1 + @pytest.mark.multisig + def test_show_multisig_xpubs(self, client): + nodes = [ + btc.get_public_node( + client, tools.parse_path(f"48h/0h/{i}h"), coin_name="Bitcoin" + ) + for i in range(3) + ] + multisig = messages.MultisigRedeemScriptType( + nodes=[n.node for n in nodes], + signatures=[b"", b"", b""], + address_n=[0, 0], + m=2, + ) + + xpubs = [[n.xpub[i * 16 : (i + 1) * 16] for i in range(5)] for n in nodes] + + for i in range(3): + + def input_flow(): + yield # show address + assert client.debug.wait_layout().lines == [ + "Multisig 2 of 3", + "34yJV2b2GtbmxfZNw", + "jPyuyUYkUbUnogqa8", + ] + + client.debug.press_no() + yield # show QR code + assert client.debug.wait_layout().text.startswith("Qr") + + client.debug.press_no() + yield # show XPUB#1 + lines = client.debug.wait_layout().lines + assert lines[0] == "XPUB #1 " + ("(yours)" if i == 0 else "(others)") + assert lines[1:] == xpubs[0] + # just for UI test + client.debug.swipe_up() + + client.debug.press_no() + yield # show XPUB#2 + lines = client.debug.wait_layout().lines + assert lines[0] == "XPUB #2 " + ("(yours)" if i == 1 else "(others)") + assert lines[1:] == xpubs[1] + # just for UI test + client.debug.swipe_up() + + client.debug.press_no() + yield # show XPUB#3 + lines = client.debug.wait_layout().lines + assert lines[0] == "XPUB #3 " + ("(yours)" if i == 2 else "(others)") + assert lines[1:] == xpubs[2] + # just for UI test + client.debug.swipe_up() + + client.debug.press_yes() + + with client: + client.set_input_flow(input_flow) + btc.get_address( + client, + "Bitcoin", + tools.parse_path(f"48h/0h/{i}h/0/0"), + show_display=True, + multisig=multisig, + script_type=messages.InputScriptType.SPENDMULTISIG, + ) + @pytest.mark.multisig @pytest.mark.setup_client(mnemonic=MNEMONIC12) def test_show_multisig_15(self, client): @@ -70,9 +139,9 @@ class TestMsgGetaddressShow: pubs = [] for x in range(15): - pubs.append(proto.HDNodePathType(node=node, address_n=[x])) + pubs.append(messages.HDNodePathType(node=node, address_n=[x])) - multisig = proto.MultisigRedeemScriptType( + multisig = messages.MultisigRedeemScriptType( pubkeys=pubs, signatures=[b""] * 15, m=15 ) diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index d0a00c4dc..330d4481e 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -122,6 +122,7 @@ "test_msg_getaddress_show.py-test_show": "903c42539d1b09cb75fda744afc9f645daf71fecf6ee993b231837a86172c0b8", "test_msg_getaddress_show.py-test_show_multisig_15": "81fd8fc77f7e48369b528b304db16a11d970dac052b045d9988e5ded580251f4", "test_msg_getaddress_show.py-test_show_multisig_3": "c11622e28edfd26e91be1c4fb67e1301b66ce61d70ec24946d1cb3f78e38377b", +"test_msg_getaddress_show.py-test_show_multisig_xpubs": "d89621c445de3ddcd0c922860869dc1edc1f00a3756cacd558b9d0b9cc6782ac", "test_msg_getecdhsessionkey.py-test_ecdh": "75fe462e6afa73742949ede4f3529d2e0ec08f8f1b67c04a57189c8657fcbdcd", "test_msg_getentropy.py::test_entropy[128]": "a722fa2048fa3102889ec05558d25f837a364ef2a118e85975683e10a56f1356", "test_msg_getentropy.py::test_entropy[129]": "a722fa2048fa3102889ec05558d25f837a364ef2a118e85975683e10a56f1356",