descriptor: add derive_bip32_key to derive a given descriptor key
What changed, and why it matters
This commit adds a new public API to libwally-core that lets users derive BIP32 keys directly from a parsed Bitcoin/Elements descriptor. It also fixes two consistency bugs in how descriptor keys are looked up: it now correctly routes requests for the special 'blinding key' index, and it correctly marks SLIP77 blinding key nodes so callers can tell what kind of key they are getting. There is no direct evidence in the commit that these changes fix an exploitable vulnerability; they appear to be correctness and API-completeness improvements.
Treat as a routine feature/correctness patch. Review callers of the new derive API to ensure they do not accidentally expose derived private keys or misuse SLIP77 private-key output. Verify the refactored public-key offset in generate_script against the test suite and any downstream consumers that depend on exact descriptor script generation.
Security signals we found
New API exposes derivation of private BIP32 keys from descriptors; misuse by callers could leak private key material
SLIP77 blinding keys are returned in the private-key field of an ext_key, which may surprise callers and lead to incorrect handling
Refactoring of generate_script changes the public-key extraction offset logic from a conditional x-only offset to EC_PUBLIC_KEY_LEN - output_len; this is intended to be equivalent but is a sensitive crypto code path
Blinding-key lookup now consistently handled across get_key and get_key_features, fixing prior inconsistency
No explicit security bug, CVE, or vulnerability description in commit message or diff
Evidence from the diff
The patch introduces wally_descriptor_derive_bip32_key/_alloc and a helper node_derive_key. The helper centralizes derivation for public keys, private keys, raw/SLIP77 blinding keys, and BIP32 nodes. It refactors generate_script to use the same helper, and updates descriptor_get_key to handle WALLY_MS_BLINDING_KEY_INDEX consistently across wally_descriptor_get_key and wally_descriptor_get_key_features. A SLIP77 node is now flagged with WALLY_MS_IS_RAW | WALLY_MS_IS_SLIP77 so feature queries return the right type. The change touches C, C++, Java SWIG, Python, WASM JS/TS bindings and tests.
Changed components
src/descriptor.cinclude/wally_descriptor.hinclude/wally.hppsrc/swig_java/swig.isrc/swig_python/python_extra.py_insrc/swig_python/swig.isrc/wasm_package/src/functions.jssrc/wasm_package/src/index.d.tstools/wasm_exports.shInspect captured patch +234 / −49
### include/wally.hpp
@@ -534,6 +534,18 @@ inline int descriptor_canonicalize(const DESCRIPTOR& descriptor, uint32_t flags,
return detail::check_ret(__FUNCTION__, ret);
}
+template <class DESCRIPTOR>
+inline int descriptor_derive_bip32_key(const DESCRIPTOR& descriptor, size_t index, uint32_t variant, uint32_t multi_index, uint32_t child_num, uint32_t flags, struct ext_key* output) {
+ int ret = ::wally_descriptor_derive_bip32_key(detail::get_p(descriptor), index, variant, multi_index, child_num, flags, output);
+ return detail::check_ret(__FUNCTION__, ret);
+}
+
+template <class DESCRIPTOR>
+inline int descriptor_derive_bip32_key_alloc(const DESCRIPTOR& descriptor, size_t index, uint32_t variant, uint32_t multi_index, uint32_t child_num, uint32_t flags, struct ext_key** output) {
+ int ret = ::wally_descriptor_derive_bip32_key_alloc(detail::get_p(descriptor), index, variant, multi_index, child_num, flags, output);
+ return detail::check_ret(__FUNCTION__, ret);
+}
+
inline int descriptor_free(struct wally_descriptor* descriptor) {
int ret = ::wally_descriptor_free(descriptor);
return detail::check_ret(__FUNCTION__, ret);
### include/wally_descriptor.h
@@ -7,6 +7,7 @@
extern "C" {
#endif
+struct ext_key;
struct wally_map;
/** An opaque type holding a parsed minscript/descriptor expression */
struct wally_descriptor;
@@ -306,6 +307,49 @@ WALLY_CORE_API int wally_descriptor_get_key_origin_path_str(
size_t index,
char **output);
+
+/**
+ * Derive a BIP32 extended key from a parsed output descriptor or miniscript expression.
+ *
+ * :param descriptor: Parsed output descriptor or miniscript expression.
+ * :param index: The zero-based index of the key to get, or `WALLY_MS_BLINDING_KEY_INDEX`
+ *| to fetch the descriptors blinding key representaton (if any).
+ * :param variant: The variant of descriptor to derive from. See `wally_descriptor_get_num_variants`.
+ * :param multi_index: The multi-path item to derive from. See `wally_descriptor_get_num_paths`.
+ * :param child_num: The BIP32 child number to derive, or 0 for static descriptors.
+ * :param flags: Use `BIP32_FLAG_KEY_PUBLIC` to return public keys from private keys,
+ *| and `BIP32_FLAG_SKIP_HASH` to avoid populating the derived key fingerprint.
+ * :param output: Destination for the resulting derived key.
+ *
+ * .. note:: The returned key may be bare (have only the public or private key populated).
+ *| x-only bare keys have a prefix byte of 0x00. The caller can use `wally_descriptor_get_key_features` to
+ *| determine the type of a given key before extracting data from it.
+ *
+ * .. note:: SLIP77 blinding keys are returned in the private key of the extended key.
+ */
+WALLY_CORE_API int wally_descriptor_derive_bip32_key(
+ const struct wally_descriptor *descriptor,
+ size_t index,
+ uint32_t variant,
+ uint32_t multi_index,
+ uint32_t child_num,
+ uint32_t flags,
+ struct ext_key *output);
+
+/**
+ * Derive a BIP32 extended key from a parsed output descriptor or miniscript expression.
+ *
+ * See `wally_descriptor_derive_bip32_key`.
+ */
+WALLY_CORE_API int wally_descriptor_derive_bip32_key_alloc(
+ const struct wally_descriptor *descriptor,
+ size_t index,
+ uint32_t variant,
+ uint32_t multi_index,
+ uint32_t child_num,
+ uint32_t flags,
+ struct ext_key **output);
+
/**
* Get the maximum length of a script corresponding to an output descriptor.
*
### src/descriptor.c
@@ -622,6 +622,73 @@ static void node_free(ms_node *node)
}
}
+static int node_derive_key(ms_ctx* ctx, const ms_node* node,
+ uint32_t flags, struct ext_key* output)
+{
+ const unsigned char* data = (const unsigned char*)node->data;
+
+ wally_clear(output, sizeof(*output));
+
+ if (node->kind == KIND_PUBLIC_KEY) {
+ int ret;
+ if (node->data_len == EC_XONLY_PUBLIC_KEY_LEN) {
+ memcpy(output->pub_key+1, data, node->data_len);
+ ret = WALLY_OK;
+ } else
+ ret = wally_ec_public_key_compress(data, node->data_len,
+ output->pub_key,
+ sizeof(output->pub_key));
+ if (ret == WALLY_OK)
+ ret = bip32_key_strip_private_key(output); /* Mark as pubkey only */
+ return ret;
+ } else if (node->kind == KIND_PRIVATE_KEY) {
+ int ret;
+ if (!(flags & BIP32_FLAG_KEY_PUBLIC)) {
+ memcpy(output->priv_key + 1, data, sizeof(output->priv_key) - 1);
+ return WALLY_OK;
+ }
+ ret = wally_ec_public_key_from_private_key(data, node->data_len,
+ output->pub_key, sizeof(output->pub_key));
+ if (ret == WALLY_OK)
+ ret = bip32_key_strip_private_key(output); /* Mark as pubkey only */
+ return ret;
+ } else if (node->kind == KIND_RAW && node->parent &&
+ node->parent->kind == KIND_DESCRIPTOR_SLIP77) {
+ /* SLIP77 blinding key is returned as a private key */
+ memcpy(output->priv_key + 1, data, sizeof(output->priv_key) - 1);
+ return WALLY_OK;
+ } else if ((node->kind & KIND_BIP32) == KIND_BIP32) {
+ int ret = bip32_key_from_base58_n(node->data, node->data_len, output);
+ if (ret == WALLY_OK && node->child_path_len) {
+ size_t path_len;
+ const uint32_t path_flags = BIP32_FLAG_STR_WILDCARD |
+ BIP32_FLAG_STR_BARE |
+ BIP32_FLAG_STR_MULTIPATH;
+ const uint32_t derive_flags = flags & (BIP32_FLAG_SKIP_HASH |
+ BIP32_FLAG_KEY_PUBLIC);
+ const bool is_ranged = node->flags & WALLY_MS_IS_RANGED;
+ const bool is_multi = node->flags & WALLY_MS_IS_MULTIPATH;
+ struct ext_key derived;
+
+ ret = bip32_path_from_str_n(node->child_path, node->child_path_len,
+ is_ranged ? ctx->child_num : 0,
+ is_multi ? ctx->multi_index : 0,
+ path_flags, ctx->path_buff, ctx->max_path_elems,
+ &path_len);
+ if (ret == WALLY_OK)
+ ret = bip32_key_from_parent_path(output, ctx->path_buff, path_len,
+ derive_flags, &derived);
+ if (ret == WALLY_OK)
+ memcpy(output, &derived, sizeof(derived));
+ wally_clear(&derived, sizeof(derived));
+ } else if (ret == WALLY_OK && (flags & BIP32_FLAG_KEY_PUBLIC)) {
+ ret = bip32_key_strip_private_key(output);
+ }
+ return ret;
+ }
+ return WALLY_ERROR; /* Not a key node */
+}
+
static bool has_two_different_lock_states(uint32_t primary, uint32_t secondary)
{
return ((primary & PROP_G) && (secondary & PROP_H)) ||
@@ -2187,32 +2254,12 @@ static int generate_script(ms_ctx *ctx, ms_node *node,
ret = WALLY_OK; /* Return required length without writing */
} else {
struct ext_key master;
-
- ret = bip32_key_from_base58_n(node->data, node->data_len, &master);
- if (ret == WALLY_OK && node->child_path_len) {
- size_t path_len;
- const uint32_t flags = BIP32_FLAG_STR_WILDCARD |
- BIP32_FLAG_STR_BARE |
- BIP32_FLAG_STR_MULTIPATH;
- const uint32_t derive_flags = BIP32_FLAG_SKIP_HASH |
- BIP32_FLAG_KEY_PUBLIC;
- const bool is_ranged = node->flags & WALLY_MS_IS_RANGED;
- const bool is_multi = node->flags & WALLY_MS_IS_MULTIPATH;
- struct ext_key derived;
-
- ret = bip32_path_from_str_n(node->child_path, node->child_path_len,
- is_ranged ? ctx->child_num : 0,
- is_multi ? ctx->multi_index : 0,
- flags, ctx->path_buff, ctx->max_path_elems,
- &path_len);
- if (ret == WALLY_OK)
- ret = bip32_key_from_parent_path(&master, ctx->path_buff, path_len,
- derive_flags, &derived);
- if (ret == WALLY_OK)
- memcpy(&master, &derived, sizeof(master));
- }
+ ret = node_derive_key(ctx, node,
+ BIP32_FLAG_SKIP_HASH|BIP32_FLAG_KEY_PUBLIC,
+ &master);
if (ret == WALLY_OK)
- memcpy(script, master.pub_key + ((node->flags & WALLY_MS_IS_X_ONLY) ? 1 : 0), output_len);
+ memcpy(script, master.pub_key + EC_PUBLIC_KEY_LEN - output_len,
+ output_len);
wally_clear(&master, sizeof(master));
}
}
@@ -2565,6 +2612,7 @@ static int analyze_miniscript_value(ms_ctx *ctx, const char *str, size_t str_len
node->data_len = written;
node->kind = KIND_RAW;
if (kind == KIND_DESCRIPTOR_SLIP77) {
+ node->flags = WALLY_MS_IS_RAW | WALLY_MS_IS_SLIP77;
ctx->features |= (WALLY_MS_IS_ELEMENTS | WALLY_MS_IS_SLIP77);
}
}
@@ -3402,25 +3450,30 @@ int wally_descriptor_get_num_keys(const struct wally_descriptor *descriptor,
static const ms_node *descriptor_get_key(const struct wally_descriptor *descriptor,
size_t index)
{
- if (!descriptor || index >= descriptor->keys.num_items)
+ if (!descriptor)
return NULL;
- return (ms_node *)descriptor->keys.items[index].value;
-}
-
-int wally_descriptor_get_key(const struct wally_descriptor *descriptor,
- size_t index, char **output)
-{
- const ms_node *node = NULL;
#ifdef BUILD_ELEMENTS
if (index == WALLY_MS_BLINDING_KEY_INDEX) {
- if (descriptor && node_is_ct(descriptor->top_node)) {
+ const ms_node *node = NULL;
+ if (node_is_ct(descriptor->top_node)) {
node = descriptor->top_node->child;
if (node && node->kind == KIND_DESCRIPTOR_SLIP77)
node = node->child;
+ else if (node && node->kind == KIND_DESCRIPTOR_ELIP151)
+ node = NULL; /* FIXME: Support ELIP-151 derivation */
}
- } else
+ return node;
+ }
#endif
- node = descriptor_get_key(descriptor, index);
+ if (index >= descriptor->keys.num_items)
+ return NULL;
+ return (ms_node *)descriptor->keys.items[index].value;
+}
+
+int wally_descriptor_get_key(const struct wally_descriptor *descriptor,
+ size_t index, char **output)
+{
+ const ms_node *node = descriptor_get_key(descriptor, index);
if (output)
*output = 0;
@@ -3458,17 +3511,7 @@ int wally_descriptor_get_key(const struct wally_descriptor *descriptor,
int wally_descriptor_get_key_features(const struct wally_descriptor *descriptor,
size_t index, uint32_t *value_out)
{
- const ms_node *node = NULL;
-#ifdef BUILD_ELEMENTS
- if (index == WALLY_MS_BLINDING_KEY_INDEX) {
- if (descriptor && node_is_ct(descriptor->top_node)) {
- node = descriptor->top_node->child;
- if (node && node->kind == KIND_DESCRIPTOR_SLIP77)
- node = node->child;
- }
- } else
-#endif
- node = descriptor_get_key(descriptor, index);
+ const ms_node *node = descriptor_get_key(descriptor, index);
if (value_out)
*value_out = 0;
@@ -3556,6 +3599,55 @@ int wally_descriptor_get_key_origin_path_str(
return WALLY_OK;
}
+int wally_descriptor_derive_bip32_key(
+ const struct wally_descriptor *descriptor, size_t index, uint32_t variant,
+ uint32_t multi_index, uint32_t child_num, uint32_t flags, struct ext_key* output)
+{
+ ms_ctx ctx, *ctx_p = NULL;
+ const ms_node *node = descriptor_get_key(descriptor, index);
+ int ret;
+
+ if (output)
+ wally_clear(output, sizeof(*output));
+ if (!node || variant >= descriptor->num_variants ||
+ child_num >= BIP32_INITIAL_HARDENED_CHILD ||
+ (child_num && !(descriptor->features & WALLY_MS_IS_RANGED)) ||
+ multi_index >= descriptor->num_multipaths ||
+ flags & ~(BIP32_FLAG_KEY_PUBLIC|BIP32_FLAG_SKIP_HASH) || !output)
+ return WALLY_EINVAL;
+ if ((node->kind & KIND_BIP32) == KIND_BIP32 && node->child_path_len) {
+ /* Non-static key: create context required for deriving */
+ memcpy(&ctx, descriptor, sizeof(ctx));
+ ctx.variant = variant;
+ ctx.child_num = child_num;
+ ctx.multi_index = multi_index;
+ if (ctx.max_path_elems &&
+ !(ctx.path_buff = wally_malloc(ctx.max_path_elems * sizeof(uint32_t))))
+ return WALLY_ENOMEM;
+ ctx_p = &ctx;
+ }
+ ret = node_derive_key(ctx_p, node, flags, output);
+ if (ctx_p && ctx_p->path_buff)
+ wally_free(ctx_p->path_buff);
+ return ret;
+}
+
+int wally_descriptor_derive_bip32_key_alloc(
+ const struct wally_descriptor *descriptor, size_t index, uint32_t variant,
+ uint32_t multi_index, uint32_t child_num, uint32_t flags, struct ext_key** output)
+{
+ int ret;
+ OUTPUT_CHECK;
+ OUTPUT_ALLOC(struct ext_key);
+ ret = wally_descriptor_derive_bip32_key(descriptor, index, variant, multi_index,
+ child_num, flags, *output);
+ if (ret != WALLY_OK) {
+ clear_and_free(*output, sizeof(struct ext_key));
+ *output = NULL;
+ }
+ return ret;
+}
+
static const char *get_multipath_child(const char* p, uint32_t *v)
{
*v = 0;
### src/swig_java/swig.i
@@ -341,6 +341,7 @@ static jobjectArray create_jstringArray(JNIEnv *jenv, char **p, size_t len) {
%ignore bip32_key_init;
%ignore bip32_key_unserialize;
%ignore bip32_key_with_tweak_from_parent_path;
+%ignore wally_descriptor_derive_bip32_key;
%ignore wally_map_init;
%ignore wally_map_keypath_get_bip32_key_from;
%ignore wally_psbt_blind;
@@ -577,6 +578,8 @@ static jobjectArray create_jstringArray(JNIEnv *jenv, char **p, size_t len) {
%returns_array_(wally_confidential_addr_segwit_to_ec_public_key, 3, 4, EC_PUBLIC_KEY_LEN);
%returns_string(wally_confidential_addr_from_addr_segwit);
%returns_string(wally_descriptor_canonicalize);
+%returns_struct(wally_descriptor_derive_bip32_key_alloc, ext_key);
+%rename("descriptor_derive_bip32_key") wally_descriptor_derive_bip32_key_alloc;
%returns_string(wally_descriptor_get_checksum);
%returns_size_t(wally_descriptor_get_depth);
%returns_size_t(wally_descriptor_get_features);
### src/swig_python/python_extra.py_in
@@ -148,6 +148,7 @@ bip39_mnemonic_to_bytes = _wrap_bin(bip39_mnemonic_to_bytes, BIP39_ENTROPY_MAX_L
bip39_mnemonic_to_seed512 = _wrap_bin(bip39_mnemonic_to_seed512, BIP39_SEED_LEN_512)
bip85_get_bip39_entropy = _wrap_bin(bip85_get_bip39_entropy, HMAC_SHA512_LEN, resize=True)
bip85_get_rsa_entropy = _wrap_bin(bip85_get_rsa_entropy, HMAC_SHA512_LEN, resize=True)
+descriptor_derive_bip32_key = descriptor_derive_bip32_key_alloc
descriptor_get_key_origin_fingerprint = _wrap_bin(descriptor_get_key_origin_fingerprint, BIP32_KEY_FINGERPRINT_LEN)
descriptor_to_script = _wrap_bin(descriptor_to_script, descriptor_to_script_get_maximum_length, resize=True)
ec_private_key_bip341_tweak = _wrap_bin(ec_private_key_bip341_tweak, EC_PRIVATE_KEY_LEN)
### src/swig_python/swig.i
@@ -414,6 +414,7 @@ static void destroy_words(PyObject *obj) { (void)obj; }
%ignore bip32_key_init;
%ignore bip32_key_unserialize;
%ignore bip32_key_with_tweak_from_parent_path;
+%ignore wally_descriptor_derive_bip32_key;
%ignore wally_map_init;
%ignore wally_map_keypath_get_bip32_key_from;
%ignore wally_psbt_blind;
### src/test/test_descriptor.py
@@ -21,7 +21,7 @@
MS_IS_MULTIPATH = 0x2
MS_IS_PRIVATE = 0x4
MS_IS_UNCOMPRESSED = 0x08
-MS_IS_RAW = 0x010
+MS_IS_RAW = 0x10
MS_IS_DESCRIPTOR = 0x20
MS_IS_X_ONLY = 0x40
MS_IS_PARENTED = 0x80
@@ -34,6 +34,9 @@
BLINDING_KEY_INDEX = 0xffffffff
+FLAG_KEY_PRIVATE = 0x0
+FLAG_KEY_PUBLIC = 0x1
+
def wally_map_from_dict(d):
m = pointer(wally_map())
assert(wally_map_init_alloc(len(d.keys()), None, m) == WALLY_OK)
@@ -341,6 +344,7 @@ def test_policy(self):
slip77 = 'b2396b3ee20509cdb64fe24180a14a72dbd671728eaa49bac69d2bdecb5f5a04'
xpriv = 'xprvA2YKGLieCs6cWCiczALiH1jzk3VCCS5M1pGQfWPkamCdR9UpBgE2Gb8AKAyVjKHkz8v37avcfRjdcnP19dVAmZrvZQfvTcXXSAiFNQ6tTtU'
xpub1 = 'xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL'
+ xpub1_pubkey = '02d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0'
xpub2 = 'xpub6AHA9hZDN11k2ijHMeS5QqHx2KP9aMBRhTDqANMnwVtdyw2TDYRmF8PjpvwUFcL1Et8Hj59S3gTSMcUQ5gAqTz3Wd8EsMTmF3DChhqPQBnU'
def make_keys(xpubs):
@@ -400,7 +404,7 @@ def make_keys(xpubs):
[P, 'ct(@B,elpkh(@0/*))', {'@B': xpub1, '@0': xpub2}],
]
d = c_void_p()
- for flags, policy, key_items in cases:
+ for i, (flags, policy, key_items) in enumerate(cases):
keys = wally_map_from_dict(key_items)
ret = wally_descriptor_parse(policy, keys, NETWORK_LIQUID, flags, d)
self.assertEqual(ret, WALLY_OK)
@@ -410,6 +414,26 @@ def make_keys(xpubs):
self.assertEqual((ret, key_str), (WALLY_OK, key_items['@0']))
ret, key_info = wally_descriptor_get_key(d, BLINDING_KEY_INDEX)
self.assertEqual((ret, key_info), (WALLY_OK, key_items['@B']))
+ key_out = ext_key()
+ ret, features = wally_descriptor_get_key_features(d, BLINDING_KEY_INDEX)
+ self.assertEqual(ret, WALLY_OK)
+ ret = wally_descriptor_derive_bip32_key(d, BLINDING_KEY_INDEX,
+ 0, 0, 0, 0, key_out)
+ self.assertEqual(ret, WALLY_OK)
+ if i == 0:
+ # SLIP-77 blinding keys are returned in the private key
+ self.assertEqual(key_out.priv_key[0], FLAG_KEY_PRIVATE)
+ self.assertEqual(bytes(key_out.priv_key[1:]).hex(), slip77)
+ self.assertEqual(features, MS_IS_RAW|MS_IS_SLIP77)
+ elif i == 1:
+ self.assertEqual(key_out.priv_key[0], FLAG_KEY_PRIVATE)
+ self.assertEqual(bytes(key_out.priv_key[1:]).hex(), slip77)
+ self.assertEqual(features, MS_IS_RAW|MS_IS_PRIVATE)
+ else:
+ self.assertEqual(key_out.priv_key[0], FLAG_KEY_PUBLIC)
+ self.assertEqual(bytes(key_out.pub_key).hex(), xpub1_pubkey)
+ self.assertEqual(features, 0) # Standard unranged xpub
+
wally_map_free(keys)
wally_descriptor_free(d)
### src/test/util.py
@@ -321,6 +321,8 @@ class wally_psbt(Structure):
('wally_confidential_addr_to_addr_segwit', c_int, [c_char_p, c_char_p, c_char_p, c_char_p_p]),
('wally_confidential_addr_to_ec_public_key', c_int, [c_char_p, c_uint32, c_void_p, c_size_t]),
('wally_descriptor_canonicalize', c_int, [c_void_p, c_uint32, c_char_p_p]),
+ ('wally_descriptor_derive_bip32_key', c_int, [c_void_p, c_size_t, c_uint32, c_uint32, c_uint32, c_uint32, POINTER(ext_key)]),
+ ('wally_descriptor_derive_bip32_key_alloc', c_int, [c_void_p, c_size_t, c_uint32, c_uint32, c_uint32, c_uint32, POINTER(POINTER(ext_key))]),
('wally_descriptor_free', c_int, [c_void_p]),
('wally_descriptor_get_checksum', c_int, [c_void_p, c_uint32, c_char_p_p]),
('wally_descriptor_get_depth', c_int, [c_void_p, c_uint32_p]),
### src/wasm_package/src/functions.js
@@ -164,6 +164,8 @@ export const confidential_addr_to_addr = wrap('wally_confidential_addr_to_addr',
export const confidential_addr_to_addr_segwit = wrap('wally_confidential_addr_to_addr_segwit', [T.String, T.String, T.String, T.DestPtrPtr(T.String)]);
export const confidential_addr_to_ec_public_key = wrap('wally_confidential_addr_to_ec_public_key', [T.String, T.Int32, T.DestPtrSized(T.Bytes, C.EC_PUBLIC_KEY_LEN)]);
export const descriptor_canonicalize = wrap('wally_descriptor_canonicalize', [T.OpaqueRef, T.Int32, T.DestPtrPtr(T.String)]);
+export const descriptor_derive_bip32_key = wrap('wally_descriptor_derive_bip32_key_alloc', [T.OpaqueRef, T.Int32, T.Int32, T.Int32, T.Int32, T.Int32, T.DestPtrPtr(T.OpaqueRef)]);
+export const descriptor_derive_bip32_key_noalloc = wrap('wally_descriptor_derive_bip32_key', [T.OpaqueRef, T.Int32, T.Int32, T.Int32, T.Int32, T.Int32, T.OpaqueRef]);
export const descriptor_free = wrap('wally_descriptor_free', [T.OpaqueRef]);
export const descriptor_get_checksum = wrap('wally_descriptor_get_checksum', [T.OpaqueRef, T.Int32, T.DestPtrPtr(T.String)]);
export const descriptor_get_depth = wrap('wally_descriptor_get_depth', [T.OpaqueRef, T.DestPtr(T.Int32)]);
### src/wasm_package/src/index.d.ts
@@ -124,6 +124,8 @@ export function confidential_addr_to_addr(address: string, prefix: number): stri
export function confidential_addr_to_addr_segwit(address: string, confidential_addr_family: string, addr_family: string): string;
export function confidential_addr_to_ec_public_key(address: string, prefix: number): Buffer;
export function descriptor_canonicalize(descriptor: Ref_wally_descriptor, flags: number): string;
+export function descriptor_derive_bip32_key(descriptor: Ref_wally_descriptor, index: number, variant: number, multi_index: number, child_num: number, flags: number): Ref_ext_key;
+export function descriptor_derive_bip32_key_noalloc(descriptor: Ref_wally_descriptor, index: number, variant: number, multi_index: number, child_num: number, flags: number, output: Ref_ext_key): void;
export function descriptor_free(descriptor: Ref_wally_descriptor): void;
export function descriptor_get_checksum(descriptor: Ref_wally_descriptor, flags: number): string;
export function descriptor_get_depth(descriptor: Ref_wally_descriptor): number;
### tools/wasm_exports.sh
@@ -87,6 +87,8 @@ EXPORTED_FUNCTIONS="['_malloc','_free','_bip32_key_free' \
,'_wally_bzero' \
,'_wally_cleanup' \
,'_wally_descriptor_canonicalize' \
+,'_wally_descriptor_derive_bip32_key' \
+,'_wally_descriptor_derive_bip32_key_alloc' \
,'_wally_descriptor_free' \
,'_wally_descriptor_get_checksum' \
,'_wally_descriptor_get_depth' \Why this scored 33/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.