Skip to content

Commit 2c9215c

Browse files
Add support for output descriptors/miniscript.
Co-authored-by: Jon Griffiths <[email protected]>
1 parent 8bb8687 commit 2c9215c

File tree

27 files changed

+4927
-22
lines changed

27 files changed

+4927
-22
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ docs/source/bip39.rst
7070
docs/source/core.rst
7171
docs/source/crypto.rst
7272
docs/source/map.rst
73+
docs/source/descriptor.rst
7374
docs/source/psbt.rst
7475
docs/source/psbt_members.rst
7576
docs/source/script.rst

docs/source/conf.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ def extract_docs(infile, outfile):
103103
else:
104104
for m in [
105105
'core', 'crypto', 'address', 'bip32', 'bip38', 'bip39', 'map',
106-
'script', 'psbt', 'symmetric', 'transaction', 'elements', 'anti_exfil'
106+
'script', 'psbt', 'descriptor', 'symmetric', 'transaction',
107+
'elements', 'anti_exfil'
107108
]:
108109
extract_docs('../../include/wally_%s.h' % m, '%s.rst' % m)
109110

docs/source/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ libwally-core documentation
1414
map
1515
psbt
1616
script
17+
descriptor
1718
symmetric
1819
transaction
1920
elements

include/wally.hpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,66 @@ inline int cleanup(uint32_t flags) {
433433
return ret;
434434
}
435435

