[policy] Allow specifying a policy path for Multisig

While technically it's not required since there are no timelocks inside,
it's still less confusing for the end user if we allow this instead of
failing like we do currently.
This commit is contained in:
Alekos Filini 2021-02-13 11:00:03 -05:00
parent fa2610538f
commit b61427c07b
No known key found for this signature in database
GPG Key ID: 431401E4A4530061

View File

@ -506,11 +506,11 @@ impl Condition {
}
/// Errors that can happen while extracting and manipulating policies
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq)]
pub enum PolicyError {
/// Not enough items are selected to satisfy a [`SatisfiableItem::Thresh`]
/// Not enough items are selected to satisfy a [`SatisfiableItem::Thresh`] or a [`SatisfiableItem::Multisig`]
NotEnoughItemsSelected(String),
/// Index out of range for an item to satisfy a [`SatisfiableItem::Thresh`]
/// Index out of range for an item to satisfy a [`SatisfiableItem::Thresh`] or a [`SatisfiableItem::Multisig`]
IndexOutOfRange(usize),
/// Can not add to an item that is [`Satisfaction::None`] or [`Satisfaction::Complete`]
AddOnLeaf,
@ -642,10 +642,10 @@ impl Policy {
SatisfiableItem::Thresh { items, threshold } if items.len() == *threshold => {
(0..*threshold).collect()
}
SatisfiableItem::Multisig { keys, .. } => (0..keys.len()).collect(),
_ => vec![],
};
let selected = match path.get(&self.id) {
_ if !default.is_empty() => &default,
Some(arr) => arr,
_ => &default,
};
@ -682,7 +682,16 @@ impl Policy {
Ok(requirements)
}
_ if !selected.is_empty() => Err(PolicyError::TooManyItemsSelected(self.id.clone())),
SatisfiableItem::Multisig { keys, threshold } => {
if selected.len() < *threshold {
return Err(PolicyError::NotEnoughItemsSelected(self.id.clone()));
}
if let Some(item) = selected.iter().find(|i| **i >= keys.len()) {
return Err(PolicyError::IndexOutOfRange(*item));
}
Ok(Condition::default())
}
SatisfiableItem::AbsoluteTimelock { value } => Ok(Condition {
csv: None,
timelock: Some(*value),
@ -1249,4 +1258,50 @@ mod test {
//
// // TODO how should this merge timelocks?
// }
#[test]
fn test_get_condition_multisig() {
let secp = Secp256k1::gen_new();
let (_, pk0, _) = setup_keys(TPRV0_STR);
let (_, pk1, _) = setup_keys(TPRV1_STR);
let desc = descriptor!(wsh(multi(1, pk0, pk1))).unwrap();
let (wallet_desc, keymap) = desc
.into_wallet_descriptor(&secp, Network::Testnet)
.unwrap();
let signers = keymap.into();
let policy = wallet_desc
.extract_policy(&signers, &secp)
.unwrap()
.unwrap();
// no args, choose the default
let no_args = policy.get_condition(&vec![].into_iter().collect());
assert_eq!(no_args, Ok(Condition::default()));
// enough args
let eq_thresh =
policy.get_condition(&vec![(policy.id.clone(), vec![0])].into_iter().collect());
assert_eq!(eq_thresh, Ok(Condition::default()));
// more args, it doesn't really change anything
let gt_thresh =
policy.get_condition(&vec![(policy.id.clone(), vec![0, 1])].into_iter().collect());
assert_eq!(gt_thresh, Ok(Condition::default()));
// not enough args, error
let lt_thresh =
policy.get_condition(&vec![(policy.id.clone(), vec![])].into_iter().collect());
assert_eq!(
lt_thresh,
Err(PolicyError::NotEnoughItemsSelected(policy.id.clone()))
);
// index out of range
let out_of_range =
policy.get_condition(&vec![(policy.id.clone(), vec![5])].into_iter().collect());
assert_eq!(out_of_range, Err(PolicyError::IndexOutOfRange(5)));
}
}