18
18
#define MS_FLAGS_ALL (WALLY_MINISCRIPT_TAPSCRIPT | \
19
19
WALLY_MINISCRIPT_ONLY | \
20
20
WALLY_MINISCRIPT_REQUIRE_CHECKSUM | \
21
- WALLY_MINISCRIPT_POLICY_TEMPLATE)
21
+ WALLY_MINISCRIPT_POLICY_TEMPLATE | \
22
+ WALLY_MINISCRIPT_UNIQUE_KEYPATHS)
22
23
#define MS_FLAGS_CANONICALIZE (WALLY_MINISCRIPT_REQUIRE_CHECKSUM | \
23
24
WALLY_MINISCRIPT_POLICY_TEMPLATE)
24
25
@@ -204,6 +205,8 @@ static int ctx_add_key_node(ms_ctx *ctx, ms_node *node)
204
205
(unsigned char * )v , 1 , true, false);
205
206
}
206
207
208
+ static int ensure_unique_policy_keys (const ms_ctx * ctx );
209
+
207
210
/* Built-in miniscript expressions */
208
211
typedef int (* node_verify_fn_t )(ms_ctx * ctx , ms_node * node );
209
212
typedef int (* node_gen_fn_t )(ms_ctx * ctx , ms_node * node ,
@@ -381,7 +384,7 @@ static int canonicalize(const char *descriptor,
381
384
int key_index_hwm = -1 ;
382
385
const char * p = descriptor , * start ;
383
386
char * out ;
384
- bool found_policy_key = false, found_policy_single = false, found_policy_multi = false;;
387
+ bool found_policy_single = false, found_policy_multi = false;;
385
388
386
389
* output = NULL ;
387
390
* num_substitutions = 0 ;
@@ -427,7 +430,6 @@ static int canonicalize(const char *descriptor,
427
430
return WALLY_EINVAL ; /* Must be ordered with no gaps */
428
431
if (key_index > key_index_hwm )
429
432
key_index_hwm = key_index ;
430
- found_policy_key = true;
431
433
if (* p ++ != '/' )
432
434
return WALLY_EINVAL ;
433
435
++ required_len ;
@@ -453,10 +455,10 @@ static int canonicalize(const char *descriptor,
453
455
if (!* p && (flags & WALLY_MINISCRIPT_REQUIRE_CHECKSUM ))
454
456
return WALLY_EINVAL ; /* Checksum required but not present */
455
457
if (flags & WALLY_MINISCRIPT_POLICY_TEMPLATE ) {
456
- if (!found_policy_key )
457
- return WALLY_EINVAL ; /* At least one key expression must be present */
458
458
if (found_policy_single && found_policy_multi )
459
459
return WALLY_EINVAL ; /* Cannot mix cardinality of policy keys */
460
+ if (key_index_hwm == -1 || key_index_hwm != (int )vars_in -> num_items - 1 )
461
+ return WALLY_EINVAL ; /* One or more keys wasn't substituted */
460
462
}
461
463
if (!(* output = wally_malloc (required_len + 1 + DESCRIPTOR_CHECKSUM_LENGTH + 1 )))
462
464
return WALLY_ENOMEM ;
@@ -2633,9 +2635,13 @@ int wally_descriptor_parse(const char *miniscript,
2633
2635
ret = node_generation_size (ctx -> top_node , & ctx -> script_len );
2634
2636
if (ret == WALLY_OK && (flags & WALLY_MINISCRIPT_POLICY_TEMPLATE )) {
2635
2637
if (ctx -> keys .num_items != num_substitutions )
2636
- ret = WALLY_EINVAL ; /* A non-substituted key was present */
2638
+ ret = WALLY_EINVAL ; /* non-substituted key in the expression */
2639
+ else if (vars_in && ctx -> keys .num_items < vars_in -> num_items )
2640
+ ret = WALLY_EINVAL ; /* non-substituted key in substitutions */
2637
2641
else if (ctx -> num_variants > 1 || ctx -> num_multipaths > 2 )
2638
2642
ret = WALLY_EINVAL ; /* Solved cardinality must be 1 or 2 */
2643
+ else if (flags & WALLY_MINISCRIPT_UNIQUE_KEYPATHS )
2644
+ ret = ensure_unique_policy_keys (ctx );
2639
2645
}
2640
2646
}
2641
2647
if (ret != WALLY_OK ) {
@@ -2973,3 +2979,64 @@ int wally_descriptor_get_key_child_path_str(
2973
2979
return WALLY_ENOMEM ;
2974
2980
return WALLY_OK ;
2975
2981
}
2982
+
2983
+ static const char * get_multipath_child (const char * p , uint32_t * v )
2984
+ {
2985
+ * v = 0 ;
2986
+ if (* p != '<' && * p != ';' )
2987
+ return NULL ;
2988
+ else {
2989
+ ++ p ;
2990
+ while (* p >= '0' && * p <= '9' ) {
2991
+ * v *= 10 ;
2992
+ * v += (* p ++ - '0' );
2993
+ }
2994
+ if (* p == '\'' || * p == 'h' || * p == 'H' ) {
2995
+ * v |= BIP32_INITIAL_HARDENED_CHILD ;
2996
+ ++ p ;
2997
+ }
2998
+ }
2999
+ return p ;
3000
+ }
3001
+
3002
+ static int are_keys_overlapped (const ms_ctx * ctx ,
3003
+ const ms_node * lhs , const ms_node * rhs )
3004
+ {
3005
+ const char * p ;
3006
+ uint32_t l1 , l2 , r1 , r2 ;
3007
+
3008
+ if (lhs -> data_len != rhs -> data_len ||
3009
+ memcmp (lhs -> data , rhs -> data , lhs -> data_len ))
3010
+ return WALLY_OK ; /* Different root keys */
3011
+ if (lhs -> child_path_len == rhs -> child_path_len &&
3012
+ !memcmp (lhs -> child_path , rhs -> child_path , lhs -> child_path_len ))
3013
+ return WALLY_EINVAL ; /* Identical paths */
3014
+ if (!(lhs -> flags & WALLY_MS_IS_MULTIPATH ))
3015
+ return WALLY_OK ; /* Non-identical ranged, non-multipath keys */
3016
+ if (ctx -> max_path_elems != 2 || !(rhs -> flags & WALLY_MS_IS_MULTIPATH ))
3017
+ return WALLY_ERROR ; /* Should never happen! */
3018
+ /* Check the set of multi-path indices is disjoint */
3019
+ if (!(p = get_multipath_child (strchr (lhs -> child_path , '<' ), & l1 )) ||
3020
+ !get_multipath_child (p , & l2 ) ||
3021
+ !(p = get_multipath_child (strchr (rhs -> child_path , '<' ), & r1 )) ||
3022
+ !get_multipath_child (p , & r2 ))
3023
+ return WALLY_ERROR ; /* Should never happen! */
3024
+ if (l1 == r1 || l1 == r2 || l2 == r1 || l2 == r2 )
3025
+ return WALLY_EINVAL ; /* indices are not disjoint */
3026
+ return WALLY_OK ;
3027
+ }
3028
+
3029
+ static int ensure_unique_policy_keys (const ms_ctx * ctx )
3030
+ {
3031
+ size_t i , j ;
3032
+
3033
+ for (i = 0 ; i < ctx -> keys .num_items ; ++ i ) {
3034
+ const ms_node * node = descriptor_get_key (ctx , i );
3035
+ for (j = i + 1 ; j < ctx -> keys .num_items ; ++ j ) {
3036
+ int ret = are_keys_overlapped (ctx , node , descriptor_get_key (ctx , j ));
3037
+ if (ret != WALLY_OK )
3038
+ return ret ;
3039
+ }
3040
+ }
3041
+ return WALLY_OK ;
3042
+ }
0 commit comments