Skip to content

Commit 7ecf1fa

Browse files
committed
taproot: add bip341_control_block_verify
1 parent 54eaca2 commit 7ecf1fa

File tree

9 files changed

+48
-0
lines changed

9 files changed

+48
-0
lines changed

include/wally.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,12 @@ inline int bip340_tagged_hash(const BYTES& bytes, const TAG& tag, BYTES_OUT& byt
511511
return detail::check_ret(__FUNCTION__, ret);
512512
}
513513

514+
template <class BYTES>
515+
inline bool bip341_control_block_verify(const BYTES& bytes) {
516+
int ret = ::wally_bip341_control_block_verify(bytes.data(), bytes.size());
517+
return ret == WALLY_OK;
518+
}
519+
514520
template <class BYTES>
515521
inline int bzero(BYTES& bytes) {
516522
int ret = ::wally_bzero(bytes.data(), bytes.size());

include/wally_map.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,16 @@ WALLY_CORE_API int wally_merkle_path_xonly_public_key_verify(
485485
const unsigned char *val,
486486
size_t val_len);
487487

488+
/**
489+
* Verify a taproot control block as specified in BIP-0341.
490+
*
491+
* :param bytes: Control block bytes.
492+
* :param bytes_len: Length of ``bytes`` in bytes. Must be at least `EC_XONLY_PUBLIC_KEY_LEN` + 1.
493+
*/
494+
WALLY_CORE_API int wally_bip341_control_block_verify(
495+
const unsigned char *bytes,
496+
size_t bytes_len);
497+
488498
/**
489499
* Allocate and initialize a new BIP32 keypath map.
490500
*

src/map.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,15 @@ int wally_merkle_path_xonly_public_key_verify(const unsigned char *key, size_t k
674674
return WALLY_OK;
675675
}
676676

677+
int wally_bip341_control_block_verify(const unsigned char *bytes, size_t bytes_len)
678+
{
679+
const size_t min_len = 1 + EC_XONLY_PUBLIC_KEY_LEN;
680+
if (bytes_len < min_len)
681+
return WALLY_EINVAL; /* Missing parity byte and/or x-only pubkey */
682+
return wally_merkle_path_xonly_public_key_verify(bytes + 1, EC_XONLY_PUBLIC_KEY_LEN,
683+
bytes_len == min_len ? NULL : bytes + min_len, bytes_len - min_len);
684+
}
685+
677686
int wally_map_keypath_bip32_init_alloc(size_t allocation_len, struct wally_map **output)
678687
{
679688
return wally_map_init_alloc(allocation_len, wally_keypath_bip32_verify, output);

src/swig_java/swig.i

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,7 @@ static jobjectArray create_jstringArray(JNIEnv *jenv, char **p, size_t len) {
568568
%returns_string(wally_bip32_key_to_address);
569569
%returns_string(wally_bip32_key_to_addr_segwit);
570570
%returns_array_(wally_bip340_tagged_hash, 4, 5, SHA256_LEN);
571+
%returns_void__(wally_bip341_control_block_verify)
571572
%returns_size_t(wally_coinselect_assets);
572573
%returns_string(wally_confidential_addr_to_addr);
573574
%returns_array_(wally_confidential_addr_to_ec_public_key, 3, 4, EC_PUBLIC_KEY_LEN);

src/swig_python/contrib/psbt.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,23 @@ def check_keypath(self, keypaths, master, derived, pubkey, fingerprint, path):
224224
key = map_keypath_get_bip32_key_from(keypaths, 0, master)
225225
self.assertEqual(bip32_key_serialize(key, 0), bip32_key_serialize(derived, 0))
226226

227+
def check_taproot_keypath(self):
228+
# TODO: add in-situ checks on the PSBT fields
229+
# BIP-0341 control block
230+
parity = hex_to_bytes('55')
231+
xonly = hex_to_bytes('22' * 32)
232+
bad_xonly = hex_to_bytes('04' + '22' * 32)
233+
path_elem = hex_to_bytes('00' * 32)
234+
bip341_control_block_verify(parity + xonly) # No path, OK
235+
bip341_control_block_verify(parity + xonly + path_elem) # 1 path element, OK
236+
for args in [
237+
None, # Null control block
238+
parity + bad_xonly + path_elem, # Bad x-only pubkey
239+
parity + bad_xonly + path_elem[:-1], # Path length not modulo 32
240+
parity + bad_xonly + path_elem * 129, # Path length too long
241+
]:
242+
self.assertRaises(ValueError, lambda: bip341_control_block_verify(args))
243+
227244
def check_txout(self, lhs, rhs):
228245
self.assertEqual(tx_output_get_satoshi(lhs), tx_output_get_satoshi(rhs))
229246
self.assertEqual(tx_output_get_script(lhs), tx_output_get_script(rhs))
@@ -350,6 +367,7 @@ def test_psbt(self):
350367
map_keypath_add(dummy_keypaths, dummy_pubkey, dummy_fingerprint, dummy_path)
351368
self.check_keypath(dummy_keypaths, master, derived,
352369
dummy_pubkey, dummy_fingerprint, dummy_path)
370+
self.check_taproot_keypath()
353371

354372
empty_signatures = map_init(0, None)
355373
dummy_signatures = map_init(0, None) # TODO: pubkey to sig map init

src/test/util.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ class wally_psbt(Structure):
310310
('wally_bip32_key_to_addr_segwit', c_int, [POINTER(ext_key), c_char_p, c_uint32, c_char_p_p]),
311311
('wally_bip32_key_to_address', c_int, [POINTER(ext_key), c_uint32, c_uint32, c_char_p_p]),
312312
('wally_bip340_tagged_hash', c_int, [c_void_p, c_size_t, c_char_p, c_void_p, c_size_t]),
313+
('wally_bip341_control_block_verify', c_int, [c_void_p, c_size_t]),
313314
('wally_bzero', c_int, [c_void_p, c_size_t]),
314315
('wally_cleanup', c_int, [c_uint32]),
315316
('wally_coinselect_assets', c_int, [POINTER(c_uint64), c_size_t, c_uint64, c_uint64, c_uint32, POINTER(c_uint32), c_size_t, c_size_t_p]),

src/wasm_package/src/functions.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/wasm_package/src/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ export function bip32_path_from_str_n_len(path_str: string, path_str_len: number
101101
export function bip32_path_str_get_features(path_str: string): number;
102102
export function bip32_path_str_n_get_features(path_str: string, path_str_len: number): number;
103103
export function bip340_tagged_hash(bytes: Buffer|Uint8Array, tag: string): Buffer;
104+
export function bip341_control_block_verify(bytes: Buffer|Uint8Array): void;
104105
export function bip38_get_flags(bip38: string): number;
105106
export function bip38_raw_get_flags(bytes: Buffer|Uint8Array): number;
106107
export function bip39_get_languages(): string;

tools/wasm_exports.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ EXPORTED_FUNCTIONS="['_malloc','_free','_bip32_key_free' \
8383
,'_wally_bip32_key_to_addr_segwit' \
8484
,'_wally_bip32_key_to_address' \
8585
,'_wally_bip340_tagged_hash' \
86+
,'_wally_bip341_control_block_verify' \
8687
,'_wally_bzero' \
8788
,'_wally_cleanup' \
8889
,'_wally_descriptor_canonicalize' \

0 commit comments

Comments
 (0)