Skip to content

Commit 3f3f47a

Browse files
committed
descriptor: support parsing descriptors as elements descriptors
Elements core supports standard descriptors but generates (at least for taproot) different scriptpubkeys/addresses since it uses a different hash to tweak its internal keys. Allow specifying that a descriptor is an Elements descriptor when parsing, and use this to perform the correct tweak. Its unclear whether other descriptor expressions are affacted since there is no documentation of this behaviour. Elements (only via rust-elements) also supports a renamed set of descriptors and extensions which we do not attempt to support in this change.
1 parent c84170c commit 3f3f47a

File tree

4 files changed

+59
-23
lines changed

4 files changed

+59
-23
lines changed

include/wally_descriptor.h

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,20 @@ struct wally_descriptor;
1717
#define WALLY_MINISCRIPT_REQUIRE_CHECKSUM 0x04 /** Require a checksum to be present */
1818
#define WALLY_MINISCRIPT_POLICY_TEMPLATE 0x08 /** Only allow policy templates with @n BIP32 keys */
1919
#define WALLY_MINISCRIPT_UNIQUE_KEYPATHS 0x10 /** For policy templates, ensure BIP32 derivation paths differ for identical keys */
20+
#define WALLY_MINISCRIPT_IS_ELEMENTS 0x20 /** Treat non-elements expressions as elements, e.g. tr() as eltr() */
2021
#define WALLY_MINISCRIPT_DEPTH_MASK 0xffff0000 /** Mask for limiting maximum depth */
2122
#define WALLY_MINISCRIPT_DEPTH_SHIFT 16 /** Shift to convert maximum depth to flags */
2223

2324
/*** miniscript-features Miniscript/Descriptor feature flags */
24-
#define WALLY_MS_IS_RANGED 0x01 /** Allows key ranges via ``*`` */
25-
#define WALLY_MS_IS_MULTIPATH 0x02 /** Allows multiple paths via ``<a;b;c>`` */
26-
#define WALLY_MS_IS_PRIVATE 0x04 /** Contains at least one private key */
27-
#define WALLY_MS_IS_UNCOMPRESSED 0x08 /** Contains at least one uncompressed key */
28-
#define WALLY_MS_IS_RAW 0x10 /** Contains at least one raw key */
29-
#define WALLY_MS_IS_DESCRIPTOR 0x20 /** Contains only descriptor expressions (no miniscript) */
30-
#define WALLY_MS_IS_X_ONLY 0x40 /** Contains at least one x-only key */
31-
#define WALLY_MS_IS_PARENTED 0x80 /** Contains at least one key key with a parent key origin */
25+
#define WALLY_MS_IS_RANGED 0x001 /** Allows key ranges via ``*`` */
26+
#define WALLY_MS_IS_MULTIPATH 0x002 /** Allows multiple paths via ``<a;b;c>`` */
27+
#define WALLY_MS_IS_PRIVATE 0x004 /** Contains at least one private key */
28+
#define WALLY_MS_IS_UNCOMPRESSED 0x008 /** Contains at least one uncompressed key */
29+
#define WALLY_MS_IS_RAW 0x010 /** Contains at least one raw key */
30+
#define WALLY_MS_IS_DESCRIPTOR 0x020 /** Contains only descriptor expressions (no miniscript) */
31+
#define WALLY_MS_IS_X_ONLY 0x040 /** Contains at least one x-only key */
32+
#define WALLY_MS_IS_PARENTED 0x080 /** Contains at least one key key with a parent key origin */
33+
#define WALLY_MS_IS_ELEMENTS 0x100 /** Contains Elements expressions or was parsed as Elements */
3234

3335
/*** ms-canonicalization-flags Miniscript/Descriptor canonicalization flags */
3436
#define WALLY_MS_CANONICAL_NO_CHECKSUM 0x01 /** Do not include a checksum */