436+
template <class DESCRIPTOR>
437+
inline int descriptor_canonicalize(const DESCRIPTOR& descriptor, uint32_t flags, char** output) {
438+
int ret = ::wally_descriptor_canonicalize(detail::get_p(descriptor), flags, output);
439+
return ret;
440+
}
441+
442+
inline int descriptor_free(struct wally_descriptor* descriptor) {
443+
int ret = ::wally_descriptor_free(descriptor);
444+
return ret;
445+
}
446+
447+
template <class DESCRIPTOR>
448+
inline int descriptor_get_checksum(const DESCRIPTOR& descriptor, uint32_t flags, char** output) {
449+
int ret = ::wally_descriptor_get_checksum(detail::get_p(descriptor), flags, output);
450+
return ret;
451+
}
452+
453+
template <class DESCRIPTOR>
454+
inline int descriptor_get_features(const DESCRIPTOR& descriptor, uint32_t* value_out) {
455+
int ret = ::wally_descriptor_get_features(detail::get_p(descriptor), value_out);
456+
return ret;
457+
}
458+
459+
template <class DESCRIPTOR, class VARS_IN>
460+
inline int descriptor_parse(const DESCRIPTOR& descriptor, const VARS_IN& vars_in, uint32_t network, uint32_t flags, struct wally_descriptor** output) {
461+
int ret = ::wally_descriptor_parse(detail::get_p(descriptor), detail::get_p(vars_in), network, flags, output);
462+
return ret;
463+
}
464+
465+
template <class DESCRIPTOR>
466+
inline int descriptor_to_address(const DESCRIPTOR& descriptor, uint32_t variant, uint32_t child_num, uint32_t flags, char** output) {
467+
int ret = ::wally_descriptor_to_address(detail::get_p(descriptor), variant, child_num, flags, output);
468+
return ret;
469+
}
470+
471+
template <class DESCRIPTOR>
472+
inline int descriptor_to_addresses(const DESCRIPTOR& descriptor, uint32_t variant, uint32_t child_num, uint32_t flags, char** output, size_t num_outputs) {
473+
int ret = ::wally_descriptor_to_addresses(detail::get_p(descriptor), variant, child_num, flags, output, num_outputs);
474+
return ret;
475+
}
476+
477+
template <class DESCRIPTOR, class BYTES_OUT>
478+
inline int descriptor_to_script(const DESCRIPTOR& descriptor, uint32_t depth, uint32_t index, uint32_t variant, uint32_t child_num, uint32_t flags, BYTES_OUT& bytes_out, size_t* written = 0) {
479+
size_t n;
480+
int ret = ::wally_descriptor_to_script(detail::get_p(descriptor), depth, index, variant, child_num, flags, bytes_out.data(), bytes_out.size(), written ? written : &n);
481+
return written || ret != WALLY_OK ? ret : n == static_cast<size_t>(bytes_out.size()) ? WALLY_OK : WALLY_EINVAL;
482+
}
483+
484+
template <class DESCRIPTOR>
485+
inline int descriptor_to_script_get_maximum_length(const DESCRIPTOR& descriptor, uint32_t flags, size_t* written) {
486+
int ret = ::wally_descriptor_to_script_get_maximum_length(detail::get_p(descriptor), flags, written);
487+
return ret;
488+
}
489+
490+
template <class DESCRIPTOR>
491+
inline int descriptor_to_script_len(const DESCRIPTOR& descriptor, uint32_t depth, uint32_t index, uint32_t variant, uint32_t child_num, uint32_t flags, size_t* written) {
492+
int ret = ::wally_descriptor_to_script_len(detail::get_p(descriptor), depth, index, variant, child_num, flags, written);
493+
return ret;
494+
}
495+
436496
template <class PRIV_KEY>
437497
inline int ec_private_key_verify(const PRIV_KEY& priv_key) {
438498
int ret = ::wally_ec_private_key_verify(priv_key.data(), priv_key.size());

include/wally_address.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ struct ext_key;
1616
#define WALLY_CA_PREFIX_LIQUID_REGTEST 0x04 /** Liquid v1 confidential address prefix for regtest */
1717
#define WALLY_CA_PREFIX_LIQUID_TESTNET 0x17 /** Liquid v1 confidential address prefix for testnet */
1818

19+
#define WALLY_NETWORK_NONE 0x00 /** Used for miniscript parsing only */
1920
#define WALLY_NETWORK_BITCOIN_MAINNET 0x01 /** Bitcoin mainnet */
21+
#define WALLY_NETWORK_BITCOIN_REGTEST 0xff /** Bitcoin regtest: Behaves as testnet except for segwit */
2022
#define WALLY_NETWORK_BITCOIN_TESTNET 0x02 /** Bitcoin testnet */
2123
#define WALLY_NETWORK_LIQUID 0x03 /** Liquid v1 */
2224
#define WALLY_NETWORK_LIQUID_REGTEST 0x04 /** Liquid v1 regtest */

include/wally_descriptor.h

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
#ifndef LIBWALLY_CORE_DESCRIPTOR_H
2+
#define LIBWALLY_CORE_DESCRIPTOR_H
3+
4+
#include "wally_core.h"
5+
6+
#ifdef __cplusplus
7+
extern "C" {
8+
#endif
9+
10+
struct wally_map;
11+
/** An opaque type holding a parsed minscript/descriptor expression */
12+
struct wally_descriptor;
13+
14+
/* Miniscript type flag */
15+
#define WALLY_MINISCRIPT_WITNESS_SCRIPT 0x00 /** Witness script */
16+
#define WALLY_MINISCRIPT_TAPSCRIPT 0x01 /** Tapscript */
17+
#define WALLY_MINISCRIPT_ONLY 0x02 /** Only allow miniscript (not descriptor) expressions */
18+
19+
#define WALLY_MS_IS_RANGED 0x01 /** Allows key ranges via '*' */
20+
21+
22+
/**
23+
* Parse an output descriptor or miniscript expression.
24+
*
25+
* :param descriptor: Output descriptor or miniscript expression to parse.
26+
* :param vars_in: Map of variable names to values, or NULL.
27+
* :param network: ``WALLY_NETWORK_`` constant descripting the network the
28+
*| descriptor belongs to, or WALLY_NETWORK_NONE for miniscript-only expressions.
29+
* :param flags: Include ``WALLY_MINISCRIPT_ONLY`` to disallow descriptor
30+
*| expressions, ``WALLY_MINISCRIPT_TAPSCRIPT`` to use x-only pubkeys, or 0.
31+
* :param output: Destination for the resulting parsed descriptor.
32+
*| The descriptor returned should be freed using `wally_descriptor_free`.
33+
*
34+
* Variable names can be used in the descriptor string and will be substituted
35+
* with the contents of ``vars_in`` during parsing. Key names for ``vars_in``
36+
* must be 16 characters or less in length and start with a letter.
37+
*/
38+
WALLY_CORE_API int wally_descriptor_parse(
39+
const char *descriptor,
40+
const struct wally_map *vars_in,
41+
uint32_t network,
42+
uint32_t flags,
43+
struct wally_descriptor **output);
44+
45+
/**
46+
* Free a parsed output descriptor or miniscript expression.
47+
*
48+
* :param descriptor: Parsed output descriptor or miniscript expression to free.
49+
*/
50+
WALLY_CORE_API int wally_descriptor_free(
51+
struct wally_descriptor *descriptor);
52+
53+
/**
54+
* Canonicalize a descriptor.
55+
*
56+
* :param descriptor: Parsed output descriptor or miniscript expression.
57+
* :param flags: For future use. Must be 0.
58+
* :param output: Destination for the resulting canonical descriptor.
59+
*| The string returned should be freed using `wally_free_string`.
60+
*
61+
* .. note:: Other canonicalization (hardened derivation indicator
62+
* mapping, and private to public key mapping) is not yet implemented.
63+
*/
64+
WALLY_CORE_API int wally_descriptor_canonicalize(
65+
const struct wally_descriptor *descriptor,
66+
uint32_t flags,
67+
char **output);
68+
69+
/**
70+
* Create an output descriptor checksum.
71+
*
72+
* :param descriptor: Parsed output descriptor or miniscript expression.
73+
* :param flags: For future use. Must be 0.
74+
* :param output: Destination for the resulting descriptor checksum.
75+
*| The string returned should be freed using `wally_free_string`.
76+
*/
77+
WALLY_CORE_API int wally_descriptor_get_checksum(
78+
const struct wally_descriptor *descriptor,
79+
uint32_t flags,
80+
char **output);
81+
82+
/**
83+
* Get the features used in a parsed output descriptor or miniscript expression.
84+
*
85+
* :param descriptor: Parsed output descriptor or miniscript expression.
86+
* :param value_out: Destination for the resulting ``WALLY_MS_`` feature flags.
87+
*/
88+
WALLY_CORE_API int wally_descriptor_get_features(
89+
const struct wally_descriptor *descriptor,
90+
uint32_t *value_out);
91+
92+
/**
93+
* Get the maximum length of a script corresponding to an output descriptor.
94+
*
95+
* :param descriptor: Parsed output descriptor or miniscript expression.
96+
* :param flags: For future use. Must be 0.
97+
* :param written: Destination for the resulting maximum script length.
98+
*
99+
* .. note:: This function overestimates the script size required, but is
100+
*| cheap to compute (does not require script generation).
101+
*/
102+
WALLY_CORE_API int wally_descriptor_to_script_get_maximum_length(
103+
const struct wally_descriptor *descriptor,
104+
uint32_t flags,
105+
size_t *written);
106+
107+
/**
108+
* Get the length of a script corresponding to an output descriptor.
109+
*
110+
* :param descriptor: Parsed output descriptor or miniscript expression.
111+
* :param depth: Depth of the expression tree to generate from. Pass 0 to generate from the root.
112+
* :param index: The zero-based index of the child at depth ``depth`` to generate from.
113+
* :param variant: The variant of descriptor to generate. Pass 0 for the default.
114+
* :param child_num: The BIP32 child number to derive, or 0 for static descriptors.
115+
* :param flags: For future use. Must be 0.
116+
* :param written: Destination for the resulting script length.
117+
*
118+
* .. note:: This function may return a size slightly larger than the final
119+
*| scripts generated size, typically by 1-2 bytes if so.
120+
* .. note:: Computing the script length using this function is expensive, as
121+
*| it must partially generate the script. Prefer to use
122+
*| either `wally_descriptor_to_script_get_maximum_length`
123+
*| or pass a large buffer to `wally_descriptor_to_script` and retry
124+
*| the call with a larger buffer if the it was too small.
125+
*/
126+
WALLY_CORE_API int wally_descriptor_to_script_len(
127+
struct wally_descriptor *descriptor,
128+
uint32_t depth,
129+
uint32_t index,
130+
uint32_t variant,
131+
uint32_t child_num,
132+
uint32_t flags,
133+
size_t *written);
134+
135+
/**
136+
* Create a script corresponding to an output descriptor or miniscript expression.
137+
*
138+
* :param descriptor: Parsed output descriptor or miniscript expression.
139+
* :param depth: Depth of the expression tree to generate from. Pass 0 to generate from the root.
140+
* :param index: The zero-based index of the child at depth ``depth`` to generate from.
141+
* :param variant: The variant of descriptor to generate. Pass 0 for the default.
142+
* :param child_num: The BIP32 child number to derive, or 0 for static descriptors.
143+
* :param flags: For future use. Must be 0.
144+
* :param bytes_out: Destination for the resulting scriptPubKey or script.
145+
* :param len: The length of ``bytes_out`` in bytes.
146+
* :param written: Destination for the number of bytes written to ``bytes_out``.
147+
*/
148+
WALLY_CORE_API int wally_descriptor_to_script(
149+
struct wally_descriptor *descriptor,
150+
uint32_t depth,
151+
uint32_t index,
152+
uint32_t variant,
153+
uint32_t child_num,
154+
uint32_t flags,
155+
unsigned char *bytes_out,
156+
size_t len,
157+
size_t *written);
158+
159+
/**
160+
* Create an address corresponding to an output descriptor.
161+
*
162+
* :param descriptor: Parsed output descriptor.
163+
* :param variant: The variant of descriptor to generate. Pass 0 for the default.
164+
* :param child_num: The BIP32 child number to derive, or zero for static descriptors.
165+
* :param flags: For future use. Must be 0.
166+
* :param output: Destination for the resulting addresss.
167+
*| The string returned should be freed using `wally_free_string`.
168+
*/
169+
WALLY_CORE_API int wally_descriptor_to_address(
170+
struct wally_descriptor *descriptor,
171+
uint32_t variant,
172+
uint32_t child_num,
173+
uint32_t flags,
174+
char **output);
175+
176+
/**
177+
* Create addresses that correspond to the derived range of an output descriptor.
178+
*
179+
* :param descriptor: Parsed output descriptor.
180+
* :param variant: The variant of descriptor to generate. Pass 0 for the default.
181+
* :param child_num: The BIP32 child number to derive, or zero for static descriptors.
182+
* :param flags: For future use. Must be 0.
183+
* :param output: Destination for the resulting addresses.
184+
* :param num_outputs: The number of items in ``output``. Addresses will be
185+
*| generated into this array starting from child_num, incrementing by 1.
186+
*| The addresses returned should be freed using `wally_free_string`.
187+
*/
188+
WALLY_CORE_API int wally_descriptor_to_addresses(
189+
struct wally_descriptor *descriptor,
190+
uint32_t variant,
191+
uint32_t child_num,
192+
uint32_t flags,
193+
char **output,
194+
size_t num_outputs);
195+
196+
#ifdef __cplusplus
197+
}
198+
#endif
199+
200+
#endif /* LIBWALLY_CORE_DESCRIPTOR_H */

src/Makefile.am

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ include_HEADERS += $(top_srcdir)/include/wally_bip38.h
1414
include_HEADERS += $(top_srcdir)/include/wally_bip39.h
1515
include_HEADERS += $(top_srcdir)/include/wally_core.h
1616
include_HEADERS += $(top_srcdir)/include/wally_crypto.h
17+
include_HEADERS += $(top_srcdir)/include/wally_descriptor.h
1718
include_HEADERS += $(top_srcdir)/include/wally_elements.h
1819
include_HEADERS += $(top_srcdir)/include/wally_map.h
1920
include_HEADERS += $(top_srcdir)/include/wally_psbt.h
@@ -109,6 +110,7 @@ all: swig_java/wallycore.jar
109110

110111
SWIG_JAVA_TEST_DEPS = \
111112
$(sjs)/$(cbt)/test_bip32.class \
113+
$(sjs)/$(cbt)/test_descriptor.class \
112114
$(sjs)/$(cbt)/test_tx.class \
113115
$(sjs)/$(cbt)/test_scripts.class \
114116
$(sjs)/$(cbt)/test_mnemonic.class
@@ -141,6 +143,7 @@ libwallycore_la_SOURCES = \
141143
bip38.c \
142144
bip39.c \
143145
bech32.c \
146+
descriptor.c \
144147
ecdh.c \
145148
elements.c \
146149
blech32.c \
@@ -173,6 +176,7 @@ libwallycore_la_INCLUDES = \
173176
include/wally_bip39.h \
174177
include/wally_core.h \
175178
include/wally_crypto.h \
179+
include/wally_descriptor.h \
176180
include/wally_elements.h \
177181
include/wally_map.h \
178182
include/wally_psbt.h \
@@ -239,6 +243,14 @@ test_tx_LDADD = $(lib_LTLIBRARIES) @CTEST_EXTRA_STATIC@
239243
if PYTHON_MANYLINUX
240244
test_tx_LDADD += $(PYTHON_LIBS)
241245
endif
246+
TESTS += test_descriptor
247+
noinst_PROGRAMS += test_descriptor
248+
test_descriptor_SOURCES = ctest/test_descriptor.c
249+
test_descriptor_CFLAGS = -I$(top_srcdir)/include $(AM_CFLAGS)
250+
test_descriptor_LDADD = $(lib_LTLIBRARIES) @CTEST_EXTRA_STATIC@
251+
if PYTHON_MANYLINUX
252+
test_descriptor_LDADD += $(PYTHON_LIBS)
253+
endif
242254
if BUILD_ELEMENTS
243255
TESTS += test_elements_tx
244256
noinst_PROGRAMS += test_elements_tx
@@ -265,6 +277,7 @@ check-libwallycore: $(PYTHON_TEST_DEPS)
265277
$(AM_V_at)$(PYTHON_TEST) test/test_bip32.py
266278
$(AM_V_at)$(PYTHON_TEST) test/test_bip38.py
267279
$(AM_V_at)$(PYTHON_TEST) test/test_bip39.py
280+
$(AM_V_at)$(PYTHON_TEST) test/test_descriptor.py
268281
$(AM_V_at)$(PYTHON_TEST) test/test_ecdh.py
269282
$(AM_V_at)$(PYTHON_TEST) test/test_hash.py
270283
$(AM_V_at)$(PYTHON_TEST) test/test_hex.py
@@ -291,6 +304,7 @@ endif
291304
if USE_SWIG_PYTHON
292305
check-swig-python: $(SWIG_PYTHON_TEST_DEPS)
293306
$(AM_V_at)$(PYTHON_SWIGTEST) swig_python/contrib/bip32.py
307+
$(AM_V_at)$(PYTHON_SWIGTEST) swig_python/contrib/descriptor.py
294308
$(AM_V_at)$(PYTHON_SWIGTEST) swig_python/contrib/mnemonic.py
295309
$(AM_V_at)$(PYTHON_SWIGTEST) swig_python/contrib/psbt.py
296310
$(AM_V_at)$(PYTHON_SWIGTEST) swig_python/contrib/sha.py
@@ -317,6 +331,7 @@ if BUILD_ELEMENTS
317331
$(AM_V_at)$(JAVA_TEST)test_pegs
318332
endif
319333
$(AM_V_at)$(JAVA_TEST)test_bip32
334+
$(AM_V_at)$(JAVA_TEST)test_descriptor
320335
$(AM_V_at)$(JAVA_TEST)test_mnemonic
321336
$(AM_V_at)$(JAVA_TEST)test_scripts
322337
$(AM_V_at)$(JAVA_TEST)test_tx

src/address.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ int wally_scriptpubkey_to_address(const unsigned char *scriptpubkey, size_t scri
166166
case WALLY_NETWORK_BITCOIN_MAINNET:
167167
bytes[0] = WALLY_ADDRESS_VERSION_P2PKH_MAINNET;
168168
break;
169+
case WALLY_NETWORK_BITCOIN_REGTEST:
169170
case WALLY_NETWORK_BITCOIN_TESTNET:
170171
bytes[0] = WALLY_ADDRESS_VERSION_P2PKH_TESTNET;
171172
break;
@@ -188,6 +189,7 @@ int wally_scriptpubkey_to_address(const unsigned char *scriptpubkey, size_t scri
188189
case WALLY_NETWORK_BITCOIN_MAINNET:
189190
bytes[0] = WALLY_ADDRESS_VERSION_P2SH_MAINNET;
190191
break;
192+
case WALLY_NETWORK_BITCOIN_REGTEST:
191193
case WALLY_NETWORK_BITCOIN_TESTNET:
192194
bytes[0] = WALLY_ADDRESS_VERSION_P2SH_TESTNET;
193195
break;

src/amalgamation/combined.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "bip32.c"
1111
#include "bip38.c"
1212
#include "bip39.c"
13+
#include "descriptor.c"
1314
#include "ecdh.c"
1415
#include "elements.c"
1516
#include "hex.c"

0 commit comments

Comments
 (0)