@@ -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 ,
@@ -194,44 +235,10 @@ pub struct ParsedPolicy<'a> {
194
235
}
195
236
196
237
impl < ' a > ParsedPolicy < ' a > {
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.
206
238
fn validate_keys ( & self ) -> Result < ( ) , Error > {
207
239
match & self . descriptor {
208
240
Descriptor :: Wsh ( Wsh { miniscript_expr } ) => {
209
- // in "@key_index/<left;right>", keeps track of (key_index,left) and
210
- // (key_index,right) to check for duplicates.
211
- let mut derivations_seen: Vec < ( usize , u32 ) > = Vec :: new ( ) ;
212
-
213
- let mut keys_seen: Vec < bool > = vec ! [ false ; self . policy. keys. len( ) ] ;
214
-
215
- for pk in miniscript_expr. iter_pk ( ) {
216
- let ( key_index, multipath_index_left, multipath_index_right) =
217
- parse_wallet_policy_pk ( & pk) . or ( Err ( Error :: InvalidInput ) ) ?;
218
-
219
- if derivations_seen. contains ( & ( key_index, multipath_index_left) ) {
220
- return Err ( Error :: InvalidInput ) ;
221
- }
222
- derivations_seen. push ( ( key_index, multipath_index_left) ) ;
223
- if derivations_seen. contains ( & ( key_index, multipath_index_right) ) {
224
- return Err ( Error :: InvalidInput ) ;
225
- }
226
- derivations_seen. push ( ( key_index, multipath_index_right) ) ;
227
-
228
- * keys_seen. get_mut ( key_index) . ok_or ( Error :: InvalidInput ) ? = true ;
229
- }
230
-
231
- if !keys_seen. into_iter ( ) . all ( |b| b) {
232
- return Err ( Error :: InvalidInput ) ;
233
- }
234
- Ok ( ( ) )
241
+ check_dups ( miniscript_expr. iter_pk ( ) , & self . policy . keys )
235
242
}
236
243
}
237
244
}
@@ -762,8 +769,8 @@ mod tests {
762
769
assert ! ( parse( & pol) . unwrap( ) . validate( coin) . is_ok( ) ) ;
763
770
764
771
// Duplicate path, one time in change, one time in receive. While the keys technically are
765
- // never duplicate in the final miniscript with the pubkeys inserted, we still prohibit, as
766
- // 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
767
774
// accident.
768
775
let pol = make_policy (
769
776
"wsh(or_b(pk(@0/<0;1>/*),s:pk(@0/<1;2>/*)))" ,
0 commit comments