src/ctest/test_descriptor.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ static struct wally_map_item g_key_map_items[] = {
4343
{ B("mainnet_xpriv"), B("xprvA2YKGLieCs6cWCiczALiH1jzk3VCCS5M1pGQfWPkamCdR9UpBgE2Gb8AKAyVjKHkz8v37avcfRjdcnP19dVAmZrvZQfvTcXXSAiFNQ6tTtU") },
4444
{ B("uncompressed"), B("0414fc03b8df87cd7b872996810db8458d61da8448e531569c8517b469a119d267be5645686309c6e6736dbd93940707cc9143d3cf29f1b877ff340e2cb2d259cf") },
4545
{ B("x_only"), B("b71aa79cab0ae2d83b82d44cbdc23f5dcca3797e8ba622c4e45a8f7dce28ba0e") },
46-
{ B("non_x_only"), B("03b71aa79cab0ae2d83b82d44cbdc23f5dcca3797e8ba622c4e45a8f7dce28ba0e") }
46+
{ B("non_x_only"), B("03b71aa79cab0ae2d83b82d44cbdc23f5dcca3797e8ba622c4e45a8f7dce28ba0e") },
47+
/* The taproot singlesig xpriv corresponding to Jades test_jade.py test script */
48+
{ B("jade_ss_tr_xpriv"), B("tprv8gTfWnFCND72oJZfZTokBBXcS1FzQhrtd5wNFu3FgBE76yErH49cev2Zn3Wws3o6ZwKZVZaQP1UWKVNotpPg8U6tCgGrjMfaRQJvV1Vdbi7") }
4749
};
4850

4951
static const struct wally_map g_key_map = {
@@ -395,7 +397,17 @@ static const struct descriptor_test {
395397
WALLY_NETWORK_BITCOIN_REGTEST, 0, 0, 0, NULL, 0,
396398
"51205fb8e39dbbdc7c831af59e44a9b2997f9daaf72c3e965b30982f3c731539e1db",
397399
"tp2ky708"
398-
},{
400+
},
401+
#ifdef BUILD_ELEMENTS
402+
{
403+
"descriptor - Elements tr",
404+
"tr([59d1f3b0/86h/1h/0h]jade_ss_tr_xpriv/0/*)",
405+
WALLY_NETWORK_NONE, 0, 0, 0, NULL, WALLY_MINISCRIPT_IS_ELEMENTS,
406+
"5120900d1d75269396d4220c4529527dbcb746a6093c7209cea2d76a87c8ab9447fc",
407+
"3d4maj53"
408+
},
409+
#endif
410+
{
399411
"descriptor - A single key",
400412
"wsh(c:pk_k(key_1))",
401413
WALLY_NETWORK_NONE, 0, 0, 0, NULL, 0,

src/descriptor.c

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
WALLY_MINISCRIPT_ONLY | \
1919
WALLY_MINISCRIPT_REQUIRE_CHECKSUM | \
2020
WALLY_MINISCRIPT_POLICY_TEMPLATE | \
21-
WALLY_MINISCRIPT_UNIQUE_KEYPATHS)
21+
WALLY_MINISCRIPT_UNIQUE_KEYPATHS | \
22+
WALLY_MINISCRIPT_IS_ELEMENTS)
2223
#define MS_FLAGS_CANONICALIZE (WALLY_MINISCRIPT_REQUIRE_CHECKSUM | \
2324
WALLY_MINISCRIPT_POLICY_TEMPLATE)
2425

@@ -177,8 +178,8 @@ typedef struct ms_node_t {
177178
uint32_t data_len;
178179
uint32_t child_path_len;
179180
char wrapper_str[12];
181+
unsigned short flags; /* WALLY_MS_IS_ flags */
180182
unsigned char builtin;
181-
unsigned char flags; /* WALLY_MS_IS_ flags */
182183
} ms_node;
183184

