@@ -301,7 +301,14 @@ static const struct addr_ver_t *addr_ver_from_family(
301301static const struct ms_builtin_t * builtin_get (const ms_node * node );
302302static int generate_script (ms_ctx * ctx , ms_node * node ,
303303 unsigned char * script , size_t script_len , size_t * written );
304- static bool is_valid_policy_map (const struct wally_map * map_in );
304+ static int is_valid_policy_map (const struct wally_map * map_in , bool * is_elements );
305+
306+ static bool is_elements_policy_map (const struct wally_map * map_in )
307+ {
308+ /* Elements policy maps must have the blinding key @B first */
309+ return map_in -> num_items && map_in -> items [0 ].key_len == 2 &&
310+ map_in -> items [0 ].key [0 ] == '@' && map_in -> items [0 ].key [1 ] == 'B' ;
311+ }
305312
306313/* Wrapper for strtoll */
307314static bool strtoll_n (const char * str , size_t str_len , int64_t * v )
@@ -399,7 +406,8 @@ static bool is_identifer_char(char c)
399406 return (c >= 'a' && c <= 'z' ) || (c >= 'A' && c <= 'Z' ) || (c >= '0' && c <= '9' ) || c == '_' ;
400407}
401408static bool is_policy_start_char (char c ) { return c == '@' ; }
402- static bool is_policy_identifer_char (char c ) { return c >= '0' && c <= '9' ; }
409+ static bool is_policy_id_char (char c ) { return c >= '0' && c <= '9' ; }
410+ static bool is_elements_policy_id_char (char c ) { return c == 'B' || is_policy_id_char (c ); }
403411
404412static int canonicalize_impl (const char * descriptor ,
405413 const struct wally_map * vars_in , uint32_t flags ,
@@ -411,18 +419,24 @@ static int canonicalize_impl(const char *descriptor,
411419 int key_index_hwm = -1 ;
412420 const char * p = descriptor , * start ;
413421 char * out ;
414- bool found_policy_single = false, found_policy_multi = false;;
422+ bool found_policy_single = false, found_policy_multi = false;
423+ bool found_policy_elements = false;
415424
416425 * output = NULL ;
417426 * num_substitutions = 0 ;
418427 if (!descriptor || (flags & ~MS_FLAGS_CANONICALIZE ))
419428 return WALLY_EINVAL ;
420429
421430 if (flags & WALLY_MINISCRIPT_POLICY_TEMPLATE ) {
422- if (!is_valid_policy_map (vars_in ))
423- return WALLY_EINVAL ; /* Invalid policy variables given */
431+ const int ret = is_valid_policy_map (vars_in , & found_policy_elements );
432+ if (ret != WALLY_OK )
433+ return ret ; /* Invalid policy variables given */
434+ #ifndef BUILD_ELEMENTS
435+ if (found_policy_elements )
436+ return WALLY_EINVAL ; /* No Elements support */
437+ #endif
424438 is_id_start = is_policy_start_char ;
425- is_id_char = is_policy_identifer_char ;
439+ is_id_char = found_policy_elements ? is_elements_policy_id_char : is_policy_id_char ;
426440 }
427441
428442 /* First, find the length of the canonicalized descriptor */
@@ -457,6 +471,17 @@ static int canonicalize_impl(const char *descriptor,
457471 return WALLY_EINVAL ; /* Must be ordered with no gaps */
458472 if (key_index > key_index_hwm )
459473 key_index_hwm = key_index ;
474+ if (found_policy_elements && key_index == 0 ) {
475+ /* The blinding key in a ct() policy */
476+ if (* p != ')' && * p != ',' )
477+ return WALLY_EINVAL ; /* Must be a single key */
478+ continue ;
479+ }
480+ /* Check for a key path. Note that policies, unlike
481+ * raw descriptors, cannot be used to encode single
482+ * keys (as they are used to register wallet structures,
483+ * not to expose single addresses).
484+ */
460485 if (* p ++ != '/' )
461486 return WALLY_EINVAL ;
462487 ++ required_len ;
@@ -2331,7 +2356,7 @@ static int analyze_key_hex(ms_ctx *ctx, ms_node *node,
23312356}
23322357
23332358static int analyze_miniscript_key (ms_ctx * ctx , uint32_t flags ,
2334- ms_node * node , ms_node * parent )
2359+ ms_node * node , ms_node * parent , bool force_ct )
23352360{
23362361 unsigned char privkey [2 + EC_PRIVATE_KEY_LEN + BASE58_CHECKSUM_LEN ];
23372362 struct ext_key extkey ;
@@ -2340,10 +2365,11 @@ static int analyze_miniscript_key(ms_ctx *ctx, uint32_t flags,
23402365 bool is_hex ;
23412366#ifdef BUILD_ELEMENTS
23422367 /* Whether we are the blinding key child of a ct() expression */
2343- const bool is_ct_key = parent && parent -> kind == KIND_DESCRIPTOR_CT &&
2344- !parent -> child ; /* If no child, we are the first child */
2368+ const bool is_ct_key = force_ct || ( parent && parent -> kind == KIND_DESCRIPTOR_CT &&
2369+ !parent -> child ) ; /* If no child, we are the first child */
23452370#else
23462371 const bool is_ct_key = false;
2372+ (void )force_ct ;
23472373#endif
23482374
23492375 if (!node || (parent && !parent -> builtin ))
@@ -2546,7 +2572,8 @@ static int analyze_miniscript_value(ms_ctx *ctx, const char *str, size_t str_len
25462572 node -> kind = KIND_NUMBER ;
25472573 return WALLY_OK ;
25482574 }
2549- return analyze_miniscript_key (ctx , flags , node , parent );
2575+ const bool force_ct = false;
2576+ return analyze_miniscript_key (ctx , flags , node , parent , force_ct );
25502577}
25512578
25522579static int analyze_miniscript (ms_ctx * ctx , const char * str , size_t str_len ,
@@ -2880,7 +2907,7 @@ static uint32_t get_max_depth(const char *miniscript, size_t miniscript_len)
28802907 return depth == 1 ? max_depth : 0xffffffff ;
28812908}
28822909
2883- static bool is_valid_policy_map (const struct wally_map * map_in )
2910+ static int is_valid_policy_map (const struct wally_map * map_in , bool * is_elements )
28842911{
28852912 struct wally_map keys ;
28862913 ms_ctx ctx ;
@@ -2889,6 +2916,8 @@ static bool is_valid_policy_map(const struct wally_map *map_in)
28892916 size_t i ;
28902917 int ret = WALLY_OK ;
28912918
2919+ * is_elements = false;
2920+
28922921 if (!map_in || !map_in -> num_items )
28932922 return WALLY_EINVAL ; /* Must contain at least one key expression */
28942923
@@ -2898,37 +2927,56 @@ static bool is_valid_policy_map(const struct wally_map *map_in)
28982927 for (i = 0 ; ret == WALLY_OK && i < map_in -> num_items ; ++ i ) {
28992928 const struct wally_map_item * item = & map_in -> items [i ];
29002929 if (!item -> key || item -> key_len < 2 || item -> key [0 ] != '@' ||
2901- !strtoll_n ((const char * )item -> key + 1 , item -> key_len - 1 , & v ) || v < 0 ) {
2902- /* Policy keys can only be @n: positive integers */
2903- ret = WALLY_EINVAL ;
2904- } else if ((size_t )v != i )
2905- ret = WALLY_EINVAL ; /* Must be sorted in order from 0-n */
2906- else if (!item -> value || !item -> value_len )
2907- ret = WALLY_EINVAL ; /* No key value */
2908- else if (!(node = wally_calloc (sizeof (* node ))))
2930+ !item -> value || !item -> value_len )
2931+ goto fail ; /* No valid key/value */
2932+
2933+ if (i == 0 && item -> key_len == 2 && item -> key [1 ] == 'B' ) {
2934+ /* @B can be used as the first (blinding) key for ct() policies */
2935+ * is_elements = true;
2936+ } else {
2937+ /* Policy keys can only be @n: positive integers,
2938+ and must be sorted in order from 0-n */
2939+ if (!strtoll_n ((const char * )item -> key + 1 , item -> key_len - 1 , & v ) ||
2940+ v < 0 || (size_t )v + (* is_elements ? 1 : 0 ) != i )
2941+ goto fail ;
2942+ }
2943+
2944+ if (!(node = wally_calloc (sizeof (* node )))) {
29092945 ret = WALLY_ENOMEM ;
2910- else {
2911- node -> data = (const char * )item -> value ;
2912- node -> data_len = item -> value_len ;
2913- if (analyze_miniscript_key (& ctx , 0 , node , NULL ) != WALLY_OK ||
2914- node -> kind != KIND_BIP32_PUBLIC_KEY ||
2915- node -> child_path_len ) {
2946+ goto fail_nomem ;
2947+ }
2948+
2949+ /* Parse the key data */
2950+ node -> data = (const char * )item -> value ;
2951+ node -> data_len = item -> value_len ;
2952+ const bool force_ct = * is_elements && i == 0 ;
2953+ ret = analyze_miniscript_key (& ctx , 0 , node , NULL , force_ct );
2954+ if (ret == WALLY_OK ) {
2955+ if (force_ct && node -> kind == KIND_PRIVATE_KEY ) {
2956+ /* Valid 64 byte hex blinding key: */
2957+ /* no-op */ ;
2958+ } else if (node -> kind != KIND_BIP32_PUBLIC_KEY || node -> child_path_len ) {
29162959 ret = WALLY_EINVAL ; /* Only BIP32 xpubs are allowed */
2917- } else if (ctx .features & (WALLY_MS_IS_MULTIPATH | WALLY_MS_IS_RANGED )) {
2960+ }
2961+ if (ctx .features & (WALLY_MS_IS_MULTIPATH | WALLY_MS_IS_RANGED )) {
29182962 /* Range or multipath must be part of the expression, not the key */
29192963 ret = WALLY_EINVAL ;
2920- } else {
2964+ } else if ( ret == WALLY_OK ) {
29212965 ret = wally_map_add (& keys , item -> value , item -> value_len , NULL , 0 );
29222966 }
2923- node_free (node );
29242967 }
2968+ node_free (node );
29252969 }
2926- if (ret == WALLY_OK && keys .num_items != map_in -> num_items )
2927- ret = WALLY_EINVAL ; /* One of more keys is not unique */
2970+ if (ret == WALLY_OK && keys .num_items != map_in -> num_items ) {
2971+ /* One of more keys is not unique */
2972+ fail :
2973+ ret = WALLY_EINVAL ;
2974+ }
2975+ fail_nomem :
29282976 clear_and_free (ctx .keys .items ,
29292977 ctx .keys .num_items * sizeof (* ctx .keys .items ));
29302978 wally_map_clear (& keys );
2931- return ret == WALLY_OK ;
2979+ return ret ;
29322980}
29332981
29342982int wally_descriptor_parse (const char * miniscript ,
@@ -2966,6 +3014,10 @@ int wally_descriptor_parse(const char *miniscript,
29663014 if (ret == WALLY_OK )
29673015 ret = canonicalize_impl (miniscript , vars_in , flags & MS_FLAGS_CANONICALIZE ,
29683016 & ctx -> src , & num_substitutions );
3017+ if (ret == WALLY_OK && (flags & WALLY_MINISCRIPT_POLICY_TEMPLATE )) {
3018+ if (!num_substitutions )
3019+ ret = WALLY_EINVAL ; /* Policy with no keys substituted */
3020+ }
29693021 if (ret == WALLY_OK ) {
29703022 ctx -> src_len = strlen (ctx -> src );
29713023 ctx -> features = WALLY_MS_IS_DESCRIPTOR ; /* Un-set if miniscript found */
@@ -2981,12 +3033,17 @@ int wally_descriptor_parse(const char *miniscript,
29813033 if (ret == WALLY_OK )
29823034 ret = node_generation_size (ctx -> top_node , & ctx -> script_len );
29833035 if (ret == WALLY_OK && (flags & WALLY_MINISCRIPT_POLICY_TEMPLATE )) {
2984- if (ctx -> keys .num_items != num_substitutions )
3036+ const bool have_blinding_key = is_elements_policy_map (vars_in );
3037+ const size_t num_skipped_keys = have_blinding_key ? 1 : 0 ;
3038+ if (ctx -> keys .num_items != num_substitutions - num_skipped_keys )
29853039 ret = WALLY_EINVAL ; /* non-substituted key in the expression */
2986- else if (vars_in && ctx -> keys .num_items < vars_in -> num_items )
3040+ else if (vars_in && ctx -> keys .num_items + num_skipped_keys < vars_in -> num_items )
29873041 ret = WALLY_EINVAL ; /* non-substituted key in substitutions */
29883042 else if (ctx -> num_variants > 1 || ctx -> num_multipaths > 2 )
29893043 ret = WALLY_EINVAL ; /* Solved cardinality must be 1 or 2 */
3044+ else if ((ctx -> features & (WALLY_MS_IS_SLIP77 | WALLY_MS_IS_ELIP150 )) &&
3045+ !have_blinding_key )
3046+ ret = WALLY_EINVAL ; /* this ct policy requires a blinding key var */
29903047 else if (flags & WALLY_MINISCRIPT_UNIQUE_KEYPATHS )
29913048 ret = ensure_unique_policy_keys (ctx );
29923049 }
0 commit comments