diff --git a/include/wally_descriptor.h b/include/wally_descriptor.h new file mode 100644 index 000000000..ecec21d24 --- /dev/null +++ b/include/wally_descriptor.h @@ -0,0 +1,170 @@ +#ifndef LIBWALLY_CORE_DESCRIPTOR_H +#define LIBWALLY_CORE_DESCRIPTOR_H + +#include "wally_core.h" +#include "wally_address.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define WALLY_NETWORK_BITCOIN_REGTEST 0xff /** Bitcoin regtest */ + +/* miniscript type flag */ +#define WALLY_MINISCRIPT_WITNESS_SCRIPT 0x00 +#define WALLY_MINISCRIPT_TAPSCRIPT 0x01 + +#ifdef SWIG +struct wally_descriptor_address_item; +struct wally_descriptor_addresses; +#else +/** A descriptor address */ +struct wally_descriptor_address_item { + uint32_t child_num; + char *address; + size_t address_len; +}; + +/** A descriptor addresses */ +struct wally_descriptor_addresses { + struct wally_descriptor_address_item *items; + size_t num_items; +}; +#endif + +#ifndef SWIG_PYTHON +/** + * Free addresses allocated by `wally_descriptor_to_addresses`. + * + * :param addresses: addresses to free. + */ +WALLY_CORE_API int wally_free_descriptor_addresses( + struct wally_descriptor_addresses *addresses); +#endif /* SWIG_PYTHON */ + +/** + * Create a script corresponding to a miniscript string. + * + * :param miniscript: Miniscript string. + * :param key_name_array: Array of key policy name string. + * :param key_value_array: Array of key mapped value string. + * :param array_len: Length of the array of key policy name. + * :param derive_child_num: Number of the derive path. + * :param flags: For analyze type. + * see WALLY_MINISCRIPT_WITNESS_SCRIPT, WALLY_MINISCRIPT_TAPSCRIPT. + * :param script: Destination for the resulting scriptpubkey. + * :param script_len: Length of the script array. + * :param written: Destination for the using scriptpubkey length. + */ +WALLY_CORE_API int wally_descriptor_parse_miniscript( + const char *miniscript, + const char **key_name_array, + const char **key_value_array, + size_t array_len, + uint32_t derive_child_num, + uint32_t flags, + unsigned char *script, + size_t script_len, + size_t *written); + +/** + * Create a scriptpubkey corresponding to a output descriptor. + * + * :param descriptor: Output descriptor. + * :param key_name_array: Array of key policy name string. + * :param key_value_array: Array of key mapped value string. + * :param array_len: Length of the array of key policy name. + * :param derive_child_num: Number of the derive path. + * :param network: Number of the network. (bitcoin regtest is set ``0xff``) + * :param target_depth: Number of the descriptor depth. Default is 0. + * :param target_index: Number of the descriptor index. Default is 0. + * :param flags: For future use. Must be 0. + * :param script: Destination for the resulting scriptpubkey. + * :param script_len: Length of the script array. + * :param written: Destination for the using scriptpubkey length. + */ +WALLY_CORE_API int wally_descriptor_to_scriptpubkey( + const char *descriptor, + const char **key_name_array, + const char **key_value_array, + size_t array_len, + uint32_t derive_child_num, + uint32_t network, + uint32_t target_depth, + uint32_t target_index, + uint32_t flags, + unsigned char *script, + size_t script_len, + size_t *written); + +/** + * Create an address corresponding to a output descriptor. + * + * :param descriptor: Output descriptor. + * :param key_name_array: Array of key policy name string. + * :param key_value_array: Array of key mapped value string. + * :param array_len: Length of the array of key policy name. + * :param derive_child_num: Number of the derive path. + * :param network: Number of the network. (bitcoin regtest is set ``0xff``) + * :param flags: For future use. Must be 0. + * :param output: Destination for the resulting address string. + *| The string returned should be freed using `wally_free_string`. + */ +WALLY_CORE_API int wally_descriptor_to_address( + const char *descriptor, + const char **key_name_array, + const char **key_value_array, + size_t array_len, + uint32_t derive_child_num, + uint32_t network, + uint32_t flags, + char **output); + +/** + * Create addresses that corresponds to the derived range of a output descriptor. + * + * :param descriptor: Output descriptor. + * :param key_name_array: Array of key policy name string. + * :param key_value_array: Array of key mapped value string. + * :param array_len: Length of the array of key policy name. + * :param start_child_num: Number of the derive start path. + * :param end_child_num: Number of the derive end path. + * :param network: Number of the network. (bitcoin regtest is set ``0xff``) + * :param flags: For future use. Must be 0. + * :param addresses: Destination for the resulting addresses. + *| The string returned should be freed using `wally_free_descriptor_addresses`. + */ +WALLY_CORE_API int wally_descriptor_to_addresses( + const char *descriptor, + const char **key_name_array, + const char **key_value_array, + size_t array_len, + uint32_t start_child_num, + uint32_t end_child_num, + uint32_t network, + uint32_t flags, + struct wally_descriptor_addresses *addresses); + +/** + * Create an output descriptor checksum. + * + * :param descriptor: Output descriptor. + * :param key_name_array: Array of key policy name string. + * :param key_value_array: Array of key mapped value string. + * :param array_len: Length of the array of key policy name. + * :param flags: For future use. Must be 0. + * :param output: Destination for the resulting descriptor string. + */ +WALLY_CORE_API int wally_descriptor_create_checksum( + const char *descriptor, + const char **key_name_array, + const char **key_value_array, + size_t array_len, + uint32_t flags, + char **output); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBWALLY_CORE_DESCRIPTOR_H */ diff --git a/src/Makefile.am b/src/Makefile.am index 6c8ec51ee..3f0e0d991 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,6 +14,7 @@ include_HEADERS += $(top_srcdir)/include/wally_bip38.h include_HEADERS += $(top_srcdir)/include/wally_bip39.h include_HEADERS += $(top_srcdir)/include/wally_core.h include_HEADERS += $(top_srcdir)/include/wally_crypto.h +include_HEADERS += $(top_srcdir)/include/wally_descriptor.h include_HEADERS += $(top_srcdir)/include/wally_elements.h include_HEADERS += $(top_srcdir)/include/wally_psbt.h include_HEADERS += $(top_srcdir)/include/wally_script.h @@ -183,6 +184,7 @@ libwallycore_la_SOURCES = \ bip38.c \ bip39.c \ bech32.c \ + descriptor.c \ ecdh.c \ elements.c \ blech32.c \ @@ -214,6 +216,7 @@ libwallycore_la_INCLUDES = \ include/wally_bip39.h \ include/wally_core.h \ include/wally_crypto.h \ + include/wally_descriptor.h \ include/wally_elements.h \ include/wally_psbt.h \ include/wally_script.h \ @@ -271,6 +274,14 @@ test_tx_LDADD = $(lib_LTLIBRARIES) @CTEST_EXTRA_STATIC@ if PYTHON_MANYLINUX test_tx_LDADD += $(PYTHON_LIBS) endif +TESTS += test_descriptor +noinst_PROGRAMS += test_descriptor +test_descriptor_SOURCES = ctest/test_descriptor.c +test_descriptor_CFLAGS = -I$(top_srcdir)/include $(AM_CFLAGS) +test_descriptor_LDADD = $(lib_LTLIBRARIES) @CTEST_EXTRA_STATIC@ +if PYTHON_MANYLINUX +test_descriptor_LDADD += $(PYTHON_LIBS) +endif if BUILD_ELEMENTS TESTS += test_elements_tx noinst_PROGRAMS += test_elements_tx @@ -302,6 +313,7 @@ if RUN_PYTHON_TESTS $(AM_V_at)$(PYTHON_TEST) test/test_bip32.py $(AM_V_at)$(PYTHON_TEST) test/test_bip38.py $(AM_V_at)$(PYTHON_TEST) test/test_bip39.py + $(AM_V_at)$(PYTHON_TEST) test/test_descriptor.py $(AM_V_at)$(PYTHON_TEST) test/test_ecdh.py $(AM_V_at)$(PYTHON_TEST) test/test_hash.py $(AM_V_at)$(PYTHON_TEST) test/test_hex.py diff --git a/src/ctest/test_descriptor.c b/src/ctest/test_descriptor.c new file mode 100644 index 000000000..b292ca096 --- /dev/null +++ b/src/ctest/test_descriptor.c @@ -0,0 +1,1315 @@ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +/* + { + pubkey: '038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048', + privkey: 'cNha6ams8o6qokphL3XfcUTRs7ggweD3SWn7YXLtB3Rrm3QDNxD4' + },{ + pubkey: '03a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7', + privkey: 'cQbGCCA1P9aGWiyrGVXueofGJZmQAHBQhrrsX49rsExFKzeGTXT2' + },{ + pubkey: '03b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284', + privkey: 'cQezKD6V8dtqkLz1Mh6JHYiz1TsZBXyizTtzY1xm3pqdMsxJ6wXT' + },{ + pubkey: '04a238b0cbea14c9b3f59d0a586a82985f69af3da50579ed5971eefa41e6758ee7f1d77e4d673c6e7aac39759bb762d22259e27bf93572e9d5e363d5a64b6c062b', + privkey: 'bc2f39635ef2e24b4689345fb68c615987b6b0388fdffb57f907bd44445603a4' + } + */ + +struct wally_miniscript_ref_test { + const char *miniscript; + const char *scriptpubkey; +}; + +struct wally_miniscript_test { + const char *name; + const char *descriptor; + const char *scriptpubkey; + const char *miniscript; + const char *p2wsh_scriptpubkey; +}; + +struct wally_miniscript_taproot_test { + const char *miniscript; + const char *scriptpubkey; + uint32_t flags; +}; + +struct wally_descriptor_test { + const char *name; + const char *descriptor; + const char *scriptpubkey; + const uint32_t *bip32_index; + const char *checksum; +}; + +struct wally_descriptor_depth_test { + const char *name; + const char *descriptor; + const uint32_t depth; + const uint32_t index; + const char *scriptpubkey; +}; + +struct wally_descriptor_address_test { + const char *name; + const char *descriptor; + const uint32_t bip32_index; + const uint32_t network; + const char *address; +}; + +struct wally_descriptor_address_list_test { + const char *name; + const char *descriptor; + const uint32_t start_index; + const uint32_t end_index; + const uint32_t network; + const size_t address_list_num; + const char *address_list[30]; +}; + +struct wally_descriptor_err_test { + const char *name; + const char *descriptor; + const uint32_t network; +}; + +static const char *const g_miniscript_keyname_list[] = { + "key_1", + "key_2", + "key_3", + "key_likely", + "key_unlikely", + "key_user", + "key_service", + "key_local", + "key_remote", + "key_revocation", + "H", /* HASH160(key_local) */ +}; + +static const uint32_t g_miniscript_index_0 = 0; +static const uint32_t g_miniscript_index_16 = 0x10; + +static const char *const g_miniscript_keyvalue_list[] = { + "038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048", + "03a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7", + "03b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284", + "038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048", + "03a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7", + "038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048", + "03a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7", + "038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048", + "03a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7", + "03b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284", + "d0721279e70d39fb4aa409b52839a0056454e3b5", /* HASH160(key_local) */ +}; + +struct wally_miniscript_ref_test g_miniscript_ref_test_table[] = { + /* Randomly generated test set that covers the majority of type and node type combinations */ + {"lltvln:after(1231488000)", "6300676300676300670400046749b1926869516868"}, + {"uuj:and_v(v:multi(2,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a,025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),after(1231488000))", "6363829263522103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a21025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc52af0400046749b168670068670068"}, + {"or_b(un:multi(2,03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),al:older(16))", "63522103daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee872921024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c9752ae926700686b63006760b2686c9b"}, + {"j:and_v(vdv:after(1567547623),older(2016))", "829263766304e7e06e5db169686902e007b268"}, + {"t:and_v(vu:hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),v:sha256(ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5))", "6382012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876700686982012088a820ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc58851"}, + {"t:andor(multi(3,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),v:older(4194305),v:sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2))", "532102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a14602975562102e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd1353ae6482012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2886703010040b2696851"}, + {"or_d(multi(1,02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9),or_b(multi(3,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a),su:after(500000)))", "512102f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f951ae73645321022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a0121032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f2103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a53ae7c630320a107b16700689b68"}, + {"or_d(sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6),and_n(un:after(499999999),older(4194305)))", "82012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68773646304ff64cd1db19267006864006703010040b26868"}, + {"and_v(or_i(v:multi(2,02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb),v:multi(2,03e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)),sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68))", "63522102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee52103774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb52af67522103e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a21025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc52af6882012088a820d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c6887"}, + {"j:and_b(multi(2,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),s:or_i(older(1),older(4252898)))", "82926352210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c9752ae7c6351b26703e2e440b2689a68"}, + {"and_b(older(16),s:or_d(sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),n:after(1567547623)))", "60b27c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87736404e7e06e5db192689a"}, + {"j:and_v(v:hash160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))", "82926382012088a91420195b5a3d650c17f0f29f91c33f8f6335193d078882012088a82096de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c4787736460b26868"}, + {"and_b(hash256(32ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac),a:and_b(hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),a:older(1)))", "82012088aa2032ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac876b82012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876b51b26c9a6c9a"}, + {"thresh(2,multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),a:multi(1,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),ac:pk_k(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01))", "522103a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c721036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0052ae6b5121036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0051ae6c936b21022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01ac6c935287"}, + {"and_n(sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68),t:or_i(v:older(4252898),v:older(144)))", "82012088a820d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68876400676303e2e440b26967029000b269685168"}, + {"or_d(d:and_v(v:older(4252898),v:older(4252898)),sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6))", "766303e2e440b26903e2e440b26968736482012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68768"}, + {"c:and_v(or_c(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),v:multi(1,02c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db)),pk_k(03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764512102c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db51af682103acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbeac"}, + {"c:and_v(or_c(multi(2,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00,02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),v:ripemd160(1b0f3c404d12075c68c938f9f60ebea4f74941a0)),pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "5221036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a002102352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d552ae6482012088a6141b0f3c404d12075c68c938f9f60ebea4f74941a088682103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac"}, + {"and_v(andor(hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),v:hash256(939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735),v:older(50000)),after(499999999))", "82012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b2587640350c300b2696782012088aa20939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735886804ff64cd1db1"}, + {"andor(hash256(5f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040),j:and_v(v:hash160(3a2bff0da9d96868e66abc4427bea4691cf61ccd),older(4194305)),ripemd160(44d90e2d3714c8663b632fcf0f9d5f22192cc4c8))", "82012088aa205f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040876482012088a61444d90e2d3714c8663b632fcf0f9d5f22192cc4c8876782926382012088a9143a2bff0da9d96868e66abc4427bea4691cf61ccd8803010040b26868"}, + {"or_i(c:and_v(v:after(500000),pk_k(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),sha256(d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f946))", "630320a107b1692102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ac6782012088a820d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f9468768"}, + {"thresh(2,c:pk_h(025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc),s:sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),a:hash160(dd69735817e0e3f6f826a9238dc2e291184f0131))", "76a9145dedfbf9ea599dd4e3ca6a80b333c472fd0b3f6988ac7c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87936b82012088a914dd69735817e0e3f6f826a9238dc2e291184f0131876c935287"}, + {"and_n(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),uc:and_v(v:older(144),pk_k(03fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ce)))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764006763029000b2692103fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ceac67006868"}, + {"and_n(c:pk_k(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),and_b(l:older(4252898),a:older(16)))", "2103daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729ac64006763006703e2e440b2686b60b26c9a68"}, + {"c:or_i(and_v(v:older(16),pk_h(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)),pk_h(026a245bf6dc698504c89a20cfded60853152b695336c28063b61c65cbd269e6b4))", "6360b26976a9149fc5dbe5efdce10374a4dd4053c93af540211718886776a9142fbd32c8dd59ee7c17e66cb6ebea7e9846c3040f8868ac"}, + {"or_d(c:pk_h(02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),andor(c:pk_k(024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),older(2016),after(1567547623)))", "76a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac736421024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97ac6404e7e06e5db16702e007b26868"}, + {"c:andor(ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e),and_v(v:hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),pk_h(03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a)))", "82012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba876482012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b258876a914dd100be7d9aea5721158ebde6d6a1fd8fff93bb1886776a9149fc5dbe5efdce10374a4dd4053c93af5402117188868ac"}, + {"c:andor(u:ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),or_i(pk_h(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01),pk_h(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)))", "6382012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba87670068646376a9149652d86bedf43ad264362e6e6eba6eb764508127886776a914751e76e8199196d454941c45d1b3a323f1433bd688686776a91420d637c1a6404d2227f3561fdbaff5a680dba6488868ac"}, + {"c:or_i(andor(c:pk_h(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),pk_h(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01),pk_h(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),pk_k(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e))", "6376a914fcd35ddacad9f2d5be5e464639441c6065e6955d88ac6476a91406afd46bcdfd22ef94ac122aa11f241244a37ecc886776a9149652d86bedf43ad264362e6e6eba6eb7645081278868672102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e68ac"}, +}; + +struct wally_miniscript_test g_miniscript_test_table[] = { + { + "miniscript - A single key", + "c:pk_k(038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048)", + "21038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048ac", + "c:pk_k(key_1)", + "0020fa5bf4aae3ee617c6cce1976f6d7d285c359613ffeed481f1067f62bc0f54852" + }, + { + "miniscript - One of two keys (equally likely)", + "or_b(c:pk_k(038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048),sc:pk_k(03a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7))", + "21038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048ac7c2103a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7ac9b", + "or_b(c:pk_k(key_1),sc:pk_k(key_2))", + "002018a9df986ba10bcd8f503f495cab5fd00c9fb23c05143e65dbba49ef4d8a825f" + }, + { + "miniscript - A user and a 2FA service need to sign off, but after 90 days the user alone is enough", + "and_v(vc:pk_k(038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048),or_d(c:pk_k(03a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7),older(12960)))", + "21038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048ad2103a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7ac736402a032b268", + "and_v(vc:pk_k(key_user),or_d(c:pk_k(key_service),older(12960)))", + "00201264946c666958d9522f63dcdcfc85941bdd5b9308b1e6c68696857506f6cced" + }, + { + "miniscript - A 3-of-3 that turns into a 2-of-3 after 90 days", + "thresh(3,c:pk_k(038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048),sc:pk_k(03a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7),sc:pk_k(03b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284),sdv:older(12960))", + "21038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048ac7c2103a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7ac937c2103b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284ac937c766302a032b26968935387", + "thresh(3,c:pk_k(key_1),sc:pk_k(key_2),sc:pk_k(key_3),sdv:older(12960))", + "0020ab3551bec623130218a9ca5da0e3adb82b8569f91355a483653a49f2a2dd6e70" + }, + { + "miniscript - The BOLT #3 to_local policy", + "andor(c:pk_k(038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048),older(1008),c:pk_k(03b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284))", + "21038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048ac642103b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284ac6702f003b268", + "andor(c:pk_k(key_local),older(1008),c:pk_k(key_revocation))", + "0020052cf1e9c90e9a2883d890467a6a01837e21b3b755a743c9d96a2b6f8285d7c0" + }, + { + "miniscript - The BOLT #3 offered HTLC policy ", + "t:or_c(c:pk_k(03b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284),and_v(vc:pk_k(03a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7),or_c(c:pk_k(038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048),v:hash160(d0721279e70d39fb4aa409b52839a0056454e3b5))))", + "2103b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284ac642103a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7ad21038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048ac6482012088a914d0721279e70d39fb4aa409b52839a0056454e3b588686851", + "t:or_c(c:pk_k(key_revocation),and_v(vc:pk_k(key_remote),or_c(c:pk_k(key_local),v:hash160(H))))", + "0020f9259363db0facc7b97ab2c0294c4f21a0cd56b01bb54ecaaa5899012aae1bc2" + }, + { + "miniscript - The BOLT #3 received HTLC policy ", + "andor(c:pk_k(03a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7),or_i(and_v(vc:pk_h(038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048),hash160(d0721279e70d39fb4aa409b52839a0056454e3b5)),older(1008)),c:pk_k(03b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284))", + "2103a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7ac642103b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284ac676376a914d0721279e70d39fb4aa409b52839a0056454e3b588ad82012088a914d0721279e70d39fb4aa409b52839a0056454e3b5876702f003b26868", + "andor(c:pk_k(key_remote),or_i(and_v(vc:pk_h(key_local),hash160(H)),older(1008)),c:pk_k(key_revocation))", + "002087515e0059c345eaa5cccbaa9cd16ad1266e7a69e350db82d8e1f33c86285303" + }, + { + "miniscript(2) - A single key", + "pk(038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048)", + "21038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048ac", + "pk(key_1)", + "0020fa5bf4aae3ee617c6cce1976f6d7d285c359613ffeed481f1067f62bc0f54852" + }, + { + "miniscript(2) - One of two keys (equally likely)", + "or_b(pk(038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048),s:pk(03a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7))", + "21038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048ac7c2103a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7ac9b", + "or_b(pk(key_1),s:pk(key_2))", + "002018a9df986ba10bcd8f503f495cab5fd00c9fb23c05143e65dbba49ef4d8a825f" + }, + { + "miniscript(2) - A user and a 2FA service need to sign off, but after 90 days the user alone is enough", + "and_v(v:pk(038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048),or_d(pk(03a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7),older(12960)))", + "21038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048ad2103a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7ac736402a032b268", + "and_v(v:pk(key_user),or_d(pk(key_service),older(12960)))", + "00201264946c666958d9522f63dcdcfc85941bdd5b9308b1e6c68696857506f6cced" + }, + { + "miniscript(2) - A 3-of-3 that turns into a 2-of-3 after 90 days", + "thresh(3,pk(038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048),s:pk(03a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7),s:pk(03b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284),sdv:older(12960))", + "21038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048ac7c2103a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7ac937c2103b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284ac937c766302a032b26968935387", + "thresh(3,pk(key_1),s:pk(key_2),s:pk(key_3),sdv:older(12960))", + "0020ab3551bec623130218a9ca5da0e3adb82b8569f91355a483653a49f2a2dd6e70" + }, + { + "miniscript(2) - The BOLT #3 to_local policy", + "andor(pk(038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048),older(1008),pk(03b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284))", + "21038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048ac642103b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284ac6702f003b268", + "andor(pk(key_local),older(1008),pk(key_revocation))", + "0020052cf1e9c90e9a2883d890467a6a01837e21b3b755a743c9d96a2b6f8285d7c0" + }, + { + "miniscript(2) - The BOLT #3 offered HTLC policy ", + "t:or_c(pk(03b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284),and_v(v:pk(03a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7),or_c(pk(038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048),v:hash160(d0721279e70d39fb4aa409b52839a0056454e3b5))))", + "2103b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284ac642103a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7ad21038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048ac6482012088a914d0721279e70d39fb4aa409b52839a0056454e3b588686851", + "t:or_c(pk(key_revocation),and_v(v:pk(key_remote),or_c(pk(key_local),v:hash160(H))))", + "0020f9259363db0facc7b97ab2c0294c4f21a0cd56b01bb54ecaaa5899012aae1bc2" + }, + { + "miniscript(2) - The BOLT #3 received HTLC policy ", + "andor(pk(03a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7),or_i(and_v(v:pkh(038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048),hash160(d0721279e70d39fb4aa409b52839a0056454e3b5)),older(1008)),pk(03b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284))", + "2103a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7ac642103b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284ac676376a914d0721279e70d39fb4aa409b52839a0056454e3b588ad82012088a914d0721279e70d39fb4aa409b52839a0056454e3b5876702f003b26868", + "andor(pk(key_remote),or_i(and_v(v:pkh(key_local),hash160(H)),older(1008)),pk(key_revocation))", + "002087515e0059c345eaa5cccbaa9cd16ad1266e7a69e350db82d8e1f33c86285303" + }, + { + "miniscript(2) - The BOLT #3 received HTLC policy ", + "andor(pk(03a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7),or_i(and_v(v:pkh(038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048),hash160(d0721279e70d39fb4aa409b52839a0056454e3b5)),older(1008)),pk(03b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284))", + "2103a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7ac642103b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284ac676376a914d0721279e70d39fb4aa409b52839a0056454e3b588ad82012088a914d0721279e70d39fb4aa409b52839a0056454e3b5876702f003b26868", + "andor(pk(key_remote),or_i(and_v(v:pkh(key_local),hash160(H)),older(1008)),pk(key_revocation))", + "002087515e0059c345eaa5cccbaa9cd16ad1266e7a69e350db82d8e1f33c86285303" + }, +}; + +struct wally_miniscript_taproot_test g_miniscript_taproot_test_table[] = { + { + "c:pk_k(daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729)", + "20daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729ac", + WALLY_MINISCRIPT_TAPSCRIPT + }, + { + "c:pk_k([bd16bee5/0]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/1)", + "208c6f5956c3cc7251d483fc683fa06b22d4e2ddc7496a2590acee36c4a313f816ac", + WALLY_MINISCRIPT_TAPSCRIPT + }, + { + "c:pk_k(L1AAHuEC7XuDM7pJ7yHLEqYK1QspMo8n1kgxyZVdgvEpVC1rkUrM)", + "20ff7e7b1d3c4ba385cb1f2e6423bf30c96fb5007e7917b09ec1b6c965ef644d13ac", + WALLY_MINISCRIPT_TAPSCRIPT + }, +}; + +struct wally_descriptor_test g_descriptor_test_table[] = { + { + "descriptor - p2pk", + "pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)#gn28ywm7", + "210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac", + NULL, + "gn28ywm7" + },{ + "descriptor - p2pkh", + "pkh(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)", + "76a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac", + NULL, + "8fhd9pwu" + },{ + "descriptor - p2wpkh", + "wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)", + "00147dd65592d0ab2fe0d0257d571abf032cd9db93dc", + NULL, + "8zl0zxma" + },{ + "descriptor - p2sh-p2wpkh", + "sh(wpkh(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", + "a914cc6ffbc0bf31af759451068f90ba7a0272b6b33287", + NULL, + "qkrrc7je" + },{ + "descriptor - combo(p2wpkh)", + "combo(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)", + "0014751e76e8199196d454941c45d1b3a323f1433bd6", + NULL, + "lq9sf04s" + },{ + "descriptor - combo(p2pkh)", + "combo(04a238b0cbea14c9b3f59d0a586a82985f69af3da50579ed5971eefa41e6758ee7f1d77e4d673c6e7aac39759bb762d22259e27bf93572e9d5e363d5a64b6c062b)", + "76a91448cb866ee3edb295e4cfeb3da65b4003ab9fa6a288ac", + NULL, + "r3wj6k68" + },{ + "descriptor - p2sh-p2wsh", + "sh(wsh(pkh(02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13)))", + "a91455e8d5e8ee4f3604aba23c71c2684fa0a56a3a1287", + NULL, + "2wtr0ej5" + },{ + "descriptor - multisig", + "multi(1,022f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)", + "5121022f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe421025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc52ae", + NULL, + "hzhjw406" + },{ + "descriptor - p2sh-multi", + "sh(multi(2,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))", + "a914a6a8b030a38762f4c1f5cbe387b61a3c5da5cd2687", + NULL, + "y9zthqta" + },{ + "descriptor - p2sh-sortedmulti", + "sh(sortedmulti(2,03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01))", + "a914a6a8b030a38762f4c1f5cbe387b61a3c5da5cd2687", + NULL, + "qwx6n9lh" + },{ + "descriptor - p2wsh-multi", + "wsh(multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a))", + "0020773d709598b76c4e3b575c08aad40658963f9322affc0f8c28d1d9a68d0c944a", + NULL, + "en3tu306" + },{ + "descriptor - p2sh-p2wsh-multi", + "sh(wsh(multi(1,03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8,03499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)))", + "a914aec509e284f909f769bb7dda299a717c87cc97ac87", + NULL, + "ks05yr6p" + },{ + "descriptor - p2pk-xpub", + "pk(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8)", + "210339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2ac", + NULL, + "axav5m0j" + },{ + "descriptor - p2pkh-xpub-derive", + "pkh(xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw/1/2)", + "76a914f833c08f02389c451ae35ec797fccf7f396616bf88ac", + NULL, + "kczqajcv" + },{ + "descriptor - p2pkh-parent-derive", + "pkh([d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*)", + "76a914d234825a563de8b4fd31d2b30f60b1e60fe57ee788ac", + &g_miniscript_index_16, + "ml40v0wf" + },{ + "descriptor - p2wsh-multi-xpub", + "wsh(multi(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/*))", + "00204616bb4e66d0b540b480c5b26c619385c4c2b83ed79f4f3eab09b01745443a55", + &g_miniscript_index_16, + "t2zpj2eu" + },{ + "descriptor - p2wsh-sortedmulti-xpub", + "wsh(sortedmulti(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/*))", + "002002aeee9c3773dfecfe6215f2eea2908776b1232513a700e1ee516b634883ecb0", + &g_miniscript_index_16, + "v66cvalc" + },{ + "descriptor - addr-btc-legacy-testnet", + "addr(moUfpGiXWcFd5ueRn3988VDqRSkB5NrEmW)", + "76a91457526b1a1534d4bde788253281649fc2e91dc70b88ac", + NULL, + "9amhxcar" + },{ + "descriptor - addr-btc-segwit-mainnet", + "addr(bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3)", + "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262", + NULL, + "8kzm8txf" + },{ + "descriptor - raw-checksum", + "raw(6a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e)#zf2avljj", + "6a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e", + NULL, + "zf2avljj" + },{ + "descriptor - p2pkh-xpriv", + "pkh(xprvA2YKGLieCs6cWCiczALiH1jzk3VCCS5M1pGQfWPkamCdR9UpBgE2Gb8AKAyVjKHkz8v37avcfRjdcnP19dVAmZrvZQfvTcXXSAiFNQ6tTtU/1h/2)", + "76a914b28d12ab72a51b10114b17ce76b536265194e1fb88ac", + NULL, + "wghlxksl" + },{ + "descriptor - p2pkh-privkey-wif mainnet", + "pkh(L1AAHuEC7XuDM7pJ7yHLEqYK1QspMo8n1kgxyZVdgvEpVC1rkUrM)", + "76a91492ed3283cfb01caec1163aefba29caf1182f478e88ac", + NULL, + "qm00tjwh" + },{ + "descriptor - p2pkh-privkey-wif testnet uncompress", + "pkh(936Xapr4wpeuiKToGeXtEcsVJAfE6ze8KUEb2UQu72rzBQsMZdX)", + "76a91477b6f27ac523d8b9aa8abcfc94fd536493202ae088ac", + NULL, + "9gv5p2gj" + },{ + "descriptor - A single key", + "wsh(c:pk_k(key_1))", + "0020fa5bf4aae3ee617c6cce1976f6d7d285c359613ffeed481f1067f62bc0f54852", + NULL, + "k05wwvdw" + },{ + "descriptor - One of two keys (equally likely)", + "wsh(or_b(c:pk_k(key_1),sc:pk_k(key_2)))", + "002018a9df986ba10bcd8f503f495cab5fd00c9fb23c05143e65dbba49ef4d8a825f", + NULL, + "4dxelu43" + },{ + "descriptor - A user and a 2FA service need to sign off, but after 90 days the user alone is enough", + "wsh(and_v(vc:pk_k(key_user),or_d(c:pk_k(key_service),older(12960))))", + "00201264946c666958d9522f63dcdcfc85941bdd5b9308b1e6c68696857506f6cced", + NULL, + "7sxzh689" + },{ + "descriptor - A 3-of-3 that turns into a 2-of-3 after 90 days", + "wsh(thresh(3,c:pk_k(key_1),sc:pk_k(key_2),sc:pk_k(key_3),sdv:older(12960)))", + "0020ab3551bec623130218a9ca5da0e3adb82b8569f91355a483653a49f2a2dd6e70", + NULL, + "vvsamau0" + },{ + "descriptor - The BOLT #3 to_local policy", + "wsh(andor(c:pk_k(key_local),older(1008),c:pk_k(key_revocation)))", + "0020052cf1e9c90e9a2883d890467a6a01837e21b3b755a743c9d96a2b6f8285d7c0", + NULL, + "0j0vrece" + },{ + "descriptor - The BOLT #3 offered HTLC policy", + "wsh(t:or_c(c:pk_k(key_revocation),and_v(vc:pk_k(key_remote),or_c(c:pk_k(key_local),v:hash160(H)))))", + "0020f9259363db0facc7b97ab2c0294c4f21a0cd56b01bb54ecaaa5899012aae1bc2", + NULL, + "v4t6pzy6" + },{ + "descriptor - The BOLT #3 received HTLC policy", + "wsh(andor(c:pk_k(key_remote),or_i(and_v(vc:pk_h(key_local),hash160(H)),older(1008)),c:pk_k(key_revocation)))", + "002087515e0059c345eaa5cccbaa9cd16ad1266e7a69e350db82d8e1f33c86285303", + NULL, + "8rap84p2" + },{ + "descriptor - derive key index 0.", + "wsh(multi(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/*))", + "002064969d8cdca2aa0bb72cfe88427612878db98a5f07f9a7ec6ec87b85e9f9208b", + &g_miniscript_index_0, + "t2zpj2eu" + }, +}; + +struct wally_descriptor_depth_test g_descriptor_depth_test_table[] = { + { + "descriptor depth - p2sh-p2wsh-multi (p2sh-p2wsh)", + "sh(wsh(multi(1,03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8,03499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)))", + 0, + 0, + "a914aec509e284f909f769bb7dda299a717c87cc97ac87" + }, + { + "descriptor depth - p2sh-p2wsh-multi (p2wsh)", + "sh(wsh(multi(1,03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8,03499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)))", + 1, + 0, + "0020ef8110fa7ddefb3e2d02b2c1b1480389b4bc93f606281570cfc20dba18066aee" + }, + { + "descriptor depth - p2sh-p2wsh-multi (multi)", + "sh(wsh(multi(1,03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8,03499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)))", + 2, + 0, + "512103f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa82103499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e42102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e53ae" + }, + { + "descriptor depth - p2sh-p2wsh-multi (multi[0])", + "sh(wsh(multi(1,03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8,03499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)))", + 3, + 0, + "51" + }, + { + "descriptor depth - p2sh-p2wsh-multi (multi[1])", + "sh(wsh(multi(1,03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8,03499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)))", + 3, + 1, + "03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8" + }, + { + "descriptor depth - p2sh-p2wsh-multi (multi[2])", + "sh(wsh(multi(1,03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8,03499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)))", + 3, + 2, + "03499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4" + }, + { + "descriptor depth - p2sh-p2wsh-multi (multi[3])", + "sh(wsh(multi(1,03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8,03499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)))", + 3, + 3, + "02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e" + }, +}; + +struct wally_descriptor_address_test g_descriptor_address_test_table[] = { + { + "address - p2pkh - mainnet", + "pkh(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)", + 0, + WALLY_NETWORK_BITCOIN_MAINNET, + "1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP" + },{ + "address - p2pkh - testnet", + "pkh(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)", + 0, + WALLY_NETWORK_BITCOIN_TESTNET, + "mg8Jz5776UdyiYcBb9Z873NTozEiADRW5H" + },{ + "address - p2pkh - regtest", + "pkh(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)", + 0, + WALLY_NETWORK_BITCOIN_REGTEST, + "mg8Jz5776UdyiYcBb9Z873NTozEiADRW5H" + },{ + "address - p2wpkh - mainnet", + "wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)", + 0, + WALLY_NETWORK_BITCOIN_MAINNET, + "bc1q0ht9tyks4vh7p5p904t340cr9nvahy7u3re7zg" + },{ + "address - p2wpkh - testnet", + "wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)", + 0, + WALLY_NETWORK_BITCOIN_TESTNET, + "tb1q0ht9tyks4vh7p5p904t340cr9nvahy7um9zdem" + },{ + "address - p2wpkh - regtest", + "wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)", + 0, + WALLY_NETWORK_BITCOIN_REGTEST, + "bcrt1q0ht9tyks4vh7p5p904t340cr9nvahy7uevmqwj" + },{ + "address - p2sh-p2wpkh - mainnet", + "sh(wpkh(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", + 0, + WALLY_NETWORK_BITCOIN_MAINNET, + "3LKyvRN6SmYXGBNn8fcQvYxW9MGKtwcinN" + },{ + "address - p2sh-p2wpkh - liquidv1", + "sh(wpkh(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", + 0, + WALLY_NETWORK_LIQUID, + "H1pVQ7VtauJK4v7ixvwFQpDFYW2Q6eiPVx" + },{ + "address - p2sh-p2wpkh - liquidregtest", + "sh(wpkh(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", + 0, + WALLY_NETWORK_LIQUID_REGTEST, + "XVzCr2EG9PyrWX8qr2visL1aCfJMhGTZyS" + },{ + "address - p2sh-p2wsh - mainnet", + "sh(wsh(pkh(02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13)))", + 0, + WALLY_NETWORK_BITCOIN_MAINNET, + "39XGHYpYmJV9sGFoGHZeU2rLkY6r1MJ6C1" + },{ + "address - p2sh-p2wsh - liquidv1", + "sh(wsh(pkh(02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13)))", + 0, + WALLY_NETWORK_LIQUID, + "Gq1mmExLuSEwfzzk6YtUxJ769grv6T5Tak" + },{ + "address - p2wsh-multi - mainnet", + "wsh(multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a))", + 0, + WALLY_NETWORK_BITCOIN_MAINNET, + "bc1qwu7hp9vckakyuw6htsy244qxtztrlyez4l7qlrpg68v6drgvj39qn4zazc" + },{ + "address - p2wsh-multi - testnet", + "wsh(multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a))", + 0, + WALLY_NETWORK_BITCOIN_TESTNET, + "tb1qwu7hp9vckakyuw6htsy244qxtztrlyez4l7qlrpg68v6drgvj39qya5jch" + },{ + "address - p2wsh-multi - regtest", + "wsh(multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a))", + 0, + WALLY_NETWORK_BITCOIN_REGTEST, + "bcrt1qwu7hp9vckakyuw6htsy244qxtztrlyez4l7qlrpg68v6drgvj39qfy75dd" + },{ + "address - p2wsh-multi - liquidv1", + "wsh(multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a))", + 0, + WALLY_NETWORK_LIQUID, + "ex1qwu7hp9vckakyuw6htsy244qxtztrlyez4l7qlrpg68v6drgvj39q06fgz7" + },{ + "address - p2wsh-multi - liquidregtest", + "wsh(multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a))", + 0, + WALLY_NETWORK_LIQUID_REGTEST, + "ert1qwu7hp9vckakyuw6htsy244qxtztrlyez4l7qlrpg68v6drgvj39qchk2yf" + },{ + "address - p2pkh-xpub-derive", + "pkh(xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw/1/2)", + 0, + WALLY_NETWORK_BITCOIN_MAINNET, + "1PdNaNxbyQvHW5QHuAZenMGVHrrRaJuZDJ" + },{ + "address - p2pkh-parent-derive", + "pkh([d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*)", + 0, + WALLY_NETWORK_BITCOIN_MAINNET, + "14qCH92HCyDDBFFZdhDt1WMfrMDYnBFYMF" + },{ + "address - p2wsh-multi-xpub", + "wsh(multi(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/*))", + 0, + WALLY_NETWORK_BITCOIN_MAINNET, + "bc1qvjtfmrxu524qhdevl6yyyasjs7xmnzjlqlu60mrwepact60eyz9s9xjw0c" + },{ + "address - p2wsh-sortedmulti-xpub", + "wsh(sortedmulti(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/*))", + 0, + WALLY_NETWORK_BITCOIN_MAINNET, + "bc1qvjtfmrxu524qhdevl6yyyasjs7xmnzjlqlu60mrwepact60eyz9s9xjw0c" + },{ + "address - addr-btc-legacy-testnet", + "addr(moUfpGiXWcFd5ueRn3988VDqRSkB5NrEmW)", + 0, + WALLY_NETWORK_BITCOIN_TESTNET, + "moUfpGiXWcFd5ueRn3988VDqRSkB5NrEmW" + },{ + "address - addr-btc-segwit-mainnet", + "addr(bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3)", + 0, + WALLY_NETWORK_BITCOIN_MAINNET, + "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3" + },{ + "address - p2pkh-xpriv", + "pkh(xprvA2YKGLieCs6cWCiczALiH1jzk3VCCS5M1pGQfWPkamCdR9UpBgE2Gb8AKAyVjKHkz8v37avcfRjdcnP19dVAmZrvZQfvTcXXSAiFNQ6tTtU/1h/2)", + 0, + WALLY_NETWORK_BITCOIN_MAINNET, + "1HH6H4km128m4NsJMNVN2qqCHukbEhgU3V" + },{ + "address - A single key", + "wsh(c:pk_k(key_1))", + 0, + WALLY_NETWORK_BITCOIN_MAINNET, + "bc1qlfdlf2hraeshcmxwr9m0d47jshp4jcfllmk5s8csvlmzhs84fpfqa6ufv5" + },{ + "address - One of two keys (equally likely)", + "wsh(or_b(c:pk_k(key_1),sc:pk_k(key_2)))", + 0, + WALLY_NETWORK_BITCOIN_MAINNET, + "bc1qrz5alxrt5y9umr6s8ay4e26l6qxflv3uq52ruewmhfy77nv2sf0spz2em3" + },{ + "address - A user and a 2FA service need to sign off, but after 90 days the user alone is enough", + "wsh(and_v(vc:pk_k(key_user),or_d(c:pk_k(key_service),older(12960))))", + 0, + WALLY_NETWORK_BITCOIN_MAINNET, + "bc1qzfjfgmrxd9vdj530v0wdely9jsda6kunpzc7d35xj6zh2phkenkstn6ur7" + },{ + "address - A 3-of-3 that turns into a 2-of-3 after 90 days", + "wsh(thresh(3,c:pk_k(key_1),sc:pk_k(key_2),sc:pk_k(key_3),sdv:older(12960)))", + 0, + WALLY_NETWORK_BITCOIN_MAINNET, + "bc1q4v64r0kxyvfsyx9fefw6pcadhq4c260ezd26fqm98fyl9gkadecqy9uufs" + },{ + "address - The BOLT #3 to_local policy", + "wsh(andor(c:pk_k(key_local),older(1008),c:pk_k(key_revocation)))", + 0, + WALLY_NETWORK_BITCOIN_MAINNET, + "bc1qq5k0r6wfp6dz3q7cjpr856spsdlzrvah2kn58jwedg4klq596lqq90rr7h" + },{ + "address - The BOLT #3 offered HTLC policy", + "wsh(t:or_c(c:pk_k(key_revocation),and_v(vc:pk_k(key_remote),or_c(c:pk_k(key_local),v:hash160(H)))))", + 0, + WALLY_NETWORK_BITCOIN_MAINNET, + "bc1qlyjexc7mp7kv0wt6ktqzjnz0yxsv644srw65aj42tzvsz24wr0pqc6enkg" + },{ + "address - The BOLT #3 received HTLC policy", + "wsh(andor(c:pk_k(key_remote),or_i(and_v(vc:pk_h(key_local),hash160(H)),older(1008)),c:pk_k(key_revocation)))", + 0, + WALLY_NETWORK_BITCOIN_MAINNET, + "bc1qsag4uqzecdz74fwvew4fe5t26ynxu7nfudgdhqkcu8enep3g2vpsvp0wl0" + }, +}; + +struct wally_descriptor_address_list_test g_descriptor_addresses_test_table[] = { + { + "address list - p2wsh multisig (0-29)", + "wsh(multi(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/*))#t2zpj2eu", + 0, + 29, + WALLY_NETWORK_BITCOIN_MAINNET, + 30, + { + "bc1qvjtfmrxu524qhdevl6yyyasjs7xmnzjlqlu60mrwepact60eyz9s9xjw0c", + "bc1qp6rfclasvmwys7w7j4svgc2mrujq9m73s5shpw4e799hwkdcqlcsj464fw", + "bc1qsflxzyj2f2evshspl9n5n745swcvs5k7p5t8qdww5unxpjwdvw5qx53ms4", + "bc1qmhmj2mswyvyj4az32mzujccvd4dgr8s0lfzaum4n4uazeqc7xxvsr7e28n", + "bc1qjeu2wa5jwvs90tv9t9xz99njnv3we3ux04fn7glw3vqsk4ewuaaq9kdc9t", + "bc1qc6626sa08a4ktk3nqjrr65qytt9k273u24mfy2ld004g76jzxmdqjgpm2c", + "bc1qwlq7jjqcklrcqypvdndjx0fyrudgrymm67gcx3e09sekgs28u47smq0lx5", + "bc1qx8qq9k2mtqarugg3ctcsm2um22ahmq5uttrecy5ufku0ukfgpwrs7epn38", + "bc1qgrs4qzvw4aat2k38fvmrqf3ucaanqz2wxe5yy5cewwmqn06evxgq02wv43", + "bc1qnkpr4y7fp7jwad3gfngczwsv9069rq96cl7lpq4h9j3eng9mwjzsssr520", + "bc1q7yzadku3kxs855wgjxnyr2nk3e44ed75p07lzhnj53ynpczg78nq0leae5", + "bc1qpg9ag0ugqeucujyagca0n3httpgrgcsxftfgpymvmdeuyyejq9ks79c99t", + "bc1qt2sv92tuklq28hptplvq7v75mmc8h6a0ynd7vd7y0h07mr8uzf5seh30gh", + "bc1qdyfk0c5ksrxg6klz93acchg0xvavduzv3g4zj02fa3tm2yfy445q27zmar", + "bc1qrpfz6zpargqu9s2qy0ef9uk82x6fcg6jfwjhxdaewgj880nxj2rqt0hwcm", + "bc1qz6l0ar69xhk209nfdna68fkkg9tqp7pz7eq8mmu6hf5lvpltfx9slc9y6y", + "bc1qgcttknnx6z65pdyqckexccvnshzv9wp76705704tpxcpw32y8f2suf5fx8", + "bc1q0pauhlw2y4nyc2hud7dsmtc97k6kc30nz5u05dt6stahrfwy68tsnvl7l6", + "bc1qhgv6v7jgxxpf0cpzxd9zga52mx3c5xrnkvchk35ypavesumh8yqscvxrjh", + "bc1qrshvtv8ldqpdtv4z9z8fsah3plkl57drk7d8xgasgwj6puxpcxessp57hv", + "bc1qma56gu8mxywqjpeh56cwltmaddrtvyxec4ppdx4j733j8wtva09qnldwgs", + "bc1qj25wzn56y79x6tm67hpwr9d8vew87nk8asgwcc8mp53g4wh6hr9s2lh8nn", + "bc1q2ct0r07txjd32gh5c0cwg59ml0ahrzg07q3cm5naykdzdstmxhmqe8rtdu", + "bc1qn3n488yufhn2zfxtu4c7cqrmasqslrkmdyh7jen3yx8lj9z4cdfq03v349", + "bc1q89u4zs3vxyyznzzp99w8n8w7rh6hr4z3nvvtvkhyzkkqsgppvv8sgq0hqh", + "bc1q588dgge2vx0azcslfktlpeehqlh6y34hg6ur3rluxmkkm28f69dswj664f", + "bc1qv9eul0xtc8pg0sheuxp5ve9z7kl95j00efdxts8ae7ls7utcl4mq67jgqp", + "bc1qygswuelpc3rcuvzmempn0ku9h35fcpnc6sjd6h6exq4zx9zvxmrqz2eacm", + "bc1q2t74yd2ec7qx4j5xe9pj2y522whj3lz4lmhsxeasu8z00ggapgnqjxvlnk", + "bc1qp0rlvd76cmsv9ls5jv9az8kmra44rzgm5mz8008ypyfjayk70r8qwrg6c6", + }, + }, + { + "address list - p2wsh multisig (30-40)", + "wsh(multi(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/*))#t2zpj2eu", + 30, + 40, + WALLY_NETWORK_BITCOIN_MAINNET, + 11, + { + "bc1qz7ymgzfyx5x0qk04e0je54zwlh8mcshzwdmyd72jpgp33zkl3ekqxl6xuc", + "bc1qt07wnht6j90aczg7e7wsvnpzxveueyud34a90d99phm7apesvp0sw63ceh", + "bc1qwwl8fkywdhpn2xh8k95qglhkrjlt7xp60nahvc6yderj53wg79rs8kdfrv", + "bc1qxu7g60rcjlfulna079ccmta7ytazck82vwth3hktqeey2e5vh4lqp4s0a3", + "bc1q8v89njuqn66w7elpxjy79j2fpksnafje2xs0l268typfm553hwcqsw9wza", + "bc1qn66uht0ndvdw6nna8pm8nhjhulrp8yq84rcarkfr3u5nprdzyq0sx3k9g6", + "bc1q3em7pyxvyte20n5mx4yeswkfq7vkj77xty06vu5gk47z7tews48q39g324", + "bc1qytjx24vzm7q5munv9yn3j7ltg23q86sqxnzunhhvsrx5hrnu47rsplzqux", + "bc1q283cq3dknnypqzjdtkhx3mjq7ncex5snfjpcl0vuq5k8v9nmcr8sxfdfr2", + "bc1qqdte9nnnam9zpgg5zfttyw7hmgh0secxnj6ukrq20c60fcjx7lhqv6am95", + "bc1qd6ffgpayzpywa6hps0c65xuur5letl9hdy3pv5y40t8p9nrjpdtqqkan7a" + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + }, + }, +}; + +struct wally_descriptor_err_test g_descriptor_err_test_table[] = { + { + "descriptor errchk - invalid checksum", + "wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)#8rap84p2", + WALLY_NETWORK_BITCOIN_MAINNET + },{ + "descriptor errchk - privkey - unmatch network1", + "wpkh(cSMSHUGbEiZQUXVw9zA33yT3m8fgC27rn2XEGZJupwCpsRS3rAYa)", + WALLY_NETWORK_BITCOIN_MAINNET + },{ + "descriptor errchk - privkey - unmatch network2", + "wpkh(cSMSHUGbEiZQUXVw9zA33yT3m8fgC27rn2XEGZJupwCpsRS3rAYa)", + WALLY_NETWORK_LIQUID + },{ + "descriptor errchk - privkey - unmatch network3", + "wpkh(L4gLCkRn5VfJsDSWsrrC57kqVgq6Z2sjeRELmim5zzAgJisysh17)", + WALLY_NETWORK_BITCOIN_TESTNET + },{ + "descriptor errchk - privkey - unmatch network4", + "wpkh(L4gLCkRn5VfJsDSWsrrC57kqVgq6Z2sjeRELmim5zzAgJisysh17)", + WALLY_NETWORK_BITCOIN_REGTEST + },{ + "descriptor errchk - privkey - unmatch network5", + "wpkh(L4gLCkRn5VfJsDSWsrrC57kqVgq6Z2sjeRELmim5zzAgJisysh17)", + WALLY_NETWORK_LIQUID_REGTEST + },{ + "descriptor errchk - xpubkey - unmatch network1", + "wpkh(tpubD6NzVbkrYhZ4XJDrzRvuxHEyQaPd1mwwdDofEJwekX18tAdsqeKfxss79AJzg1431FybXg5rfpTrJF4iAhyR7RubberdzEQXiRmXGADH2eA)", + WALLY_NETWORK_BITCOIN_MAINNET + },{ + "descriptor errchk - xpubkey - unmatch network2", + "wpkh(tpubD6NzVbkrYhZ4XJDrzRvuxHEyQaPd1mwwdDofEJwekX18tAdsqeKfxss79AJzg1431FybXg5rfpTrJF4iAhyR7RubberdzEQXiRmXGADH2eA)", + WALLY_NETWORK_LIQUID + },{ + "descriptor errchk - xpubkey - unmatch network3", + "wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB)", + WALLY_NETWORK_BITCOIN_TESTNET + },{ + "descriptor errchk - xpubkey - unmatch network4", + "wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB)", + WALLY_NETWORK_BITCOIN_REGTEST + },{ + "descriptor errchk - xpubkey - unmatch network5", + "wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB)", + WALLY_NETWORK_LIQUID_REGTEST + },{ + "descriptor errchk - xprivkey - unmatch network1", + "wpkh(tprv8jDG3g2yc8vh71x9ejCDSfMz4AuQRx7MMNBXXvpD4jh7CkDuB3ZmnLVcEM99jgg5MaSp7gYNpnKS5dvkGqq7ad8X63tE7yFaMGTfp6gD54p)", + WALLY_NETWORK_BITCOIN_MAINNET + },{ + "descriptor errchk - xprivkey - unmatch network2", + "wpkh(tprv8jDG3g2yc8vh71x9ejCDSfMz4AuQRx7MMNBXXvpD4jh7CkDuB3ZmnLVcEM99jgg5MaSp7gYNpnKS5dvkGqq7ad8X63tE7yFaMGTfp6gD54p)", + WALLY_NETWORK_LIQUID + },{ + "descriptor errchk - xprivkey - unmatch network3", + "wpkh(xprvA2YKGLieCs6cWCiczALiH1jzk3VCCS5M1pGQfWPkamCdR9UpBgE2Gb8AKAyVjKHkz8v37avcfRjdcnP19dVAmZrvZQfvTcXXSAiFNQ6tTtU)", + WALLY_NETWORK_BITCOIN_TESTNET + },{ + "descriptor errchk - xprivkey - unmatch network4", + "wpkh(xprvA2YKGLieCs6cWCiczALiH1jzk3VCCS5M1pGQfWPkamCdR9UpBgE2Gb8AKAyVjKHkz8v37avcfRjdcnP19dVAmZrvZQfvTcXXSAiFNQ6tTtU)", + WALLY_NETWORK_BITCOIN_REGTEST + },{ + "descriptor errchk - xprivkey - unmatch network5", + "wpkh(xprvA2YKGLieCs6cWCiczALiH1jzk3VCCS5M1pGQfWPkamCdR9UpBgE2Gb8AKAyVjKHkz8v37avcfRjdcnP19dVAmZrvZQfvTcXXSAiFNQ6tTtU)", + WALLY_NETWORK_LIQUID_REGTEST + },{ + "descriptor errchk - addr - unmatch network1", + "addr(bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3)", + WALLY_NETWORK_BITCOIN_TESTNET + },{ + "descriptor errchk - addr - unmatch network2", + "addr(ex1qwu7hp9vckakyuw6htsy244qxtztrlyez4l7qlrpg68v6drgvj39q06fgz7)", + WALLY_NETWORK_LIQUID_REGTEST + }, +}; + +struct wally_descriptor_err_test g_address_err_test_table[] = { + { + "address errchk - invalid network", + "wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)", + 0xf0 + },{ + "address errchk - addr - unmatch network1", + "addr(bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3)", + WALLY_NETWORK_BITCOIN_TESTNET + },{ + "address errchk - addr - unmatch network2", + "addr(ex1qwu7hp9vckakyuw6htsy244qxtztrlyez4l7qlrpg68v6drgvj39q06fgz7)", + WALLY_NETWORK_LIQUID_REGTEST + },{ + "address errchk - unsupport address - p2pk", + "pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)", + WALLY_NETWORK_BITCOIN_MAINNET + },{ + "address errchk - unsupport address - raw", + "raw(6a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e)#zf2avljj", + WALLY_NETWORK_BITCOIN_MAINNET + }, +}; + +static bool check_parse_miniscript(const char *function, const char *descriptor, + const char *expected, + const char **key_name_list, + const char **key_value_list, size_t list_num, + uint32_t flags) +{ + size_t written = 0; + unsigned char script[520]; + char *hex = NULL; + int ret; + bool is_success = false; + uint32_t index = 0; + + ret = wally_descriptor_parse_miniscript( + descriptor, + key_name_list, + key_value_list, + list_num, + index, + flags, + script, + sizeof(script), + &written); + if (ret != WALLY_OK) { + printf("wally_descriptor_parse_miniscript NG[%d]\n", ret); + return false; + } + + ret = wally_hex_from_bytes(script, written, &hex); + if (ret != WALLY_OK) { + printf("wally_hex_from_bytes NG[%d]\n", ret); + return false; + } + + if (strncmp(hex, expected, strlen(hex) + 1) == 0) { + is_success = true; + } else + printf("%s:\n Input: %s\n Output: %s\n Expect: %s\n", + function, descriptor, hex, expected); + + wally_free_string(hex); + return is_success; +} + +static bool check_descriptor_to_scriptpubkey(const char *function, + const char *descriptor, + const char *expected, + const uint32_t *bip32_index, + const char *expected_checksum) +{ + size_t written = 0; + unsigned char script[520]; + char *hex = NULL; + char *checksum = NULL; + int ret; + bool is_success = false; + uint32_t network = 0; + uint32_t desc_depth = 0; + uint32_t desc_index = 0; + uint32_t flag = 0; + uint32_t index = 0; + if (bip32_index) { + index = *bip32_index; + } + + ret = wally_descriptor_to_scriptpubkey( + descriptor, + (const char **)g_miniscript_keyname_list, + (const char **)g_miniscript_keyvalue_list, + sizeof(g_miniscript_keyname_list) / sizeof(char *), + index, + network, + desc_depth, + desc_index, + flag, + script, + sizeof(script), + &written); + if (ret != WALLY_OK) { + printf("wally_descriptor_to_scriptpubkey NG[%d]\n", ret); + return false; + } + + ret = wally_hex_from_bytes(script, written, &hex); + if (ret != WALLY_OK) { + printf("wally_hex_from_bytes NG[%d]\n", ret); + return false; + } + + ret = wally_descriptor_create_checksum( + descriptor, + (const char **)g_miniscript_keyname_list, + (const char **)g_miniscript_keyvalue_list, + sizeof(g_miniscript_keyname_list) / sizeof(char *), + flag, + &checksum); + if (ret != WALLY_OK) { + printf("wally_descriptor_create_checksum NG[%d]\n", ret); + wally_free_string(hex); + return false; + } + + if (strncmp(hex, expected, strlen(hex) + 1) != 0) { + printf("%s:\n Input: %s\n Output: %s\n", function, descriptor, hex); + } else if (strncmp(checksum, expected_checksum, 9) != 0) { + printf("%s:\n Input: %s\n Checksum: %s\n", function, descriptor, checksum); + } else + is_success = true; + + wally_free_string(checksum); + wally_free_string(hex); + return is_success; +} + +static bool check_descriptor_to_scriptpubkey_depth(const char *function, + const char *descriptor, + const uint32_t depth, + const uint32_t index, + const char *expected_scriptpubkey) +{ + size_t written = 0; + unsigned char script[520]; + char *hex = NULL; + int ret; + bool is_success = false; + uint32_t network = 0; + uint32_t flag = 0; + + ret = wally_descriptor_to_scriptpubkey( + descriptor, + (const char **)g_miniscript_keyname_list, + (const char **)g_miniscript_keyvalue_list, + sizeof(g_miniscript_keyname_list) / sizeof(char *), + 0, + network, + depth, + index, + flag, + script, + sizeof(script), + &written); + if (ret != WALLY_OK) { + printf("wally_descriptor_to_scriptpubkey NG[%d]\n", ret); + return false; + } + + ret = wally_hex_from_bytes(script, written, &hex); + if (ret != WALLY_OK) { + printf("wally_hex_from_bytes NG[%d]\n", ret); + return false; + } + + if (strncmp(hex, expected_scriptpubkey, strlen(hex) + 1) != 0) { + printf("%s:\n Input: %s\n Output: %s\n", function, descriptor, hex); + } else + is_success = true; + + wally_free_string(hex); + return is_success; +} + +static bool check_descriptor_to_address(const char *function, + const char *descriptor, + const uint32_t bip32_index, + const uint32_t network, + const char *expected_address) +{ + char *address = NULL; + int ret; + uint32_t flag = 0; + bool is_success = false; + + ret = wally_descriptor_to_address( + descriptor, + (const char **)g_miniscript_keyname_list, + (const char **)g_miniscript_keyvalue_list, + sizeof(g_miniscript_keyname_list) / sizeof(char *), + bip32_index, + network, + flag, + &address); + if (ret != WALLY_OK) { + printf("wally_descriptor_to_address NG[%d]\n", ret); + return false; + } + + if (strncmp(expected_address, address, strlen(expected_address) + 1) != 0) { + printf("%s:\n Address: %s\n Expect: %s\n", function, address, expected_address); + } else + is_success = true; + + wally_free_string(address); + return is_success; +} + +static bool check_descriptor_to_addresses(const char *function, + const char *descriptor, + const uint32_t start_index, + const uint32_t end_index, + const uint32_t network, + const char **expected_address_list, + size_t address_list_len) +{ + struct wally_descriptor_addresses addresses = {NULL, 0}; + int ret; + uint32_t flag = 0; + uint32_t index = 0; + bool is_success = true; + + ret = wally_descriptor_to_addresses( + descriptor, + (const char **)g_miniscript_keyname_list, + (const char **)g_miniscript_keyvalue_list, + sizeof(g_miniscript_keyname_list) / sizeof(char *), + start_index, + end_index, + network, + flag, + &addresses); + if (ret != WALLY_OK) { + printf("wally_descriptor_to_addresses NG[%d]\n", ret); + return false; + } + + if (addresses.num_items != address_list_len) { + printf("%s:\n Address length: %zu\n Expect: %zu\n", function, addresses.num_items, address_list_len); + } else { + for (index = 0; index < address_list_len; ++index) { + const char *expected_address = expected_address_list[index]; + const char *addr = addresses.items[index].address; + uint32_t child_num = addresses.items[index].child_num; + uint32_t exp_child_num = start_index + index; + if (strncmp(expected_address, addr, strlen(expected_address) + 1) != 0) { + printf("%s:\n Address[%u]: %s\n Expect: %s\n", function, index, addr, expected_address_list[index]); + is_success = false; + } + if (child_num != exp_child_num) { + printf("%s:\n childNum[%u]: %u\n Expect: %u\n", function, index, child_num, exp_child_num); + is_success = false; + } + } + } + + wally_free_descriptor_addresses(&addresses); + return is_success; +} + +static bool check_descriptor_scriptpubkey_error(const char *function, + const char *descriptor, + const uint32_t network) +{ + int ret; + size_t written = 0; + unsigned char script[520]; + uint32_t flag = 0; + uint32_t desc_depth = 0; + uint32_t desc_index = 0; + + ret = wally_descriptor_to_scriptpubkey( + descriptor, + (const char **)g_miniscript_keyname_list, + (const char **)g_miniscript_keyvalue_list, + sizeof(g_miniscript_keyname_list) / sizeof(char *), + 0, + network, + desc_depth, + desc_index, + flag, + script, + sizeof(script), + &written); + if (ret == WALLY_EINVAL) { + return true; + } else if (ret != WALLY_OK) { + printf("wally_descriptor_to_scriptpubkey NG[%d]\n", ret); + return false; + } + printf("wally_descriptor_to_scriptpubkey Fail[Not Error] name[%s]\n", function); + + return false; +} + +static bool check_descriptor_address_error(const char *function, + const char *descriptor, + const uint32_t network) +{ + char *address = NULL; + int ret; + uint32_t flag = 0; + + ret = wally_descriptor_to_address( + descriptor, + (const char **)g_miniscript_keyname_list, + (const char **)g_miniscript_keyvalue_list, + sizeof(g_miniscript_keyname_list) / sizeof(char *), + 0, + network, + flag, + &address); + if (ret == WALLY_EINVAL) { + return true; + } else if (ret != WALLY_OK) { + printf("wally_descriptor_to_address NG[%d]\n", ret); + return false; + } + printf("wally_descriptor_to_address Fail[Not Error] name[%s]\n", function); + + wally_free_string(address); + return false; +} + +int main(void) +{ + bool tests_ok = true; + size_t index; + size_t miniscript_ref_max = sizeof(g_miniscript_ref_test_table) / sizeof(struct wally_miniscript_ref_test); + size_t miniscript_max = sizeof(g_miniscript_test_table) / sizeof(struct wally_miniscript_test); + size_t miniscript_tr_max = sizeof(g_miniscript_taproot_test_table) / sizeof(struct wally_miniscript_taproot_test); + size_t desc_max = sizeof(g_descriptor_test_table) / sizeof(struct wally_descriptor_test); + size_t desc_depth_max = sizeof(g_descriptor_depth_test_table) / sizeof(struct wally_descriptor_depth_test); + size_t addr_max = sizeof(g_descriptor_address_test_table) / sizeof(struct wally_descriptor_address_test); + size_t addr_list_max = sizeof(g_descriptor_addresses_test_table) / sizeof(struct wally_descriptor_address_list_test); + size_t desc_err_max = sizeof(g_descriptor_err_test_table) / sizeof(struct wally_descriptor_err_test); + size_t addr_err_max = sizeof(g_address_err_test_table) / sizeof(struct wally_descriptor_err_test); + + for (index = 0; index < miniscript_ref_max; ++index) { + if (!check_parse_miniscript( + g_miniscript_ref_test_table[index].miniscript, + g_miniscript_ref_test_table[index].miniscript, + g_miniscript_ref_test_table[index].scriptpubkey, + NULL, NULL, 0, 0)) { + printf("[%s] test failed!\n", g_miniscript_ref_test_table[index].miniscript); + tests_ok = false; + } + } + + for (index = 0; index < miniscript_max; ++index) { + if (!check_parse_miniscript( + g_miniscript_test_table[index].name, + g_miniscript_test_table[index].descriptor, + g_miniscript_test_table[index].scriptpubkey, + NULL, NULL, 0, 0)) { + printf("[%s] test failed!\n", g_miniscript_test_table[index].name); + tests_ok = false; + } + } + + for (index = 0; index < miniscript_tr_max; ++index) { + if (!check_parse_miniscript( + g_miniscript_taproot_test_table[index].miniscript, + g_miniscript_taproot_test_table[index].miniscript, + g_miniscript_taproot_test_table[index].scriptpubkey, + NULL, NULL, 0, + g_miniscript_taproot_test_table[index].flags)) { + printf("[%s] test failed!\n", g_miniscript_taproot_test_table[index].miniscript); + tests_ok = false; + } + } + + for (index = 0; index < desc_max; ++index) { + if (!check_descriptor_to_scriptpubkey( + g_descriptor_test_table[index].name, + g_descriptor_test_table[index].descriptor, + g_descriptor_test_table[index].scriptpubkey, + g_descriptor_test_table[index].bip32_index, + g_descriptor_test_table[index].checksum)) { + printf("[%s] test failed!\n", g_descriptor_test_table[index].name); + tests_ok = false; + } + } + + for (index = 0; index < desc_depth_max; ++index) { + if (!check_descriptor_to_scriptpubkey_depth( + g_descriptor_depth_test_table[index].name, + g_descriptor_depth_test_table[index].descriptor, + g_descriptor_depth_test_table[index].depth, + g_descriptor_depth_test_table[index].index, + g_descriptor_depth_test_table[index].scriptpubkey)) { + printf("[%s] keylist test failed!\n", g_descriptor_depth_test_table[index].name); + tests_ok = false; + } + } + + for (index = 0; index < addr_max; ++index) { + if (!check_descriptor_to_address( + g_descriptor_address_test_table[index].name, + g_descriptor_address_test_table[index].descriptor, + g_descriptor_address_test_table[index].bip32_index, + g_descriptor_address_test_table[index].network, + g_descriptor_address_test_table[index].address)) { + printf("[%s] test failed!\n", g_descriptor_address_test_table[index].name); + tests_ok = false; + } + } + + for (index = 0; index < addr_list_max; ++index) { + if (!check_descriptor_to_addresses( + g_descriptor_addresses_test_table[index].name, + g_descriptor_addresses_test_table[index].descriptor, + g_descriptor_addresses_test_table[index].start_index, + g_descriptor_addresses_test_table[index].end_index, + g_descriptor_addresses_test_table[index].network, + g_descriptor_addresses_test_table[index].address_list, + g_descriptor_addresses_test_table[index].address_list_num)) { + printf("[%s] test failed!\n", g_descriptor_addresses_test_table[index].name); + tests_ok = false; + } + } + + for (index = 0; index < desc_err_max; ++index) { + if (!check_descriptor_scriptpubkey_error( + g_descriptor_err_test_table[index].name, + g_descriptor_err_test_table[index].descriptor, + g_descriptor_err_test_table[index].network)) { + printf("[%s] test failed!\n", g_descriptor_err_test_table[index].name); + tests_ok = false; + } + } + + for (index = 0; index < addr_err_max; ++index) { + if (!check_descriptor_address_error( + g_address_err_test_table[index].name, + g_address_err_test_table[index].descriptor, + g_address_err_test_table[index].network)) { + printf("[%s] test failed!\n", g_address_err_test_table[index].name); + tests_ok = false; + } + } + + return tests_ok ? 0 : 1; +} diff --git a/src/descriptor.c b/src/descriptor.c new file mode 100644 index 000000000..e6d81b495 --- /dev/null +++ b/src/descriptor.c @@ -0,0 +1,3825 @@ +#include "internal.h" + +#include "ccan/ccan/crypto/ripemd160/ripemd160.h" +#include "ccan/ccan/crypto/sha256/sha256.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* Reference */ +size_t scriptint_to_bytes(int64_t signed_v, unsigned char *bytes_out); + +/* Definition */ +/* Properties and expressions definition */ +#define MINISCRIPT_TYPE_B 0x01 /* Base expressions */ +#define MINISCRIPT_TYPE_V 0x02 /* Verify expressions */ +#define MINISCRIPT_TYPE_K 0x04 /* Key expressions */ +#define MINISCRIPT_TYPE_W 0x08 /* Wrapped expressions */ +#define MINISCRIPT_TYPE_MASK 0x0F /* expressions mask */ + +#define MINISCRIPT_PROPERTY_Z 0x00000100 /* Zero-arg property */ +#define MINISCRIPT_PROPERTY_O 0x00000200 /* One-arg property */ +#define MINISCRIPT_PROPERTY_N 0x00000400 /* Nonzero arg property */ +#define MINISCRIPT_PROPERTY_D 0x00000800 /* Dissatisfiable property */ +#define MINISCRIPT_PROPERTY_U 0x00001000 /* Unit property */ +#define MINISCRIPT_PROPERTY_E 0x00002000 /* Expression property */ +#define MINISCRIPT_PROPERTY_F 0x00004000 /* Forced property */ +#define MINISCRIPT_PROPERTY_S 0x00008000 /* Safe property */ +#define MINISCRIPT_PROPERTY_M 0x00010000 /* Nonmalleable property */ +#define MINISCRIPT_PROPERTY_X 0x00020000 /* Expensive verify */ + +#define DESCRIPTOR_KIND_MINISCRIPT 0x01 +#define DESCRIPTOR_KIND_DESCRIPTOR 0x02 /* Output Descriptor */ + +#define DESCRIPTOR_KIND_FRAGMENT 0x01 +#define DESCRIPTOR_KIND_SCRIPT 0x02 /* Output Descriptor script */ +#define DESCRIPTOR_KIND_RAW 0x04 /* Output Descriptor */ +#define DESCRIPTOR_KIND_NUMBER 0x08 /* Output Descriptor */ +#define DESCRIPTOR_KIND_ADDRESS 0x10 /* Output Descriptor */ +#define DESCRIPTOR_KIND_KEY 0x20 /* Output Descriptor */ + +#define DESCRIPTOR_KIND_BASE58 (0x0100 | DESCRIPTOR_KIND_ADDRESS) +#define DESCRIPTOR_KIND_BECH32 (0x0200 | DESCRIPTOR_KIND_ADDRESS) + +#define DESCRIPTOR_KIND_PUBLIC_KEY (0x001000 | DESCRIPTOR_KIND_KEY) +#define DESCRIPTOR_KIND_PRIVATE_KEY (0x002000 | DESCRIPTOR_KIND_KEY) +#define DESCRIPTOR_KIND_BIP32 (0x004000 | DESCRIPTOR_KIND_KEY) +#define DESCRIPTOR_KIND_BIP32_PRIVATE_KEY (0x010000 | DESCRIPTOR_KIND_BIP32) +#define DESCRIPTOR_KIND_BIP32_PUBLIC_KEY (0x020000 | DESCRIPTOR_KIND_BIP32) + +/* OP_0 properties: Bzudemsx */ +#define MINISCRIPT_PROPERTY_OP_0 (MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_Z | \ + MINISCRIPT_PROPERTY_U | MINISCRIPT_PROPERTY_D | \ + MINISCRIPT_PROPERTY_E | MINISCRIPT_PROPERTY_M | \ + MINISCRIPT_PROPERTY_S | MINISCRIPT_PROPERTY_X) +/* OP_1 properties: Bzufmx */ +#define MINISCRIPT_PROPERTY_OP_1 (MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_Z | \ + MINISCRIPT_PROPERTY_U | MINISCRIPT_PROPERTY_F | \ + MINISCRIPT_PROPERTY_M | MINISCRIPT_PROPERTY_X) + +#define DESCRIPTOR_LIMIT_LENGTH 1000000 +#define DESCRIPTOR_BIP32_PATH_NUM_MAX 256 +#define DESCRIPTOR_REDEEM_SCRIPT_MAX_SIZE 520 +#define DESCRIPTOR_WITNESS_SCRIPT_MAX_SIZE 10000 +#define DESCRIPTOR_MINISCRIPT_MUILTI_MAX 20 +#define DESCRIPTOR_KEY_NAME_MAX_LENGTH 16 +#define DESCRIPTOR_KEY_VALUE_MAX_LENGTH 130 +#define DESCRIPTOR_NUMBER_BYTE_MAX_LENGTH 18 +#define DESCRIPTOR_MIN_SIZE 20 +#define XONLY_PUBLIC_KEY_LEN 32 + +#define DESCRIPTOR_CHECKSUM_LENGTH 8 + +/* output descriptor */ +#define DESCRIPTOR_KIND_DESCRIPTOR_PK (0x00000100 | DESCRIPTOR_KIND_DESCRIPTOR) +#define DESCRIPTOR_KIND_DESCRIPTOR_PKH (0x00000200 | DESCRIPTOR_KIND_DESCRIPTOR) +#define DESCRIPTOR_KIND_DESCRIPTOR_MULTI (0x00000300 | DESCRIPTOR_KIND_DESCRIPTOR) +#define DESCRIPTOR_KIND_DESCRIPTOR_SORTEDMULTI \ + (0x00000400 | DESCRIPTOR_KIND_DESCRIPTOR) +#define DESCRIPTOR_KIND_DESCRIPTOR_SH (0x00000500 | DESCRIPTOR_KIND_DESCRIPTOR) +#define DESCRIPTOR_KIND_DESCRIPTOR_WPKH (0x00010000 | DESCRIPTOR_KIND_DESCRIPTOR) +#define DESCRIPTOR_KIND_DESCRIPTOR_WSH (0x00020000 | DESCRIPTOR_KIND_DESCRIPTOR) +#define DESCRIPTOR_KIND_DESCRIPTOR_COMBO (0x00030000 | DESCRIPTOR_KIND_DESCRIPTOR) +#define DESCRIPTOR_KIND_DESCRIPTOR_ADDR (0x00040000 | DESCRIPTOR_KIND_DESCRIPTOR) +#define DESCRIPTOR_KIND_DESCRIPTOR_RAW (0x00050000 | DESCRIPTOR_KIND_DESCRIPTOR) +#define DESCRIPTOR_KIND_DESCRIPTOR_MASK (0xffffff00 | DESCRIPTOR_KIND_DESCRIPTOR) + +/* miniscript */ +#define DESCRIPTOR_KIND_MINISCRIPT_PK (0x00000100 | DESCRIPTOR_KIND_MINISCRIPT) +#define DESCRIPTOR_KIND_MINISCRIPT_PKH (0x00000200 | DESCRIPTOR_KIND_MINISCRIPT) +#define DESCRIPTOR_KIND_MINISCRIPT_MULTI (0x00000300 | DESCRIPTOR_KIND_MINISCRIPT) +#define DESCRIPTOR_KIND_MINISCRIPT_PK_K (0x00001000 | DESCRIPTOR_KIND_MINISCRIPT) +#define DESCRIPTOR_KIND_MINISCRIPT_PK_H (0x00002000 | DESCRIPTOR_KIND_MINISCRIPT) +#define DESCRIPTOR_KIND_MINISCRIPT_OLDER (0x00010000 | DESCRIPTOR_KIND_MINISCRIPT) +#define DESCRIPTOR_KIND_MINISCRIPT_AFTER (0x00020000 | DESCRIPTOR_KIND_MINISCRIPT) +#define DESCRIPTOR_KIND_MINISCRIPT_SHA256 (0x00030000 | DESCRIPTOR_KIND_MINISCRIPT) +#define DESCRIPTOR_KIND_MINISCRIPT_HASH256 (0x00040000 | DESCRIPTOR_KIND_MINISCRIPT) +#define DESCRIPTOR_KIND_MINISCRIPT_RIPEMD160 (0x00050000 | DESCRIPTOR_KIND_MINISCRIPT) +#define DESCRIPTOR_KIND_MINISCRIPT_HASH160 (0x00060000 | DESCRIPTOR_KIND_MINISCRIPT) +#define DESCRIPTOR_KIND_MINISCRIPT_THRESH (0x00070000 | DESCRIPTOR_KIND_MINISCRIPT) +#define DESCRIPTOR_KIND_MINISCRIPT_ANDOR (0x01000000 | DESCRIPTOR_KIND_MINISCRIPT) +#define DESCRIPTOR_KIND_MINISCRIPT_AND_V (0x02000000 | DESCRIPTOR_KIND_MINISCRIPT) +#define DESCRIPTOR_KIND_MINISCRIPT_AND_B (0x03000000 | DESCRIPTOR_KIND_MINISCRIPT) +#define DESCRIPTOR_KIND_MINISCRIPT_AND_N (0x04000000 | DESCRIPTOR_KIND_MINISCRIPT) +#define DESCRIPTOR_KIND_MINISCRIPT_OR_B (0x05000000 | DESCRIPTOR_KIND_MINISCRIPT) +#define DESCRIPTOR_KIND_MINISCRIPT_OR_C (0x06000000 | DESCRIPTOR_KIND_MINISCRIPT) +#define DESCRIPTOR_KIND_MINISCRIPT_OR_D (0x07000000 | DESCRIPTOR_KIND_MINISCRIPT) +#define DESCRIPTOR_KIND_MINISCRIPT_OR_I (0x08000000 | DESCRIPTOR_KIND_MINISCRIPT) +#define DESCRIPTOR_KIND_MINISCRIPT_MASK (0xffffff00 | DESCRIPTOR_KIND_MINISCRIPT) + +#define DESCRIPTOR_TYPE_WRAPPER_A (0x00000100) +#define DESCRIPTOR_TYPE_WRAPPER_S (0x00000200) +#define DESCRIPTOR_TYPE_WRAPPER_C (0x00000400) +#define DESCRIPTOR_TYPE_WRAPPER_T (0x00000800) +#define DESCRIPTOR_TYPE_WRAPPER_D (0x00001000) +#define DESCRIPTOR_TYPE_WRAPPER_V (0x00002000) +#define DESCRIPTOR_TYPE_WRAPPER_J (0x00004000) +#define DESCRIPTOR_TYPE_WRAPPER_N (0x00008000) +#define DESCRIPTOR_TYPE_WRAPPER_L (0x00010000) +#define DESCRIPTOR_TYPE_WRAPPER_U (0x00020000) +#define DESCRIPTOR_TYPE_WRAPPER_MASK (0xffffff00) + +/* Type */ +struct miniscript_node_t; + +typedef int (*wally_verify_descriptor_t)( + struct miniscript_node_t *node, + struct miniscript_node_t *parent); + +typedef int (*wally_descriptor_to_script_t)( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len); + +typedef int (*wally_miniscript_wrapper_to_script_t)( + unsigned char *script, + size_t script_len, + size_t *write_len); + +/* Struct */ +struct wally_descriptor_script_item { + uint32_t child_num; + unsigned char *script; + size_t script_len; +}; + +struct miniscript_item_t { + const char *name; + int kind; + uint32_t type_properties; + int inner_num; + wally_verify_descriptor_t verify_function; + wally_descriptor_to_script_t generate_function; +}; + +struct miniscript_wrapper_item_t { + const char *name; + int kind; + uint32_t type_properties; + int inner_num; + wally_verify_descriptor_t verify_function; + wally_miniscript_wrapper_to_script_t generate_function; +}; + +struct miniscript_node_t { + const struct miniscript_item_t *info; + struct miniscript_node_t *next; + struct miniscript_node_t *back; + struct miniscript_node_t *child; + struct miniscript_node_t *parent; + unsigned int chain_count; + char wrapper_str[12]; + int kind; + uint32_t type_properties; + uint32_t wrapper; + int64_t number; + char *data; + char *derive_path; + char *key_origin_info; + uint32_t data_size; + uint32_t derive_path_len; + uint32_t key_origin_info_len; + uint32_t network_type; + bool is_derive; + bool is_uncompress_key; + bool is_xonly_key; +}; + +struct multisig_sort_data_t { + struct miniscript_node_t *node; + unsigned char script[EC_PUBLIC_KEY_UNCOMPRESSED_LEN]; + uint32_t script_size; +}; + +struct address_script_t { + unsigned char network; + unsigned char version_p2pkh; + unsigned char version_p2sh; + unsigned char version_wif; + const char *segwit_prefix; +}; + +/* Function prototype */ +static int convert_bip32_path_to_array( + const char *path, + uint32_t *bip32_array, + uint32_t array_num, + bool is_private, + uint32_t *count, + int8_t *astarisk_index); +static int generate_by_wrapper_c( + unsigned char *script, + size_t script_len, + size_t *write_len); +static int analyze_miniscript_addr( + const char *message, + struct miniscript_node_t *node, + struct miniscript_node_t *parent_node, + const struct address_script_t *target_addr_item, + unsigned char *script, + size_t script_len, + size_t *write_len); +static int generate_script_from_miniscript( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + uint32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len); +static int generate_script_from_number( + int64_t number, + struct miniscript_node_t *parent, + unsigned char *script, + size_t script_len, + size_t *write_len); + +/* Function */ +static int check_ascii_string(const char *message, size_t max) +{ + size_t index; + + if (!message) + return WALLY_EINVAL; + + for (index = 0; message[index] != '\0'; ++index) + if ((message[index] < 0x20) || (message[index] == 0x7f) || (index > max)) + return WALLY_EINVAL; + + return WALLY_OK; +} + +static int realloc_substr_buffer(size_t need_len, char **buffer, size_t *buffer_len) +{ + size_t need_size = ((need_len / 64) + 2) * 64; + if ((*buffer == NULL) || (need_size > *buffer_len)) { + if (*buffer != NULL) + wally_free(*buffer); + + *buffer = (char *) wally_malloc(need_size); + if (*buffer == NULL) + return WALLY_ENOMEM; + + *buffer_len = need_size; + } + return WALLY_OK; +} + +static int32_t get_child_list_count(struct miniscript_node_t *node) +{ + int32_t ret = 0; + struct miniscript_node_t *chain = node->child; + while (chain != NULL) { + ++ret; + chain = chain->next; + } + return ret; +} + +static void free_miniscript_node(struct miniscript_node_t *node) +{ + if (!node) + return; + if (node->child) { + struct miniscript_node_t *child = node->child; + struct miniscript_node_t *next; + while (child) { + next = child->next; + free_miniscript_node(child); + child = next; + } + } + + if (node->data && node->data_size) { + wally_bzero(node->data, node->data_size); + wally_free(node->data); + } + if (node->derive_path && node->derive_path_len) { + wally_bzero(node->derive_path, node->derive_path_len); + wally_free(node->derive_path); + } + if (node->key_origin_info && node->key_origin_info_len) { + wally_bzero(node->key_origin_info, node->key_origin_info_len); + wally_free(node->key_origin_info); + } + + wally_bzero(node, sizeof(struct miniscript_node_t)); + wally_free(node); +} + +int check_type_properties(uint32_t property) +{ + /* K, V, B, W all conflict with each other */ + switch (property & MINISCRIPT_TYPE_MASK) { + case MINISCRIPT_TYPE_B: + case MINISCRIPT_TYPE_V: + case MINISCRIPT_TYPE_K: + case MINISCRIPT_TYPE_W: + break; + default: + return WALLY_EINVAL; + } + + if ((property & MINISCRIPT_PROPERTY_Z) && (property & MINISCRIPT_PROPERTY_O)) + return WALLY_EINVAL; + if ((property & MINISCRIPT_PROPERTY_N) && (property & MINISCRIPT_PROPERTY_Z)) + return WALLY_EINVAL; + if ((property & MINISCRIPT_TYPE_V) && (property & MINISCRIPT_PROPERTY_D)) + return WALLY_EINVAL; + if ((property & MINISCRIPT_TYPE_K) && !(property & MINISCRIPT_PROPERTY_U)) + return WALLY_EINVAL; + if ((property & MINISCRIPT_TYPE_V) && (property & MINISCRIPT_PROPERTY_U)) + return WALLY_EINVAL; + if ((property & MINISCRIPT_PROPERTY_E) && (property & MINISCRIPT_PROPERTY_F)) + return WALLY_EINVAL; + if ((property & MINISCRIPT_PROPERTY_E) && !(property & MINISCRIPT_PROPERTY_D)) + return WALLY_EINVAL; + if ((property & MINISCRIPT_TYPE_V) && (property & MINISCRIPT_PROPERTY_E)) + return WALLY_EINVAL; + if ((property & MINISCRIPT_PROPERTY_D) && (property & MINISCRIPT_PROPERTY_F)) + return WALLY_EINVAL; + if ((property & MINISCRIPT_TYPE_V) && !(property & MINISCRIPT_PROPERTY_F)) + return WALLY_EINVAL; + if ((property & MINISCRIPT_TYPE_K) && !(property & MINISCRIPT_PROPERTY_S)) + return WALLY_EINVAL; + if ((property & MINISCRIPT_PROPERTY_Z) && !(property & MINISCRIPT_PROPERTY_M)) + return WALLY_EINVAL; + + return WALLY_OK; +} + +static int verify_descriptor_sh(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + if (parent || (get_child_list_count(node) != node->info->inner_num) || !node->child->info) + return WALLY_EINVAL; + + node->type_properties = node->child->type_properties; + return WALLY_OK; +} + +static bool has_uncompressed_key_by_child(struct miniscript_node_t *node) +{ + struct miniscript_node_t *child = node->child; + while (child) { + if (child->is_uncompress_key) + return true; + if (has_uncompressed_key_by_child(child)) + return true; + + child = child->next; + } + return false; +} + +static int verify_descriptor_wsh(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + if (parent && (!parent->info || (parent->info->kind != DESCRIPTOR_KIND_DESCRIPTOR_SH))) + return WALLY_EINVAL; + if ((get_child_list_count(node) != node->info->inner_num) || !node->child->info) + return WALLY_EINVAL; + if (has_uncompressed_key_by_child(node)) + return WALLY_EINVAL; + + node->type_properties = node->child->type_properties; + return WALLY_OK; +} + +static int verify_descriptor_pk(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + (void)parent; + if ((get_child_list_count(node) != node->info->inner_num) || node->child->info || + ((node->child->kind & DESCRIPTOR_KIND_KEY) != DESCRIPTOR_KIND_KEY)) + return WALLY_EINVAL; + + node->type_properties = node->info->type_properties; + return WALLY_OK; +} + +static int verify_descriptor_pkh(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + return verify_descriptor_pk(node, parent); +} + +static int verify_descriptor_wpkh(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + struct miniscript_node_t *parent_item = parent; + if (parent && (!parent->info || (parent->info->kind & DESCRIPTOR_KIND_MINISCRIPT))) + return WALLY_EINVAL; + if ((get_child_list_count(node) != node->info->inner_num) || node->child->info || + ((node->child->kind & DESCRIPTOR_KIND_KEY) != DESCRIPTOR_KIND_KEY)) + return WALLY_EINVAL; + + while (parent_item != NULL) { + if (parent_item->kind == DESCRIPTOR_KIND_DESCRIPTOR_WSH) + return WALLY_EINVAL; + parent_item = parent_item->parent; + } + + if (has_uncompressed_key_by_child(node)) + return WALLY_EINVAL; + + return WALLY_OK; +} + +static int verify_descriptor_combo(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + if (parent) + return WALLY_EINVAL; + + /* Since the combo is of multiple return types, the return value is wpkh or pkh. */ + if (has_uncompressed_key_by_child(node)) { + return verify_descriptor_pkh(node, parent); + } else { + return verify_descriptor_wpkh(node, parent); + } +} + +static int verify_descriptor_multi(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + struct miniscript_node_t *top = NULL; + struct miniscript_node_t *key = NULL; + uint32_t require_num = 0; + uint32_t count = (uint32_t) get_child_list_count(node); + (void)parent; + + if ((count < 2) || (count - 1 > DESCRIPTOR_MINISCRIPT_MUILTI_MAX)) + return WALLY_EINVAL; + + top = node->child; + require_num = (uint32_t) top->number; + if (!top->next || top->info || (top->kind != DESCRIPTOR_KIND_NUMBER) || + (top->number <= 0) || (count < require_num)) + return WALLY_EINVAL; + + key = top->next; + while (key) { + if (key->info || !(key->kind & DESCRIPTOR_KIND_KEY)) + return WALLY_EINVAL; + + key = key->next; + } + + node->type_properties = node->info->type_properties; + return WALLY_OK; +} + +static int verify_descriptor_sortedmulti(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + return verify_descriptor_multi(node, parent); +} + +static int verify_descriptor_addr(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + if (parent || (get_child_list_count(node) != node->info->inner_num) || node->child->info || + ((node->child->kind & DESCRIPTOR_KIND_ADDRESS) != DESCRIPTOR_KIND_ADDRESS)) + return WALLY_EINVAL; + + return WALLY_OK; +} + +static int verify_descriptor_raw(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + if (parent || (get_child_list_count(node) != node->info->inner_num) || node->child->info || + ((node->child->kind & DESCRIPTOR_KIND_RAW) == 0)) + return WALLY_EINVAL; + + return WALLY_OK; +} + +static int verify_miniscript_pkh(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + (void)parent; + if ((get_child_list_count(node) != node->info->inner_num) || node->child->info || + ((node->child->kind & DESCRIPTOR_KIND_KEY) != DESCRIPTOR_KIND_KEY)) + return WALLY_EINVAL; + + node->type_properties = node->info->type_properties; + return WALLY_OK; +} + +static int verify_miniscript_pk(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + return verify_miniscript_pkh(node, parent); +} + +static int verify_miniscript_older(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + (void)parent; + if ((get_child_list_count(node) != node->info->inner_num) || node->child->info || + (node->child->kind != DESCRIPTOR_KIND_NUMBER) || + (node->child->number <= 0) || (node->child->number > 0x7fffffff)) + return WALLY_EINVAL; + + node->type_properties = node->info->type_properties; + return WALLY_OK; +} + +static int verify_miniscript_after(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + return verify_miniscript_older(node, parent); +} + +static int verify_miniscript_hash_type(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + (void)parent; + if ((get_child_list_count(node) != node->info->inner_num) || node->child->info || + ((node->child->kind & DESCRIPTOR_KIND_RAW) == 0)) + return WALLY_EINVAL; + + node->type_properties = node->info->type_properties; + return WALLY_OK; +} + +static int verify_miniscript_sha256(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + return verify_miniscript_hash_type(node, parent); +} + +static int verify_miniscript_hash256(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + return verify_miniscript_hash_type(node, parent); +} + +static int verify_miniscript_ripemd160(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + return verify_miniscript_hash_type(node, parent); +} + +static int verify_miniscript_hash160(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + return verify_miniscript_hash_type(node, parent); +} + +static uint32_t verify_miniscript_andor_property(uint32_t x_property, uint32_t y_property, uint32_t z_property) +{ + /* Y and Z are both B, K, or V */ + uint32_t prop = MINISCRIPT_PROPERTY_X; + uint32_t need_x = MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_U; + uint32_t need_yz = MINISCRIPT_TYPE_B | MINISCRIPT_TYPE_K | MINISCRIPT_TYPE_V; + if (!(x_property & MINISCRIPT_TYPE_B) || !(x_property & need_x)) + return 0; + if (!(y_property & z_property & need_yz)) + return 0; + + prop |= y_property & z_property & need_yz; + prop |= x_property & y_property & z_property & MINISCRIPT_PROPERTY_Z; + prop |= (x_property | (y_property & z_property)) & MINISCRIPT_PROPERTY_O; + prop |= y_property & z_property & MINISCRIPT_PROPERTY_U; + prop |= z_property & MINISCRIPT_PROPERTY_D; + if ((x_property & MINISCRIPT_PROPERTY_S) || (y_property & MINISCRIPT_PROPERTY_F)) { + prop |= z_property & MINISCRIPT_PROPERTY_F; + prop |= x_property & z_property & MINISCRIPT_PROPERTY_E; + } + if ((x_property & MINISCRIPT_PROPERTY_E) && + ((x_property | y_property | z_property) & MINISCRIPT_PROPERTY_S)) { + prop |= x_property & y_property & z_property & MINISCRIPT_PROPERTY_M; + } + prop |= z_property & (x_property | y_property) & MINISCRIPT_PROPERTY_S; + return prop; +} + +static int verify_miniscript_andor(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + (void)parent; + if (get_child_list_count(node) != node->info->inner_num) + return WALLY_EINVAL; + + node->type_properties = verify_miniscript_andor_property( + node->child->type_properties, + node->child->next->type_properties, + node->child->next->next->type_properties); + if (!node->type_properties) + return WALLY_EINVAL; + + return WALLY_OK; +} + +static int verify_miniscript_two_param_check(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + (void)parent; + if (get_child_list_count(node) != node->info->inner_num) + return WALLY_EINVAL; + + return WALLY_OK; +} + +static uint32_t verify_miniscript_and_v_property(uint32_t x_property, uint32_t y_property) +{ + uint32_t prop = 0; + prop |= x_property & MINISCRIPT_PROPERTY_N; + prop |= y_property & (MINISCRIPT_PROPERTY_U | MINISCRIPT_PROPERTY_X); + prop |= x_property & y_property & + (MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_M | MINISCRIPT_PROPERTY_Z); + prop |= (x_property | y_property) & MINISCRIPT_PROPERTY_S; + if (x_property & MINISCRIPT_TYPE_V) + prop |= y_property & (MINISCRIPT_TYPE_K | MINISCRIPT_TYPE_V | MINISCRIPT_TYPE_B); + if (x_property & MINISCRIPT_PROPERTY_Z) + prop |= y_property & MINISCRIPT_PROPERTY_N; + if ((x_property | y_property) & MINISCRIPT_PROPERTY_Z) + prop |= (x_property | y_property) & MINISCRIPT_PROPERTY_O; + if ((y_property & MINISCRIPT_PROPERTY_F) || (x_property & MINISCRIPT_PROPERTY_S)) + prop |= MINISCRIPT_PROPERTY_F; + if (!(prop & MINISCRIPT_TYPE_MASK)) + return 0; + + return prop; +} + +static int verify_miniscript_and_v(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + int ret = verify_miniscript_two_param_check(node, parent); + if (ret == WALLY_OK) { + node->type_properties = verify_miniscript_and_v_property( + node->child->type_properties, + node->child->next->type_properties); + if (!node->type_properties) + ret = WALLY_EINVAL; + } + return ret; +} + +static int verify_miniscript_and_b(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + uint32_t x_prop, y_prop; + int ret = verify_miniscript_two_param_check(node, parent); + if (ret != WALLY_OK) + return ret; + + x_prop = node->child->type_properties; + y_prop = node->child->next->type_properties; + node->type_properties = MINISCRIPT_PROPERTY_U | MINISCRIPT_PROPERTY_X; + node->type_properties |= x_prop & y_prop & + (MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_Z | MINISCRIPT_PROPERTY_M); + node->type_properties |= (x_prop | y_prop) & MINISCRIPT_PROPERTY_S; + node->type_properties |= x_prop & MINISCRIPT_PROPERTY_N; + if (y_prop & MINISCRIPT_TYPE_W) + node->type_properties |= x_prop & MINISCRIPT_TYPE_B; + if ((x_prop | y_prop) & MINISCRIPT_PROPERTY_Z) + node->type_properties |= (x_prop | y_prop) & MINISCRIPT_PROPERTY_O; + if (x_prop & MINISCRIPT_PROPERTY_Z) + node->type_properties |= y_prop & MINISCRIPT_PROPERTY_N; + if ((x_prop & y_prop) & MINISCRIPT_PROPERTY_S) + node->type_properties |= x_prop & y_prop & MINISCRIPT_PROPERTY_E; + if (((x_prop & y_prop) & MINISCRIPT_PROPERTY_F) || + !(~x_prop & (MINISCRIPT_PROPERTY_S | MINISCRIPT_PROPERTY_F)) || + !(~y_prop & (MINISCRIPT_PROPERTY_S | MINISCRIPT_PROPERTY_F))) + node->type_properties |= MINISCRIPT_PROPERTY_F; + + return WALLY_OK; +} + +static int verify_miniscript_and_n(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + int ret = verify_miniscript_two_param_check(node, parent); + if (ret == WALLY_OK) { + node->type_properties = verify_miniscript_andor_property( + node->child->type_properties, + node->child->next->type_properties, + MINISCRIPT_PROPERTY_OP_0); + if (!node->type_properties) + ret = WALLY_EINVAL; + } + return ret; +} + +static int verify_miniscript_or_b(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + uint32_t x_prop, y_prop; + int ret = verify_miniscript_two_param_check(node, parent); + if (ret != WALLY_OK) + return ret; + + x_prop = node->child->type_properties; + y_prop = node->child->next->type_properties; + node->type_properties = MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_U | MINISCRIPT_PROPERTY_X; + node->type_properties |= x_prop & y_prop & + (MINISCRIPT_PROPERTY_Z | MINISCRIPT_PROPERTY_S | MINISCRIPT_PROPERTY_E); + if (!(~x_prop & (MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_D)) && + !(~y_prop & (MINISCRIPT_TYPE_W | MINISCRIPT_PROPERTY_D))) + node->type_properties |= MINISCRIPT_TYPE_B; + if ((x_prop | y_prop) & MINISCRIPT_PROPERTY_Z) + node->type_properties |= (x_prop | y_prop) & MINISCRIPT_PROPERTY_O; + if (((x_prop | y_prop) & MINISCRIPT_PROPERTY_S) && + ((x_prop & y_prop) & MINISCRIPT_PROPERTY_E)) + node->type_properties |= x_prop & y_prop & MINISCRIPT_PROPERTY_M; + + return WALLY_OK; +} + +static int verify_miniscript_or_c(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + uint32_t x_prop, y_prop; + int ret = verify_miniscript_two_param_check(node, parent); + if (ret != WALLY_OK) + return ret; + + x_prop = node->child->type_properties; + y_prop = node->child->next->type_properties; + node->type_properties = MINISCRIPT_PROPERTY_F | MINISCRIPT_PROPERTY_X; + node->type_properties |= x_prop & y_prop & (MINISCRIPT_PROPERTY_Z | MINISCRIPT_PROPERTY_S); + if (!(~x_prop & (MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_U))) + node->type_properties |= y_prop & MINISCRIPT_TYPE_V; + if (y_prop & MINISCRIPT_PROPERTY_Z) + node->type_properties |= x_prop & MINISCRIPT_PROPERTY_O; + if ((x_prop & MINISCRIPT_PROPERTY_E) && ((x_prop | y_prop) & MINISCRIPT_PROPERTY_S)) + node->type_properties |= x_prop & y_prop & MINISCRIPT_PROPERTY_M; + + return ret; +} + +static int verify_miniscript_or_d(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + uint32_t x_prop, y_prop; + int ret = verify_miniscript_two_param_check(node, parent); + if (ret != WALLY_OK) + return ret; + + x_prop = node->child->type_properties; + y_prop = node->child->next->type_properties; + node->type_properties = MINISCRIPT_PROPERTY_X; + node->type_properties |= x_prop & y_prop & + (MINISCRIPT_PROPERTY_Z | MINISCRIPT_PROPERTY_E | MINISCRIPT_PROPERTY_S); + node->type_properties |= y_prop & + (MINISCRIPT_PROPERTY_U | MINISCRIPT_PROPERTY_F | MINISCRIPT_PROPERTY_D); + if (!(~x_prop & (MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_U))) + node->type_properties |= y_prop & MINISCRIPT_TYPE_B; + if (y_prop & MINISCRIPT_PROPERTY_Z) + node->type_properties |= x_prop & MINISCRIPT_PROPERTY_O; + if ((x_prop & MINISCRIPT_PROPERTY_E) && ((x_prop | y_prop) & MINISCRIPT_PROPERTY_S)) + node->type_properties |= x_prop & y_prop & MINISCRIPT_PROPERTY_M; + + return WALLY_OK; +} + +static uint32_t verify_miniscript_or_i_property(uint32_t x_property, uint32_t y_property) +{ + uint32_t prop = MINISCRIPT_PROPERTY_X; + prop |= x_property & y_property & + (MINISCRIPT_TYPE_V | MINISCRIPT_TYPE_B | MINISCRIPT_TYPE_K | + MINISCRIPT_PROPERTY_U | MINISCRIPT_PROPERTY_F | MINISCRIPT_PROPERTY_S); + if (!(prop & MINISCRIPT_TYPE_MASK)) + return 0; + + prop |= (x_property | y_property) & MINISCRIPT_PROPERTY_D; + if ((x_property & y_property) & MINISCRIPT_PROPERTY_Z) + prop |= MINISCRIPT_PROPERTY_O; + if ((x_property | y_property) & MINISCRIPT_PROPERTY_F) + prop |= (x_property | y_property) & MINISCRIPT_PROPERTY_E; + if ((x_property | y_property) & MINISCRIPT_PROPERTY_S) + prop |= x_property & y_property & MINISCRIPT_PROPERTY_M; + + return prop; +} + +static int verify_miniscript_or_i(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + int ret = verify_miniscript_two_param_check(node, parent); + if (ret == WALLY_OK) { + node->type_properties = verify_miniscript_or_i_property( + node->child->type_properties, + node->child->next->type_properties); + if (!node->type_properties) + ret = WALLY_EINVAL; + } + return ret; +} + +static int verify_miniscript_thresh(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + struct miniscript_node_t *top = NULL; + struct miniscript_node_t *child = NULL; + uint32_t count = 0; + uint32_t k = 0; + bool all_e = true; + bool all_m = true; + uint32_t args = 0; + uint32_t num_s = 0; + (void)parent; + + if (get_child_list_count(node) < 4) + return WALLY_EINVAL; + + top = node->child; + if (top->info || (top->kind != DESCRIPTOR_KIND_NUMBER) || (top->number < 0)) + return WALLY_EINVAL; + + k = (uint32_t) top->number; + if (k < 1) + return WALLY_EINVAL; + + child = top->next; + while (child) { + if (!child->info) + return WALLY_EINVAL; + + if (!count) { + if (~(child->type_properties) & (MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_U)) + return WALLY_EINVAL; + } else if (~(child->type_properties) & (MINISCRIPT_TYPE_W | MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_U)) + return WALLY_EINVAL; + + if (~(child->type_properties) & MINISCRIPT_PROPERTY_E) + all_e = false; + if (~(child->type_properties) & MINISCRIPT_PROPERTY_M) + all_m = false; + if ((child->type_properties) & MINISCRIPT_PROPERTY_S) + ++num_s; + if ((child->type_properties) & MINISCRIPT_PROPERTY_Z) + args += (~(child->type_properties) & MINISCRIPT_PROPERTY_O) ? 2 : 1; + + ++count; + child = child->next; + } + + if (k > count) + return WALLY_EINVAL; + + node->type_properties = MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_U; + if (args == 0) + node->type_properties |= MINISCRIPT_PROPERTY_Z; + else if (args == 1) + node->type_properties |= MINISCRIPT_PROPERTY_O; + if (all_e && num_s == count) + node->type_properties |= MINISCRIPT_PROPERTY_E; + if (all_e && all_m && num_s >= count - k) + node->type_properties |= MINISCRIPT_PROPERTY_M; + if (num_s >= count - k + 1) + node->type_properties |= MINISCRIPT_PROPERTY_S; + + return WALLY_OK; +} + +static int verify_miniscript_wrapper_a(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + uint32_t x_prop = node->type_properties; + (void)parent; + + if (!(x_prop & MINISCRIPT_TYPE_B)) + return WALLY_EINVAL; + + node->type_properties &= ~MINISCRIPT_TYPE_B; + node->type_properties |= MINISCRIPT_TYPE_W; + node->type_properties &= MINISCRIPT_TYPE_MASK | MINISCRIPT_PROPERTY_U | + MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_F | + MINISCRIPT_PROPERTY_E | MINISCRIPT_PROPERTY_M | + MINISCRIPT_PROPERTY_S; + node->type_properties |= MINISCRIPT_PROPERTY_X; + + return WALLY_OK; +} + + +static int verify_miniscript_wrapper_s(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + uint32_t x_prop = node->type_properties; + uint32_t need_type = MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_O; + (void)parent; + + if ((x_prop & need_type) != need_type) + return WALLY_EINVAL; + + node->type_properties &= ~need_type; + node->type_properties |= MINISCRIPT_TYPE_W; + node->type_properties &= MINISCRIPT_TYPE_MASK | MINISCRIPT_PROPERTY_U | + MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_F | + MINISCRIPT_PROPERTY_E | MINISCRIPT_PROPERTY_M | + MINISCRIPT_PROPERTY_S | MINISCRIPT_PROPERTY_X; + + return WALLY_OK; +} + +static int verify_miniscript_wrapper_c(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + uint32_t x_prop = node->type_properties; + (void)parent; + if (!(x_prop & MINISCRIPT_TYPE_K)) + return WALLY_EINVAL; + + node->type_properties &= ~MINISCRIPT_TYPE_K; + node->type_properties |= MINISCRIPT_TYPE_B; + node->type_properties &= MINISCRIPT_TYPE_MASK | MINISCRIPT_PROPERTY_O | + MINISCRIPT_PROPERTY_N | MINISCRIPT_PROPERTY_D | + MINISCRIPT_PROPERTY_F | MINISCRIPT_PROPERTY_E | + MINISCRIPT_PROPERTY_M; + node->type_properties |= MINISCRIPT_PROPERTY_U | MINISCRIPT_PROPERTY_S; + + return WALLY_OK; +} + +static int verify_miniscript_wrapper_t(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + (void)parent; + node->type_properties = verify_miniscript_and_v_property( + node->type_properties, MINISCRIPT_PROPERTY_OP_1); + if (!(node->type_properties & MINISCRIPT_TYPE_MASK)) + return WALLY_EINVAL; + /* prop >= MINISCRIPT_PROPERTY_F */ + return WALLY_OK; +} + +static int verify_miniscript_wrapper_d(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + uint32_t x_prop = node->type_properties; + uint32_t need_type = MINISCRIPT_TYPE_V | MINISCRIPT_PROPERTY_Z; + (void)parent; + + if ((x_prop & need_type) != need_type) + return WALLY_EINVAL; + + node->type_properties &= ~need_type; + node->type_properties |= MINISCRIPT_TYPE_B; + node->type_properties &= MINISCRIPT_TYPE_MASK | MINISCRIPT_PROPERTY_M | MINISCRIPT_PROPERTY_S; + node->type_properties |= MINISCRIPT_PROPERTY_N | MINISCRIPT_PROPERTY_U | + MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_X; + if (x_prop & MINISCRIPT_PROPERTY_Z) + node->type_properties |= MINISCRIPT_PROPERTY_O; + if (x_prop & MINISCRIPT_PROPERTY_F) { + node->type_properties &= ~MINISCRIPT_PROPERTY_F; + node->type_properties |= MINISCRIPT_PROPERTY_E; + } + return WALLY_OK; +} + +static int verify_miniscript_wrapper_v(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + uint32_t x_prop = node->type_properties; + (void)parent; + + if (!(x_prop & MINISCRIPT_TYPE_B)) + return WALLY_EINVAL; + + node->type_properties &= ~MINISCRIPT_TYPE_B; + node->type_properties |= MINISCRIPT_TYPE_V; + node->type_properties &= MINISCRIPT_TYPE_MASK | MINISCRIPT_PROPERTY_Z | + MINISCRIPT_PROPERTY_O | MINISCRIPT_PROPERTY_N | + MINISCRIPT_PROPERTY_M | MINISCRIPT_PROPERTY_S; + node->type_properties |= MINISCRIPT_PROPERTY_F | MINISCRIPT_PROPERTY_X; + return WALLY_OK; +} + +static int verify_miniscript_wrapper_j(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + uint32_t x_prop = node->type_properties; + uint32_t need_type = MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_N; + (void)parent; + + if ((x_prop & need_type) != need_type) + return WALLY_EINVAL; + + node->type_properties &= MINISCRIPT_TYPE_MASK | MINISCRIPT_PROPERTY_O | + MINISCRIPT_PROPERTY_U | MINISCRIPT_PROPERTY_M | + MINISCRIPT_PROPERTY_S; + node->type_properties |= MINISCRIPT_PROPERTY_N | MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_X; + if (x_prop & MINISCRIPT_PROPERTY_F) { + node->type_properties &= ~MINISCRIPT_PROPERTY_F; + node->type_properties |= MINISCRIPT_PROPERTY_E; + } + return WALLY_OK; +} + +static int verify_miniscript_wrapper_n(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + (void)parent; + if (!(node->type_properties & MINISCRIPT_TYPE_B)) + return WALLY_EINVAL; + + node->type_properties &= MINISCRIPT_TYPE_MASK | MINISCRIPT_PROPERTY_Z | + MINISCRIPT_PROPERTY_O | MINISCRIPT_PROPERTY_N | + MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_F | + MINISCRIPT_PROPERTY_E | MINISCRIPT_PROPERTY_M | + MINISCRIPT_PROPERTY_S; + node->type_properties |= MINISCRIPT_PROPERTY_X; + return WALLY_OK; +} + +static int verify_miniscript_wrapper_l(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + (void)parent; + node->type_properties = verify_miniscript_or_i_property( + MINISCRIPT_PROPERTY_OP_0, node->type_properties); + if (!node->type_properties) + return WALLY_EINVAL; + return WALLY_OK; +} + +static int verify_miniscript_wrapper_u(struct miniscript_node_t *node, struct miniscript_node_t *parent) +{ + (void)parent; + node->type_properties = verify_miniscript_or_i_property( + node->type_properties, MINISCRIPT_PROPERTY_OP_0); + if (!node->type_properties) + return WALLY_EINVAL; + return WALLY_OK; +} + +static int generate_by_miniscript_pk_k( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + int ret; + + if (!node->child || (script_len < EC_PUBLIC_KEY_LEN * 2) || (parent && !parent->info)) + return WALLY_EINVAL; + + ret = generate_script_from_miniscript(node->child, node, derive_child_num, &script[1], script_len - 1, write_len); + if (ret != WALLY_OK) + return ret; + + if (*write_len + 1 > DESCRIPTOR_REDEEM_SCRIPT_MAX_SIZE) + return WALLY_EINVAL; + + script[0] = (unsigned char)*write_len; + ++(*write_len); + return ret; +} + +static int generate_by_miniscript_pk_h( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + int ret; + size_t child_write_len = *write_len; + unsigned char pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN]; + + if (!node->child || (script_len < WALLY_SCRIPTPUBKEY_P2PKH_LEN - 1) || (parent && !parent->info)) + return WALLY_EINVAL; + if (node->child->is_xonly_key) + return WALLY_EINVAL; + + ret = generate_script_from_miniscript(node->child, node, derive_child_num, pubkey, sizeof(pubkey), &child_write_len); + if (ret != WALLY_OK) + return ret; + + ret = wally_hash160(pubkey, child_write_len, &script[3], HASH160_LEN); + if (ret != WALLY_OK) + return ret; + + script[0] = OP_DUP; + script[1] = OP_HASH160; + script[2] = HASH160_LEN; + script[HASH160_LEN + 3] = OP_EQUALVERIFY; + *write_len = HASH160_LEN + 4; + return ret; +} + +static int generate_by_descriptor_sh( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + int ret; + size_t child_write_len = *write_len; + unsigned char buf[WALLY_SCRIPTPUBKEY_P2SH_LEN]; + if (!node->child || (script_len < WALLY_SCRIPTPUBKEY_P2SH_LEN) || (parent && !parent->info)) + return WALLY_EINVAL; + + ret = generate_script_from_miniscript(node->child, node, derive_child_num, script, script_len, &child_write_len); + if (ret != WALLY_OK) + return ret; + + if (child_write_len > DESCRIPTOR_REDEEM_SCRIPT_MAX_SIZE) + return WALLY_EINVAL; + + ret = wally_scriptpubkey_p2sh_from_bytes(script, child_write_len, WALLY_SCRIPT_HASH160, buf, WALLY_SCRIPTPUBKEY_P2SH_LEN, write_len); + if (ret == WALLY_OK) + memcpy(script, buf, *write_len); + + return ret; +} + +static int generate_by_descriptor_wsh( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + int ret; + size_t child_write_len = *write_len; + unsigned char output[WALLY_SCRIPTPUBKEY_P2WSH_LEN]; + + if (!node->child || (script_len < sizeof(output)) || (parent && !parent->info)) + return WALLY_EINVAL; + + ret = generate_script_from_miniscript(node->child, node, derive_child_num, script, script_len, &child_write_len); + if (ret != WALLY_OK) + return ret; + + if (child_write_len > DESCRIPTOR_REDEEM_SCRIPT_MAX_SIZE) + return WALLY_EINVAL; + + ret = wally_witness_program_from_bytes(script, child_write_len, WALLY_SCRIPT_SHA256, output, WALLY_SCRIPTPUBKEY_P2WSH_LEN, write_len); + if (ret == WALLY_OK) + memcpy(script, output, *write_len); + + return ret; +} + +static int generate_by_descriptor_pk( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + int ret; + + ret = generate_by_miniscript_pk_k(node, parent, derive_child_num, script, script_len, write_len); + if (ret != WALLY_OK) + return ret; + + return generate_by_wrapper_c(script, script_len, write_len); +} + +static int generate_by_descriptor_pkh( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + int ret; + + if (script_len < WALLY_SCRIPTPUBKEY_P2PKH_LEN) + return WALLY_EINVAL; + + ret = generate_by_miniscript_pk_h(node, parent, derive_child_num, script, script_len, write_len); + if (ret != WALLY_OK) + return ret; + + return generate_by_wrapper_c(script, script_len, write_len); +} + +static int generate_by_descriptor_wpkh( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + int ret; + size_t child_write_len = *write_len; + unsigned char output[WALLY_SCRIPTPUBKEY_P2WPKH_LEN]; + + if (!node->child || (script_len < sizeof(output)) || (parent && !parent->info)) + return WALLY_EINVAL; + + ret = generate_script_from_miniscript(node->child, node, derive_child_num, script, script_len, &child_write_len); + if (ret != WALLY_OK) + return ret; + + if (child_write_len > DESCRIPTOR_REDEEM_SCRIPT_MAX_SIZE) + return WALLY_EINVAL; + + ret = wally_witness_program_from_bytes(script, child_write_len, WALLY_SCRIPT_HASH160, output, WALLY_SCRIPTPUBKEY_P2WPKH_LEN, write_len); + if (ret == WALLY_OK) + memcpy(script, output, *write_len); + + return ret; +} + +static int generate_by_descriptor_combo( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + if (has_uncompressed_key_by_child(node)) { + return generate_by_descriptor_pkh( + node, parent, derive_child_num, script, script_len, write_len); + } else { + return generate_by_descriptor_wpkh( + node, parent, derive_child_num, script, script_len, write_len); + } +} + +static int compare_multisig_node(const void *source, const void *destination) +{ + const struct multisig_sort_data_t *src = (const struct multisig_sort_data_t *)source; + const struct multisig_sort_data_t *dest = (const struct multisig_sort_data_t *)destination; + uint32_t index = 0; + if (src->script_size != dest->script_size) { + /* Head byte of compressed pubkey and uncompressed pubkey are different. */ + return (int)src->script[0] - (int)dest->script[0]; + } + + for (; index < src->script_size; ++index) { + if (src->script[index] != dest->script[index]) { + return (int)src->script[index] - (int)dest->script[index]; + } + } + return 0; +} + +static int generate_by_descriptor_multisig( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + int flag, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + int ret; + size_t child_write_len = 0; + size_t offset; + uint32_t count = 0; + uint32_t index = 0; + struct miniscript_node_t *child = node->child; + struct miniscript_node_t *temp_node; + struct multisig_sort_data_t *sorted_node_array; + size_t check_len = (DESCRIPTOR_REDEEM_SCRIPT_MAX_SIZE > script_len) ? script_len : DESCRIPTOR_REDEEM_SCRIPT_MAX_SIZE; + + if (!child || (parent && !parent->info)) + return WALLY_EINVAL; + + ret = generate_script_from_miniscript(child, node, derive_child_num, script, script_len, &offset); + if (ret != WALLY_OK) + return ret; + + temp_node = child->next; + while (temp_node != NULL) { + ++count; + temp_node = temp_node->next; + } + + sorted_node_array = (struct multisig_sort_data_t *)wally_malloc( + count * sizeof(struct multisig_sort_data_t)); + if (!sorted_node_array) + return WALLY_ENOMEM; + + temp_node = child->next; + while (temp_node != NULL) { + sorted_node_array[index].node = temp_node; + ++index; + temp_node = temp_node->next; + } + + if (ret == WALLY_OK) { + for (index = 0; index < count; ++index) { + child_write_len = 0; + ret = generate_script_from_miniscript( + sorted_node_array[index].node, node, derive_child_num, + sorted_node_array[index].script, + sizeof(sorted_node_array[index].script), + &child_write_len); + if (ret != WALLY_OK) + break; + sorted_node_array[index].script_size = (uint32_t)child_write_len; + } + } + if (ret == WALLY_OK) { + if (flag == WALLY_SCRIPT_MULTISIG_SORTED) { + qsort(sorted_node_array, count, sizeof(struct multisig_sort_data_t), + compare_multisig_node); + } + for (index = 0; index < count; ++index) { + if (offset + sorted_node_array[index].script_size + 1 > check_len) { + ret = WALLY_EINVAL; + break; + } + + memcpy(&script[offset + 1], sorted_node_array[index].script, + sorted_node_array[index].script_size); + script[offset] = (unsigned char) sorted_node_array[index].script_size; + offset += sorted_node_array[index].script_size + 1; + } + } + + if (ret == WALLY_OK) { + ret = generate_script_from_number((int64_t)count, parent, &script[offset], + check_len - offset, &child_write_len); + } + + if (ret == WALLY_OK) { + offset += child_write_len; + if (offset + 1 > check_len) { + ret = WALLY_EINVAL; + } else { + script[offset] = OP_CHECKMULTISIG; + *write_len = offset + 1; + } + } + wally_free(sorted_node_array); + return WALLY_OK; +} + +static int generate_by_descriptor_multi( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + return generate_by_descriptor_multisig( + node, parent, derive_child_num, 0, script, script_len, write_len); +} + +static int generate_by_descriptor_sorted_multi( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + return generate_by_descriptor_multisig(node, parent, derive_child_num, + WALLY_SCRIPT_MULTISIG_SORTED, + script, script_len, write_len); +} + + +static int generate_by_descriptor_addr( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + int ret; + if (!node->child || (script_len == 0) || (parent && !parent->info)) + return WALLY_EINVAL; + + ret = generate_script_from_miniscript( + node->child, node, derive_child_num, script, script_len, write_len); + if (*write_len > DESCRIPTOR_REDEEM_SCRIPT_MAX_SIZE) + return WALLY_EINVAL; + + return ret; +} + +static int generate_by_descriptor_raw( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + int ret; + if (!node->child || (script_len == 0) || (parent && !parent->info)) + return WALLY_EINVAL; + + ret = generate_script_from_miniscript( + node->child, node, derive_child_num, script, script_len, write_len); + if (*write_len > DESCRIPTOR_REDEEM_SCRIPT_MAX_SIZE) + return WALLY_EINVAL; + + return ret; +} + +static int generate_by_miniscript_older( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + int ret; + size_t child_write_len = *write_len; + if (!node->child || (script_len < DESCRIPTOR_MIN_SIZE) || (parent && !parent->info)) + return WALLY_EINVAL; + + ret = generate_script_from_miniscript( + node->child, node, derive_child_num, script, script_len, &child_write_len); + if (ret != WALLY_OK) + return ret; + + if (child_write_len + 1 > DESCRIPTOR_REDEEM_SCRIPT_MAX_SIZE) + return WALLY_EINVAL; + + script[child_write_len] = OP_CHECKSEQUENCEVERIFY; + *write_len = child_write_len + 1; + return ret; +} + +static int generate_by_miniscript_after( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + int ret; + size_t child_write_len = *write_len; + if (!node->child || (script_len < DESCRIPTOR_MIN_SIZE) || (parent && !parent->info)) + return WALLY_EINVAL; + + ret = generate_script_from_miniscript( + node->child, node, derive_child_num, script, script_len, &child_write_len); + if (ret != WALLY_OK) + return ret; + + if (child_write_len + 1 > DESCRIPTOR_REDEEM_SCRIPT_MAX_SIZE) + return WALLY_EINVAL; + + script[child_write_len] = OP_CHECKLOCKTIMEVERIFY; + *write_len = child_write_len + 1; + return ret; +} + +static int generate_by_miniscript_crypto( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char crypto_op_code, + unsigned char crypto_size, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + int ret; + size_t child_write_len = *write_len; + size_t check_len = (DESCRIPTOR_REDEEM_SCRIPT_MAX_SIZE > script_len) ? + script_len : DESCRIPTOR_REDEEM_SCRIPT_MAX_SIZE; + + if (!node->child || (script_len < (size_t)(crypto_size + 8)) || (parent && !parent->info)) + return WALLY_EINVAL; + + ret = generate_script_from_miniscript(node->child, node, derive_child_num, + &script[6], script_len - 8, &child_write_len); + if (ret != WALLY_OK) + return ret; + + if (child_write_len + 7 > check_len) + return WALLY_EINVAL; + + script[0] = OP_SIZE; + script[1] = 0x01; + script[2] = 0x20; + script[3] = OP_EQUALVERIFY; + script[4] = crypto_op_code; + script[5] = crypto_size; + script[child_write_len + 6] = OP_EQUAL; + *write_len = child_write_len + 7; + return ret; +} + +static int generate_by_miniscript_sha256( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + return generate_by_miniscript_crypto(node, parent, derive_child_num, OP_SHA256, SHA256_LEN, + script, script_len, write_len); +} + +static int generate_by_miniscript_hash256( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + return generate_by_miniscript_crypto(node, parent, derive_child_num, OP_HASH256, SHA256_LEN, + script, script_len, write_len); +} + +static int generate_by_miniscript_ripemd160( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + return generate_by_miniscript_crypto(node, parent, derive_child_num, OP_RIPEMD160, + HASH160_LEN, script, script_len, write_len); +} + +static int generate_by_miniscript_hash160( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + return generate_by_miniscript_crypto(node, parent, derive_child_num, OP_HASH160, HASH160_LEN, + script, script_len, write_len); +} + +static int generate_by_miniscript_concat( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + size_t target_num, + const size_t *reference_indexes, + unsigned char *prev_insert, + size_t prev_insert_num, + unsigned char *first_insert, + size_t first_insert_num, + unsigned char *second_insert, + size_t second_insert_num, + unsigned char *last_append, + size_t last_append_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + int ret; + size_t output_len; + size_t total = prev_insert_num + first_insert_num + second_insert_num; + size_t index = 0; + size_t offset = 0; + struct miniscript_node_t *child[3]; + size_t default_indexes[] = {0, 1, 2}; + const size_t *indexes = reference_indexes; + size_t check_len = (DESCRIPTOR_REDEEM_SCRIPT_MAX_SIZE > script_len) ? + script_len : DESCRIPTOR_REDEEM_SCRIPT_MAX_SIZE; + + if (!node->child || (parent && !parent->info)) + return WALLY_EINVAL; + + if (!reference_indexes) + indexes = default_indexes; + + wally_bzero(child, sizeof(child)); + + for (index = 0; index < target_num; ++index) { + child[index] = (index == 0) ? node->child : child[index - 1]->next; + if (!child[index]) + return WALLY_EINVAL; + } + + for (index = 0; index < target_num; ++index) { + if ((index == 0) && prev_insert_num) { + memcpy(script + offset, prev_insert, prev_insert_num); + offset += prev_insert_num; + } + if ((index == 1) && first_insert_num) { + memcpy(script + offset, first_insert, first_insert_num); + offset += first_insert_num; + } + if ((index == 2) && second_insert_num) { + memcpy(script + offset, second_insert, second_insert_num); + offset += second_insert_num; + } + + output_len = 0; + ret = generate_script_from_miniscript(child[indexes[index]], node, derive_child_num, + &script[offset], script_len - offset - 1, + &output_len); + if (ret != WALLY_OK) + return ret; + + offset += output_len; + total += output_len; + if (total > check_len) + return WALLY_EINVAL; + } + + if (total + last_append_num > check_len) + return WALLY_EINVAL; + if (last_append_num) { + memcpy(script + offset, last_append, last_append_num); + offset += last_append_num; + } + + if (ret == WALLY_OK) + *write_len = offset; + + return ret; +} + +static int generate_by_miniscript_andor( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + unsigned char first_op[] = {OP_NOTIF}; + unsigned char second_op[] = {OP_ELSE}; + unsigned char last_op[] = {OP_ENDIF}; + const size_t indexes[] = {0, 2, 1}; + /* [X] NOTIF 0 ELSE [Y] ENDIF */ + return generate_by_miniscript_concat( + node, + parent, + derive_child_num, + 3, + indexes, + NULL, + 0, + first_op, + sizeof(first_op) / sizeof(unsigned char), + second_op, + sizeof(second_op) / sizeof(unsigned char), + last_op, + sizeof(last_op) / sizeof(unsigned char), + script, + script_len, + write_len); +} + +static int generate_by_miniscript_and_v( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + /* [X] [Y] */ + const size_t indexes[] = {0, 1}; + return generate_by_miniscript_concat( + node, + parent, + derive_child_num, + 2, + indexes, + NULL, + 0, + NULL, + 0, + NULL, + 0, + NULL, + 0, + script, + script_len, + write_len); +} + +static int generate_by_miniscript_and_b( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + unsigned char append = OP_BOOLAND; + const size_t indexes[] = {0, 1}; + /* [X] [Y] BOOLAND */ + return generate_by_miniscript_concat( + node, + parent, + derive_child_num, + 2, + indexes, + NULL, + 0, + NULL, + 0, + NULL, + 0, + &append, + 1, + script, + script_len, + write_len); +} + +static int generate_by_miniscript_and_n( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + unsigned char middle_op[] = {OP_NOTIF, OP_0, OP_ELSE}; + unsigned char last_op[] = {OP_ENDIF}; + const size_t indexes[] = {0, 1}; + /* [X] NOTIF 0 ELSE [Y] ENDIF */ + return generate_by_miniscript_concat( + node, + parent, + derive_child_num, + 2, + indexes, + NULL, + 0, + middle_op, + sizeof(middle_op) / sizeof(unsigned char), + NULL, + 0, + last_op, + sizeof(last_op) / sizeof(unsigned char), + script, + script_len, + write_len); +} + +static int generate_by_miniscript_or_b( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + unsigned char append = OP_BOOLOR; + const size_t indexes[] = {0, 1}; + /* [X] [Y] OP_BOOLOR */ + return generate_by_miniscript_concat( + node, + parent, + derive_child_num, + 2, + indexes, + NULL, + 0, + NULL, + 0, + NULL, + 0, + &append, + 1, + script, + script_len, + write_len); +} + +static int generate_by_miniscript_or_c( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + unsigned char middle_op[] = {OP_NOTIF}; + unsigned char last_op[] = {OP_ENDIF}; + const size_t indexes[] = {0, 1}; + /* [X] NOTIF [Z] ENDIF */ + return generate_by_miniscript_concat( + node, + parent, + derive_child_num, + 2, + indexes, + NULL, + 0, + middle_op, + sizeof(middle_op) / sizeof(unsigned char), + NULL, + 0, + last_op, + sizeof(last_op) / sizeof(unsigned char), + script, + script_len, + write_len); +} + +static int generate_by_miniscript_or_d( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + unsigned char middle_op[] = {OP_IFDUP, OP_NOTIF}; + unsigned char last_op[] = {OP_ENDIF}; + const size_t indexes[] = {0, 1}; + /* [X] IFDUP NOTIF [Z] ENDIF */ + return generate_by_miniscript_concat( + node, + parent, + derive_child_num, + 2, + indexes, + NULL, + 0, + middle_op, + sizeof(middle_op) / sizeof(unsigned char), + NULL, + 0, + last_op, + sizeof(last_op) / sizeof(unsigned char), + script, + script_len, + write_len); +} + +static int generate_by_miniscript_or_i( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + unsigned char top_op[] = {OP_IF}; + unsigned char middle_op[] = {OP_ELSE}; + unsigned char last_op[] = {OP_ENDIF}; + const size_t indexes[] = {0, 1}; + /* IF [X] ELSE [Z] ENDIF */ + return generate_by_miniscript_concat( + node, + parent, + derive_child_num, + 2, + indexes, + top_op, + sizeof(top_op) / sizeof(unsigned char), + middle_op, + sizeof(middle_op) / sizeof(unsigned char), + NULL, + 0, + last_op, + sizeof(last_op) / sizeof(unsigned char), + script, + script_len, + write_len); +} + +static int generate_by_miniscript_thresh( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + int32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + /* [X1] [X2] ADD ... [Xn] ADD EQUAL */ + int ret; + size_t output_len; + size_t offset = 0; + size_t count = 0; + struct miniscript_node_t *child = node->child; + size_t check_len = (DESCRIPTOR_REDEEM_SCRIPT_MAX_SIZE > script_len) ? script_len : DESCRIPTOR_REDEEM_SCRIPT_MAX_SIZE; + + if (!child || (parent && !parent->info)) + return WALLY_EINVAL; + + child = child->next; + while (child != NULL) { + output_len = 0; + ret = generate_script_from_miniscript(child, + node, + derive_child_num, + &script[offset], + script_len - offset - 1, + &output_len); + if (ret != WALLY_OK) + return ret; + + ++count; + offset += output_len; + if (offset >= check_len) + return WALLY_EINVAL; + + if (count != 1) { + if (offset + 1 >= check_len) + return WALLY_EINVAL; + + script[offset] = OP_ADD; + ++offset; + } + + child = child->next; + } + + ret = generate_script_from_miniscript(node->child, + node, + derive_child_num, + &script[offset], + script_len - offset - 1, + &output_len); + if (ret != WALLY_OK) + return ret; + + offset += output_len; + if (offset + 1 >= check_len) + return WALLY_EINVAL; + + script[offset] = OP_EQUAL; + *write_len = offset + 1; + return WALLY_OK; +} + +static int generate_by_wrapper_a( + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + size_t used_len = *write_len; + if (!used_len || (used_len + 2 > script_len) || (used_len + 2 > DESCRIPTOR_WITNESS_SCRIPT_MAX_SIZE)) + return WALLY_EINVAL; + + memmove(script + 1, script, used_len); + script[0] = OP_TOALTSTACK; + script[used_len + 1] = OP_FROMALTSTACK; + *write_len = used_len + 2; + return WALLY_OK; +} + +static int generate_by_wrapper_s( + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + size_t used_len = *write_len; + if (!used_len || (used_len + 1 > script_len) || (used_len + 1 > DESCRIPTOR_WITNESS_SCRIPT_MAX_SIZE)) + return WALLY_EINVAL; + + memmove(script + 1, script, used_len); + script[0] = OP_SWAP; + *write_len = used_len + 1; + return WALLY_OK; +} + +static int generate_by_wrapper_c( + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + size_t used_len = *write_len; + if (!used_len || (used_len + 1 > script_len) || (used_len + 1 > DESCRIPTOR_WITNESS_SCRIPT_MAX_SIZE)) + return WALLY_EINVAL; + + script[used_len] = OP_CHECKSIG; + *write_len = used_len + 1; + return WALLY_OK; +} + +static int generate_by_wrapper_t( + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + size_t used_len = *write_len; + if (!used_len || (used_len + 1 > script_len) || (used_len + 1 > DESCRIPTOR_WITNESS_SCRIPT_MAX_SIZE)) + return WALLY_EINVAL; + + script[used_len] = OP_1; + *write_len = used_len + 1; + return WALLY_OK; +} + +static int generate_by_wrapper_d( + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + size_t used_len = *write_len; + if (!used_len || (used_len + 3 > script_len) || (used_len + 3 > DESCRIPTOR_WITNESS_SCRIPT_MAX_SIZE)) + return WALLY_EINVAL; + + memmove(script + 2, script, used_len); + script[0] = OP_DUP; + script[1] = OP_IF; + script[used_len + 2] = OP_ENDIF; + *write_len = used_len + 3; + return WALLY_OK; +} + +static int generate_by_wrapper_v( + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + size_t used_len = *write_len; + if (!used_len) + return WALLY_EINVAL; + + if (script[used_len - 1] == OP_EQUAL) { + script[used_len - 1] = OP_EQUALVERIFY; + } else if (script[used_len - 1] == OP_NUMEQUAL) { + script[used_len - 1] = OP_NUMEQUALVERIFY; + } else if (script[used_len - 1] == OP_CHECKSIG) { + script[used_len - 1] = OP_CHECKSIGVERIFY; + } else if (script[used_len - 1] == OP_CHECKMULTISIG) { + script[used_len - 1] = OP_CHECKMULTISIGVERIFY; + } else if (script[used_len - 1] == OP_CHECKMULTISIG) { + script[used_len - 1] = OP_CHECKMULTISIGVERIFY; + } else if ((used_len + 1) > script_len) { + return WALLY_EINVAL; + } else { + script[used_len] = OP_VERIFY; + *write_len = used_len + 1; + if (*write_len > DESCRIPTOR_WITNESS_SCRIPT_MAX_SIZE) + return WALLY_EINVAL; + } + return WALLY_OK; +} + +static int generate_by_wrapper_j( + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + size_t used_len = *write_len; + if (!used_len || (used_len + 4 > script_len) || (used_len + 4 > DESCRIPTOR_WITNESS_SCRIPT_MAX_SIZE)) + return WALLY_EINVAL; + + memmove(script + 3, script, used_len); + script[0] = OP_SIZE; + script[1] = OP_0NOTEQUAL; + script[2] = OP_IF; + script[used_len + 3] = OP_ENDIF; + *write_len = used_len + 4; + return WALLY_OK; +} + +static int generate_by_wrapper_n( + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + size_t used_len = *write_len; + if (!used_len || (used_len + 1 > script_len) || (used_len + 1 > DESCRIPTOR_WITNESS_SCRIPT_MAX_SIZE)) + return WALLY_EINVAL; + + script[used_len] = OP_0NOTEQUAL; + *write_len = used_len + 1; + return WALLY_OK; +} + +static int generate_by_wrapper_l( + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + size_t used_len = *write_len; + if (!used_len || (used_len + 4 > script_len) || (used_len + 4 > DESCRIPTOR_WITNESS_SCRIPT_MAX_SIZE)) + return WALLY_EINVAL; + + memmove(script + 3, script, used_len); + script[0] = OP_IF; + script[1] = OP_0; + script[2] = OP_ELSE; + script[used_len + 3] = OP_ENDIF; + *write_len = used_len + 4; + return WALLY_OK; +} + +static int generate_by_wrapper_u( + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + size_t used_len = *write_len; + if (!used_len || (used_len + 4 > script_len) || (used_len + 4 > DESCRIPTOR_WITNESS_SCRIPT_MAX_SIZE)) + return WALLY_EINVAL; + + memmove(script + 1, script, used_len); + script[0] = OP_IF; + script[used_len + 1] = OP_ELSE; + script[used_len + 2] = OP_0; + script[used_len + 3] = OP_ENDIF; + *write_len = used_len + 4; + return WALLY_OK; +} + +static const struct miniscript_item_t miniscript_info_table[] = { + /* output descriptor */ + { + "sh", DESCRIPTOR_KIND_DESCRIPTOR_SH, 0, 1, verify_descriptor_sh, generate_by_descriptor_sh + }, + { + "wsh", DESCRIPTOR_KIND_DESCRIPTOR_WSH, 0, 1, verify_descriptor_wsh, generate_by_descriptor_wsh + }, + { /* c:pk_k */ + "pk", DESCRIPTOR_KIND_DESCRIPTOR_PK | DESCRIPTOR_KIND_MINISCRIPT_PK, + MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_O | MINISCRIPT_PROPERTY_N | MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_U | MINISCRIPT_PROPERTY_E | MINISCRIPT_PROPERTY_M | MINISCRIPT_PROPERTY_S | MINISCRIPT_PROPERTY_X, + 1, verify_descriptor_pk, generate_by_descriptor_pk + }, + { /* c:pk_h */ + "pkh", DESCRIPTOR_KIND_DESCRIPTOR_PKH | DESCRIPTOR_KIND_MINISCRIPT_PKH, + MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_N | MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_U | MINISCRIPT_PROPERTY_E | MINISCRIPT_PROPERTY_M | MINISCRIPT_PROPERTY_S | MINISCRIPT_PROPERTY_X, + 1, verify_descriptor_pkh, generate_by_descriptor_pkh + }, + { + "wpkh", DESCRIPTOR_KIND_DESCRIPTOR_WPKH, 0, 1, verify_descriptor_wpkh, generate_by_descriptor_wpkh + }, + { + "combo", DESCRIPTOR_KIND_DESCRIPTOR_COMBO, 0, 1, verify_descriptor_combo, generate_by_descriptor_combo + }, + { + "multi", DESCRIPTOR_KIND_DESCRIPTOR_MULTI | DESCRIPTOR_KIND_MINISCRIPT_MULTI, + MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_N | MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_U | MINISCRIPT_PROPERTY_E | MINISCRIPT_PROPERTY_M | MINISCRIPT_PROPERTY_S, + 1, verify_descriptor_multi, generate_by_descriptor_multi + }, + { + "sortedmulti", DESCRIPTOR_KIND_DESCRIPTOR_SORTEDMULTI, 0, -1, verify_descriptor_sortedmulti, generate_by_descriptor_sorted_multi + }, + { + "addr", DESCRIPTOR_KIND_DESCRIPTOR_ADDR, 0, 1, verify_descriptor_addr, generate_by_descriptor_addr + }, + { + "raw", DESCRIPTOR_KIND_DESCRIPTOR_RAW, 0, 1, verify_descriptor_raw, generate_by_descriptor_raw + }, + /* miniscript */ + { + "pk_k", DESCRIPTOR_KIND_MINISCRIPT_PK_K, + MINISCRIPT_TYPE_K | MINISCRIPT_PROPERTY_O | MINISCRIPT_PROPERTY_N | MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_U | MINISCRIPT_PROPERTY_E | MINISCRIPT_PROPERTY_M | MINISCRIPT_PROPERTY_S | MINISCRIPT_PROPERTY_X, + 1, verify_miniscript_pk, generate_by_miniscript_pk_k + }, + { + "pk_h", DESCRIPTOR_KIND_MINISCRIPT_PK_H, + MINISCRIPT_TYPE_K | MINISCRIPT_PROPERTY_N | MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_U | MINISCRIPT_PROPERTY_E | MINISCRIPT_PROPERTY_M | MINISCRIPT_PROPERTY_S | MINISCRIPT_PROPERTY_X, + 1, verify_miniscript_pkh, generate_by_miniscript_pk_h + }, + { + "older", DESCRIPTOR_KIND_MINISCRIPT_OLDER, + MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_Z | MINISCRIPT_PROPERTY_F | MINISCRIPT_PROPERTY_M | MINISCRIPT_PROPERTY_X, + 1, verify_miniscript_older, generate_by_miniscript_older + }, + { + "after", DESCRIPTOR_KIND_MINISCRIPT_AFTER, + MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_Z | MINISCRIPT_PROPERTY_F | MINISCRIPT_PROPERTY_M | MINISCRIPT_PROPERTY_X, + 1, verify_miniscript_after, generate_by_miniscript_after + }, + { + "sha256", DESCRIPTOR_KIND_MINISCRIPT_SHA256, + MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_O | MINISCRIPT_PROPERTY_N | MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_U | MINISCRIPT_PROPERTY_M, + 1, verify_miniscript_sha256, generate_by_miniscript_sha256 + }, + { + "hash256", DESCRIPTOR_KIND_MINISCRIPT_HASH256, + MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_O | MINISCRIPT_PROPERTY_N | MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_U | MINISCRIPT_PROPERTY_M, + 1, verify_miniscript_hash256, generate_by_miniscript_hash256 + }, + { + "ripemd160", DESCRIPTOR_KIND_MINISCRIPT_RIPEMD160, + MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_O | MINISCRIPT_PROPERTY_N | MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_U | MINISCRIPT_PROPERTY_M, + 1, verify_miniscript_ripemd160, generate_by_miniscript_ripemd160 + }, + { + "hash160", DESCRIPTOR_KIND_MINISCRIPT_HASH160, + MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_O | MINISCRIPT_PROPERTY_N | MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_U | MINISCRIPT_PROPERTY_M, + 1, verify_miniscript_hash160, generate_by_miniscript_hash160 + }, + { + "andor", DESCRIPTOR_KIND_MINISCRIPT_ANDOR, 0, 3, verify_miniscript_andor, generate_by_miniscript_andor + }, + { + "and_v", DESCRIPTOR_KIND_MINISCRIPT_AND_V, 0, 2, verify_miniscript_and_v, generate_by_miniscript_and_v + }, + { + "and_b", DESCRIPTOR_KIND_MINISCRIPT_AND_B, MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_U, + 2, verify_miniscript_and_b, generate_by_miniscript_and_b + }, + { + "and_n", DESCRIPTOR_KIND_MINISCRIPT_AND_N, 0, 2, verify_miniscript_and_n, generate_by_miniscript_and_n + }, + { + "or_b", DESCRIPTOR_KIND_MINISCRIPT_OR_B, + MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_U, + 2, verify_miniscript_or_b, generate_by_miniscript_or_b + }, + { + "or_c", DESCRIPTOR_KIND_MINISCRIPT_OR_C, MINISCRIPT_TYPE_V, 2, verify_miniscript_or_c, generate_by_miniscript_or_c + }, + { + "or_d", DESCRIPTOR_KIND_MINISCRIPT_OR_D, MINISCRIPT_TYPE_B, 2, verify_miniscript_or_d, generate_by_miniscript_or_d + }, + { + "or_i", DESCRIPTOR_KIND_MINISCRIPT_OR_I, 0, 2, verify_miniscript_or_i, generate_by_miniscript_or_i + }, + { + "thresh", DESCRIPTOR_KIND_MINISCRIPT_THRESH, + MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_U, + -1, verify_miniscript_thresh, generate_by_miniscript_thresh + } +}; + +static const struct miniscript_wrapper_item_t miniscript_wrapper_table[] = { + { "a", DESCRIPTOR_TYPE_WRAPPER_A, MINISCRIPT_TYPE_W | MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_U, + 0, verify_miniscript_wrapper_a, generate_by_wrapper_a }, + { "s", DESCRIPTOR_TYPE_WRAPPER_S, 0, 0, verify_miniscript_wrapper_s, generate_by_wrapper_s }, + { "c", DESCRIPTOR_TYPE_WRAPPER_C, 0, 0, verify_miniscript_wrapper_c, generate_by_wrapper_c }, + { "t", DESCRIPTOR_TYPE_WRAPPER_T, 0, 0, verify_miniscript_wrapper_t, generate_by_wrapper_t }, + { "d", DESCRIPTOR_TYPE_WRAPPER_D, 0, 0, verify_miniscript_wrapper_d, generate_by_wrapper_d }, + { "v", DESCRIPTOR_TYPE_WRAPPER_V, 0, 0, verify_miniscript_wrapper_v, generate_by_wrapper_v }, + { "j", DESCRIPTOR_TYPE_WRAPPER_J, 0, 0, verify_miniscript_wrapper_j, generate_by_wrapper_j }, + { "n", DESCRIPTOR_TYPE_WRAPPER_N, 0, 0, verify_miniscript_wrapper_n, generate_by_wrapper_n }, + { "l", DESCRIPTOR_TYPE_WRAPPER_L, 0, 0, verify_miniscript_wrapper_l, generate_by_wrapper_l }, + { "u", DESCRIPTOR_TYPE_WRAPPER_U, 0, 0, verify_miniscript_wrapper_u, generate_by_wrapper_u }, +}; + +static uint32_t convert_miniscript_wrapper_flag(const char *wrapper) +{ + uint32_t result = 0; + size_t index; + size_t max = sizeof(miniscript_wrapper_table) / sizeof(struct miniscript_wrapper_item_t); + + for (index = 0; index < max; ++index) { + if (strchr(wrapper, miniscript_wrapper_table[index].name[0]) != NULL) { + result |= (uint32_t)miniscript_wrapper_table[index].kind; + } + } + return result; +} + +static const struct miniscript_item_t *search_miniscript_info(const char *name, int target) +{ + const struct miniscript_item_t *result = NULL; + size_t index; + size_t max = sizeof(miniscript_info_table) / sizeof(struct miniscript_item_t); + size_t name_len = strlen(name) + 1; + + for (index = 0; index < max; ++index) { + if ((miniscript_info_table[index].kind & target) == 0) + continue; + if (memcmp(name, miniscript_info_table[index].name, name_len) == 0) { + result = &miniscript_info_table[index]; + break; + } + } + return result; +} + +static int convert_bip32_path_to_array( + const char *path, + uint32_t *bip32_array, + uint32_t array_num, + bool is_private, + uint32_t *count, + int8_t *astarisk_index) +{ + int ret = WALLY_OK; + char *buf; + char *temp; + char *addr; + size_t len; + size_t index; + bool hardened; + uint32_t *array; + int32_t value; + char *err_ptr = NULL; + int8_t asta_index = -1; + + buf = wally_strdup(path); + if (!buf) + return WALLY_ENOMEM; + + array = (uint32_t *) wally_malloc(sizeof(uint32_t) * DESCRIPTOR_BIP32_PATH_NUM_MAX); + if (!array) { + wally_free_string(buf); + return WALLY_ENOMEM; + } + + addr = buf; + if (buf[0] == '/') + addr += 1; + + for (index = 0; index < array_num + 1; ++index) { + if (*addr == '\0') + break; + + if (index == array_num) { + ret = WALLY_EINVAL; + break; + } + + temp = strchr(addr, '/'); + if (temp) { + *temp = '\0'; + } + len = strlen(addr); + if (!len) { + ret = WALLY_EINVAL; + break; + } + hardened = false; + if ((addr[len - 1] == '\'') || (addr[len - 1] == 'h') || (addr[len - 1] == 'H')) { + if (!is_private) { + ret = WALLY_EINVAL; + break; + } + addr[len - 1] = '\0'; + hardened = true; + --len; + } + + if (*addr == '\0') { + ret = WALLY_EINVAL; + break; + } + if (asta_index != -1) { + ret = WALLY_EINVAL; + break; + } else if ((len == 1) && (addr[len - 1] == '*')) { + asta_index = (int8_t)index; + array[index] = 0; + } else { + value = strtol(addr, &err_ptr, 10); + if ((err_ptr != NULL && *err_ptr != '\0') || (value < 0)) { + ret = WALLY_EINVAL; + break; + } + array[index] = (uint32_t)value; + } + + if (hardened) { + array[index] |= 0x80000000; + } + + if (temp) { + addr = temp + 1; + } else { + ++index; + break; + } + } + + if ((ret == WALLY_OK) && bip32_array) { + memcpy(bip32_array, array, sizeof(uint32_t) * index); + if (astarisk_index) + *astarisk_index = asta_index; + if (count) + *count = (uint32_t)index; + } + + wally_free(array); + wally_free_string(buf); + return ret; +} + +static int generate_script_from_number( + int64_t number, + struct miniscript_node_t *parent, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + int ret = WALLY_OK; + if (parent && !parent->info) + return WALLY_EINVAL; + + if (script_len < DESCRIPTOR_NUMBER_BYTE_MAX_LENGTH) + return WALLY_EINVAL; + + if (number == 0) { + script[0] = 0; + *write_len = 1; + } else if (number == -1) { + script[0] = OP_1NEGATE; + *write_len = 1; + } else if ((number > 0) && (number <= 16)) { + script[0] = OP_1 + number - 1; + *write_len = 1; + } else { + unsigned char number_bytes[DESCRIPTOR_NUMBER_BYTE_MAX_LENGTH]; + size_t output_len = scriptint_to_bytes(number, number_bytes); + if (!output_len) + return WALLY_EINVAL; + + ret = wally_script_push_from_bytes(number_bytes, output_len, 0, + script, script_len, write_len); + } + return ret; +} + +static int generate_script_from_miniscript( + struct miniscript_node_t *node, + struct miniscript_node_t *parent, + uint32_t derive_child_num, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + int ret; + size_t output_len = 0; + size_t len; + size_t index; + size_t table_idx; + size_t max; + const struct miniscript_wrapper_item_t *item = NULL; + + if (node->info) { + output_len = *write_len; + ret = node->info->generate_function(node, parent, derive_child_num, script, + script_len, &output_len); + if (ret != WALLY_OK) + return ret; + + if (node->wrapper != 0) { + /* back from wrapper string */ + max = sizeof(miniscript_wrapper_table) / sizeof(struct miniscript_item_t); + len = strlen(node->wrapper_str); + for (index = len; index > 0; --index) { + item = NULL; + for (table_idx = 0; table_idx < max; ++table_idx) { + if (miniscript_wrapper_table[table_idx].name[0] == node->wrapper_str[index - 1]) { + item = &miniscript_wrapper_table[table_idx]; + break; + } + } + if (!item) { + return WALLY_EINVAL; + } + + ret = item->generate_function(script, script_len, &output_len); + if (ret != WALLY_OK) + return ret; + } + } + *write_len = output_len; + return ret; + } + + /* value data */ + if ((node->kind & DESCRIPTOR_KIND_RAW) || (node->kind == DESCRIPTOR_KIND_PUBLIC_KEY)) { + ret = wally_hex_to_bytes(node->data, script, script_len, write_len); + if (((ret == WALLY_OK)) && (node->kind == DESCRIPTOR_KIND_PUBLIC_KEY)) { + if (*write_len == EC_PUBLIC_KEY_UNCOMPRESSED_LEN) + node->is_uncompress_key = true; + else if (*write_len == XONLY_PUBLIC_KEY_LEN) + node->is_xonly_key = true; + } + } else if (node->kind == DESCRIPTOR_KIND_NUMBER) { + ret = generate_script_from_number(node->number, parent, script, script_len, write_len); + } else if ((node->kind == DESCRIPTOR_KIND_BASE58) || (node->kind == DESCRIPTOR_KIND_BECH32)) { + ret = analyze_miniscript_addr(node->data, NULL, NULL, NULL, script, script_len, write_len); + } else if (node->kind == DESCRIPTOR_KIND_PRIVATE_KEY) { + unsigned char privkey[2 + EC_PRIVATE_KEY_LEN + BASE58_CHECKSUM_LEN]; + unsigned char pubkey[EC_PUBLIC_KEY_LEN]; + if (script_len < EC_PUBLIC_KEY_UNCOMPRESSED_LEN) + return WALLY_EINVAL; + + ret = wally_base58_to_bytes(node->data, BASE58_FLAG_CHECKSUM, privkey, + sizeof(privkey), &output_len); + if ((ret == WALLY_OK) && (output_len < EC_PRIVATE_KEY_LEN + 1)) + return WALLY_EINVAL; + + ret = wally_ec_public_key_from_private_key(&privkey[1], EC_PRIVATE_KEY_LEN, + pubkey, sizeof(pubkey)); + if (ret == WALLY_OK) { + if (privkey[0] == WALLY_ADDRESS_VERSION_WIF_MAINNET) { + if ((node->network_type != 0) && (node->network_type != WALLY_NETWORK_BITCOIN_MAINNET)) + return WALLY_EINVAL; + node->network_type = WALLY_NETWORK_BITCOIN_MAINNET; + } else { + if ((node->network_type != 0) && (node->network_type != WALLY_NETWORK_BITCOIN_TESTNET)) + return WALLY_EINVAL; + node->network_type = WALLY_NETWORK_BITCOIN_TESTNET; + } + } + if (ret == WALLY_OK) { + if ((output_len == EC_PRIVATE_KEY_LEN + 2) && (privkey[EC_PRIVATE_KEY_LEN + 1] == 1)) { + if (node->is_xonly_key) { + memcpy(script, &pubkey[1], XONLY_PUBLIC_KEY_LEN); + *write_len = XONLY_PUBLIC_KEY_LEN; + } else { + memcpy(script, pubkey, EC_PUBLIC_KEY_LEN); + *write_len = EC_PUBLIC_KEY_LEN; + } + } else { + ret = wally_ec_public_key_decompress(pubkey, sizeof(pubkey), script, + EC_PUBLIC_KEY_UNCOMPRESSED_LEN); + if (ret == WALLY_OK) { + *write_len = EC_PUBLIC_KEY_UNCOMPRESSED_LEN; + node->is_uncompress_key = true; + } + } + } + } else if ((node->kind & DESCRIPTOR_KIND_BIP32) == DESCRIPTOR_KIND_BIP32) { + unsigned char bip32_serialized[BIP32_SERIALIZED_LEN + BASE58_CHECKSUM_LEN]; + struct ext_key extkey; + struct ext_key derive_extkey; + uint32_t bip32_array[DESCRIPTOR_BIP32_PATH_NUM_MAX]; + int8_t astarisk_index = -1; + uint32_t count = 0; + + wally_bzero(&extkey, sizeof(struct ext_key)); + wally_bzero(&derive_extkey, sizeof(struct ext_key)); + ret = wally_base58_to_bytes(node->data, BASE58_FLAG_CHECKSUM, + bip32_serialized, sizeof(bip32_serialized), &output_len); + if (ret != WALLY_OK) + return ret; + if (output_len > sizeof(bip32_serialized)) + return WALLY_EINVAL; + + ret = bip32_key_unserialize(bip32_serialized, output_len, &extkey); + if (ret != WALLY_OK) + return ret; + + if (((node->kind == DESCRIPTOR_KIND_BIP32_PRIVATE_KEY) && (extkey.version == BIP32_VER_MAIN_PRIVATE)) || + ((node->kind != DESCRIPTOR_KIND_BIP32_PRIVATE_KEY) && (extkey.version == BIP32_VER_MAIN_PUBLIC))) { + if (node->network_type != 0 && node->network_type != WALLY_NETWORK_BITCOIN_MAINNET) { + return WALLY_EINVAL; + } + node->network_type = WALLY_NETWORK_BITCOIN_MAINNET; + } else { + if (node->network_type != 0 && node->network_type != WALLY_NETWORK_BITCOIN_TESTNET) { + return WALLY_EINVAL; + } + node->network_type = WALLY_NETWORK_BITCOIN_TESTNET; + } + + if (node->derive_path && node->derive_path[0] != '\0') { + ret = convert_bip32_path_to_array(node->derive_path, bip32_array, + DESCRIPTOR_BIP32_PATH_NUM_MAX, + (node->kind == DESCRIPTOR_KIND_BIP32_PRIVATE_KEY), + &count, &astarisk_index); + if (ret != WALLY_OK) + return ret; + + if (astarisk_index >= 0) + bip32_array[astarisk_index] |= derive_child_num; + + ret = bip32_key_from_parent_path(&extkey, bip32_array, count, + BIP32_FLAG_KEY_PUBLIC, &derive_extkey); + if (ret != WALLY_OK) + return ret; + + memcpy(&extkey, &derive_extkey, sizeof(extkey)); + } + if (node->is_xonly_key) { + memcpy(script, &extkey.pub_key[1], XONLY_PUBLIC_KEY_LEN); + *write_len = XONLY_PUBLIC_KEY_LEN; + } else { + memcpy(script, extkey.pub_key, EC_PUBLIC_KEY_LEN); + *write_len = EC_PUBLIC_KEY_LEN; + } + } else { + return WALLY_EINVAL; + } + + return ret; +} + +static uint64_t poly_mod_descriptor_checksum(uint64_t c, int val) +{ + uint8_t c0 = c >> 35; + c = ((c & 0x7ffffffff) << 5) ^ val; + if (c0 & 1) c ^= 0xf5dee51989; + if (c0 & 2) c ^= 0xa9fdca3312; + if (c0 & 4) c ^= 0x1bab10e32d; + if (c0 & 8) c ^= 0x3706b1677a; + if (c0 & 16) c ^= 0x644d626ffd; + return c; +} + +static int generate_descriptor_checksum(const char *descriptor, char *checksum) +{ + /* + * base: + * bitcoin/src/script/descriptor.cpp + * std::string DescriptorChecksum(const Span& span) + */ + + /** A character set designed such that: + * - The most common 'unprotected' descriptor characters (hex, keypaths) are in the first group of 32. + * - Case errors cause an offset that's a multiple of 32. + * - As many alphabetic characters are in the same group (while following the above restrictions). + * + * If p(x) gives the position of a character c in this character set, every group of 3 characters + * (a,b,c) is encoded as the 4 symbols (p(a) & 31, p(b) & 31, p(c) & 31, (p(a) / 32) + 3 * (p(b) / 32) + 9 * (p(c) / 32). + * This means that changes that only affect the lower 5 bits of the position, or only the higher 2 bits, will just + * affect a single symbol. + * + * As a result, within-group-of-32 errors count as 1 symbol, as do cross-group errors that don't affect + * the position within the groups. + */ + static const char *input_charset = + "0123456789()[],'/*abcdefgh@:$%{}" + "IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~" + "ijklmnopqrstuvwxyzABCDEFGH`#\"\\ "; + + /* The character set for the checksum itself (same as bech32). */ + static const char *checksum_charset = + "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; + + uint64_t c = 1; + int cls = 0; + int clscount = 0; + int j; + char ch; + size_t pos; + size_t max = strlen(input_charset); + size_t idx; + + for (idx = 0; idx < strlen(descriptor); ++idx) { + ch = descriptor[idx]; + for (pos = 0; pos < max; ++pos) { + if (ch == input_charset[pos]) + break; + } + if (pos == max) + return WALLY_EINVAL; + /* Emit a symbol for the position inside the group, for every character. */ + c = poly_mod_descriptor_checksum(c, pos & 31); + /* Accumulate the group numbers */ + cls = cls * 3 + (int)(pos >> 5); + if (++clscount == 3) { + /* Emit an extra symbol representing the group numbers, for every 3 characters. */ + c = poly_mod_descriptor_checksum(c, cls); + cls = 0; + clscount = 0; + } + } + if (clscount > 0) + c = poly_mod_descriptor_checksum(c, cls); + for (j = 0; j < 8; ++j) + c = poly_mod_descriptor_checksum(c, 0); + c ^= 1; + + for (j = 0; j < DESCRIPTOR_CHECKSUM_LENGTH; ++j) + checksum[j] = checksum_charset[(c >> (5 * (7 - j))) & 31]; + + return WALLY_OK; +} + +const struct address_script_t g_network_address_table[] = { + { + WALLY_NETWORK_BITCOIN_MAINNET, + WALLY_ADDRESS_VERSION_P2PKH_MAINNET, + WALLY_ADDRESS_VERSION_P2SH_MAINNET, + WALLY_ADDRESS_VERSION_WIF_MAINNET, + "bc" + }, + { + WALLY_NETWORK_BITCOIN_TESTNET, + WALLY_ADDRESS_VERSION_P2PKH_TESTNET, + WALLY_ADDRESS_VERSION_P2SH_TESTNET, + WALLY_ADDRESS_VERSION_WIF_TESTNET, + "tb" + }, + { /* bitcoin regtest */ + WALLY_NETWORK_BITCOIN_REGTEST, + WALLY_ADDRESS_VERSION_P2PKH_TESTNET, + WALLY_ADDRESS_VERSION_P2SH_TESTNET, + WALLY_ADDRESS_VERSION_WIF_TESTNET, + "bcrt" + }, + { + WALLY_NETWORK_LIQUID, + WALLY_ADDRESS_VERSION_P2PKH_LIQUID, + WALLY_ADDRESS_VERSION_P2SH_LIQUID, + WALLY_ADDRESS_VERSION_WIF_MAINNET, + "ex" + }, + { + WALLY_NETWORK_LIQUID_REGTEST, + WALLY_ADDRESS_VERSION_P2PKH_LIQUID_REGTEST, + WALLY_ADDRESS_VERSION_P2SH_LIQUID_REGTEST, + WALLY_ADDRESS_VERSION_WIF_TESTNET, + "ert" + }, +}; + +static int analyze_miniscript_addr( + const char *message, + struct miniscript_node_t *node, + struct miniscript_node_t *parent_node, + const struct address_script_t *target_addr_item, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + int ret; + char *target = NULL; + char addr_family[90]; + char buf[SHA256_LEN + 2]; + uint32_t version; + unsigned char bytes_base58_decode[1 + HASH160_LEN + BASE58_CHECKSUM_LEN]; + size_t written; + size_t index; + size_t addr_tbl_max = sizeof(g_network_address_table) / sizeof(struct address_script_t); + int size; + bool is_p2sh = false; + const struct address_script_t *addr_item = NULL; + + if (parent_node && !node) + return WALLY_EINVAL; + + if (script && ((script_len < sizeof(buf)) || !write_len)) + return WALLY_EINVAL; + + if (node) { + node->data = wally_strdup(message); + if (!node->data) + return WALLY_ENOMEM; + + node->data_size = (uint32_t)strlen(message); + } + + ret = wally_base58_to_bytes(message, BASE58_FLAG_CHECKSUM, bytes_base58_decode, + sizeof(bytes_base58_decode), &written); + if (ret == WALLY_OK) { + if (written != HASH160_LEN + 1) + return WALLY_EINVAL; + + version = bytes_base58_decode[0]; + for (index = 0; index < addr_tbl_max; ++index) { + if (version == g_network_address_table[index].version_p2pkh) { + addr_item = &g_network_address_table[index]; + break; + } else if (version == g_network_address_table[index].version_p2sh) { + addr_item = &g_network_address_table[index]; + is_p2sh = true; + break; + } + } + + if (addr_item == NULL) + return WALLY_EINVAL; + if (target_addr_item) { + if (target_addr_item->network == WALLY_NETWORK_BITCOIN_REGTEST && + addr_item->network == WALLY_NETWORK_BITCOIN_TESTNET) { + /* do nothing (bitcoin regtest) */ + } else if (target_addr_item->network != addr_item->network) + return WALLY_EINVAL; + } + if (node) + node->kind = DESCRIPTOR_KIND_BASE58; + + if (!script) { + /* do nothing */ + } else if (is_p2sh) { + ret = wally_scriptpubkey_p2sh_from_bytes(bytes_base58_decode + 1, + HASH160_LEN, + 0, + script, + script_len, + write_len); + } else { + ret = wally_scriptpubkey_p2pkh_from_bytes(bytes_base58_decode + 1, + HASH160_LEN, + 0, + script, + script_len, + write_len); + } + return ret; + } + + /* segwit */ + wally_bzero(addr_family, sizeof(addr_family)); + target = strchr(message, '1'); + if (target && (sizeof(addr_family))) { + size = (int)(target - message); + if (size < (int)sizeof(addr_family)) { + memcpy(addr_family, message, size); + } + } + if (target_addr_item) { + written = strlen(addr_family) + 1; + for (index = 0; index < addr_tbl_max; ++index) { + if (memcmp(addr_family, g_network_address_table[index].segwit_prefix, written) == 0) { + if (target_addr_item->network != g_network_address_table[index].network) + return WALLY_EINVAL; + + break; + } + } + } + + ret = wally_addr_segwit_to_bytes(message, addr_family, 0, (unsigned char *)buf, + sizeof(buf), &written); + if ((ret == WALLY_OK) && (written != (HASH160_LEN + 2)) && (written != (SHA256_LEN + 2))) + ret = WALLY_EINVAL; + + if (ret == WALLY_OK) { + if (node) + node->kind = DESCRIPTOR_KIND_BECH32; + if (script) { + memcpy(script, buf, written); + *write_len = written; + } + } + return ret; +} + +static int analyze_miniscript_key( + const struct address_script_t *addr_item, + uint32_t flags, + struct miniscript_node_t *node, + struct miniscript_node_t *parent_node) +{ + int ret; + size_t str_len = strlen(node->data); + size_t buf_len; + char *buf = NULL; + int size; + unsigned char pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN]; + unsigned char privkey_bytes[2 + EC_PRIVATE_KEY_LEN + BASE58_CHECKSUM_LEN]; + struct ext_key extkey; + + if (!node || (parent_node && !parent_node->info)) + return WALLY_EINVAL; + + wally_bzero(&extkey, sizeof(struct ext_key)); + + /* + * key origin identification + * https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md#key-origin-identification + */ + if (node->data[0] == '[') { + buf = strchr(node->data, ']'); + if (!buf) + return WALLY_EINVAL; + + size = (int)(buf - node->data + 1); + node->key_origin_info = (char *) wally_malloc(size + 1); + if (!node->key_origin_info) + return WALLY_ENOMEM; + + memcpy(node->key_origin_info, node->data, size); + node->key_origin_info[size] = '\0'; + node->key_origin_info_len = size; + /* cut parent path */ + memmove(node->data, buf + 1, str_len - size); + str_len = str_len - size; + node->data[str_len] = '\0'; + } + + /* check key (public key) */ + if (((flags & WALLY_MINISCRIPT_TAPSCRIPT) == 0) && + ((str_len == EC_PUBLIC_KEY_LEN * 2) || (str_len == EC_PUBLIC_KEY_UNCOMPRESSED_LEN * 2))) { + ret = wally_hex_to_bytes(node->data, pubkey, sizeof(pubkey), &buf_len); + if (ret == WALLY_OK) { + node->kind = DESCRIPTOR_KIND_PUBLIC_KEY; + if (str_len == EC_PUBLIC_KEY_UNCOMPRESSED_LEN * 2) + node->is_uncompress_key = true; + return wally_ec_public_key_verify(pubkey, buf_len); + } + } + else if (((flags & WALLY_MINISCRIPT_TAPSCRIPT) != 0) && + (str_len == XONLY_PUBLIC_KEY_LEN * 2)) { + ret = wally_hex_to_bytes(node->data, pubkey, sizeof(pubkey), &buf_len); + if (ret == WALLY_OK) { + node->kind = DESCRIPTOR_KIND_PUBLIC_KEY; + node->is_xonly_key = true; + memmove(pubkey + 1, pubkey, buf_len); + pubkey[0] = 2; + return wally_ec_public_key_verify(pubkey, buf_len + 1); + } + } + + /* check key (private key(wif)) */ + ret = wally_base58_to_bytes(node->data, BASE58_FLAG_CHECKSUM, privkey_bytes, + sizeof(privkey_bytes), &buf_len); + if ((ret == WALLY_OK) && (buf_len <= EC_PRIVATE_KEY_LEN + 2)) { + if (addr_item && (addr_item->version_wif != privkey_bytes[0])) + return WALLY_EINVAL; + + if ((buf_len == EC_PRIVATE_KEY_LEN + 1) || + ((buf_len == EC_PRIVATE_KEY_LEN + 2) && (privkey_bytes[EC_PRIVATE_KEY_LEN + 1] == 0x01))) { + node->kind = DESCRIPTOR_KIND_PRIVATE_KEY; + if (buf_len == EC_PRIVATE_KEY_LEN + 1) { + node->is_uncompress_key = true; + if ((flags & WALLY_MINISCRIPT_TAPSCRIPT) != 0) + return WALLY_EINVAL; + } + if ((flags & WALLY_MINISCRIPT_TAPSCRIPT) != 0) + node->is_xonly_key = true; + return wally_ec_private_key_verify(&privkey_bytes[1], EC_PRIVATE_KEY_LEN); + } + return WALLY_EINVAL; + } + + /* check bip32 key */ + buf = strchr(node->data, '/'); + if (buf) { + if (buf[1] == '/') + return WALLY_EINVAL; + + node->derive_path = wally_strdup(buf); + if (!node->derive_path) + return WALLY_ENOMEM; + + node->derive_path_len = (uint32_t)strlen(node->derive_path); + *buf = '\0'; + str_len = strlen(node->data); + if (strchr(node->derive_path, '*') != NULL) + node->is_derive = true; + } + + unsigned char bip32_serialized[BIP32_SERIALIZED_LEN + BASE58_CHECKSUM_LEN]; + ret = wally_base58_to_bytes(node->data, + BASE58_FLAG_CHECKSUM, + bip32_serialized, + sizeof(bip32_serialized), + &buf_len); + if (ret != WALLY_OK) + return ret; + if (buf_len > sizeof(bip32_serialized)) + return WALLY_EINVAL; + + ret = bip32_key_unserialize(bip32_serialized, buf_len, &extkey); + if (ret != WALLY_OK) + return ret; + + if (extkey.priv_key[0] == BIP32_FLAG_KEY_PRIVATE) { + node->kind = DESCRIPTOR_KIND_BIP32_PRIVATE_KEY; + if (extkey.version == BIP32_VER_MAIN_PRIVATE) { + if (addr_item && (addr_item->network != WALLY_NETWORK_BITCOIN_MAINNET) && + (addr_item->network != WALLY_NETWORK_LIQUID)) + return WALLY_EINVAL; + } else { + if (addr_item && ((addr_item->network == WALLY_NETWORK_BITCOIN_MAINNET) || + (addr_item->network == WALLY_NETWORK_LIQUID))) + return WALLY_EINVAL; + } + } else { + node->kind = DESCRIPTOR_KIND_BIP32_PUBLIC_KEY; + if (extkey.version == BIP32_VER_MAIN_PUBLIC) { + if (addr_item && (addr_item->network != WALLY_NETWORK_BITCOIN_MAINNET) && + (addr_item->network != WALLY_NETWORK_LIQUID)) + return WALLY_EINVAL; + } else { + if (addr_item && ((addr_item->network == WALLY_NETWORK_BITCOIN_MAINNET) || + (addr_item->network == WALLY_NETWORK_LIQUID))) + return WALLY_EINVAL; + } + } + + if ((flags & WALLY_MINISCRIPT_TAPSCRIPT) != 0) + node->is_xonly_key = true; + if (node->derive_path && node->derive_path[0] != '\0') + ret = convert_bip32_path_to_array(node->derive_path, + NULL, + DESCRIPTOR_BIP32_PATH_NUM_MAX, + (extkey.priv_key[0] == BIP32_FLAG_KEY_PRIVATE), + NULL, + NULL); + return ret; +} + +static int analyze_miniscript_value( + const char *message, + const char **key_name_array, + const char **key_value_array, + size_t array_len, + uint32_t *network, + uint32_t flags, + struct miniscript_node_t *node, + struct miniscript_node_t *parent_node) +{ + int ret; + size_t index; + size_t str_len; + size_t buf_len; + char *buf = NULL; + char *err_ptr = NULL; + size_t addr_tbl_max = sizeof(g_network_address_table) / sizeof(struct address_script_t); + const struct address_script_t *addr_item = NULL; + + if (!node || (parent_node && !parent_node->info) || !message || !message[0]) + return WALLY_EINVAL; + + if (network) { + for (index = 0; index < addr_tbl_max; ++index) { + if (*network == g_network_address_table[index].network) { + addr_item = &g_network_address_table[index]; + break; + } + } + if (addr_item == NULL) + return WALLY_EINVAL; + } + + str_len = strlen(message); + + if (parent_node && (parent_node->info->kind == DESCRIPTOR_KIND_DESCRIPTOR_ADDR)) + return analyze_miniscript_addr(message, node, parent_node, addr_item, NULL, 0, NULL); + + /* check key_name_array */ + if (array_len) { + for (index = 0; index < array_len; ++index) { + if (strncmp(message, key_name_array[index], str_len + 1) == 0) { + node->data = wally_strdup(key_value_array[index]); + if (!node->data) + return WALLY_ENOMEM; + + str_len = strlen(node->data); + node->data_size = (uint32_t)str_len; + break; + } + } + } + + if (!node->data) { + node->data = wally_strdup(message); + node->data_size = (uint32_t)str_len; + } + + if (parent_node && + ((parent_node->info->kind == DESCRIPTOR_KIND_DESCRIPTOR_RAW) || + (parent_node->info->kind == DESCRIPTOR_KIND_MINISCRIPT_SHA256) || + (parent_node->info->kind == DESCRIPTOR_KIND_MINISCRIPT_HASH256) || + (parent_node->info->kind == DESCRIPTOR_KIND_MINISCRIPT_RIPEMD160) || + (parent_node->info->kind == DESCRIPTOR_KIND_MINISCRIPT_HASH160))) { + buf = wally_strdup(node->data); + if (!buf) + return WALLY_ENOMEM; + + ret = wally_hex_to_bytes(node->data, (unsigned char *)buf, str_len, &buf_len); + if (ret == WALLY_OK) { + node->kind = DESCRIPTOR_KIND_RAW; + } + wally_free_string(buf); + return ret; + } + + node->number = strtoll(node->data, &err_ptr, 10); + if ((err_ptr == NULL) || (*err_ptr == '\0')) { + node->kind = DESCRIPTOR_KIND_NUMBER; + node->type_properties = MINISCRIPT_TYPE_B | MINISCRIPT_PROPERTY_Z | + MINISCRIPT_PROPERTY_U | MINISCRIPT_PROPERTY_M | + MINISCRIPT_PROPERTY_X; + if (node->number == 0) { + node->type_properties |= MINISCRIPT_PROPERTY_D | MINISCRIPT_PROPERTY_E | + MINISCRIPT_PROPERTY_S; + } else { + node->type_properties |= MINISCRIPT_PROPERTY_F; + } + return WALLY_OK; + } + + return analyze_miniscript_key(addr_item, flags, node, parent_node); +} + +static int analyze_miniscript( + const char *miniscript, + const char **key_name_array, + const char **key_value_array, + size_t array_len, + uint32_t target, + uint32_t *network, + uint32_t flags, + struct miniscript_node_t *prev_node, + struct miniscript_node_t *parent_node, + struct miniscript_node_t **generate_node, + char **script_ignore_checksum) +{ + int ret = WALLY_OK; + char *sub_str = NULL; + size_t index; + size_t str_len; + size_t sub_str_len = 0; + size_t offset = 0; + size_t child_offset = 0; + uint32_t indent = 0; + bool collect_child = false; + bool exist_indent = false; + bool copy_child = false; + char buffer[64]; + char checksum[12]; + char work_checksum[12]; + size_t checksum_len = 0; + size_t checksum_index = 0; + struct miniscript_node_t *node; + struct miniscript_node_t *child = NULL; + struct miniscript_node_t *prev_child = NULL; + + str_len = strlen(miniscript); + + node = (struct miniscript_node_t *) wally_malloc(sizeof(struct miniscript_node_t)); + if (!node) + return WALLY_ENOMEM; + + wally_bzero(node, sizeof(struct miniscript_node_t)); + wally_bzero(buffer, sizeof(buffer)); + wally_bzero(checksum, sizeof(checksum)); + wally_bzero(work_checksum, sizeof(work_checksum)); + if (parent_node) + node->parent = parent_node; + + for (index = 0; index < str_len; ++index) { + if (!node->info && (miniscript[index] == ':')) { + if (index - offset > sizeof(node->wrapper_str)) { + ret = WALLY_EINVAL; + break; + } + memcpy(node->wrapper_str, &miniscript[offset], index - offset); + offset = index + 1; + } else if (miniscript[index] == '(') { + if (!node->info && (indent == 0)) { + collect_child = true; + memcpy(buffer, &miniscript[offset], index - offset); + node->info = search_miniscript_info(buffer, target); + if (!node->info) { + ret = WALLY_EINVAL; + break; + } else if ((node->wrapper_str[0] != '\0') && + ((node->info->kind & DESCRIPTOR_KIND_MINISCRIPT) == 0)) { + ret = WALLY_EINVAL; + break; + } + offset = index + 1; + child_offset = offset; + } + ++indent; + exist_indent = true; + } else if (miniscript[index] == ')') { + if (indent) { + --indent; + if (collect_child && (indent == 0)) { + collect_child = false; + offset = index + 1; + copy_child = true; + } + } + exist_indent = true; + } else if (miniscript[index] == ',') { + if (collect_child && (indent == 1)) { + copy_child = true; + } + exist_indent = true; + } else if (miniscript[index] == '#') { + if (!parent_node && node->info && !collect_child && (indent == 0)) { + checksum_index = index; + checksum_len = strlen(&miniscript[index + 1]); + if (sizeof(checksum) > checksum_len) { + memcpy(checksum, &miniscript[index + 1], checksum_len); + } else { + ret = WALLY_EINVAL; + } + break; /* end */ + } + } + + if (copy_child) { + ret = realloc_substr_buffer(index - child_offset, &sub_str, &sub_str_len); + if (ret != WALLY_OK) + break; + + memcpy(sub_str, &miniscript[child_offset], index - child_offset); + sub_str[index - child_offset] = '\0'; + ret = analyze_miniscript(sub_str, + key_name_array, + key_value_array, + array_len, + target, + network, + flags, + prev_child, + node, + &child, + NULL); + if (ret != WALLY_OK) + break; + + prev_child = child; + child = NULL; + copy_child = false; + if (miniscript[index] == ',') { + offset = index + 1; + child_offset = offset; + } + } + } + + if ((ret == WALLY_OK) && !exist_indent) + ret = analyze_miniscript_value(miniscript, + key_name_array, + key_value_array, + array_len, + network, + flags, + node, + parent_node); + + if ((ret == WALLY_OK) && node->info && (node->info->kind & DESCRIPTOR_KIND_MINISCRIPT) && + (node->wrapper_str[0] != '\0')) { + node->wrapper = convert_miniscript_wrapper_flag(node->wrapper_str); + } + + if ((ret == WALLY_OK) && node->info && node->info->verify_function) + ret = node->info->verify_function(node, parent_node); + + if ((ret == WALLY_OK) && !parent_node && checksum_index) { + /* check checksum */ + ret = realloc_substr_buffer(checksum_index + 1, &sub_str, &sub_str_len); + if (ret == WALLY_OK) { + memcpy(sub_str, miniscript, checksum_index); + sub_str[checksum_index] = '\0'; + if (script_ignore_checksum) + *script_ignore_checksum = wally_strdup(sub_str); + + ret = generate_descriptor_checksum(sub_str, work_checksum); + if ((ret == WALLY_OK) && (memcmp(checksum, work_checksum, DESCRIPTOR_CHECKSUM_LENGTH) != 0)) { + ret = WALLY_EINVAL; + } + } + } + if ((ret == WALLY_OK) && !parent_node && script_ignore_checksum) { + if (!checksum_index) + *script_ignore_checksum = wally_strdup(miniscript); + + if (*script_ignore_checksum == NULL) + ret = WALLY_ENOMEM; + } + + if ((ret == WALLY_OK) && node->wrapper) { + const struct miniscript_wrapper_item_t *item = NULL; + size_t len; + size_t table_idx; + size_t max; + /* back from wrapper string */ + max = sizeof(miniscript_wrapper_table) / sizeof(struct miniscript_item_t); + len = strlen(node->wrapper_str); + for (index = len; index > 0; --index) { + item = NULL; + for (table_idx = 0; table_idx < max; ++table_idx) { + if (miniscript_wrapper_table[table_idx].name[0] == node->wrapper_str[index - 1]) { + item = &miniscript_wrapper_table[table_idx]; + break; + } + } + if (!item) { + ret = WALLY_EINVAL; + break; + } + + ret = item->verify_function(node, NULL); + if (ret != WALLY_OK) + break; + } + if (ret == WALLY_OK) + ret = check_type_properties(node->type_properties); + } + + if (ret == WALLY_OK) { + *generate_node = node; + if (parent_node && !parent_node->child) + parent_node->child = node; + if (prev_node) { + node->chain_count = prev_node->chain_count + 1; + node->back = prev_node; + prev_node->next = node; + } else { + node->chain_count = 1; + } + } else { + free_miniscript_node(node); + } + + if (sub_str) + wally_bzero(sub_str, sub_str_len); + wally_free(sub_str); + return ret; +} + +static int convert_script_from_node( + struct miniscript_node_t *top_node, + uint32_t derive_child_num, + uint32_t depth, + uint32_t index, + unsigned char *script, + size_t script_len, + size_t *write_len) +{ + int ret; + char *buf; + size_t output_len = 0; + struct miniscript_node_t *target_node = top_node; + uint32_t count; + + for (count = 0; count < depth; ++count) { + if (!target_node->child) + return WALLY_EINVAL; + target_node = target_node->child; + } + for (count = 0; count < index; ++count) { + if (!target_node->next) + return WALLY_EINVAL; + target_node = target_node->next; + } + + buf = wally_malloc(DESCRIPTOR_LIMIT_LENGTH); + if (!buf) + return WALLY_ENOMEM; + + ret = generate_script_from_miniscript(target_node, + NULL, + derive_child_num, + (unsigned char *)buf, + DESCRIPTOR_LIMIT_LENGTH, + &output_len); + if (ret == WALLY_OK) { + *write_len = output_len; + if (output_len > script_len) { + /* return WALLY_OK, but data is not written. */ + } else { + memcpy(script, buf, output_len); + } + } else { + *write_len = 0; + } + + wally_bzero(buf, DESCRIPTOR_LIMIT_LENGTH); + wally_free(buf); + return ret; +} + +static int parse_miniscript( + const char *miniscript, + const char **key_name_array, + const char **key_value_array, + size_t array_len, + uint32_t flags, + uint32_t target, + uint32_t *network, + uint32_t descriptor_depth, + uint32_t descriptor_index, + struct wally_descriptor_script_item *script_item, + size_t item_len, + uint32_t *properties, + char **script_ignore_checksum) +{ + int ret; + size_t index; + unsigned char *work_script = NULL; + size_t work_script_len = 0; + unsigned char *temp_script = NULL; + size_t temp_script_len = 0; + size_t write_len; + struct miniscript_node_t *top_node = NULL; + + if (((flags & ~0x1) != 0) || + !miniscript || (array_len && (!key_name_array || !key_value_array)) || + (!array_len && (key_name_array || key_value_array)) || + check_ascii_string(miniscript, DESCRIPTOR_LIMIT_LENGTH)) + return WALLY_EINVAL; + + if (array_len) { + for (index = 0; index < array_len; ++index) { + if (!key_name_array[index] || !key_value_array[index]) + return WALLY_EINVAL; + if (check_ascii_string(key_name_array[index], DESCRIPTOR_KEY_NAME_MAX_LENGTH) || + check_ascii_string(key_value_array[index], DESCRIPTOR_KEY_VALUE_MAX_LENGTH)) { + return WALLY_EINVAL; + } + } + } + + ret = analyze_miniscript(miniscript, key_name_array, key_value_array, array_len, + target, network, flags, + NULL, NULL, &top_node, script_ignore_checksum); + if ((ret == WALLY_OK) && (target & DESCRIPTOR_KIND_DESCRIPTOR) && + (!top_node->info || !(top_node->info->kind & DESCRIPTOR_KIND_DESCRIPTOR))) + ret = WALLY_EINVAL; + if ((ret == WALLY_OK) && script_item) { + for (index = 0; index < item_len; ++index) { + write_len = 0; + + temp_script = script_item[index].script; + temp_script_len = script_item[index].script_len; + if (!temp_script) { + if (!work_script) { + work_script = (unsigned char *)wally_malloc(DESCRIPTOR_LIMIT_LENGTH); + if (!work_script) { + ret = WALLY_ENOMEM; + break; + } + work_script_len = DESCRIPTOR_LIMIT_LENGTH; + } + temp_script = work_script; + temp_script_len = work_script_len; + } + ret = convert_script_from_node(top_node, script_item[index].child_num, + descriptor_depth, descriptor_index, + temp_script, temp_script_len, + &write_len); + if (ret != WALLY_OK) + break; + if (!script_item[index].script) { + script_item[index].script = (unsigned char *)wally_malloc(write_len); + if (!script_item[index].script) { + ret = WALLY_ENOMEM; + break; + } + memcpy(script_item[index].script, temp_script, write_len); + } + script_item[index].script_len = write_len; + } + } + if ((ret == WALLY_OK) && properties) + *properties = top_node->type_properties; + + if ((ret != WALLY_OK) && script_ignore_checksum) + wally_free_string(*script_ignore_checksum); + if (work_script) { + wally_bzero(work_script, work_script_len); + wally_free(work_script); + } + free_miniscript_node(top_node); + return ret; +} + +static int descriptor_scriptpubkey_to_address( + const struct address_script_t *address_item, + unsigned char *script, + size_t script_len, + char **output) +{ + int ret; + int script_type = 0; + size_t hash_len = 0; + unsigned char hash[SHA256_LEN + 1]; + + if ((script_len == WALLY_SCRIPTPUBKEY_P2PKH_LEN) && + (script[0] == OP_DUP) && + (script[1] == OP_HASH160) && + (script[2] == HASH160_LEN) && + (script[WALLY_SCRIPTPUBKEY_P2PKH_LEN - 2] == OP_EQUALVERIFY) && + (script[WALLY_SCRIPTPUBKEY_P2PKH_LEN - 1] == OP_CHECKSIG)) { + script_type = WALLY_SCRIPT_TYPE_P2PKH; + hash[0] = address_item->version_p2pkh; + memcpy(&hash[1], &script[3], HASH160_LEN); + hash_len = HASH160_LEN + 1; + } else if ((script_len == WALLY_SCRIPTPUBKEY_P2SH_LEN) && + (script[0] == OP_HASH160) && + (script[1] == HASH160_LEN) && + (script[WALLY_SCRIPTPUBKEY_P2SH_LEN - 1] == OP_EQUAL)) { + script_type = WALLY_SCRIPT_TYPE_P2SH; + hash[0] = address_item->version_p2sh; + memcpy(&hash[1], &script[2], HASH160_LEN); + hash_len = HASH160_LEN + 1; + } else if ((script_len == WALLY_SCRIPTPUBKEY_P2WPKH_LEN) && + (script[0] == OP_0) && (script[1] == HASH160_LEN)) { + script_type = WALLY_SCRIPT_TYPE_P2WPKH; + } else if ((script_len == WALLY_SCRIPTPUBKEY_P2WSH_LEN) && + (script[0] == OP_0) && + (script[1] == SHA256_LEN)) { + script_type = WALLY_SCRIPT_TYPE_P2WSH; + /* feature: append witness v1 */ + } else { + ret = WALLY_EINVAL; + } + + if ((script_type == WALLY_SCRIPT_TYPE_P2PKH) || (script_type == WALLY_SCRIPT_TYPE_P2SH)) { + ret = wally_base58_from_bytes(hash, hash_len, BASE58_FLAG_CHECKSUM, output); + } else if ((script_type == WALLY_SCRIPT_TYPE_P2WPKH) || + (script_type == WALLY_SCRIPT_TYPE_P2WSH)) { + ret = wally_addr_segwit_from_bytes(script, script_len, address_item->segwit_prefix, 0, output); + } + return ret; +} + +static void free_descriptor_address_item( + struct wally_descriptor_address_item *item, size_t item_len) +{ + size_t index; + if (item) { + for (index = 0; index < item_len; ++index) { + if (item[index].address) { + wally_clear((void *)item[index].address, item[index].address_len); + wally_free((void *)item[index].address); + } + wally_clear((void *)&item[index], sizeof(item[index])); + } + wally_clear((void *)item, item_len * sizeof(struct wally_descriptor_address_item)); + wally_free((void *)item); + } +} + +int wally_free_descriptor_addresses( + struct wally_descriptor_addresses *addresses) +{ + if (!addresses) + return WALLY_EINVAL; + free_descriptor_address_item(addresses->items, addresses->num_items); + wally_clear((void *)addresses, sizeof(*addresses)); + return WALLY_OK; +} + +int wally_descriptor_parse_miniscript( + const char *miniscript, + const char **key_name_array, + const char **key_value_array, + size_t array_len, + uint32_t derive_child_num, + uint32_t flags, + unsigned char *script, + size_t script_len, + size_t *written) +{ + int ret; + struct wally_descriptor_script_item script_item; + + if (!script || !written || !script_len) + return WALLY_EINVAL; + + if (written) + *written = 0; + + wally_bzero(&script_item, sizeof(script_item)); + script_item.child_num = derive_child_num; + script_item.script = script; + script_item.script_len = script_len; + + ret = parse_miniscript( + miniscript, + key_name_array, + key_value_array, + array_len, + flags, + DESCRIPTOR_KIND_MINISCRIPT, + NULL, + 0, + 0, + &script_item, + 1, + NULL, + NULL); + if (ret == WALLY_OK) + *written = script_item.script_len; + return ret; +} + +int wally_descriptor_to_scriptpubkey( + const char *descriptor, + const char **key_name_array, + const char **key_value_array, + size_t array_len, + uint32_t derive_child_num, + uint32_t network, + uint32_t target_depth, + uint32_t target_index, + uint32_t flags, + unsigned char *script, + size_t script_len, + size_t *written) +{ + int ret; + size_t index; + size_t addr_tbl_max = sizeof(g_network_address_table) / sizeof(struct address_script_t); + const struct address_script_t *addr_item = NULL; + struct wally_descriptor_script_item script_item; + + if (!script || !written || !script_len) + return WALLY_EINVAL; + + if (network == WALLY_NETWORK_BITCOIN_REGTEST) + addr_item = &g_network_address_table[2]; /* bitcoin regtest */ + else + for (index = 0; index < addr_tbl_max; ++index) { + if (network == (uint32_t)g_network_address_table[index].network) { + addr_item = &g_network_address_table[index]; + break; + } + } + + if (written) + *written = 0; + + wally_bzero(&script_item, sizeof(script_item)); + script_item.child_num = derive_child_num; + script_item.script = script; + script_item.script_len = script_len; + + ret = parse_miniscript( + descriptor, + key_name_array, + key_value_array, + array_len, + flags, + DESCRIPTOR_KIND_MINISCRIPT | DESCRIPTOR_KIND_DESCRIPTOR, + (!addr_item) ? NULL : &network, + target_depth, + target_index, + &script_item, + 1, + NULL, + NULL); + if (ret == WALLY_OK) + *written = script_item.script_len; + return ret; +} + +int wally_descriptor_to_address( + const char *descriptor, + const char **key_name_array, + const char **key_value_array, + size_t array_len, + uint32_t derive_child_num, + uint32_t network, + uint32_t flags, + char **output) +{ + int ret; + size_t index; + size_t addr_tbl_max = sizeof(g_network_address_table) / sizeof(struct address_script_t); + const struct address_script_t *addr_item = NULL; + struct wally_descriptor_script_item script_item; + + if (!output) + return WALLY_EINVAL; + + if (network == WALLY_NETWORK_BITCOIN_REGTEST) + addr_item = &g_network_address_table[2]; /* bitcoin regtest */ + else + for (index = 0; index < addr_tbl_max; ++index) { + if (network == (uint32_t)g_network_address_table[index].network) { + addr_item = &g_network_address_table[index]; + break; + } + } + + if (!addr_item) + return WALLY_EINVAL; + + wally_bzero(&script_item, sizeof(script_item)); + script_item.child_num = derive_child_num; + + ret = parse_miniscript( + descriptor, + key_name_array, + key_value_array, + array_len, + flags, + DESCRIPTOR_KIND_MINISCRIPT | DESCRIPTOR_KIND_DESCRIPTOR, + (!addr_item) ? NULL : &network, + 0, + 0, + &script_item, + 1, + NULL, + NULL); + if (ret == WALLY_OK) + ret = descriptor_scriptpubkey_to_address(addr_item, + script_item.script, + script_item.script_len, + output); + + if (script_item.script) { + wally_bzero((char *)script_item.script, script_item.script_len); + wally_free(script_item.script); + } + return ret; +} + +int wally_descriptor_to_addresses( + const char *descriptor, + const char **key_name_array, + const char **key_value_array, + size_t array_len, + uint32_t start_child_num, + uint32_t end_child_num, + uint32_t network, + uint32_t flags, + struct wally_descriptor_addresses *addresses) +{ + int ret; + uint32_t child_num; + uint32_t num_items; + size_t index; + size_t addr_tbl_max = sizeof(g_network_address_table) / sizeof(struct address_script_t); + const struct address_script_t *addr_item = NULL; + struct wally_descriptor_address_item *address_items = NULL; + struct wally_descriptor_script_item *script_items = NULL; + + if (!addresses || !descriptor || (start_child_num > end_child_num)) + return WALLY_EINVAL; + + if (network == WALLY_NETWORK_BITCOIN_REGTEST) + addr_item = &g_network_address_table[2]; + else + for (index = 0; index < addr_tbl_max; ++index) { + if (network == (uint32_t)g_network_address_table[index].network) { + addr_item = &g_network_address_table[index]; + break; + } + } + + if (!addr_item) + return WALLY_EINVAL; + + num_items = end_child_num - start_child_num + 1; + + address_items = (struct wally_descriptor_address_item *) wally_malloc(sizeof(struct wally_descriptor_address_item) * num_items); + if (!address_items) + return WALLY_ENOMEM; + + script_items = (struct wally_descriptor_script_item *) wally_malloc(sizeof(struct wally_descriptor_script_item) * num_items); + if (!script_items) { + wally_free((void *)address_items); + return WALLY_ENOMEM; + } + wally_bzero(address_items, sizeof(struct wally_descriptor_address_item) * num_items); + wally_bzero(script_items, sizeof(struct wally_descriptor_script_item) * num_items); + + index = 0; + for (child_num = start_child_num; child_num <= end_child_num; ++child_num) { + script_items[index].child_num = child_num; + address_items[index].child_num = child_num; + ++index; + } + + ret = parse_miniscript( + descriptor, + key_name_array, + key_value_array, + array_len, + flags, + DESCRIPTOR_KIND_MINISCRIPT | DESCRIPTOR_KIND_DESCRIPTOR, + (!addr_item) ? NULL : &network, + 0, + 0, + script_items, + num_items, + NULL, + NULL); + if (ret == WALLY_OK) { + for (index = 0; index < num_items; ++index) { + ret = descriptor_scriptpubkey_to_address(addr_item, + script_items[index].script, + script_items[index].script_len, + &address_items[index].address); + if (ret != WALLY_OK) + break; + address_items[index].address_len = strlen(address_items[index].address); + } + } + + if (ret == WALLY_OK) { + addresses->items = address_items; + addresses->num_items = num_items; + address_items = NULL; + } + + if (script_items) { + for (index = 0; index < num_items; ++index) { + if (script_items[index].script) { + wally_bzero((char *)script_items[index].script, script_items[index].script_len); + wally_free(script_items[index].script); + } + } + wally_bzero((char *)script_items, sizeof(struct wally_descriptor_script_item) * num_items); + wally_free((void *)script_items); + } + if (address_items) + free_descriptor_address_item(address_items, num_items); + return ret; +} + +int wally_descriptor_create_checksum( + const char *descriptor, + const char **key_name_array, + const char **key_value_array, + size_t array_len, + uint32_t flags, + char **output) +{ + int ret = WALLY_OK; + char checksum[9]; + char *ignore_checksum_descriptor = NULL; + + if (!descriptor || !output || flags) + return WALLY_EINVAL; + + wally_bzero(checksum, sizeof(checksum)); + ret = parse_miniscript( + descriptor, + key_name_array, + key_value_array, + array_len, + flags, + DESCRIPTOR_KIND_MINISCRIPT | DESCRIPTOR_KIND_DESCRIPTOR, + NULL, + 0, + 0, + NULL, + 0, + NULL, + &ignore_checksum_descriptor); + if (ret != WALLY_OK) + return ret; + + ret = generate_descriptor_checksum(ignore_checksum_descriptor, checksum); + if (ret == WALLY_OK) { + *output = wally_strdup(checksum); + if (*output == NULL) + ret = WALLY_ENOMEM; + } + wally_free_string(ignore_checksum_descriptor); + return ret; +} diff --git a/src/script.c b/src/script.c index 8e4429442..7ca36965a 100644 --- a/src/script.c +++ b/src/script.c @@ -169,7 +169,7 @@ static size_t scriptint_get_length(int64_t signed_v) return len + (last & 0x80 ? 1 : 0); } -static size_t scriptint_to_bytes(int64_t signed_v, unsigned char *bytes_out) +size_t scriptint_to_bytes(int64_t signed_v, unsigned char *bytes_out) { uint64_t v = signed_v < 0 ? -signed_v : signed_v; size_t len = 0; diff --git a/src/test/test_descriptor.py b/src/test/test_descriptor.py new file mode 100644 index 000000000..d6793425a --- /dev/null +++ b/src/test/test_descriptor.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python +import unittest +from util import * + +NETWORK_BTC_MAIN = 0x01 +NETWORK_BTC_TEST = 0x02 +NETWORK_BTC_REG = 0xff +NETWORK_LIQUID = 0x03 +NETWORK_LIQUID_REG = 0x04 + +SCRIPTPUBKEY_P2WPKH_LEN = 22 + +class DescriptorTests(unittest.TestCase): + + def test_parse_miniscript(self): + script, script_len = make_cbuffer('00' * 256 * 2) + key_arr = [ + "key_local", + "key_remote", + "key_revocation", + "H", + ] + value_arr = [ + "038bc7431d9285a064b0328b6333f3a20b86664437b6de8f4e26e6bbdee258f048", + "03a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7", + "03b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284", + "d0721279e70d39fb4aa409b52839a0056454e3b5", # HASH160(key_local) + ] + s_arr_num = len(key_arr) + key_arr_p = (POINTER(c_char) * s_arr_num)() + value_arr_p = (POINTER(c_char) * s_arr_num)() + for i in range(s_arr_num): + key_arr_p[i] = create_string_buffer(key_arr[i].encode('utf-8')) + value_arr_p[i] = create_string_buffer(value_arr[i].encode('utf-8')) + + for miniscript, child_num, expect_script in [ + ('t:andor(multi(3,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),v:older(4194305),v:sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2))', 0, '532102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a14602975562102e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd1353ae6482012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2886703010040b2696851'), + ('andor(c:pk_k(key_remote),or_i(and_v(vc:pk_h(key_local),hash160(H)),older(1008)),c:pk_k(key_revocation))', 0, '2103a22745365f673e658f0d25eb0afa9aaece858c6a48dfe37a67210c2e23da8ce7ac642103b428da420cd337c7208ed42c5331ebb407bb59ffbe3dc27936a227c619804284ac676376a914d0721279e70d39fb4aa409b52839a0056454e3b588ad82012088a914d0721279e70d39fb4aa409b52839a0056454e3b5876702f003b26868'), + ]: + ret, written = wally_descriptor_parse_miniscript(miniscript, key_arr_p, value_arr_p, s_arr_num, child_num, 0, script, script_len) + self.assertEqual(ret, WALLY_OK) + self.assertEqual(written, len(expect_script) / 2) + self.assertEqual(script[:written], unhexlify(expect_script)) + + def test_descriptor_to_scriptpubkey(self): + script, script_len = make_cbuffer('00' * 64 * 2) + + for descriptor, child_num, network, expect_script in [ + ('wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)', 0, NETWORK_BTC_MAIN, '00147dd65592d0ab2fe0d0257d571abf032cd9db93dc'), + ]: + ret, written = wally_descriptor_to_scriptpubkey(descriptor, None, None, 0, child_num, network, 0, 0, 0, script, script_len) + self.assertEqual(ret, WALLY_OK) + self.assertEqual(written, SCRIPTPUBKEY_P2WPKH_LEN) + self.assertEqual(script[:written], unhexlify(expect_script)) + + def test_descriptor_to_address(self): + # Valid args + for descriptor, child_num, network, expect_addr in [ + ('wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)', 0, NETWORK_BTC_TEST, 'tb1q0ht9tyks4vh7p5p904t340cr9nvahy7um9zdem'), + ]: + ret, addr = wally_descriptor_to_address(descriptor, None, None, 0, child_num, network, 0) + self.assertEqual(ret, WALLY_OK) + self.assertEqual(addr, expect_addr) + + def test_descriptor_to_addresses(self): + # Valid args + for descriptor, start_num, end_num, network, expect_addr_list in [ + ('wsh(multi(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/*))', 0, 10, NETWORK_BTC_MAIN, + [ + 'bc1qvjtfmrxu524qhdevl6yyyasjs7xmnzjlqlu60mrwepact60eyz9s9xjw0c', + 'bc1qp6rfclasvmwys7w7j4svgc2mrujq9m73s5shpw4e799hwkdcqlcsj464fw', + 'bc1qsflxzyj2f2evshspl9n5n745swcvs5k7p5t8qdww5unxpjwdvw5qx53ms4', + 'bc1qmhmj2mswyvyj4az32mzujccvd4dgr8s0lfzaum4n4uazeqc7xxvsr7e28n', + 'bc1qjeu2wa5jwvs90tv9t9xz99njnv3we3ux04fn7glw3vqsk4ewuaaq9kdc9t', + 'bc1qc6626sa08a4ktk3nqjrr65qytt9k273u24mfy2ld004g76jzxmdqjgpm2c', + 'bc1qwlq7jjqcklrcqypvdndjx0fyrudgrymm67gcx3e09sekgs28u47smq0lx5', + 'bc1qx8qq9k2mtqarugg3ctcsm2um22ahmq5uttrecy5ufku0ukfgpwrs7epn38', + 'bc1qgrs4qzvw4aat2k38fvmrqf3ucaanqz2wxe5yy5cewwmqn06evxgq02wv43', + 'bc1qnkpr4y7fp7jwad3gfngczwsv9069rq96cl7lpq4h9j3eng9mwjzsssr520', + 'bc1q7yzadku3kxs855wgjxnyr2nk3e44ed75p07lzhnj53ynpczg78nq0leae5', + ]), + ]: + addr_list_p = pointer(wally_descriptor_addresses()) + ret = wally_descriptor_to_addresses(descriptor, None, None, 0, start_num, end_num, network, 0, addr_list_p) + self.assertEqual(ret, WALLY_OK) + self.assertEqual(len(expect_addr_list), addr_list_p[0].num_items) + for i in range(len(expect_addr_list)): + self.assertEqual(expect_addr_list[i].encode('utf-8'), addr_list_p[0].items[i].address) + + def test_create_descriptor_checksum(self): + # Valid args + for descriptor, expect_checksum in [ + ('wsh(multi(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/*))', 't2zpj2eu'), + ]: + ret, checksum = wally_descriptor_create_checksum(descriptor, None, None, 0, 0) + self.assertEqual(ret, WALLY_OK) + self.assertEqual(checksum, expect_checksum) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/test/util.py b/src/test/util.py index f4e455075..074e087f4 100755 --- a/src/test/util.py +++ b/src/test/util.py @@ -53,6 +53,15 @@ class ext_key(Structure): ('pub_key', c_ubyte * 33), ('pub_key_tweak_sum', c_ubyte * 32)] +class wally_descriptor_address_item(Structure): + _fields_ = [('child_num', c_uint), + ('address', c_char_p), + ('address_len', c_ulong)] + +class wally_descriptor_addresses(Structure): + _fields_ = [('items', POINTER(wally_descriptor_address_item)), + ('num_items', c_ulong)] + # Sentinel classes for returning output parameters class c_char_p_p_class(object): pass @@ -281,6 +290,11 @@ class wally_psbt(Structure): ('wally_confidential_addr_to_addr', c_int, [c_char_p, c_uint, c_char_p_p]), ('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_uint, c_void_p, c_ulong]), + ('wally_descriptor_create_checksum', c_int, [c_char_p, c_void_p, c_void_p, c_ulong, c_uint, c_char_p_p]), + ('wally_descriptor_parse_miniscript', c_int, [c_char_p, c_void_p, c_void_p, c_ulong, c_uint, c_uint, c_void_p, c_ulong, c_ulong_p]), + ('wally_descriptor_to_address', c_int, [c_char_p, c_void_p, c_void_p, c_ulong, c_uint, c_uint, c_uint, c_char_p_p]), + ('wally_descriptor_to_addresses', c_int, [c_char_p, c_void_p, c_void_p, c_ulong, c_uint, c_uint, c_uint, c_uint, POINTER(wally_descriptor_addresses)]), + ('wally_descriptor_to_scriptpubkey', c_int, [c_char_p, c_void_p, c_void_p, c_ulong, c_uint, c_uint, c_uint, c_uint, c_uint, c_void_p, c_ulong, c_ulong_p]), ('wally_ec_private_key_verify', c_int, [c_void_p, c_ulong]), ('wally_ec_public_key_decompress', c_int, [c_void_p, c_ulong, c_void_p, c_ulong]), ('wally_ec_public_key_from_private_key', c_int, [c_void_p, c_ulong, c_void_p, c_ulong]), diff --git a/tools/msvc/build.bat b/tools/msvc/build.bat index 42c0a2d2c..55a876464 100644 --- a/tools/msvc/build.bat +++ b/tools/msvc/build.bat @@ -12,7 +12,7 @@ REM problems. Make renamed copies as a workaround. copy src\ccan\ccan\str\hex\hex.c src\ccan\ccan\str\hex\hex_.c copy src\ccan\ccan\base64\base64.c src\ccan\ccan\base64\base64_.c -if %ELEMENTS_BUILD% == "elements" ( +if "%ELEMENTS_BUILD%" == "elements" ( set ELEMENTS_OPT=/DBUILD_ELEMENTS ) else ( set ELEMENTS_OPT= @@ -21,4 +21,4 @@ if %ELEMENTS_BUILD% == "elements" ( REM Compile everything (wally, ccan, libsecp256k) in one lump. REM Define USE_ECMULT_STATIC_PRECOMPUTATION to pick up the REM ecmult_static_context.h file generated previously -cl /utf-8 /DUSE_ECMULT_STATIC_PRECOMPUTATION /DECMULT_WINDOW_SIZE=16 /DWALLY_CORE_BUILD %ELEMENTS_OPT% /DHAVE_CONFIG_H /DSECP256K1_BUILD /I%LIBWALLY_DIR%\src\wrap_js\windows_config /I%LIBWALLY_DIR% /I%LIBWALLY_DIR%\src /I%LIBWALLY_DIR%\include /I%LIBWALLY_DIR%\src\ccan /I%LIBWALLY_DIR%\src\ccan\base64 /I%LIBWALLY_DIR%\src\secp256k1 /Zi /LD src/aes.c src/anti_exfil.c src/base58.c src/base64.c src/bech32.c src/bip32.c src/bip38.c src/bip39.c src/blech32.c src/ecdh.c src/elements.c src/hex.c src/hmac.c src/internal.c src/mnemonic.c src/pbkdf2.c src/psbt.c src/script.c src/scrypt.c src/sign.c src/symmetric.c src/transaction.c src/wif.c src/wordlist.c src/ccan/ccan/crypto/ripemd160/ripemd160.c src/ccan/ccan/crypto/sha256/sha256.c src/ccan/ccan/crypto/sha512/sha512.c src/ccan/ccan/base64/base64_.c src\ccan\ccan\str\hex\hex_.c src/secp256k1/src/secp256k1.c /Fewally.dll +cl /utf-8 /DUSE_ECMULT_STATIC_PRECOMPUTATION /DECMULT_WINDOW_SIZE=16 /DWALLY_CORE_BUILD %ELEMENTS_OPT% /DHAVE_CONFIG_H /DSECP256K1_BUILD /I%LIBWALLY_DIR%\src\wrap_js\windows_config /I%LIBWALLY_DIR% /I%LIBWALLY_DIR%\src /I%LIBWALLY_DIR%\include /I%LIBWALLY_DIR%\src\ccan /I%LIBWALLY_DIR%\src\ccan\base64 /I%LIBWALLY_DIR%\src\secp256k1 /Zi /LD src/aes.c src/anti_exfil.c src/base58.c src/base64.c src/bech32.c src/bip32.c src/bip38.c src/bip39.c src/blech32.c src/descriptor.c src/ecdh.c src/elements.c src/hex.c src/hmac.c src/internal.c src/mnemonic.c src/pbkdf2.c src/psbt.c src/script.c src/scrypt.c src/sign.c src/symmetric.c src/transaction.c src/wif.c src/wordlist.c src/ccan/ccan/crypto/ripemd160/ripemd160.c src/ccan/ccan/crypto/sha256/sha256.c src/ccan/ccan/crypto/sha512/sha512.c src/ccan/ccan/base64/base64.c src\ccan\ccan\str\hex\hex_.c src/secp256k1/src/secp256k1.c /Fewally.dll