Skip to content

Commit 3dfb8fa

Browse files
committed
Merge branch 'tapminiscript'
2 parents 07f7a9d + aa9b932 commit 3dfb8fa

File tree

11 files changed

+875
-97
lines changed

11 files changed

+875
-97
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ customers cannot upgrade their bootloader, its changes are recorded separately.
88

99
### [Unreleased]
1010
- Bitcoin: add support for sending to silent payment (BIP-352) addresses
11+
- Bitcoin: add support for Taproot wallet policies and Miniscript on Taproot (MiniTapscript)
1112
- Bitcoin: add support for regtest
1213
- Cardano: add support for vote delegation
1314

src/keystore.c

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -941,9 +941,10 @@ bool keystore_secp256k1_schnorr_bip86_pubkey(const uint8_t* pubkey33, uint8_t* p
941941
return secp256k1_xonly_pubkey_serialize(ctx, pubkey_out, &tweaked_xonly_pubkey) == 1;
942942
}
943943

944-
static bool _schnorr_bip86_keypair(
944+
static bool _schnorr_keypair(
945945
const uint32_t* keypath,
946946
size_t keypath_len,
947+
const uint8_t* tweak,
947948
secp256k1_keypair* keypair_out,
948949
secp256k1_xonly_pubkey* pubkey_out)
949950
{
@@ -959,36 +960,32 @@ static bool _schnorr_bip86_keypair(
959960
if (!secp256k1_keypair_create(ctx, keypair_out, secret_key)) {
960961
return false;
961962
}
962-
if (!secp256k1_keypair_xonly_pub(ctx, pubkey_out, NULL, keypair_out)) {
963-
return false;
964-
}
965-
uint8_t pubkey_serialized[32] = {0};
966-
if (!secp256k1_xonly_pubkey_serialize(ctx, pubkey_serialized, pubkey_out)) {
967-
return false;
963+
if (tweak != NULL) {
964+
if (secp256k1_keypair_xonly_tweak_add(ctx, keypair_out, tweak) != 1) {
965+
return false;
966+
}
968967
}
969-
uint8_t hash[32] = {0};
970-
_tagged_hash("TapTweak", pubkey_serialized, sizeof(pubkey_serialized), hash);
971-
972-
if (secp256k1_keypair_xonly_tweak_add(ctx, keypair_out, hash) != 1) {
968+
if (!secp256k1_keypair_xonly_pub(ctx, pubkey_out, NULL, keypair_out)) {
973969
return false;
974970
}
975-
return secp256k1_keypair_xonly_pub(ctx, pubkey_out, NULL, keypair_out) == 1;
971+
return true;
976972
}
977973

978974
static void _cleanup_keypair(secp256k1_keypair* keypair)
979975
{
980976
util_zero(keypair, sizeof(secp256k1_keypair));
981977
}
982978

983-
bool keystore_secp256k1_schnorr_bip86_sign(
979+
bool keystore_secp256k1_schnorr_sign(
984980
const uint32_t* keypath,
985981
size_t keypath_len,
986982
const uint8_t* msg32,
983+
const uint8_t* tweak,
987984
uint8_t* sig64_out)
988985
{
989986
secp256k1_keypair __attribute__((__cleanup__(_cleanup_keypair))) keypair = {0};
990987
secp256k1_xonly_pubkey pubkey = {0};
991-
if (!_schnorr_bip86_keypair(keypath, keypath_len, &keypair, &pubkey)) {
988+
if (!_schnorr_keypair(keypath, keypath_len, tweak, &keypair, &pubkey)) {
992989
return false;
993990
}
994991
const secp256k1_context* ctx = wally_get_secp_context();

src/keystore.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,12 +295,15 @@ USE_RESULT bool keystore_secp256k1_schnorr_bip86_pubkey(
295295
* @param[in] keypath derivation keypath
296296
* @param[in] keypath_len number of elements in keypath
297297
* @param[in] msg32 32 byte message to sign
298+
* @param[in] tweak 32 bytes, tweak private key before signing with this tweak. Use NULL to not
299+
* tweak.
298300
* @param[out] sig64_out resulting 64 byte signature
299301
*/
300-
USE_RESULT bool keystore_secp256k1_schnorr_bip86_sign(
302+
USE_RESULT bool keystore_secp256k1_schnorr_sign(
301303
const uint32_t* keypath,
302304
size_t keypath_len,
303305
const uint8_t* msg32,
306+
const uint8_t* tweak,
304307
uint8_t* sig64_out);
305308

306309
/**

src/rust/bitbox02-rust/src/hww/api/bitcoin.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,8 @@ async fn address_policy(
254254
.await?;
255255
}
256256

257-
let address = common::Payload::from_policy(&parsed, keypath)?.address(coin_params)?;
257+
let address =
258+
common::Payload::from_policy(coin_params, &parsed, keypath)?.address(coin_params)?;
258259
if display {
259260
confirm::confirm(&confirm::Params {
260261
title,

src/rust/bitbox02-rust/src/hww/api/bitcoin/bip341.rs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,16 @@ pub struct Args {
2727
pub hash_outputs: [u8; 32],
2828
// Data about this input:
2929
pub input_index: u32,
30+
// tapleaf_hash as described in https://github.com/bitcoin/bips/blob/85cda4e225b4d5fd7aff403f69d827f23f6afbbc/bip-0342.mediawiki#common-signature-message-extension
31+
// Providing this means we use the above tapscript message extension.
32+
pub tapleaf_hash: Option<[u8; 32]>,
3033
}
3134

3235
/// Compute the BIP341 signature hash.
3336
///
3437
/// https://github.com/bitcoin/bips/blob/bb8dc57da9b3c6539b88378348728a2ff43f7e9c/bip-0341.mediawiki#common-signature-message
3538
///
36-
/// The hash_type is assumed 0 (`SIGHASH_DEFAULT`). The `ext_flag` is
37-
/// assumed 0 and `annex` is assumed to be not present.
39+
/// The hash_type is assumed 0 (`SIGHASH_DEFAULT`). `annex` is assumed to be not present.
3840
pub fn sighash(args: &Args) -> [u8; 32] {
3941
let tag = Sha256::digest(b"TapSighash");
4042
let mut ctx = Sha256::new();
@@ -53,10 +55,28 @@ pub fn sighash(args: &Args) -> [u8; 32] {
5355
ctx.update(args.hash_sequences);
5456
ctx.update(args.hash_outputs);
5557
// spend_type is 0 because ext_flag is 0 and annex is absent.
56-
ctx.update(0u8.to_le_bytes());
58+
let ext_flag = if args.tapleaf_hash.is_some() {
59+
// ext_flag = 1 for Taproot leaf scripts
60+
// See https://github.com/bitcoin/bips/blob/85cda4e225b4d5fd7aff403f69d827f23f6afbbc/bip-0342.mediawiki#common-signature-message-extension
61+
1
62+
} else {
63+
0
64+
};
65+
let spend_type: u8 = 2 * ext_flag;
66+
ctx.update(spend_type.to_le_bytes());
5767
// Data about this input:
5868
ctx.update(args.input_index.to_le_bytes());
5969

70+
if let Some(hash) = args.tapleaf_hash.as_ref() {
71+
// See https://github.com/bitcoin/bips/blob/85cda4e225b4d5fd7aff403f69d827f23f6afbbc/bip-0342.mediawiki#common-signature-message-extension
72+
// tapleaf_hash
73+
ctx.update(hash);
74+
// keyversion
75+
ctx.update(0u8.to_le_bytes());
76+
// codesep_pos - we do not use any OP_CODESEPARATORs.
77+
let codesep_pos: u32 = 0xFFFFFFFF;
78+
ctx.update(codesep_pos.to_le_bytes());
79+
}
6080
ctx.finalize().into()
6181
}
6282

@@ -79,7 +99,25 @@ mod tests {
7999
hash_sequences: *b"\x18\x95\x9c\x72\x21\xab\x5c\xe9\xe2\x6c\x3c\xd6\x7b\x22\xc2\x4f\x8b\xaa\x54\xba\xc2\x81\xd8\xe6\xb0\x5e\x40\x0e\x6c\x3a\x95\x7e",
80100
hash_outputs: *b"\xa2\xe6\xda\xb7\xc1\xf0\xdc\xd2\x97\xc8\xd6\x16\x47\xfd\x17\xd8\x21\x54\x1e\xa6\x9c\x3c\xc3\x7d\xcb\xad\x7f\x90\xd4\xeb\x4b\xc5",
81101
input_index: 4,
102+
tapleaf_hash: None,
82103
}),
83104
*b"\x4f\x90\x0a\x0b\xae\x3f\x14\x46\xfd\x48\x49\x0c\x29\x58\xb5\xa0\x23\x22\x8f\x01\x66\x1c\xda\x34\x96\xa1\x1d\xa5\x02\xa7\xf7\xef");
84105
}
106+
107+
#[test]
108+
fn test_sighash_tapleaf() {
109+
assert_eq!(
110+
sighash(&Args {
111+
version: 2,
112+
locktime: 500000000,
113+
hash_prevouts: *b"\xe3\xb3\x3b\xb4\xef\x3a\x52\xad\x1f\xff\xb5\x55\xc0\xd8\x28\x28\xeb\x22\x73\x70\x36\xea\xeb\x02\xa2\x35\xd8\x2b\x90\x9c\x4c\x3f",
114+
hash_amounts: *b"\x58\xa6\x96\x4a\x4f\x5f\x8f\x0b\x64\x2d\xed\x0a\x8a\x55\x3b\xe7\x62\x2a\x71\x9d\xa7\x1d\x1f\x5b\xef\xce\xfc\xde\xe8\xe0\xfd\xe6",
115+
hash_scriptpubkeys: *b"\x23\xad\x0f\x61\xad\x2b\xca\x5b\xa6\xa7\x69\x3f\x50\xfc\xe9\x88\xe1\x7c\x37\x80\xbf\x2b\x1e\x72\x0c\xfb\xb3\x8f\xbd\xd5\x2e\x21",
116+
hash_sequences: *b"\x18\x95\x9c\x72\x21\xab\x5c\xe9\xe2\x6c\x3c\xd6\x7b\x22\xc2\x4f\x8b\xaa\x54\xba\xc2\x81\xd8\xe6\xb0\x5e\x40\x0e\x6c\x3a\x95\x7e",
117+
hash_outputs: *b"\xa2\xe6\xda\xb7\xc1\xf0\xdc\xd2\x97\xc8\xd6\x16\x47\xfd\x17\xd8\x21\x54\x1e\xa6\x9c\x3c\xc3\x7d\xcb\xad\x7f\x90\xd4\xeb\x4b\xc5",
118+
input_index: 4,
119+
tapleaf_hash: Some(*b"\x34\xe7\x21\x15\xc0\x9c\x91\x3c\x8b\xe1\x2e\x46\xfc\x14\x5f\xcf\x7c\x53\xca\xd9\xca\x2a\x05\xf9\x3a\x7c\xa2\xe0\xca\x88\xd0\x07"),
120+
}),
121+
*b"\xba\xe0\xaa\xcb\xa5\xae\xa9\xee\xbe\x19\xe1\x57\xa9\x8f\x1e\xe7\x0d\x7d\x28\x8c\x28\x0f\x27\x3e\x63\xbb\x8a\x85\xd1\xee\xf3\xc2");
122+
}
85123
}

src/rust/bitbox02-rust/src/hww/api/bitcoin/common.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2022 Shift Crypto AG
1+
// Copyright 2022-2024 Shift Crypto AG
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -156,6 +156,7 @@ impl Payload {
156156
/// derived using keypath m/48'/1'/0'/3'/11/5 derives the payload for
157157
/// wsh(and_v(v:pk(@0/11/5),pk(@1/21/5))).
158158
pub fn from_policy(
159+
params: &Params,
159160
policy: &super::policies::ParsedPolicy,
160161
keypath: &[u32],
161162
) -> Result<Self, Error> {
@@ -165,6 +166,16 @@ impl Payload {
165166
data: Sha256::digest(wsh.witness_script()).to_vec(),
166167
output_type: BtcOutputType::P2wsh,
167168
}),
169+
super::policies::Descriptor::Tr(tr) => {
170+
if params.taproot_support {
171+
Ok(Payload {
172+
data: tr.output_key().to_vec(),
173+
output_type: BtcOutputType::P2tr,
174+
})
175+
} else {
176+
Err(Error::InvalidInput)
177+
}
178+
}
168179
}
169180
}
170181

@@ -186,7 +197,7 @@ impl Payload {
186197
keypath[keypath.len() - 2],
187198
keypath[keypath.len() - 1],
188199
),
189-
ValidatedScriptConfig::Policy(policy) => Self::from_policy(policy, keypath),
200+
ValidatedScriptConfig::Policy(policy) => Self::from_policy(params, policy, keypath),
190201
}
191202
}
192203

0 commit comments

Comments
 (0)