@@ -137,6 +137,47 @@ fn get_change_and_address_index<R: core::convert::AsRef<str>, T: core::iter::Ite
137
137
Err ( Error :: InvalidInput )
138
138
}
139
139
140
+ /// Check that it is impossible to create a derivation with duplicate pubkeys, assuming all the keys
141
+ /// in the key vector are distinct.
142
+ ///
143
+ /// Even though the rust-miniscript library checks for duplicate keys, it does so on the raw
144
+ /// miniscript, which would not catch e.g. that `wsh(or_b(pk(@0/<0;1>/*),s:pk(@0/<2;1>/*)))` has a
145
+ /// duplicate change derivation if we derive at the receive path.
146
+ ///
147
+ /// Also checks that each key is used, e.g. if there are 3 keys in the key vector, @0, @1 and @2
148
+ /// must be present.
149
+ fn check_dups < R : core:: convert:: AsRef < str > , T : core:: iter:: Iterator < Item = R > > (
150
+ pubkeys : T ,
151
+ keys : & [ pb:: KeyOriginInfo ] ,
152
+ ) -> Result < ( ) , Error > {
153
+ // in "@key_index/<left;right>", keeps track of (key_index,left) and
154
+ // (key_index,right) to check for duplicates.
155
+ let mut derivations_seen: Vec < ( usize , u32 ) > = Vec :: new ( ) ;
156
+
157
+ let mut keys_seen: Vec < bool > = vec ! [ false ; keys. len( ) ] ;
158
+
159
+ for pk in pubkeys {
160
+ let ( key_index, multipath_index_left, multipath_index_right) =
161
+ parse_wallet_policy_pk ( pk. as_ref ( ) ) . or ( Err ( Error :: InvalidInput ) ) ?;
162
+
163
+ if derivations_seen. contains ( & ( key_index, multipath_index_left) ) {
164
+ return Err ( Error :: InvalidInput ) ;
165
+ }
166
+ derivations_seen. push ( ( key_index, multipath_index_left) ) ;
167
+ if derivations_seen. contains ( & ( key_index, multipath_index_right) ) {
168
+ return Err ( Error :: InvalidInput ) ;
169
+ }
170
+ derivations_seen. push ( ( key_index, multipath_index_right) ) ;
171
+
172
+ * keys_seen. get_mut ( key_index) . ok_or ( Error :: InvalidInput ) ? = true ;
173
+ }
174
+
175
+ if !keys_seen. into_iter ( ) . all ( |b| b) {
176
+ return Err ( Error :: InvalidInput ) ;
177
+ }
178
+ Ok ( ( ) )
179
+ }
180
+
140
181
struct WalletPolicyPkTranslator < ' a > {
141
182
keys : & ' a [ pb:: KeyOriginInfo ] ,
142
183
is_change : bool ,
@@ -174,67 +215,30 @@ impl<'a> miniscript::Translator<String, bitcoin::PublicKey, Error>
174
215
175
216
/// See `ParsedPolicy`.
176
217
#[ derive( Debug ) ]
177
- pub struct Wsh < ' a > {
178
- policy : & ' a Policy ,
218
+ pub struct Wsh {
179
219
miniscript_expr : miniscript:: Miniscript < String , miniscript:: Segwitv0 > ,
180
220
}
181
221
182
- /// Result of `parse() `.
222
+ /// See `ParsedPolicy `.
183
223
#[ derive( Debug ) ]
184
- pub enum ParsedPolicy < ' a > {
224
+ pub enum Descriptor {
185
225
// `wsh(...)` policies
186
- Wsh ( Wsh < ' a > ) ,
226
+ Wsh ( Wsh ) ,
187
227
// `tr(...)` Taproot etc. in the future.
188
228
}
189
229
190
- impl < ' a > ParsedPolicy < ' a > {
191
- fn get_policy ( & self ) -> & Policy {
192
- match self {
193
- Self :: Wsh ( Wsh { policy , .. } ) => policy ,
194
- }
195
- }
230
+ /// Result of `parse()`.
231
+ # [ derive ( Debug ) ]
232
+ pub struct ParsedPolicy < ' a > {
233
+ policy : & ' a Policy ,
234
+ pub descriptor : Descriptor ,
235
+ }
196
236
197
- /// Check that it is impossible to create a derivation with duplicate pubkeys, assuming all the
198
- /// keys in the key vector are distinct.
199
- ///
200
- /// Even though the rust-miniscript library checks for duplicate keys, it does so on the raw
201
- /// miniscript, which would not catch e.g. that `wsh(or_b(pk(@0/<0;1>/*),s:pk(@0/<2;1>/*)))` has
202
- /// a duplicate change derivation if we derive at the receive path.
203
- ///
204
- /// Also checks that each key is used, e.g. if there are 3 keys in the key vector, @0, @1 and @2
205
- /// must be present.
237
+ impl < ' a > ParsedPolicy < ' a > {
206
238
fn validate_keys ( & self ) -> Result < ( ) , Error > {
207
- match self {
208
- Self :: Wsh ( Wsh {
209
- policy,
210
- miniscript_expr,
211
- } ) => {
212
- // in "@key_index/<left;right>", keeps track of (key_index,left) and
213
- // (key_index,right) to check for duplicates.
214
- let mut derivations_seen: Vec < ( usize , u32 ) > = Vec :: new ( ) ;
215
-
216
- let mut keys_seen: Vec < bool > = vec ! [ false ; policy. keys. len( ) ] ;
217
-
218
- for pk in miniscript_expr. iter_pk ( ) {
219
- let ( key_index, multipath_index_left, multipath_index_right) =
220
- parse_wallet_policy_pk ( & pk) . or ( Err ( Error :: InvalidInput ) ) ?;
221
-
222
- if derivations_seen. contains ( & ( key_index, multipath_index_left) ) {
223
- return Err ( Error :: InvalidInput ) ;
224
- }
225
- derivations_seen. push ( ( key_index, multipath_index_left) ) ;
226
- if derivations_seen. contains ( & ( key_index, multipath_index_right) ) {
227
- return Err ( Error :: InvalidInput ) ;
228
- }
229
- derivations_seen. push ( ( key_index, multipath_index_right) ) ;
230
-
231
- * keys_seen. get_mut ( key_index) . ok_or ( Error :: InvalidInput ) ? = true ;
232
- }
233
-
234
- if !keys_seen. into_iter ( ) . all ( |b| b) {
235
- return Err ( Error :: InvalidInput ) ;
236
- }
237
- Ok ( ( ) )
239
+ match & self . descriptor {
240
+ Descriptor :: Wsh ( Wsh { miniscript_expr } ) => {
241
+ check_dups ( miniscript_expr. iter_pk ( ) , & self . policy . keys )
238
242
}
239
243
}
240
244
}
@@ -248,7 +252,7 @@ impl<'a> ParsedPolicy<'a> {
248
252
pub fn validate ( & self , coin : BtcCoin ) -> Result < ( ) , Error > {
249
253
check_enabled ( coin) ?;
250
254
251
- let policy = self . get_policy ( ) ;
255
+ let policy = self . policy ;
252
256
253
257
if policy. keys . len ( ) > MAX_KEYS {
254
258
return Err ( Error :: InvalidInput ) ;
@@ -297,13 +301,10 @@ impl<'a> ParsedPolicy<'a> {
297
301
/// wsh(and_v(v:pk(@0/0/5),pk(@1/20/5))).
298
302
/// The same derived using `is_change=true` derives: wsh(and_v(v:pk(@0/1/5),pk(@1/21/5)))
299
303
pub fn witness_script ( & self , is_change : bool , address_index : u32 ) -> Result < Vec < u8 > , Error > {
300
- match self {
301
- Self :: Wsh ( Wsh {
302
- policy,
303
- miniscript_expr,
304
- } ) => {
304
+ match & self . descriptor {
305
+ Descriptor :: Wsh ( Wsh { miniscript_expr } ) => {
305
306
let mut translator = WalletPolicyPkTranslator {
306
- keys : policy. keys . as_ref ( ) ,
307
+ keys : self . policy . keys . as_ref ( ) ,
307
308
is_change,
308
309
address_index,
309
310
} ;
@@ -322,27 +323,27 @@ impl<'a> ParsedPolicy<'a> {
322
323
/// derived using keypath m/48'/1'/0'/3'/11/5 derives:
323
324
/// wsh(and_v(v:pk(@0/11/5),pk(@1/21/5))).
324
325
pub fn witness_script_at_keypath ( & self , keypath : & [ u32 ] ) -> Result < Vec < u8 > , Error > {
325
- match self {
326
- Self :: Wsh ( Wsh {
327
- policy ,
328
- miniscript_expr,
329
- } ) => {
330
- let ( is_change , address_index ) =
331
- get_change_and_address_index ( miniscript_expr . iter_pk ( ) , & policy . keys , keypath ) ?;
326
+ match & self . descriptor {
327
+ Descriptor :: Wsh ( Wsh { miniscript_expr } ) => {
328
+ let ( is_change , address_index ) = get_change_and_address_index (
329
+ miniscript_expr. iter_pk ( ) ,
330
+ & self . policy . keys ,
331
+ keypath ,
332
+ ) ?;
332
333
self . witness_script ( is_change, address_index)
333
334
}
334
335
}
335
336
}
336
337
337
338
/// Returns true if the address-level keypath points to a change address.
338
339
pub fn is_change_keypath ( & self , keypath : & [ u32 ] ) -> Result < bool , Error > {
339
- match self {
340
- Self :: Wsh ( Wsh {
341
- policy ,
342
- miniscript_expr,
343
- } ) => {
344
- let ( is_change , _ ) =
345
- get_change_and_address_index ( miniscript_expr . iter_pk ( ) , & policy . keys , keypath ) ?;
340
+ match & self . descriptor {
341
+ Descriptor :: Wsh ( Wsh { miniscript_expr } ) => {
342
+ let ( is_change , _ ) = get_change_and_address_index (
343
+ miniscript_expr. iter_pk ( ) ,
344
+ & self . policy . keys ,
345
+ keypath ,
346
+ ) ?;
346
347
Ok ( is_change)
347
348
}
348
349
}
@@ -364,10 +365,10 @@ pub fn parse(policy: &Policy) -> Result<ParsedPolicy, Error> {
364
365
miniscript:: Miniscript :: from_str ( & desc[ 4 ..desc. len ( ) - 1 ] )
365
366
. or ( Err ( Error :: InvalidInput ) ) ?;
366
367
367
- Ok ( ParsedPolicy :: Wsh ( Wsh {
368
+ Ok ( ParsedPolicy {
368
369
policy,
369
- miniscript_expr,
370
- } ) )
370
+ descriptor : Descriptor :: Wsh ( Wsh { miniscript_expr } ) ,
371
+ } )
371
372
}
372
373
_ => Err ( Error :: InvalidInput ) ,
373
374
}
@@ -595,10 +596,9 @@ mod tests {
595
596
fn test_parse_wsh_miniscript ( ) {
596
597
// Parse a valid example and check that the keys are collected as is as strings.
597
598
let policy = make_policy ( "wsh(pk(@0/**))" , & [ ] ) ;
598
- match parse ( & policy) . unwrap ( ) {
599
- ParsedPolicy :: Wsh ( Wsh {
600
- ref miniscript_expr,
601
- ..
599
+ match & parse ( & policy) . unwrap ( ) . descriptor {
600
+ Descriptor :: Wsh ( Wsh {
601
+ miniscript_expr, ..
602
602
} ) => {
603
603
assert_eq ! (
604
604
miniscript_expr. iter_pk( ) . collect:: <Vec <String >>( ) ,
@@ -609,10 +609,9 @@ mod tests {
609
609
610
610
// Parse another valid example and check that the keys are collected as is as strings.
611
611
let policy = make_policy ( "wsh(or_b(pk(@0/**),s:pk(@1/**)))" , & [ ] ) ;
612
- match parse ( & policy) . unwrap ( ) {
613
- ParsedPolicy :: Wsh ( Wsh {
614
- ref miniscript_expr,
615
- ..
612
+ match & parse ( & policy) . unwrap ( ) . descriptor {
613
+ Descriptor :: Wsh ( Wsh {
614
+ miniscript_expr, ..
616
615
} ) => {
617
616
assert_eq ! (
618
617
miniscript_expr. iter_pk( ) . collect:: <Vec <String >>( ) ,
@@ -770,8 +769,8 @@ mod tests {
770
769
assert ! ( parse( & pol) . unwrap( ) . validate( coin) . is_ok( ) ) ;
771
770
772
771
// Duplicate path, one time in change, one time in receive. While the keys technically are
773
- // never duplicate in the final miniscript with the pubkeys inserted, we still prohibit, as
774
- // it does not look like there would be a sane use case for this and would likely be an
772
+ // never duplicate in the final miniscript with the pubkeys inserted, we still prohibit it,
773
+ // as it does not look like there would be a sane use case for this and would likely be an
775
774
// accident.
776
775
let pol = make_policy (
777
776
"wsh(or_b(pk(@0/<0;1>/*),s:pk(@0/<1;2>/*)))" ,
0 commit comments