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_NO_UNUSED | \
22
+ WALLY_MINISCRIPT_POLICY)
23
+ #define MS_FLAGS_CANONICALIZE (WALLY_MINISCRIPT_REQUIRE_CHECKSUM | \
24
+ WALLY_MINISCRIPT_NO_UNUSED | WALLY_MINISCRIPT_POLICY)
21
25
22
26
/* Properties and expressions definition */
23
27
#define TYPE_NONE 0x00
@@ -270,6 +274,7 @@ static const struct addr_ver_t *addr_ver_from_family(
270
274
static const struct ms_builtin_t * builtin_get (const ms_node * node );
271
275
static int generate_script (ms_ctx * ctx , ms_node * node ,
272
276
unsigned char * script , size_t script_len , size_t * written );
277
+ static bool is_valid_policy_map (const struct wally_map * map_in );
273
278
274
279
/* Wrapper for strtoll */
275
280
static 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
355
360
return WALLY_OK ;
356
361
}
357
362
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 )
359
366
{
360
367
return (c >= 'a' && c <= 'z' ) || (c >= 'A' && c <= 'Z' ) || (c >= '0' && c <= '9' ) || c == '_' ;
361
368
}
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' ; }
362
371
363
372
static int canonicalize (const char * descriptor ,
364
373
const struct wally_map * vars_in , uint32_t flags ,
365
374
char * * output )
366
375
{
367
376
const size_t VAR_MAX_NAME_LEN = 16 ;
377
+ is_identifer_fn is_id_start = is_identifer_char , is_id_char = is_identifer_char ;
368
378
size_t required_len = 0 ;
369
379
const char * p = descriptor , * start ;
370
380
char * out ;
371
381
372
382
if (output )
373
383
* output = NULL ;
374
384
375
- if (!descriptor || (flags & ~WALLY_MINISCRIPT_REQUIRE_CHECKSUM ) || !output )
385
+ if (!descriptor || (flags & ~MS_FLAGS_CANONICALIZE ) || !output )
376
386
return WALLY_EINVAL ;
377
387
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
+
378
395
/* First, find the length of the canonicalized descriptor */
379
396
while (* p && * p != '#' ) {
380
- while (* p && * p != '#' && !is_identifer_char (* p )) {
397
+ while (* p && * p != '#' && !is_id_start (* p )) {
381
398
++ required_len ;
382
399
++ p ;
383
400
}
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 ))
386
405
++ p ;
387
406
if (p != start ) {
388
407
const bool starts_with_digit = * start >= '0' && * start <= '9' ;
@@ -394,36 +413,60 @@ static int canonicalize(const char *descriptor,
394
413
const struct wally_map_item * item ;
395
414
item = wally_map_get (vars_in , (unsigned char * )start , lookup_len );
396
415
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
+ }
397
431
}
398
432
}
399
433
}
400
434
401
435
if (!* p && (flags & WALLY_MINISCRIPT_REQUIRE_CHECKSUM ))
402
436
return WALLY_EINVAL ; /* Checksum required but not present */
403
-
404
437
if (!(* output = wally_malloc (required_len + 1 + DESCRIPTOR_CHECKSUM_LENGTH + 1 )))
405
438
return WALLY_ENOMEM ;
406
439
407
440
p = descriptor ;
408
441
out = * output ;
409
442
while (* p && * p != '#' ) {
410
- while (* p && * p != '#' && !is_identifer_char (* p )) {
443
+ while (* p && * p != '#' && !is_id_start (* p )) {
411
444
* out ++ = * p ++ ;
412
445
}
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 ))
415
450
++ p ;
416
451
if (p != start ) {
417
452
const bool is_number = * start >= '0' && * start <= '9' ;
418
453
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 )
420
455
memcpy (out , start , lookup_len );
421
- } else {
456
+ else {
422
457
/* Lookup the potential identifier */
423
458
const struct wally_map_item * item ;
424
459
item = wally_map_get (vars_in , (unsigned char * )start , lookup_len );
425
460
lookup_len = item ? item -> value_len : lookup_len ;
426
461
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
+ }
427
470
}
428
471
out += lookup_len ;
429
472
}
@@ -2455,6 +2498,47 @@ static uint32_t get_max_depth(const char *miniscript, size_t miniscript_len)
2455
2498
return depth == 1 ? max_depth : 0xffffffff ;
2456
2499
}
2457
2500
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
+
2458
2542
int wally_descriptor_parse (const char * miniscript ,
2459
2543
const struct wally_map * vars_in ,
2460
2544
uint32_t network , uint32_t flags ,
@@ -2480,8 +2564,7 @@ int wally_descriptor_parse(const char *miniscript,
2480
2564
ctx -> addr_ver = addr_ver ;
2481
2565
ctx -> num_variants = 1 ;
2482
2566
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 ,
2485
2568
& ctx -> src );
2486
2569
if (ret == WALLY_OK ) {
2487
2570
ctx -> src_len = strlen (ctx -> src );
0 commit comments