1818#define MS_FLAGS_ALL (WALLY_MINISCRIPT_TAPSCRIPT | \
1919 WALLY_MINISCRIPT_ONLY | \
2020 WALLY_MINISCRIPT_REQUIRE_CHECKSUM | \
21- WALLY_MINISCRIPT_POLICY_TEMPLATE)
21+ WALLY_MINISCRIPT_POLICY_TEMPLATE | \
22+ WALLY_MINISCRIPT_UNIQUE_KEYPATHS)
2223#define MS_FLAGS_CANONICALIZE (WALLY_MINISCRIPT_REQUIRE_CHECKSUM | \
2324 WALLY_MINISCRIPT_POLICY_TEMPLATE)
2425
@@ -204,6 +205,8 @@ static int ctx_add_key_node(ms_ctx *ctx, ms_node *node)
204205 (unsigned char * )v , 1 , true, false);
205206}
206207
208+ static int ensure_unique_policy_keys (const ms_ctx * ctx );
209+
207210/* Built-in miniscript expressions */
208211typedef int (* node_verify_fn_t )(ms_ctx * ctx , ms_node * node );
209212typedef int (* node_gen_fn_t )(ms_ctx * ctx , ms_node * node ,
@@ -381,7 +384,7 @@ static int canonicalize(const char *descriptor,
381384 int key_index_hwm = -1 ;
382385 const char * p = descriptor , * start ;
383386 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;;
385388
386389 * output = NULL ;
387390 * num_substitutions = 0 ;
@@ -427,7 +430,6 @@ static int canonicalize(const char *descriptor,
427430 return WALLY_EINVAL ; /* Must be ordered with no gaps */
428431 if (key_index > key_index_hwm )
429432 key_index_hwm = key_index ;
430- found_policy_key = true;
431433 if (* p ++ != '/' )
432434 return WALLY_EINVAL ;
433435 ++ required_len ;
@@ -453,10 +455,10 @@ static int canonicalize(const char *descriptor,
453455 if (!* p && (flags & WALLY_MINISCRIPT_REQUIRE_CHECKSUM ))
454456 return WALLY_EINVAL ; /* Checksum required but not present */
455457 if (flags & WALLY_MINISCRIPT_POLICY_TEMPLATE ) {
456- if (!found_policy_key )
457- return WALLY_EINVAL ; /* At least one key expression must be present */
458458 if (found_policy_single && found_policy_multi )
459459 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 */
460462 }
461463 if (!(* output = wally_malloc (required_len + 1 + DESCRIPTOR_CHECKSUM_LENGTH + 1 )))
462464 return WALLY_ENOMEM ;
@@ -2633,9 +2635,13 @@ int wally_descriptor_parse(const char *miniscript,
26332635 ret = node_generation_size (ctx -> top_node , & ctx -> script_len );
26342636 if (ret == WALLY_OK && (flags & WALLY_MINISCRIPT_POLICY_TEMPLATE )) {
26352637 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 */
26372641 else if (ctx -> num_variants > 1 || ctx -> num_multipaths > 2 )
26382642 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 );
26392645 }
26402646 }
26412647 if (ret != WALLY_OK ) {
@@ -2973,3 +2979,64 @@ int wally_descriptor_get_key_child_path_str(
29732979 return WALLY_ENOMEM ;
29742980 return WALLY_OK ;
29752981}
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