17
17
#define NUM_ELEMS (a ) (sizeof(a) / sizeof(a[0]))
18
18
#define MS_FLAGS_ALL (WALLY_MINISCRIPT_TAPSCRIPT | \
19
19
WALLY_MINISCRIPT_ONLY | \
20
- WALLY_MINISCRIPT_REQUIRE_CHECKSUM)
20
+ WALLY_MINISCRIPT_REQUIRE_CHECKSUM | \
21
+ WALLY_MINISCRIPT_POLICY)
22
+ #define MS_FLAGS_CANONICALIZE (WALLY_MINISCRIPT_REQUIRE_CHECKSUM | \
23
+ WALLY_MINISCRIPT_POLICY)
21
24
22
25
/* Properties and expressions definition */
23
26
#define TYPE_NONE 0x00
@@ -270,6 +273,7 @@ static const struct addr_ver_t *addr_ver_from_family(
270
273
static const struct ms_builtin_t * builtin_get (const ms_node * node );
271
274
static int generate_script (ms_ctx * ctx , ms_node * node ,
272
275
unsigned char * script , size_t script_len , size_t * written );
276
+ static bool is_valid_policy_map (const struct wally_map * map_in );
273
277
274
278
/* Wrapper for strtoll */
275
279
static bool strtoll_n (const char * str , size_t str_len , int64_t * v )
@@ -355,34 +359,48 @@ static int generate_checksum(const char *str, size_t str_len, char *checksum_out
355
359
return WALLY_OK ;
356
360
}
357
361
358
- static inline bool is_identifer_char (char c )
362
+ typedef bool (* is_identifer_fn )(char c );
363
+
364
+ static bool is_identifer_char (char c )
359
365
{
360
366
return (c >= 'a' && c <= 'z' ) || (c >= 'A' && c <= 'Z' ) || (c >= '0' && c <= '9' ) || c == '_' ;
361
367
}
368
+ static bool is_policy_start_char (char c ) { return c == '@' ; }
369
+ static bool is_policy_identifer_char (char c ) { return c >= '0' && c <= '9' ; }
362
370
363
371
static int canonicalize (const char * descriptor ,
364
372
const struct wally_map * vars_in , uint32_t flags ,
365
373
char * * output )
366
374
{
367
375
const size_t VAR_MAX_NAME_LEN = 16 ;
376
+ is_identifer_fn is_id_start = is_identifer_char , is_id_char = is_identifer_char ;
368
377
size_t required_len = 0 ;
369
378
const char * p = descriptor , * start ;
370
379
char * out ;
371
380
372
381
if (output )
373
382
* output = NULL ;
374
383
375
- if (!descriptor || (flags & ~WALLY_MINISCRIPT_REQUIRE_CHECKSUM ) || !output )
384
+ if (!descriptor || (flags & ~MS_FLAGS_CANONICALIZE ) || !output )
376
385
return WALLY_EINVAL ;
377
386
387
+ if (flags & WALLY_MINISCRIPT_POLICY ) {
388
+ if (!is_valid_policy_map (vars_in ))
389
+ return WALLY_EINVAL ; /* Invalid policy variables given */
390
+ is_id_start = is_policy_start_char ;
391
+ is_id_char = is_policy_identifer_char ;
392
+ }
393
+
378
394
/* First, find the length of the canonicalized descriptor */
379
395
while (* p && * p != '#' ) {
380
- while (* p && * p != '#' && !is_identifer_char (* p )) {
396
+ while (* p && * p != '#' && !is_id_start (* p )) {
381
397
++ required_len ;
382
398
++ p ;
383
399
}
384
- start = p ;
385
- while (is_identifer_char (* p ))
400
+ if (!is_id_start (* p ))
401
+ break ;
402
+ start = p ++ ;
403
+ while (is_id_char (* p ))
386
404
++ p ;
387
405
if (p != start ) {
388
406
const bool starts_with_digit = * start >= '0' && * start <= '9' ;
@@ -394,36 +412,60 @@ static int canonicalize(const char *descriptor,
394
412
const struct wally_map_item * item ;
395
413
item = wally_map_get (vars_in , (unsigned char * )start , lookup_len );
396
414
required_len += item ? item -> value_len : lookup_len ;
415
+ if (item && flags & WALLY_MINISCRIPT_POLICY ) {
416
+ if (* p ++ != '/' )
417
+ return WALLY_EINVAL ;
418
+ ++ required_len ;
419
+ if (* p == '<' )
420
+ continue ;
421
+ if (* p ++ != '*' )
422
+ return WALLY_EINVAL ;
423
+ if (* p == '*' ) {
424
+ ++ p ;
425
+ required_len += strlen ("<0;1>/*" );
426
+ } else {
427
+ required_len += 1 ;
428
+ }
429
+ }
397
430
}
398
431
}
399
432
}
400
433
401
434
if (!* p && (flags & WALLY_MINISCRIPT_REQUIRE_CHECKSUM ))
402
435
return WALLY_EINVAL ; /* Checksum required but not present */
403
-
404
436
if (!(* output = wally_malloc (required_len + 1 + DESCRIPTOR_CHECKSUM_LENGTH + 1 )))
405
437
return WALLY_ENOMEM ;
406
438
407
439
p = descriptor ;
408
440
out = * output ;
409
441
while (* p && * p != '#' ) {
410
- while (* p && * p != '#' && !is_identifer_char (* p )) {
442
+ while (* p && * p != '#' && !is_id_start (* p )) {
411
443
* out ++ = * p ++ ;
412
444
}
413
- start = p ;
414
- while (is_identifer_char (* p ))
445
+ if (!is_id_start (* p ))
446
+ break ;
447
+ start = p ++ ;
448
+ while (is_id_char (* p ))
415
449
++ p ;
416
450
if (p != start ) {
417
451
const bool is_number = * start >= '0' && * start <= '9' ;
418
452
size_t lookup_len = p - start ;
419
- if (!vars_in || lookup_len > VAR_MAX_NAME_LEN || is_number ) {
453
+ if (!vars_in || lookup_len > VAR_MAX_NAME_LEN || is_number )
420
454
memcpy (out , start , lookup_len );
421
- } else {
455
+ else {
422
456
/* Lookup the potential identifier */
423
457
const struct wally_map_item * item ;
424
458
item = wally_map_get (vars_in , (unsigned char * )start , lookup_len );
425
459
lookup_len = item ? item -> value_len : lookup_len ;
426
460
memcpy (out , item ? (char * )item -> value : start , lookup_len );
461
+ if (item && flags & WALLY_MINISCRIPT_POLICY ) {
462
+ if (p [1 ] == '*' && p [2 ] == '*' ) {
463
+ out += lookup_len ;
464
+ lookup_len = strlen ("/<0;1>/*" );
465
+ memcpy (out , "/<0;1>/*" , lookup_len );
466
+ p += strlen ("/**" );
467
+ }
468
+ }
427
469
}
428
470
out += lookup_len ;
429
471
}
@@ -2455,6 +2497,47 @@ static uint32_t get_max_depth(const char *miniscript, size_t miniscript_len)
2455
2497
return depth == 1 ? max_depth : 0xffffffff ;
2456
2498
}
2457
2499
2500
+ static bool is_valid_policy_map (const struct wally_map * map_in )
2501
+ {
2502
+ ms_ctx ctx ;
2503
+ ms_node * node ;
2504
+ int64_t v ;
2505
+ size_t i ;
2506
+ int ret = WALLY_OK ;
2507
+
2508
+ if (!map_in || !map_in -> num_items )
2509
+ return WALLY_EINVAL ; /* Must contain at least one key expression */
2510
+
2511
+ memset (& ctx , 0 , sizeof (ctx ));
2512
+
2513
+ for (i = 0 ; ret == WALLY_OK && i < map_in -> num_items ; ++ i ) {
2514
+ const struct wally_map_item * item = & map_in -> items [i ];
2515
+ if (!item -> key || item -> key_len < 2 || item -> key [0 ] != '@' ||
2516
+ !strtoll_n ((const char * )item -> key + 1 , item -> key_len - 1 , & v ) || v < 0 )
2517
+ ret = WALLY_EINVAL ; /* Policy keys can only be @n */
2518
+ else if ((size_t )v != i )
2519
+ ret = WALLY_EINVAL ; /* Must be sorted in order from 0-n */
2520
+ else if (!item -> value || !item -> value_len )
2521
+ ret = WALLY_EINVAL ; /* No key value */
2522
+ else if (!(node = wally_calloc (sizeof (* node ))))
2523
+ ret = WALLY_EINVAL ;
2524
+ else {
2525
+ node -> data = (const char * )item -> value ;
2526
+ node -> data_len = item -> value_len ;
2527
+ if (analyze_miniscript_key (& ctx , 0 , node , NULL ) != WALLY_OK ||
2528
+ node -> kind != KIND_BIP32_PUBLIC_KEY ||
2529
+ node -> child_path_len ) {
2530
+ ret = WALLY_EINVAL ; /* Only BIP32 xpubs are allowed */
2531
+ } else if (ctx .features & (WALLY_MS_IS_MULTIPATH | WALLY_MS_IS_RANGED )) {
2532
+ /* Range or multipath must be part of the expression, not the key */
2533
+ ret = WALLY_EINVAL ;
2534
+ }
2535
+ }
2536
+ node_free (node );
2537
+ }
2538
+ return ret == WALLY_OK ;
2539
+ }
2540
+
2458
2541
int wally_descriptor_parse (const char * miniscript ,
2459
2542
const struct wally_map * vars_in ,
2460
2543
uint32_t network , uint32_t flags ,
@@ -2480,8 +2563,7 @@ int wally_descriptor_parse(const char *miniscript,
2480
2563
ctx -> addr_ver = addr_ver ;
2481
2564
ctx -> num_variants = 1 ;
2482
2565
ctx -> num_multipaths = 1 ;
2483
- ret = canonicalize (miniscript , vars_in ,
2484
- flags & WALLY_MINISCRIPT_REQUIRE_CHECKSUM ,
2566
+ ret = canonicalize (miniscript , vars_in , flags & MS_FLAGS_CANONICALIZE ,
2485
2567
& ctx -> src );
2486
2568
if (ret == WALLY_OK ) {
2487
2569
ctx -> src_len = strlen (ctx -> src );
0 commit comments