184185
typedef struct wally_descriptor {
@@ -1293,7 +1294,7 @@ static int generate_sh_wpkh(ms_ctx *ctx, ms_node *node,
12931294
ms_node sh_node = { NULL, node, NULL, KIND_DESCRIPTOR_SH,
12941295
TYPE_NONE, 0, NULL, NULL, 0, 0,
12951296
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
1296-
builtin_sh_index, 0 };
1297+
0, builtin_sh_index };
12971298

12981299
if (ctx->variant != 3)
12991300
return WALLY_ERROR; /* Should only be called to generate sh-wpkh */
@@ -1423,6 +1424,7 @@ static int generate_tr(ms_ctx *ctx, ms_node *node,
14231424
unsigned char tweaked[EC_PUBLIC_KEY_LEN];
14241425
unsigned char pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN + 1];
14251426
size_t pubkey_len = 0;
1427+
uint32_t tweak_flags = 0;
14261428
int ret;
14271429

14281430
/* Generate a push of the x-only public key of our child */
@@ -1432,9 +1434,13 @@ static int generate_tr(ms_ctx *ctx, ms_node *node,
14321434
return WALLY_EINVAL; /* Should be PUSH_32 [x-only pubkey] */
14331435

14341436
/* Tweak it into a compressed pubkey */
1437+
#ifdef BUILD_ELEMENTS
1438+
if (node->flags & WALLY_MS_IS_ELEMENTS)
1439+
tweak_flags = EC_FLAG_ELEMENTS;
1440+
#endif
14351441
ret = wally_ec_public_key_bip341_tweak(pubkey + 1, pubkey_len - 1,
1436-
NULL, 0, 0, /* FIXME: Support script path */
1437-
tweaked, sizeof(tweaked));
1442+
NULL, 0, /* FIXME: Support script path */
1443+
tweak_flags, tweaked, sizeof(tweaked));
14381444

14391445
if (ret == WALLY_OK && script_len >= WALLY_SCRIPTPUBKEY_P2TR_LEN) {
14401446
/* Generate the script using the x-only part of the tweaked key */
@@ -2378,6 +2384,10 @@ static int analyze_miniscript(ms_ctx *ctx, const char *str, size_t str_len,
23782384
return WALLY_ENOMEM;
23792385

23802386
node->parent = parent;
2387+
#ifdef BUILD_ELEMENTS
2388+
if (flags & WALLY_MINISCRIPT_IS_ELEMENTS)
2389+
node->flags |= WALLY_MS_IS_ELEMENTS; /* Treat this node as an elements node */
2390+
#endif
23812391

23822392
for (i = 0; i < str_len; ++i) {
23832393
if (!node->builtin && str[i] == ':') {
@@ -2745,6 +2755,11 @@ int wally_descriptor_parse(const char *miniscript,
27452755
(network != WALLY_NETWORK_NONE && !addr_ver))
27462756
return WALLY_EINVAL;
27472757

2758+
#ifndef BUILD_ELEMENTS
2759+
if (flags & WALLY_MINISCRIPT_IS_ELEMENTS) {
2760+
return WALLY_EINVAL;
2761+
}
2762+
#endif
27482763
/* Allocate a context to hold the canonicalized/parsed expression */
27492764
if (!(*output = wally_calloc(sizeof(ms_ctx))))
27502765
return WALLY_ENOMEM;
@@ -2830,6 +2845,11 @@ int wally_descriptor_to_script_get_maximum_length(
28302845
*written = 0;
28312846
if (!descriptor || (flags & ~MS_FLAGS_ALL) || !written)
28322847
return WALLY_EINVAL;
2848+
#ifndef BUILD_ELEMENTS
2849+
if (flags & WALLY_MINISCRIPT_IS_ELEMENTS) {
2850+
return WALLY_EINVAL;
2851+
}
2852+
#endif
28332853
*written = descriptor->script_len;
28342854
return WALLY_OK;
28352855
}

src/wasm_package/src/const.js

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -122,21 +122,23 @@ export const WALLY_MAJOR_VER = 1;
122122
export const WALLY_MAX_OP_RETURN_LEN = 80; /* Maximum length of OP_RETURN data push */
123123
export const WALLY_MINISCRIPT_DEPTH_MASK = 0xffff0000; /** Mask for limiting maximum depth */
124124
export const WALLY_MINISCRIPT_DEPTH_SHIFT = 16; /** Shift to convert maximum depth to flags */
125+
export const WALLY_MINISCRIPT_IS_ELEMENTS = 0x20; /** Treat non-elements expressions as elements, e.g. tr() as eltr() */
125126
export const WALLY_MINISCRIPT_ONLY = 0x02; /** Only allow miniscript (not descriptor) expressions */
126127
export const WALLY_MINISCRIPT_POLICY_TEMPLATE = 0x08; /** Only allow policy templates with @n BIP32 keys */
127128
export const WALLY_MINISCRIPT_REQUIRE_CHECKSUM = 0x04; /** Require a checksum to be present */
128129
export const WALLY_MINISCRIPT_TAPSCRIPT = 0x01; /** Tapscript, use x-only pubkeys */
129130
export const WALLY_MINISCRIPT_UNIQUE_KEYPATHS = 0x10; /** For policy templates, ensure BIP32 derivation paths differ for identical keys */
130131
export const WALLY_MINOR_VER = 3;
131132
export const WALLY_MS_CANONICAL_NO_CHECKSUM = 0x01; /** Do not include a checksum */
132-
export const WALLY_MS_IS_DESCRIPTOR = 0x20; /** Contains only descriptor expressions (no miniscript) */
133-
export const WALLY_MS_IS_MULTIPATH = 0x02; /** Allows multiple paths via ``<a;b;c>`` */
134-
export const WALLY_MS_IS_PARENTED = 0x80; /** Contains at least one key key with a parent key origin */
135-
export const WALLY_MS_IS_PRIVATE = 0x04; /** Contains at least one private key */
136-
export const WALLY_MS_IS_RANGED = 0x01; /** Allows key ranges via ``*`` */
137-
export const WALLY_MS_IS_RAW = 0x10; /** Contains at least one raw key */
138-
export const WALLY_MS_IS_UNCOMPRESSED = 0x08; /** Contains at least one uncompressed key */
139-
export const WALLY_MS_IS_X_ONLY = 0x40; /** Contains at least one x-only key */
133+
export const WALLY_MS_IS_DESCRIPTOR = 0x020; /** Contains only descriptor expressions (no miniscript) */
134+
export const WALLY_MS_IS_ELEMENTS = 0x100; /** Contains Elements expressions or was parsed as Elements */
135+
export const WALLY_MS_IS_MULTIPATH = 0x002; /** Allows multiple paths via ``<a;b;c>`` */
136+
export const WALLY_MS_IS_PARENTED = 0x080; /** Contains at least one key key with a parent key origin */
137+
export const WALLY_MS_IS_PRIVATE = 0x004; /** Contains at least one private key */
138+
export const WALLY_MS_IS_RANGED = 0x001; /** Allows key ranges via ``*`` */
139+
export const WALLY_MS_IS_RAW = 0x010; /** Contains at least one raw key */
140+
export const WALLY_MS_IS_UNCOMPRESSED = 0x008; /** Contains at least one uncompressed key */
141+
export const WALLY_MS_IS_X_ONLY = 0x040; /** Contains at least one x-only key */
140142
export const WALLY_NETWORK_BITCOIN_MAINNET = 0x01; /** Bitcoin mainnet */
141143
export const WALLY_NETWORK_BITCOIN_REGTEST = 0xff ; /** Bitcoin regtest: Behaves as testnet except for segwit */
142144
export const WALLY_NETWORK_BITCOIN_TESTNET = 0x02; /** Bitcoin testnet */

0 commit comments

Comments
 (0)