1818#define MS_FLAGS_ALL (WALLY_MINISCRIPT_TAPSCRIPT | \
1919 WALLY_MINISCRIPT_ONLY | \
2020 WALLY_MINISCRIPT_REQUIRE_CHECKSUM)
21+ #define MS_FLAGS_CANONICALIZE (WALLY_MINISCRIPT_REQUIRE_CHECKSUM | \
22+ WALLY_MINISCRIPT_POLICY)
2123
2224/* Properties and expressions definition */
2325#define TYPE_NONE 0x00
@@ -262,6 +264,7 @@ static const struct addr_ver_t *addr_ver_from_family(
262264static const struct ms_builtin_t * builtin_get (const ms_node * node );
263265static int generate_script (ms_ctx * ctx , ms_node * node ,
264266 unsigned char * script , size_t script_len , size_t * written );
267+ static bool is_valid_policy_map (const struct wally_map * map_in );
265268
266269/* Wrapper for strtoll */
267270static bool strtoll_n (const char * str , size_t str_len , int64_t * v )
@@ -347,8 +350,10 @@ static int generate_checksum(const char *str, size_t str_len, char *checksum_out
347350 return WALLY_OK ;
348351}
349352
350- static inline bool is_identifer_char (char c )
353+ static inline bool is_identifer_char (char c , uint32_t flags )
351354{
355+ if (flags & WALLY_MINISCRIPT_POLICY )
356+ return (c >= '0' && c <= '9' ) || c == '@' ;
352357 return (c >= 'a' && c <= 'z' ) || (c >= 'A' && c <= 'Z' ) || (c >= '0' && c <= '9' ) || c == '_' ;
353358}
354359
@@ -376,17 +381,20 @@ static int canonicalize(const char *descriptor,
376381 if (output )
377382 * output = NULL ;
378383
379- if (!descriptor || (flags & ~WALLY_MINISCRIPT_REQUIRE_CHECKSUM ) || !output )
384+ if (!descriptor || (flags & ~MS_FLAGS_CANONICALIZE ) || !output )
380385 return WALLY_EINVAL ;
381386
387+ if ((flags & WALLY_MINISCRIPT_POLICY ) && !is_valid_policy_map (vars_in ))
388+ return WALLY_EINVAL ; /* Invalid policy variables given */
389+
382390 /* First, find the length of the canonicalized descriptor */
383391 while (* p && * p != '#' ) {
384- while (* p && * p != '#' && !is_identifer_char (* p )) {
392+ while (* p && * p != '#' && !is_identifer_char (* p , flags )) {
385393 ++ required_len ;
386394 ++ p ;
387395 }
388396 start = p ;
389- while (is_identifer_char (* p ))
397+ while (is_identifer_char (* p , flags ))
390398 ++ p ;
391399 if (p != start ) {
392400 const bool starts_with_digit = * start >= '0' && * start <= '9' ;
@@ -410,18 +418,18 @@ static int canonicalize(const char *descriptor,
410418 p = descriptor ;
411419 out = * output ;
412420 while (* p && * p != '#' ) {
413- while (* p && * p != '#' && !is_identifer_char (* p )) {
421+ while (* p && * p != '#' && !is_identifer_char (* p , flags )) {
414422 * out ++ = * p ++ ;
415423 }
416424 start = p ;
417- while (is_identifer_char (* p ))
425+ while (is_identifer_char (* p , flags ))
418426 ++ p ;
419427 if (p != start ) {
420428 const bool is_number = * start >= '0' && * start <= '9' ;
421429 size_t lookup_len = p - start ;
422- if (!vars_in || lookup_len > VAR_MAX_NAME_LEN || is_number ) {
430+ if (!vars_in || lookup_len > VAR_MAX_NAME_LEN || is_number )
423431 memcpy (out , start , lookup_len );
424- } else {
432+ else {
425433 /* Lookup the potential identifier */
426434 const struct wally_map_item * item = lookup_identifier (vars_in , start , lookup_len );
427435 lookup_len = item ? item -> value_len - 1 : lookup_len ;
@@ -2370,6 +2378,34 @@ static int node_generate_script(ms_ctx *ctx, uint32_t depth, uint32_t index,
23702378 return ret ;
23712379}
23722380
2381+ static bool is_valid_policy_map (const struct wally_map * map_in )
2382+ {
2383+ ms_ctx ctx ;
2384+ ms_node node ;
2385+ int64_t v ;
2386+ size_t i ;
2387+ int ret = WALLY_OK ;
2388+
2389+ memset (& ctx , 0 , sizeof (ctx ));
2390+
2391+ for (i = 0 ; ret == WALLY_OK && i < map_in -> num_items ; ++ i ) {
2392+ const struct wally_map_item * item = & map_in -> items [i ];
2393+ if (!item -> key || item -> key_len < 2 || item -> key [0 ] != '@' ||
2394+ !strtoll_n ((const char * )item -> key + 1 , item -> key_len - 1 , & v ) || v < 0 )
2395+ return false; /* Policy keys can only be @n */
2396+ if (!item -> value || !item -> value_len )
2397+ return false; /* No key value */
2398+ memset (& node , 0 , sizeof (node ));
2399+ node .data = (const char * )item -> value ;
2400+ node .data_len = item -> value_len - 1 ;
2401+ if (analyze_miniscript_key (& ctx , 0 , & node , NULL ) != WALLY_OK ||
2402+ node .kind == KIND_PRIVATE_KEY )
2403+ ret = WALLY_EINVAL ; /* Policy data must be an xpub */
2404+ node_free (& node );
2405+ }
2406+ return ret == WALLY_OK ;
2407+ }
2408+
23732409int wally_descriptor_parse (const char * miniscript ,
23742410 const struct wally_map * vars_in ,
23752411 uint32_t network , uint32_t flags ,
@@ -2391,8 +2427,7 @@ int wally_descriptor_parse(const char *miniscript,
23912427 return WALLY_ENOMEM ;
23922428 ctx = * output ;
23932429 ctx -> addr_ver = addr_ver ;
2394- ret = canonicalize (miniscript , vars_in ,
2395- flags & WALLY_MINISCRIPT_REQUIRE_CHECKSUM ,
2430+ ret = canonicalize (miniscript , vars_in , flags & MS_FLAGS_CANONICALIZE ,
23962431 & ctx -> src );
23972432 if (ret == WALLY_OK ) {
23982433 ctx -> src_len = strlen (ctx -> src );
0 commit comments