1717#define NUM_ELEMS (a ) (sizeof(a) / sizeof(a[0]))
1818#define MS_FLAGS_ALL (WALLY_MINISCRIPT_TAPSCRIPT | \
1919 WALLY_MINISCRIPT_ONLY | \
20- WALLY_MINISCRIPT_REQUIRE_CHECKSUM)
20+ WALLY_MINISCRIPT_REQUIRE_CHECKSUM | \
21+ WALLY_MINISCRIPT_NO_UNUSED | \
22+ WALLY_MINISCRIPT_POLICY)
23+ #define MS_FLAGS_CANONICALIZE (WALLY_MINISCRIPT_REQUIRE_CHECKSUM | \
24+ WALLY_MINISCRIPT_NO_UNUSED | WALLY_MINISCRIPT_POLICY)
2125
2226/* Properties and expressions definition */
2327#define TYPE_NONE 0x00
@@ -270,6 +274,7 @@ static const struct addr_ver_t *addr_ver_from_family(
270274static const struct ms_builtin_t * builtin_get (const ms_node * node );
271275static int generate_script (ms_ctx * ctx , ms_node * node ,
272276 unsigned char * script , size_t script_len , size_t * written );
277+ static bool is_valid_policy_map (const struct wally_map * map_in );
273278
274279/* Wrapper for strtoll */
275280static bool strtoll_n (const char * str , size_t str_len , int64_t * v )
@@ -355,34 +360,48 @@ static int generate_checksum(const char *str, size_t str_len, char *checksum_out
355360 return WALLY_OK ;
356361}
357362
358- static inline bool is_identifer_char (char c )
363+ typedef bool (* is_identifer_fn )(char c );
364+
365+ static bool is_identifer_char (char c )
359366{
360367 return (c >= 'a' && c <= 'z' ) || (c >= 'A' && c <= 'Z' ) || (c >= '0' && c <= '9' ) || c == '_' ;
361368}
369+ static bool is_policy_start_char (char c ) { return c == '@' ; }
370+ static bool is_policy_identifer_char (char c ) { return c >= '0' && c <= '9' ; }
362371
363372static int canonicalize (const char * descriptor ,
364373 const struct wally_map * vars_in , uint32_t flags ,
365374 char * * output )
366375{
367376 const size_t VAR_MAX_NAME_LEN = 16 ;
377+ is_identifer_fn is_id_start = is_identifer_char , is_id_char = is_identifer_char ;
368378 size_t required_len = 0 ;
369379 const char * p = descriptor , * start ;
370380 char * out ;
371381
372382 if (output )
373383 * output = NULL ;
374384
375- if (!descriptor || (flags & ~WALLY_MINISCRIPT_REQUIRE_CHECKSUM ) || !output )
385+ if (!descriptor || (flags & ~MS_FLAGS_CANONICALIZE ) || !output )
376386 return WALLY_EINVAL ;
377387
388+ if (flags & WALLY_MINISCRIPT_POLICY ) {
389+ if (!is_valid_policy_map (vars_in ))
390+ return WALLY_EINVAL ; /* Invalid policy variables given */
391+ is_id_start = is_policy_start_char ;
392+ is_id_char = is_policy_identifer_char ;
393+ }
394+
378395 /* First, find the length of the canonicalized descriptor */
379396 while (* p && * p != '#' ) {
380- while (* p && * p != '#' && !is_identifer_char (* p )) {
397+ while (* p && * p != '#' && !is_id_start (* p )) {
381398 ++ required_len ;
382399 ++ p ;
383400 }
384- start = p ;
385- while (is_identifer_char (* p ))
401+ if (!is_id_start (* p ))
402+ break ;
403+ start = p ++ ;
404+ while (is_id_char (* p ))
386405 ++ p ;
387406 if (p != start ) {
388407 const bool starts_with_digit = * start >= '0' && * start <= '9' ;
@@ -394,36 +413,60 @@ static int canonicalize(const char *descriptor,
394413 const struct wally_map_item * item ;
395414 item = wally_map_get (vars_in , (unsigned char * )start , lookup_len );
396415 required_len += item ? item -> value_len : lookup_len ;
416+ if (item && flags & WALLY_MINISCRIPT_POLICY ) {
417+ if (* p ++ != '/' )
418+ return WALLY_EINVAL ;
419+ ++ required_len ;
420+ if (* p == '<' )
421+ continue ;
422+ if (* p ++ != '*' )
423+ return WALLY_EINVAL ;
424+ if (* p == '*' ) {
425+ ++ p ;
426+ required_len += strlen ("<0;1>/*" );
427+ } else {
428+ required_len += 1 ;
429+ }
430+ }
397431 }
398432 }
399433 }
400434
401435 if (!* p && (flags & WALLY_MINISCRIPT_REQUIRE_CHECKSUM ))
402436 return WALLY_EINVAL ; /* Checksum required but not present */
403-
404437 if (!(* output = wally_malloc (required_len + 1 + DESCRIPTOR_CHECKSUM_LENGTH + 1 )))
405438 return WALLY_ENOMEM ;
406439
407440 p = descriptor ;
408441 out = * output ;
409442 while (* p && * p != '#' ) {
410- while (* p && * p != '#' && !is_identifer_char (* p )) {
443+ while (* p && * p != '#' && !is_id_start (* p )) {
411444 * out ++ = * p ++ ;
412445 }
413- start = p ;
414- while (is_identifer_char (* p ))
446+ if (!is_id_start (* p ))
447+ break ;
448+ start = p ++ ;
449+ while (is_id_char (* p ))
415450 ++ p ;
416451 if (p != start ) {
417452 const bool is_number = * start >= '0' && * start <= '9' ;
418453 size_t lookup_len = p - start ;
419- if (!vars_in || lookup_len > VAR_MAX_NAME_LEN || is_number ) {
454+ if (!vars_in || lookup_len > VAR_MAX_NAME_LEN || is_number )
420455 memcpy (out , start , lookup_len );
421- } else {
456+ else {
422457 /* Lookup the potential identifier */
423458 const struct wally_map_item * item ;
424459 item = wally_map_get (vars_in , (unsigned char * )start , lookup_len );
425460 lookup_len = item ? item -> value_len : lookup_len ;
426461 memcpy (out , item ? (char * )item -> value : start , lookup_len );
462+ if (item && flags & WALLY_MINISCRIPT_POLICY ) {
463+ if (p [1 ] == '*' && p [2 ] == '*' ) {
464+ out += lookup_len ;
465+ lookup_len = strlen ("/<0;1>/*" );
466+ memcpy (out , "/<0;1>/*" , lookup_len );
467+ p += strlen ("/**" );
468+ }
469+ }
427470 }
428471 out += lookup_len ;
429472 }
@@ -2455,6 +2498,47 @@ static uint32_t get_max_depth(const char *miniscript, size_t miniscript_len)
24552498 return depth == 1 ? max_depth : 0xffffffff ;
24562499}
24572500
2501+ static bool is_valid_policy_map (const struct wally_map * map_in )
2502+ {
2503+ ms_ctx ctx ;
2504+ ms_node * node ;
2505+ int64_t v ;
2506+ size_t i ;
2507+ int ret = WALLY_OK ;
2508+
2509+ if (!map_in || !map_in -> num_items )
2510+ return WALLY_EINVAL ; /* Must contain at least one key expression */
2511+
2512+ memset (& ctx , 0 , sizeof (ctx ));
2513+
2514+ for (i = 0 ; ret == WALLY_OK && i < map_in -> num_items ; ++ i ) {
2515+ const struct wally_map_item * item = & map_in -> items [i ];
2516+ if (!item -> key || item -> key_len < 2 || item -> key [0 ] != '@' ||
2517+ !strtoll_n ((const char * )item -> key + 1 , item -> key_len - 1 , & v ) || v < 0 )
2518+ ret = WALLY_EINVAL ; /* Policy keys can only be @n */
2519+ else if ((size_t )v != i )
2520+ ret = WALLY_EINVAL ; /* Must be sorted in order from 0-n */
2521+ else if (!item -> value || !item -> value_len )
2522+ ret = WALLY_EINVAL ; /* No key value */
2523+ else if (!(node = wally_calloc (sizeof (* node ))))
2524+ ret = WALLY_EINVAL ;
2525+ else {
2526+ node -> data = (const char * )item -> value ;
2527+ node -> data_len = item -> value_len ;
2528+ if (analyze_miniscript_key (& ctx , 0 , node , NULL ) != WALLY_OK ||
2529+ node -> kind != KIND_BIP32_PUBLIC_KEY ||
2530+ node -> child_path_len ) {
2531+ ret = WALLY_EINVAL ; /* Only BIP32 xpubs are allowed */
2532+ } else if (ctx .features & (WALLY_MS_IS_MULTIPATH | WALLY_MS_IS_RANGED )) {
2533+ /* Range or multipath must be part of the expression, not the key */
2534+ ret = WALLY_EINVAL ;
2535+ }
2536+ }
2537+ node_free (node );
2538+ }
2539+ return ret == WALLY_OK ;
2540+ }
2541+
24582542int wally_descriptor_parse (const char * miniscript ,
24592543 const struct wally_map * vars_in ,
24602544 uint32_t network , uint32_t flags ,
@@ -2480,8 +2564,7 @@ int wally_descriptor_parse(const char *miniscript,
24802564 ctx -> addr_ver = addr_ver ;
24812565 ctx -> num_variants = 1 ;
24822566 ctx -> num_multipaths = 1 ;
2483- ret = canonicalize (miniscript , vars_in ,
2484- flags & WALLY_MINISCRIPT_REQUIRE_CHECKSUM ,
2567+ ret = canonicalize (miniscript , vars_in , flags & MS_FLAGS_CANONICALIZE ,
24852568 & ctx -> src );
24862569 if (ret == WALLY_OK ) {
24872570 ctx -> src_len = strlen (ctx -> src );
0 commit comments