Skip to content

Commit 6c4cd76

Browse files
Merge pull request #665 from Cypherock/feat/btc-taproot/address
feat: Btc taproot address generation
2 parents aa77d2a + 4bef159 commit 6c4cd76

File tree

6 files changed

+388
-162
lines changed

6 files changed

+388
-162
lines changed

apps/btc_family/btc_helpers.c

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,18 @@
6262

6363
#include "btc_helpers.h"
6464

65+
#include <stdint.h>
66+
#include <string.h>
67+
68+
#include "bignum.h"
6569
#include "btc_priv.h"
6670
#include "coin_utils.h"
71+
#include "ecdsa.h"
6772
#include "flash_config.h"
73+
#include "secp256k1.h"
6874
#include "segwit_addr.h"
75+
#include "sha2.h"
76+
#include "utils.h"
6977

7078
/*****************************************************************************
7179
* EXTERN VARIABLES
@@ -86,6 +94,12 @@
8694
/*****************************************************************************
8795
* STATIC VARIABLES
8896
*****************************************************************************/
97+
// sha256("TapTweak") used in BIP-340
98+
// This is used to derive the taproot address
99+
static const uint8_t tap_tweak_hash[32] = {
100+
232, 15, 225, 99, 156, 156, 160, 80, 227, 175, 27,
101+
57, 193, 67, 198, 62, 66, 156, 188, 235, 21, 217,
102+
64, 251, 181, 197, 161, 244, 175, 87, 197, 233};
89103

90104
/*****************************************************************************
91105
* GLOBAL VARIABLES
@@ -95,6 +109,99 @@
95109
* STATIC FUNCTIONS
96110
*****************************************************************************/
97111

112+
// Computes sha256(tap_tweak_hash + tap_tweak_hash + x_only_public_key +
113+
// root_hash) and stores the result in tweak_key_hash.
114+
static bool bip340_tweak_key_hash(const uint8_t *x_only_public_key,
115+
const uint8_t *root_hash,
116+
uint8_t tweak_key_hash[32]) {
117+
if (NULL == x_only_public_key || NULL == tweak_key_hash) {
118+
return false;
119+
}
120+
121+
size_t payload_size =
122+
32 * 3; // tap_tweak_hash + tap_tweak_hash + x_only_public_key
123+
if (root_hash != NULL) {
124+
payload_size += 32;
125+
}
126+
127+
uint8_t payload[payload_size];
128+
memzero(payload, payload_size);
129+
// Prepare the data for hashing
130+
memcpy(payload, tap_tweak_hash, 32);
131+
memcpy(payload + 32, tap_tweak_hash, 32);
132+
memcpy(payload + 64, x_only_public_key, 32);
133+
if (root_hash != NULL) {
134+
memcpy(payload + 32 * 3, root_hash, 32);
135+
}
136+
137+
sha256_Raw(payload, payload_size, tweak_key_hash);
138+
return true;
139+
}
140+
141+
// Calculates result = (public_key_point + tweak_key_hash * G).x
142+
static bool bip340_point_add_tweak(const ecdsa_curve *curve,
143+
const uint8_t *public_key,
144+
const uint8_t *tweak_key_hash,
145+
uint8_t result[32]) {
146+
if (NULL == public_key || NULL == tweak_key_hash || NULL == result) {
147+
return false;
148+
}
149+
150+
curve_point public_key_point = {0};
151+
if (!ecdsa_read_pubkey(curve, public_key, &public_key_point)) {
152+
return false;
153+
}
154+
155+
// Negate y-coordinate if it's odd
156+
if (bn_is_odd(&public_key_point.y)) {
157+
bn_subtract(&curve->prime, &public_key_point.y, &public_key_point.y);
158+
bn_mod(&public_key_point.y, &curve->prime);
159+
}
160+
161+
bignum256 tweak_hash_bn;
162+
bn_read_be(tweak_key_hash, &tweak_hash_bn);
163+
bn_mod(&tweak_hash_bn, &curve->order);
164+
165+
if (bn_is_zero(&tweak_hash_bn)) {
166+
return false;
167+
}
168+
169+
curve_point result_point = {0};
170+
point_multiply(curve,
171+
&tweak_hash_bn,
172+
&curve->G,
173+
&result_point); // result_point = tweak_hash_bn * G
174+
point_add(curve,
175+
&public_key_point,
176+
&result_point); // result_point = public_key_point + result_point
177+
178+
// take the x-coordinate of the result point
179+
bn_write_be(&result_point.x, result);
180+
return true;
181+
}
182+
183+
// implementation of BIP-340 tweak public key for taproot without using
184+
// secp256k1 library
185+
static bool bip340_tweak_public_key(const uint8_t *public_key,
186+
const uint8_t *root_hash,
187+
uint8_t *tweaked_public_key) {
188+
if (NULL == public_key || NULL == tweaked_public_key) {
189+
return false;
190+
}
191+
192+
uint8_t tweak_key_hash[32] = {0};
193+
if (!bip340_tweak_key_hash(public_key + 1, root_hash, tweak_key_hash)) {
194+
return false;
195+
}
196+
197+
if (!bip340_point_add_tweak(
198+
&secp256k1, public_key, tweak_key_hash, tweaked_public_key)) {
199+
return false;
200+
}
201+
202+
return true;
203+
}
204+
98205
/*****************************************************************************
99206
* GLOBAL FUNCTIONS
100207
*****************************************************************************/
@@ -208,3 +315,18 @@ void format_value(const uint64_t value_in_sat,
208315
snprintf(
209316
msg, msg_len, "%0.*f %s", precision, fee_in_btc, g_btc_app->lunit_name);
210317
}
318+
319+
int btc_get_taproot_address(uint8_t *public_key,
320+
const char *hrp,
321+
char *address) {
322+
if (NULL == public_key || NULL == hrp || NULL == address) {
323+
return 0;
324+
}
325+
326+
uint8_t tweaked_public_key[32] = {0};
327+
if (!bip340_tweak_public_key(public_key, NULL, tweaked_public_key)) {
328+
return 0;
329+
}
330+
331+
return segwit_addr_encode(address, hrp, 1, tweaked_public_key, 32);
332+
}

