From 6c9c727359aa79b836483a4c9b6663efef1090a2 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Wed, 10 Nov 2021 13:15:20 +0100 Subject: [PATCH] feat(legacy): Implement GetAddress for taproot. --- legacy/firmware/.changelog.d/1656.added.1 | 1 + legacy/firmware/fsm_msg_coin.h | 3 +- legacy/firmware/transaction.c | 67 +++++++++++++------ .../test_msg_getaddress_segwit_native.py | 13 ++-- 4 files changed, 53 insertions(+), 31 deletions(-) create mode 100644 legacy/firmware/.changelog.d/1656.added.1 diff --git a/legacy/firmware/.changelog.d/1656.added.1 b/legacy/firmware/.changelog.d/1656.added.1 new file mode 100644 index 0000000000..b54695a7d9 --- /dev/null +++ b/legacy/firmware/.changelog.d/1656.added.1 @@ -0,0 +1 @@ +Support GetAddress for Taproot addresses. diff --git a/legacy/firmware/fsm_msg_coin.h b/legacy/firmware/fsm_msg_coin.h index af03a5066f..2eb3750836 100644 --- a/legacy/firmware/fsm_msg_coin.h +++ b/legacy/firmware/fsm_msg_coin.h @@ -233,7 +233,8 @@ void fsm_msgGetAddress(const GetAddress *msg) { } bool is_cashaddr = coin->cashaddr_prefix != NULL; - bool is_bech32 = msg->script_type == InputScriptType_SPENDWITNESS; + bool is_bech32 = msg->script_type == InputScriptType_SPENDWITNESS || + msg->script_type == InputScriptType_SPENDTAPROOT; if (!fsm_layoutAddress(address, desc, is_cashaddr || is_bech32, is_cashaddr ? strlen(coin->cashaddr_prefix) + 1 : 0, msg->address_n, msg->address_n_count, false, diff --git a/legacy/firmware/transaction.c b/legacy/firmware/transaction.c index 67a43c0767..1239c59247 100644 --- a/legacy/firmware/transaction.c +++ b/legacy/firmware/transaction.c @@ -33,8 +33,10 @@ #include "ripemd160.h" #include "segwit_addr.h" #include "util.h" +#include "zkp_bip340.h" #define SEGWIT_VERSION_0 0 +#define SEGWIT_VERSION_1 1 #define CASHADDR_P2KH (0) #define CASHADDR_P2SH (8) @@ -143,21 +145,27 @@ bool compute_address(const CoinInfo *coin, InputScriptType script_type, address, MAX_ADDR_SIZE)) { return 0; } - } else if (coin->cashaddr_prefix) { - raw[0] = CASHADDR_P2SH | CASHADDR_160; - ripemd160(digest, 32, raw + 1); - if (!cash_addr_encode(address, coin->cashaddr_prefix, raw, 21)) { - return 0; + } else if (script_type == InputScriptType_SPENDADDRESS || + script_type == InputScriptType_SPENDMULTISIG) { + if (coin->cashaddr_prefix) { + raw[0] = CASHADDR_P2SH | CASHADDR_160; + ripemd160(digest, 32, raw + 1); + if (!cash_addr_encode(address, coin->cashaddr_prefix, raw, 21)) { + return 0; + } + } else { + // non-segwit p2sh multisig + prelen = address_prefix_bytes_len(coin->address_type_p2sh); + address_write_prefix_bytes(coin->address_type_p2sh, raw); + ripemd160(digest, 32, raw + prelen); + if (!base58_encode_check(raw, prelen + 20, coin->curve->hasher_base58, + address, MAX_ADDR_SIZE)) { + return 0; + } } } else { - // non-segwit p2sh multisig - prelen = address_prefix_bytes_len(coin->address_type_p2sh); - address_write_prefix_bytes(coin->address_type_p2sh, raw); - ripemd160(digest, 32, raw + prelen); - if (!base58_encode_check(raw, prelen + 20, coin->curve->hasher_base58, - address, MAX_ADDR_SIZE)) { - return 0; - } + // unsupported script type + return 0; } } else if (script_type == InputScriptType_SPENDWITNESS) { // segwit p2wpkh: pubkey hash is ripemd160 of sha256 @@ -169,6 +177,17 @@ bool compute_address(const CoinInfo *coin, InputScriptType script_type, digest, 20)) { return 0; } + } else if (script_type == InputScriptType_SPENDTAPROOT) { + // taproot + if (!coin->has_taproot || !coin->has_segwit || !coin->bech32_prefix) { + return 0; + } + uint8_t tweaked_pubkey[32]; + zkp_bip340_tweak_public_key(node->public_key + 1, NULL, tweaked_pubkey); + if (!segwit_addr_encode(address, coin->bech32_prefix, SEGWIT_VERSION_1, + tweaked_pubkey, 32)) { + return 0; + } } else if (script_type == InputScriptType_SPENDP2SHWITNESS) { // segwit p2wpkh embedded in p2sh if (!coin->has_segwit) { @@ -177,16 +196,22 @@ bool compute_address(const CoinInfo *coin, InputScriptType script_type, ecdsa_get_address_segwit_p2sh( node->public_key, coin->address_type_p2sh, coin->curve->hasher_pubkey, coin->curve->hasher_base58, address, MAX_ADDR_SIZE); - } else if (coin->cashaddr_prefix) { - ecdsa_get_address_raw(node->public_key, CASHADDR_P2KH | CASHADDR_160, - coin->curve->hasher_pubkey, raw); - if (!cash_addr_encode(address, coin->cashaddr_prefix, raw, 21)) { - return 0; + } else if (script_type == InputScriptType_SPENDADDRESS || + script_type == InputScriptType_SPENDMULTISIG) { + if (coin->cashaddr_prefix) { + ecdsa_get_address_raw(node->public_key, CASHADDR_P2KH | CASHADDR_160, + coin->curve->hasher_pubkey, raw); + if (!cash_addr_encode(address, coin->cashaddr_prefix, raw, 21)) { + return 0; + } + } else { + ecdsa_get_address(node->public_key, coin->address_type, + coin->curve->hasher_pubkey, coin->curve->hasher_base58, + address, MAX_ADDR_SIZE); } } else { - ecdsa_get_address(node->public_key, coin->address_type, - coin->curve->hasher_pubkey, coin->curve->hasher_base58, - address, MAX_ADDR_SIZE); + // unsupported script type + return 0; } return 1; } diff --git a/tests/device_tests/test_msg_getaddress_segwit_native.py b/tests/device_tests/test_msg_getaddress_segwit_native.py index f10536532d..524ed3943d 100644 --- a/tests/device_tests/test_msg_getaddress_segwit_native.py +++ b/tests/device_tests/test_msg_getaddress_segwit_native.py @@ -45,33 +45,29 @@ VECTORS = ( # coin, path, script_type, address proto.InputScriptType.SPENDWITNESS, "bc1qktmhrsmsenepnnfst8x6j27l0uqv7ggrg8x38q", ), - pytest.param( + ( "Testnet", "86'/1'/0'/0/0", proto.InputScriptType.SPENDTAPROOT, "tb1pswrqtykue8r89t9u4rprjs0gt4qzkdfuursfnvqaa3f2yql07zmq8s8a5u", - marks=pytest.mark.skip_t1, ), - pytest.param( + ( "Testnet", "86'/1'/0'/1/0", proto.InputScriptType.SPENDTAPROOT, "tb1pn2d0yjeedavnkd8z8lhm566p0f2utm3lgvxrsdehnl94y34txmts5s7t4c", - marks=pytest.mark.skip_t1, ), - pytest.param( + ( "Bitcoin", "86'/0'/0'/0/0", proto.InputScriptType.SPENDTAPROOT, "bc1ptxs597p3fnpd8gwut5p467ulsydae3rp9z75hd99w8k3ljr9g9rqx6ynaw", - marks=pytest.mark.skip_t1, ), - pytest.param( + ( "Bitcoin", "86'/0'/0'/1/0", proto.InputScriptType.SPENDTAPROOT, "bc1pgypgja2hmcx2l6s2ssq75k6ev68ved6nujcspt47dgvkp8euc70s6uegk6", - marks=pytest.mark.skip_t1, ), pytest.param( "Groestlcoin", @@ -114,7 +110,6 @@ def test_show_segwit(client, show_display, coin, path, script_type, address): # Tests https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki#test-vectors -@pytest.mark.skip_t1 @pytest.mark.setup_client( mnemonic="abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" )