Skip to content

Commit

Permalink
feat(legacy): add possibility to send to Taproot addresses
Browse files Browse the repository at this point in the history
  • Loading branch information
prusnak committed Jul 22, 2021
1 parent 45d31ca commit 898296d
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 5 deletions.
51 changes: 50 additions & 1 deletion legacy/firmware/crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ int cryptoMessageSign(const CoinInfo *coin, HDNode *node,
// segwit
signature[0] = 39 + pby;
break;
case InputScriptType_SPENDTAPROOT:
// taproot
signature[0] = 43 + pby;
break;
default:
// p2pkh
signature[0] = 31 + pby;
Expand Down Expand Up @@ -240,8 +244,29 @@ int cryptoMessageVerify(const CoinInfo *coin, const uint8_t *message,
address)) {
return 4;
}
if (witver != 0 || len != 20) {
return 2;
}
ecdsa_get_pubkeyhash(pubkey, coin->curve->hasher_pubkey, addr_raw);
if (memcmp(recovered_raw, addr_raw, len) != 0 || witver != 0 || len != 20) {
if (memcmp(recovered_raw, addr_raw, len)) {
return 2;
}
} else
// taproot
if (signature[0] >= 43 && signature[0] <= 46) {
int witver = 0;
size_t len = 0;
if (!coin->bech32_prefix ||
!segwit_addr_decode(&witver, recovered_raw, &len, coin->bech32_prefix,
address)) {
return 4;
}
if (witver != 1 || len != 32) {
return 2;
}
uint8_t tweaked_pubkey[32];
// TODO: ecdsa_tweak_pubkey(pubkey, tweaked_pubkey);
if (memcmp(tweaked_pubkey, addr_raw, len) != 0) {
return 2;
}
} else {
Expand Down Expand Up @@ -650,6 +675,7 @@ bool coin_path_check(const CoinInfo *coin, InputScriptType script_type,
valid = valid && check_cointype(coin, address_n[1], check_known);
if (check_script_type) {
valid = valid && has_multisig;
// we do not support Multisig with Taproot yet
valid = valid && (script_type == InputScriptType_SPENDMULTISIG ||
script_type == InputScriptType_SPENDP2SHWITNESS ||
script_type == InputScriptType_SPENDWITNESS);
Expand Down Expand Up @@ -717,6 +743,29 @@ bool coin_path_check(const CoinInfo *coin, InputScriptType script_type,
return valid;
}

// m/86' : BIP86 Taproot
// m / purpose' / coin_type' / account' / change / address_index
if (address_n_count > 0 && address_n[0] == (0x80000000 + 86)) {
valid = valid && coin->has_taproot;
valid = valid && (coin->bech32_prefix != NULL);
if (check_known) {
valid = valid && (address_n_count == 5);
} else {
valid = valid && (address_n_count >= 2);
}
valid = valid && check_cointype(coin, address_n[1], check_known);
if (check_script_type) {
valid = valid && (script_type == InputScriptType_SPENDTAPROOT);
}
if (check_known) {
valid = valid && ((address_n[2] & 0x80000000) == 0x80000000);
valid = valid && ((address_n[2] & 0x7fffffff) <= PATH_MAX_ACCOUNT);
valid = valid && (address_n[3] <= PATH_MAX_CHANGE);
valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
}
return valid;
}

// Green Address compatibility pattern. Will be removed in the future.
// m / [1,4] / address_index
if (address_n_count > 0 && (address_n[0] == 1 || address_n[0] == 4)) {
Expand Down
3 changes: 2 additions & 1 deletion legacy/firmware/fsm_msg_coin.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,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,
Expand Down
20 changes: 17 additions & 3 deletions legacy/firmware/signing.c
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,7 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin,
#define MIN(a, b) (((a) < (b)) ? (a) : (b))

static bool is_multisig_input_script_type(const TxInputType *txinput) {
// we do not support Multisig with Taproot yet
if (txinput->script_type == InputScriptType_SPENDMULTISIG ||
txinput->script_type == InputScriptType_SPENDP2SHWITNESS ||
txinput->script_type == InputScriptType_SPENDWITNESS) {
Expand All @@ -914,6 +915,7 @@ static bool is_multisig_input_script_type(const TxInputType *txinput) {
}

static bool is_multisig_output_script_type(const TxOutputType *txoutput) {
// we do not support Multisig with Taproot yet
if (txoutput->script_type == OutputScriptType_PAYTOMULTISIG ||
txoutput->script_type == OutputScriptType_PAYTOP2SHWITNESS ||
txoutput->script_type == OutputScriptType_PAYTOWITNESS) {
Expand All @@ -926,7 +928,8 @@ static bool is_internal_input_script_type(const TxInputType *txinput) {
if (txinput->script_type == InputScriptType_SPENDADDRESS ||
txinput->script_type == InputScriptType_SPENDMULTISIG ||
txinput->script_type == InputScriptType_SPENDP2SHWITNESS ||
txinput->script_type == InputScriptType_SPENDWITNESS) {
txinput->script_type == InputScriptType_SPENDWITNESS ||
txinput->script_type == InputScriptType_SPENDTAPROOT) {
return true;
}
return false;
Expand All @@ -936,19 +939,30 @@ static bool is_change_output_script_type(const TxOutputType *txoutput) {
if (txoutput->script_type == OutputScriptType_PAYTOADDRESS ||
txoutput->script_type == OutputScriptType_PAYTOMULTISIG ||
txoutput->script_type == OutputScriptType_PAYTOP2SHWITNESS ||
txoutput->script_type == OutputScriptType_PAYTOWITNESS) {
txoutput->script_type == OutputScriptType_PAYTOWITNESS ||
txoutput->script_type == OutputScriptType_PAYTOTAPROOT) {
return true;
}
return false;
}

static bool is_segwit_input_script_type(const TxInputType *txinput) {
if (txinput->script_type == InputScriptType_SPENDP2SHWITNESS ||
txinput->script_type == InputScriptType_SPENDWITNESS) {
txinput->script_type == InputScriptType_SPENDWITNESS ||
txinput->script_type == InputScriptType_SPENDTAPROOT) {
return true;
}
return false;
}

/*
static bool is_taproot_input_script_type(const TxInputType *txinput) {
if (txinput->script_type == InputScriptType_SPENDTAPROOT) {
return true;
}
return false;
}
*/

static bool signing_validate_input(const TxInputType *txinput) {
if (txinput->prev_hash.size != 32) {
Expand Down
21 changes: 21 additions & 0 deletions legacy/firmware/transaction.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "util.h"

#define SEGWIT_VERSION_0 0
#define SEGWIT_VERSION_1 1

#define CASHADDR_P2KH (0)
#define CASHADDR_P2SH (8)
Expand All @@ -54,6 +55,8 @@
#define TXSIZE_WITNESSPKHASH 22
/* size of a p2wsh script (1 version, 1 push, 32 hash) */
#define TXSIZE_WITNESSSCRIPT 34
/* size of a p2tr script (1 version, 1 push, 32 hash) */
#define TXSIZE_TAPROOT 34
/* size of a p2pkh script (dup, hash, push, 20 pubkeyhash, equal, checksig) */
#define TXSIZE_P2PKHASH 25
/* size of a p2sh script (hash, push, 20 scripthash, equal) */
Expand Down Expand Up @@ -169,6 +172,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];
// TODO: ecdsa_tweak_pubkey(node->public_key, 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) {
Expand Down Expand Up @@ -248,6 +262,9 @@ int compile_output(const CoinInfo *coin, AmountUnit amount_unit,
case OutputScriptType_PAYTOP2SHWITNESS:
input_script_type = InputScriptType_SPENDP2SHWITNESS;
break;
case OutputScriptType_PAYTOTAPROOT:
input_script_type = InputScriptType_SPENDTAPROOT;
break;
default:
return 0; // failed to compile output
}
Expand Down Expand Up @@ -876,6 +893,8 @@ uint32_t tx_input_weight(const CoinInfo *coin, const TxInputType *txinput) {
input_script_size += ser_length_size(input_script_size);
weight += 4 * input_script_size;
} else if (txinput->script_type == InputScriptType_SPENDWITNESS ||
// TODO: is the following line correct?
txinput->script_type == InputScriptType_SPENDTAPROOT ||
txinput->script_type == InputScriptType_SPENDP2SHWITNESS) {
if (txinput->script_type == InputScriptType_SPENDP2SHWITNESS) {
weight += 4 * (2 + (txinput->has_multisig ? TXSIZE_WITNESSSCRIPT
Expand All @@ -897,6 +916,8 @@ uint32_t tx_output_weight(const CoinInfo *coin, const TxOutputType *txoutput) {
if (txoutput->script_type == OutputScriptType_PAYTOWITNESS) {
output_script_size =
txoutput->has_multisig ? TXSIZE_WITNESSSCRIPT : TXSIZE_WITNESSPKHASH;
} else if (txoutput->script_type == OutputScriptType_PAYTOTAPROOT) {
output_script_size = TXSIZE_TAPROOT;
} else if (txoutput->script_type == OutputScriptType_PAYTOP2SHWITNESS) {
output_script_size = TXSIZE_P2SCRIPT;
} else {
Expand Down

0 comments on commit 898296d

Please sign in to comment.