apps/btc_family/btc_helpers.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,19 @@ bool btc_derivation_path_guard(const uint32_t *path, uint32_t depth);
137137
*/
138138
void format_value(uint64_t value_in_sat, char *msg, size_t msg_len);
139139

140+
/**
141+
* @brief Returns the taproot key path address string (Bech32M encoding)
142+
* @details The functions assumes public key is compressed
143+
*
144+
* @param [in] public_key 33 Byte array representation of public key.
145+
* @param [in] hrp HRP value for bech32M encoding "bc" for mainnet and
146+
* "tb" for testnet.
147+
* @param [out] address char array to store taproot address.
148+
*
149+
* @return 1 if successful and 0 if failure.
150+
*/
151+
int btc_get_taproot_address(uint8_t *public_key,
152+
const char *hrp,
153+
char *address);
154+
140155
#endif

apps/btc_family/btc_pub_key.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ static size_t btc_get_address(const uint8_t *seed,
199199
uint8_t *public_key,
200200
char *address) {
201201
HDNode node = {0};
202-
char addr[50] = "";
202+
char addr[70] = "";
203203
size_t address_length = 0;
204204

205205
if (!derive_hdnode_from_path(
@@ -230,7 +230,9 @@ static size_t btc_get_address(const uint8_t *seed,
230230
36);
231231
break;
232232

233-
// TODO: add support for taproot
233+
case PURPOSE_TAPROOT:
234+
btc_get_taproot_address(node.public_key, g_btc_app->bech32_hrp, addr);
235+
break;
234236
default:
235237
break;
236238
}

common/coin_support/coin_utils.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ void bech32_addr_encode(char *output,
127127
uint8_t data[65] = {0};
128128
size_t datalen = 0;
129129
convert_bits(data, &datalen, 5, address_bytes, byte_len, 8, 1);
130-
bech32_encode(output, hrp, data, datalen);
130+
bech32_encode(output, hrp, data, datalen, BECH32_ENCODING_BECH32);
131131
}
132132

133133
FUNC_RETURN_CODES hd_path_array_to_string(const uint32_t *path,

0 commit comments

Comments
 (0)