]> Untitled Git - bdk/commitdiff
[policy] Allow specifying a policy path for `Multisig`
authorAlekos Filini <alekos.filini@gmail.com>
Sat, 13 Feb 2021 16:00:03 +0000 (11:00 -0500)
committerAlekos Filini <alekos.filini@gmail.com>
Sat, 13 Feb 2021 16:17:07 +0000 (11:17 -0500)
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.

src/descriptor/policy.rs

index 40d8efb3bda70b41d1905b5a26c95eaa3531af94..8929e0859215e337114450c02420574588e1825c 100644 (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)));
+    }
 }