Skip to content

Commit 34bc408

Browse files
committed
policies: move duplicate key check into separate function
So it can be reused when we want to check dups in Tapscripts, when adding support for Taproot `tr(...)` descriptors/policies.
1 parent 84cf50e commit 34bc408

File tree

1 file changed

+44
-37
lines changed

1 file changed

+44
-37
lines changed

src/rust/bitbox02-rust/src/hww/api/bitcoin/policies.rs

Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,47 @@ fn get_change_and_address_index<R: core::convert::AsRef<str>, T: core::iter::Ite
137137
Err(Error::InvalidInput)
138138
}
139139

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+
140181
struct WalletPolicyPkTranslator<'a> {
141182
keys: &'a [pb::KeyOriginInfo],
142183
is_change: bool,
@@ -194,44 +235,10 @@ pub struct ParsedPolicy<'a> {
194235
}
195236

196237
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.
206238
fn validate_keys(&self) -> Result<(), Error> {
207239
match &self.descriptor {
208240
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)
235242
}
236243
}
237244
}
@@ -762,8 +769,8 @@ mod tests {
762769
assert!(parse(&pol).unwrap().validate(coin).is_ok());
763770

764771
// 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
767774
// accident.
768775
let pol = make_policy(
769776
"wsh(or_b(pk(@0/<0;1>/*),s:pk(@0/<1;2>/*)))",

0 commit comments

Comments
 (0)