Skip to content

Commit fa205c5

Browse files
Add support for output descriptors/miniscript.
Co-authored-by: Jon Griffiths <[email protected]>
1 parent 5086e72 commit fa205c5

File tree

27 files changed

+4375
-21
lines changed

27 files changed

+4375
-21
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ docs/source/bip39.rst
8080
docs/source/core.rst
8181
docs/source/crypto.rst
8282
docs/source/map.rst
83+
docs/source/descriptor.rst
8384
docs/source/psbt.rst
8485
docs/source/psbt_members.rst
8586
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: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,43 @@ inline int cleanup(uint32_t flags) {
427427
return ret;
428428
}
429429

430+
template <class DESCRIPTOR, class VARS_IN>
431+
inline int descriptor_canonicalize(const DESCRIPTOR& descriptor, const VARS_IN& vars_in, uint32_t flags, char** output) {
432+
int ret = ::wally_descriptor_canonicalize(detail::get_p(descriptor), detail::get_p(vars_in), flags, output);
433+
return ret;
434+
}
435+
436+
template <class DESCRIPTOR, class VARS_IN>
437+
inline int descriptor_get_checksum(const DESCRIPTOR& descriptor, const VARS_IN& vars_in, uint32_t flags, char** output) {
438+
int ret = ::wally_descriptor_get_checksum(detail::get_p(descriptor), detail::get_p(vars_in), flags, output);
439+
return ret;
440+
}
441+
442+
template <class DESCRIPTOR, class VARS_IN>
443+
inline int descriptor_to_address(const DESCRIPTOR& descriptor, const VARS_IN& vars_in, uint32_t child_num, uint32_t network, uint32_t flags, char** output) {
444+
int ret = ::wally_descriptor_to_address(detail::get_p(descriptor), detail::get_p(vars_in), child_num, network, flags, output);
445+
return ret;
446+
}
447+
448+
template <class DESCRIPTOR, class VARS_IN>
449+
inline int descriptor_to_addresses(const DESCRIPTOR& descriptor, const VARS_IN& vars_in, uint32_t child_num, uint32_t network, uint32_t flags, char** output, size_t output_len) {
450+
int ret = ::wally_descriptor_to_addresses(detail::get_p(descriptor), detail::get_p(vars_in), child_num, network, flags, output, output_len);
451+
return ret;
452+
}
453+
454+
template <class DESCRIPTOR, class VARS_IN, class BYTES_OUT>
455+
inline int descriptor_to_scriptpubkey(const DESCRIPTOR& descriptor, const VARS_IN& vars_in, uint32_t child_num, uint32_t network, uint32_t depth, uint32_t index, uint32_t flags, BYTES_OUT& bytes_out, size_t* written = 0) {
456+
size_t n;
457+
int ret = ::wally_descriptor_to_scriptpubkey(detail::get_p(descriptor), detail::get_p(vars_in), child_num, network, depth, index, flags, bytes_out.data(), bytes_out.size(), written ? written : &n);
458+
return written || ret != WALLY_OK ? ret : n == static_cast<size_t>(bytes_out.size()) ? WALLY_OK : WALLY_EINVAL;
459+
}
460+
461+
template <class DESCRIPTOR, class VARS_IN>
462+
inline int descriptor_to_scriptpubkey_len(const DESCRIPTOR& descriptor, const VARS_IN& vars_in, uint32_t child_num, uint32_t network, uint32_t depth, uint32_t index, uint32_t flags, size_t* written) {
463+
int ret = ::wally_descriptor_to_scriptpubkey_len(detail::get_p(descriptor), detail::get_p(vars_in), child_num, network, depth, index, flags, written);
464+
return ret;
465+
}
466+
430467
template <class PRIV_KEY>
431468
inline int ec_private_key_verify(const PRIV_KEY& priv_key) {
432469
int ret = ::wally_ec_private_key_verify(priv_key.data(), priv_key.size());
@@ -880,6 +917,19 @@ inline int map_sort(const MAP_IN& map_in, uint32_t flags) {
880917
return ret;
881918
}
882919

920+
template <class MINISCRIPT, class VARS_IN, class BYTES_OUT>
921+
inline int miniscript_to_script(const MINISCRIPT& miniscript, const VARS_IN& vars_in, uint32_t child_num, uint32_t flags, BYTES_OUT& bytes_out, size_t* written = 0) {
922+
size_t n;
923+
int ret = ::wally_miniscript_to_script(detail::get_p(miniscript), detail::get_p(vars_in), child_num, flags, bytes_out.data(), bytes_out.size(), written ? written : &n);
924+
return written || ret != WALLY_OK ? ret : n == static_cast<size_t>(bytes_out.size()) ? WALLY_OK : WALLY_EINVAL;
925+
}
926+
927+
template <class MINISCRIPT, class VARS_IN>
928+
inline int miniscript_to_script_len(const MINISCRIPT& miniscript, const VARS_IN& vars_in, uint32_t child_num, uint32_t flags, size_t* written) {
929+
int ret = ::wally_miniscript_to_script_len(detail::get_p(miniscript), detail::get_p(vars_in), child_num, flags, written);
930+
return ret;
931+
}
932+
883933
template <class PASS, class SALT, class BYTES_OUT>
884934
inline int pbkdf2_hmac_sha256(const PASS& pass, const SALT& salt, uint32_t flags, uint32_t cost, BYTES_OUT& bytes_out) {
885935
int ret = ::wally_pbkdf2_hmac_sha256(pass.data(), pass.size(), salt.data(), salt.size(), flags, cost, bytes_out.data(), bytes_out.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: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
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+
12+
/* Miniscript type flag */
13+
#define WALLY_MINISCRIPT_WITNESS_SCRIPT 0x00 /** Witness script */
14+
#define WALLY_MINISCRIPT_TAPSCRIPT 0x01 /** Tapscript */
15+
16+
/**
17+
* Canonicalize a descriptor.
18+
*
19+
* :param descriptor: Output descriptor.
20+
* :param vars_in: Map of variable names to values, or NULL.
21+
* :param flags: For future use. Must be 0.
22+
* :param output: Destination for the resulting canonical descriptor.
23+
*| The string returned should be freed using `wally_free_string`.
24+
*
25+
* Replaces any variables from ``vars_in`` with their mapped values,
26+
* and adds a checksum if required. Key names for ``vars_in`` must be 16
27+
* characters or less and start with a letter.
28+
*
29+
* .. note:: Other canonicalization (hardened derivation indicator
30+
* mapping, and private to public key mapping) is not yet implemented.
31+
*/
32+
WALLY_CORE_API int wally_descriptor_canonicalize(
33+
const char *descriptor,
34+
const struct wally_map *vars_in,
35+
uint32_t flags,
36+
char **output);
37+
38+
/**
39+
* Get the length of a script corresponding to a miniscript string.
40+
*
41+
* :param miniscript: Miniscript string.
42+
* :param vars_in: Map of variable names to values, or NULL.
43+
* :param child_num: The BIP32 child number to derive.
44+
* :param flags: Flags controlling the type of script to create. Use one of
45+
*| ``WALLY_MINISCRIPT_WITNESS_SCRIPT`` or ``WALLY_MINISCRIPT_TAPSCRIPT``.
46+
* :param written: Destination for the resulting script length.
47+
*
48+
* .. note:: Computing this length is expensive. Prefer to pass a large
49+
*| buffer to `wally_miniscript_to_script` and retry only if the
50+
*| buffer is too small.
51+
*/
52+
WALLY_CORE_API int wally_miniscript_to_script_len(
53+
const char *miniscript,
54+
const struct wally_map *vars_in,
55+
uint32_t child_num,
56+
uint32_t flags,
57+
size_t *written);
58+
59+
/**
60+
* Create a script corresponding to a miniscript string.
61+
*
62+
* :param miniscript: Miniscript string.
63+
* :param vars_in: Map of variable names to values, or NULL.
64+
* :param child_num: The BIP32 child number to derive.
65+
* :param flags: Flags controlling the type of script to create. Use one of
66+
*| ``WALLY_MINISCRIPT_WITNESS_SCRIPT`` or ``WALLY_MINISCRIPT_TAPSCRIPT``.
67+
* :param bytes_out: Destination for the resulting scriptPubKey.
68+
* :param len: The length of ``bytes_out`` in bytes.
69+
* :param written: Destination for the number of bytes written to ``bytes_out``.
70+
*/
71+
WALLY_CORE_API int wally_miniscript_to_script(
72+
const char *miniscript,
73+
const struct wally_map *vars_in,
74+
uint32_t child_num,
75+
uint32_t flags,
76+
unsigned char *bytes_out,
77+
size_t len,
78+
size_t *written);
79+
80+
/**
81+
* Get the length of a scriptPubKey corresponding to an output descriptor.
82+
*
83+
* :param descriptor: Output descriptor.
84+
* :param vars_in: Map of variable names to values, or NULL.
85+
* :param child_num: The BIP32 child number to derive.
86+
* :param network: ``WALLY_NETWORK_`` constant descripting the network to generate for.
87+
* :param depth: Number of the descriptor depth. Default is 0.
88+
* :param index: Number of the descriptor index. Default is 0.
89+
* :param flags: For future use. Must be 0.
90+
* :param written: Destination for the resulting scriptPubKey length.
91+
*
92+
* .. note:: Computing this length is expensive. Prefer to pass a large
93+
*| buffer to `wally_descriptor_to_scriptpubkey` and retry only if the
94+
*| buffer is too small.
95+
*/
96+
WALLY_CORE_API int wally_descriptor_to_scriptpubkey_len(
97+
const char *descriptor,
98+
const struct wally_map *vars_in,
99+
uint32_t child_num,
100+
uint32_t network,
101+
uint32_t depth,
102+
uint32_t index,
103+
uint32_t flags,
104+
size_t *written);
105+
106+
/**
107+
* Create a scriptPubKey corresponding to an output descriptor.
108+
*
109+
* :param descriptor: Output descriptor.
110+
* :param vars_in: Map of variable names to values, or NULL.
111+
* :param child_num: The BIP32 child number to derive.
112+
* :param network: ``WALLY_NETWORK_`` constant descripting the network to generate for.
113+
* :param depth: Number of the descriptor depth. Default is 0.
114+
* :param index: Number of the descriptor index. Default is 0.
115+
* :param flags: For future use. Must be 0.
116+
* :param bytes_out: Destination for the resulting scriptPubKey.
117+
* :param len: The length of ``bytes_out`` in bytes.
118+
* :param written: Destination for the number of bytes written to ``bytes_out``.
119+
*/
120+
WALLY_CORE_API int wally_descriptor_to_scriptpubkey(
121+
const char *descriptor,
122+
const struct wally_map *vars_in,
123+
uint32_t child_num,
124+
uint32_t network,
125+
uint32_t depth,
126+
uint32_t index,
127+
uint32_t flags,
128+
unsigned char *bytes_out,
129+
size_t len,
130+
size_t *written);
131+
132+
/**
133+
* Create an address corresponding to an output descriptor.
134+
*
135+
* :param descriptor: Output descriptor.
136+
* :param vars_in: Map of variable names to values, or NULL.
137+
* :param child_num: The BIP32 child number to derive.
138+
* :param network: ``WALLY_NETWORK_`` constant descripting the network to generate for.
139+
* :param flags: For future use. Must be 0.
140+
* :param output: Destination for the resulting addresss.
141+
*| The string returned should be freed using `wally_free_string`.
142+
*/
143+
WALLY_CORE_API int wally_descriptor_to_address(
144+
const char *descriptor,
145+
const struct wally_map *vars_in,
146+
uint32_t child_num,
147+
uint32_t network,
148+
uint32_t flags,
149+
char **output);
150+
151+
/**
152+
* Create addresses that correspond to the derived range of an output descriptor.
153+
*
154+
* :param descriptor: Output descriptor.
155+
* :param vars_in: Map of variable names to values, or NULL.
156+
* :param child_num: The first BIP32 child number to derive.
157+
* :param network: ``WALLY_NETWORK_`` constant descripting the network to generate for.
158+
* :param flags: For future use. Must be 0.
159+
* :param output: Destination for the resulting addresses.
160+
* :param num_outputs: The number of items in ``output``. Addresses will be
161+
*| generated into this array starting from child_num, incrementing by 1.
162+
*| The addresses returned should be freed using `wally_free_string`.
163+
*/
164+
WALLY_CORE_API int wally_descriptor_to_addresses(
165+
const char *descriptor,
166+
const struct wally_map *vars_in,
167+
uint32_t child_num,
168+
uint32_t network,
169+
uint32_t flags,
170+
char **output,
171+
size_t num_outputs);
172+
173+
/**
174+
* Create an output descriptor checksum.
175+
*
176+
* :param descriptor: Output descriptor.
177+
* :param vars_in: Map of variable names to values, or NULL.
178+
* :param flags: For future use. Must be 0.
179+
* :param output: Destination for the resulting descriptor checksum.
180+
*| The string returned should be freed using `wally_free_string`.
181+
*/
182+
WALLY_CORE_API int wally_descriptor_get_checksum(
183+
const char *descriptor,
184+
const struct wally_map *vars_in,
185+
uint32_t flags,
186+
char **output);
187+
188+
#ifdef __cplusplus
189+
}
190+
#endif
191+
192+
#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
@@ -158,6 +159,7 @@ all: swig_java/wallycore.jar
158159

159160
SWIG_JAVA_TEST_DEPS = \
160161
$(sjs)/$(cbt)/test_bip32.class \
162+
$(sjs)/$(cbt)/test_descriptor.class \
161163
$(sjs)/$(cbt)/test_tx.class \
162164
$(sjs)/$(cbt)/test_scripts.class \
163165
$(sjs)/$(cbt)/test_mnemonic.class
@@ -190,6 +192,7 @@ libwallycore_la_SOURCES = \
190192
bip38.c \
191193
bip39.c \
192194
bech32.c \
195+
descriptor.c \
193196
ecdh.c \
194197
elements.c \
195198
blech32.c \
@@ -222,6 +225,7 @@ libwallycore_la_INCLUDES = \
222225
include/wally_bip39.h \
223226
include/wally_core.h \
224227
include/wally_crypto.h \
228+
include/wally_descriptor.h \
225229
include/wally_elements.h \
226230
include/wally_map.h \
227231
include/wally_psbt.h \
@@ -288,6 +292,14 @@ test_tx_LDADD = $(lib_LTLIBRARIES) @CTEST_EXTRA_STATIC@
288292
if PYTHON_MANYLINUX
289293
test_tx_LDADD += $(PYTHON_LIBS)
290294
endif
295+
TESTS += test_descriptor
296+
noinst_PROGRAMS += test_descriptor
297+
test_descriptor_SOURCES = ctest/test_descriptor.c
298+
test_descriptor_CFLAGS = -I$(top_srcdir)/include $(AM_CFLAGS)
299+
test_descriptor_LDADD = $(lib_LTLIBRARIES) @CTEST_EXTRA_STATIC@
300+
if PYTHON_MANYLINUX
301+
test_descriptor_LDADD += $(PYTHON_LIBS)
302+
endif
291303
if BUILD_ELEMENTS
292304
TESTS += test_elements_tx
293305
noinst_PROGRAMS += test_elements_tx
@@ -314,6 +326,7 @@ check-libwallycore: $(PYTHON_TEST_DEPS)
314326
$(AM_V_at)$(PYTHON_TEST) test/test_bip32.py
315327
$(AM_V_at)$(PYTHON_TEST) test/test_bip38.py
316328
$(AM_V_at)$(PYTHON_TEST) test/test_bip39.py
329+
$(AM_V_at)$(PYTHON_TEST) test/test_descriptor.py
317330
$(AM_V_at)$(PYTHON_TEST) test/test_ecdh.py
318331
$(AM_V_at)$(PYTHON_TEST) test/test_hash.py
319332
$(AM_V_at)$(PYTHON_TEST) test/test_hex.py
@@ -340,6 +353,7 @@ endif
340353
if USE_SWIG_PYTHON
341354
check-swig-python: $(SWIG_PYTHON_TEST_DEPS)
342355
$(AM_V_at)$(PYTHON_SWIGTEST) swig_python/contrib/bip32.py
356+
$(AM_V_at)$(PYTHON_SWIGTEST) swig_python/contrib/descriptor.py
343357
$(AM_V_at)$(PYTHON_SWIGTEST) swig_python/contrib/mnemonic.py
344358
$(AM_V_at)$(PYTHON_SWIGTEST) swig_python/contrib/psbt.py
345359
$(AM_V_at)$(PYTHON_SWIGTEST) swig_python/contrib/sha.py
@@ -366,6 +380,7 @@ if BUILD_ELEMENTS
366380
$(AM_V_at)$(JAVA_TEST)test_pegs
367381
endif
368382
$(AM_V_at)$(JAVA_TEST)test_bip32
383+
$(AM_V_at)$(JAVA_TEST)test_descriptor
369384
$(AM_V_at)$(JAVA_TEST)test_mnemonic
370385
$(AM_V_at)$(JAVA_TEST)test_scripts
371386
$(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;

0 commit comments

Comments
 (0)