bdk_wallet/descriptor/
policy.rs

1// Bitcoin Dev Kit
2// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
3//
4// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
5//
6// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
7// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
8// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
9// You may not use this file except in accordance with one or both of these
10// licenses.
11
12//! Descriptor policy
13//!
14//! This module implements the logic to extract and represent the spending policies of a descriptor
15//! in a more human-readable format.
16//!
17//! This is an **EXPERIMENTAL** feature, API and other major changes are expected.
18//!
19//! ## Example
20//!
21//! ```
22//! # use std::sync::Arc;
23//! # use bdk_wallet::descriptor::*;
24//! # use bdk_wallet::signer::*;
25//! # use bdk_wallet::bitcoin::secp256k1::Secp256k1;
26//! use bdk_wallet::descriptor::policy::BuildSatisfaction;
27//! let secp = Secp256k1::new();
28//! let desc = "wsh(and_v(v:pk(cV3oCth6zxZ1UVsHLnGothsWNsaoxRhC6aeNi5VbSdFpwUkgkEci),or_d(pk(cVMTy7uebJgvFaSBwcgvwk8qn8xSLc97dKow4MBetjrrahZoimm2),older(12960))))";
29//!
30//! let (extended_desc, key_map) = ExtendedDescriptor::parse_descriptor(&secp, desc)?;
31//! println!("{:?}", extended_desc);
32//!
33//! let signers = Arc::new(SignersContainer::build(key_map, &extended_desc, &secp));
34//! let policy = extended_desc.extract_policy(&signers, BuildSatisfaction::None, &secp)?;
35//! println!("policy: {}", serde_json::to_string(&policy).unwrap());
36//! # Ok::<(), anyhow::Error>(())
37//! ```
38
39use crate::collections::{BTreeMap, HashSet, VecDeque};
40use alloc::string::String;
41use alloc::vec::Vec;
42use core::cmp::max;
43use miniscript::miniscript::limits::{MAX_PUBKEYS_IN_CHECKSIGADD, MAX_PUBKEYS_PER_MULTISIG};
44
45use core::fmt;
46
47use serde::ser::SerializeMap;
48use serde::{Serialize, Serializer};
49
50use bitcoin::bip32::Fingerprint;
51use bitcoin::hashes::{hash160, ripemd160, sha256};
52use bitcoin::{absolute, key::XOnlyPublicKey, relative, PublicKey, Sequence};
53
54use miniscript::descriptor::{
55    DescriptorPublicKey, ShInner, SinglePub, SinglePubKey, SortedMultiVec, WshInner,
56};
57use miniscript::{hash256, Threshold};
58use miniscript::{
59    Descriptor, Miniscript, Satisfier, ScriptContext, SigType, Terminal, ToPublicKey,
60};
61
62use crate::descriptor::ExtractPolicy;
63use crate::keys::ExtScriptContext;
64use crate::wallet::signer::{SignerId, SignersContainer};
65use crate::wallet::utils::{After, Older, SecpCtx};
66
67use super::checksum::calc_checksum;
68use super::error::Error;
69use super::XKeyUtils;
70use bitcoin::psbt::{self, Psbt};
71use miniscript::psbt::PsbtInputSatisfier;
72
73/// A unique identifier for a key
74#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
75#[serde(rename_all = "snake_case")]
76pub enum PkOrF {
77    /// A legacy public key
78    Pubkey(PublicKey),
79    /// A x-only public key
80    XOnlyPubkey(XOnlyPublicKey),
81    /// An extended key fingerprint
82    Fingerprint(Fingerprint),
83}
84
85impl PkOrF {
86    fn from_key(k: &DescriptorPublicKey, secp: &SecpCtx) -> Self {
87        match k {
88            DescriptorPublicKey::Single(SinglePub {
89                key: SinglePubKey::FullKey(pk),
90                ..
91            }) => PkOrF::Pubkey(*pk),
92            DescriptorPublicKey::Single(SinglePub {
93                key: SinglePubKey::XOnly(pk),
94                ..
95            }) => PkOrF::XOnlyPubkey(*pk),
96            DescriptorPublicKey::XPub(xpub) => PkOrF::Fingerprint(xpub.root_fingerprint(secp)),
97            DescriptorPublicKey::MultiXPub(multi) => {
98                PkOrF::Fingerprint(multi.root_fingerprint(secp))
99            }
100        }
101    }
102}
103
104/// An item that needs to be satisfied
105#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
106#[serde(tag = "type", rename_all = "UPPERCASE")]
107pub enum SatisfiableItem {
108    // Leaves
109    /// ECDSA Signature for a raw public key
110    EcdsaSignature(PkOrF),
111    /// Schnorr Signature for a raw public key
112    SchnorrSignature(PkOrF),
113    /// SHA256 preimage hash
114    Sha256Preimage {
115        /// The digest value
116        hash: sha256::Hash,
117    },
118    /// Double SHA256 preimage hash
119    Hash256Preimage {
120        /// The digest value
121        hash: hash256::Hash,
122    },
123    /// RIPEMD160 preimage hash
124    Ripemd160Preimage {
125        /// The digest value
126        hash: ripemd160::Hash,
127    },
128    /// SHA256 then RIPEMD160 preimage hash
129    Hash160Preimage {
130        /// The digest value
131        hash: hash160::Hash,
132    },
133    /// Absolute timeclock timestamp
134    AbsoluteTimelock {
135        /// The timelock value
136        value: absolute::LockTime,
137    },
138    /// Relative timelock locktime
139    RelativeTimelock {
140        /// The timelock value
141        value: relative::LockTime,
142    },
143    /// Multi-signature public keys with threshold count
144    Multisig {
145        /// The raw public key or extended key fingerprint
146        keys: Vec<PkOrF>,
147        /// The required threshold count
148        threshold: usize,
149    },
150
151    // Complex item
152    /// Threshold items with threshold count
153    Thresh {
154        /// The policy items
155        items: Vec<Policy>,
156        /// The required threshold count
157        threshold: usize,
158    },
159}
160
161impl SatisfiableItem {
162    /// Returns whether the [`SatisfiableItem`] is a leaf item
163    pub fn is_leaf(&self) -> bool {
164        !matches!(
165            self,
166            SatisfiableItem::Thresh {
167                items: _,
168                threshold: _,
169            }
170        )
171    }
172
173    /// Returns a unique id for the [`SatisfiableItem`]
174    pub fn id(&self) -> String {
175        calc_checksum(&serde_json::to_string(self).expect("Failed to serialize a SatisfiableItem"))
176            .expect("Failed to compute a SatisfiableItem id")
177    }
178}
179
180fn combinations(vec: &[usize], size: usize) -> Vec<Vec<usize>> {
181    assert!(vec.len() >= size);
182
183    let mut answer = Vec::new();
184
185    let mut queue = VecDeque::new();
186    for (index, val) in vec.iter().enumerate() {
187        let mut new_vec = Vec::with_capacity(size);
188        new_vec.push(*val);
189        queue.push_back((index, new_vec));
190    }
191
192    while let Some((index, vals)) = queue.pop_front() {
193        if vals.len() >= size {
194            answer.push(vals);
195        } else {
196            for (new_index, val) in vec.iter().skip(index + 1).enumerate() {
197                let mut cloned = vals.clone();
198                cloned.push(*val);
199                queue.push_front((new_index, cloned));
200            }
201        }
202    }
203
204    answer
205}
206
207fn mix<T: Clone>(vec: Vec<Vec<T>>) -> Vec<Vec<T>> {
208    if vec.is_empty() || vec.iter().any(Vec::is_empty) {
209        return vec![];
210    }
211
212    let mut answer = Vec::new();
213    let size = vec.len();
214
215    let mut queue = VecDeque::new();
216    for i in &vec[0] {
217        let mut new_vec = Vec::with_capacity(size);
218        new_vec.push(i.clone());
219        queue.push_back(new_vec);
220    }
221
222    while let Some(vals) = queue.pop_front() {
223        if vals.len() >= size {
224            answer.push(vals);
225        } else {
226            let level = vals.len();
227            for i in &vec[level] {
228                let mut cloned = vals.clone();
229                cloned.push(i.clone());
230                queue.push_front(cloned);
231            }
232        }
233    }
234
235    answer
236}
237
238/// Type for a map of sets of [`Condition`] items keyed by each set's index
239pub type ConditionMap = BTreeMap<usize, HashSet<Condition>>;
240/// Type for a map of folded sets of [`Condition`] items keyed by a vector of the combined set's
241/// indexes
242pub type FoldedConditionMap = BTreeMap<Vec<usize>, HashSet<Condition>>;
243
244fn serialize_folded_cond_map<S>(
245    input_map: &FoldedConditionMap,
246    serializer: S,
247) -> Result<S::Ok, S::Error>
248where
249    S: Serializer,
250{
251    let mut map = serializer.serialize_map(Some(input_map.len()))?;
252    for (k, v) in input_map {
253        let k_string = format!("{k:?}");
254        map.serialize_entry(&k_string, v)?;
255    }
256    map.end()
257}
258
259/// Represent if and how much a policy item is satisfied by the wallet's descriptor
260#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
261#[serde(tag = "type", rename_all = "UPPERCASE")]
262pub enum Satisfaction {
263    /// Only a partial satisfaction of some kind of threshold policy
264    Partial {
265        /// Total number of items
266        n: usize,
267        /// Threshold
268        m: usize,
269        /// The items that can be satisfied by the descriptor or are satisfied in the PSBT
270        items: Vec<usize>,
271        #[serde(skip_serializing_if = "Option::is_none")]
272        /// Whether the items are sorted in lexicographic order (used by `sortedmulti`)
273        sorted: Option<bool>,
274        #[serde(skip_serializing_if = "BTreeMap::is_empty")]
275        /// Extra conditions that also need to be satisfied
276        conditions: ConditionMap,
277    },
278    /// Can reach the threshold of some kind of threshold policy
279    PartialComplete {
280        /// Total number of items
281        n: usize,
282        /// Threshold
283        m: usize,
284        /// The items that can be satisfied by the descriptor
285        items: Vec<usize>,
286        #[serde(skip_serializing_if = "Option::is_none")]
287        /// Whether the items are sorted in lexicographic order (used by `sortedmulti`)
288        sorted: Option<bool>,
289        #[serde(
290            serialize_with = "serialize_folded_cond_map",
291            skip_serializing_if = "BTreeMap::is_empty"
292        )]
293        /// Extra conditions that also need to be satisfied
294        conditions: FoldedConditionMap,
295    },
296
297    /// Can satisfy the policy item
298    Complete {
299        /// Extra conditions that also need to be satisfied
300        condition: Condition,
301    },
302    /// Cannot satisfy or contribute to the policy item
303    None,
304}
305
306impl Satisfaction {
307    /// Returns whether the [`Satisfaction`] is a leaf item
308    pub fn is_leaf(&self) -> bool {
309        match self {
310            Satisfaction::None | Satisfaction::Complete { .. } => true,
311            Satisfaction::PartialComplete { .. } | Satisfaction::Partial { .. } => false,
312        }
313    }
314
315    // add `inner` as one of self's partial items. this only makes sense on partials
316    fn add(&mut self, inner: &Satisfaction, inner_index: usize) -> Result<(), PolicyError> {
317        match self {
318            Satisfaction::None | Satisfaction::Complete { .. } => Err(PolicyError::AddOnLeaf),
319            Satisfaction::PartialComplete { .. } => Err(PolicyError::AddOnPartialComplete),
320            Satisfaction::Partial {
321                n,
322                ref mut conditions,
323                ref mut items,
324                ..
325            } => {
326                if inner_index >= *n || items.contains(&inner_index) {
327                    return Err(PolicyError::IndexOutOfRange(inner_index));
328                }
329
330                match inner {
331                    // not relevant if not completed yet
332                    Satisfaction::None | Satisfaction::Partial { .. } => return Ok(()),
333                    Satisfaction::Complete { condition } => {
334                        items.push(inner_index);
335                        conditions.insert(inner_index, vec![*condition].into_iter().collect());
336                    }
337                    Satisfaction::PartialComplete {
338                        conditions: other_conditions,
339                        ..
340                    } => {
341                        items.push(inner_index);
342                        let conditions_set = other_conditions
343                            .values()
344                            .fold(HashSet::new(), |set, i| set.union(i).cloned().collect());
345                        conditions.insert(inner_index, conditions_set);
346                    }
347                }
348
349                Ok(())
350            }
351        }
352    }
353
354    fn finalize(&mut self) {
355        // if partial try to bump it to a partialcomplete
356        if let Satisfaction::Partial {
357            n,
358            m,
359            items,
360            conditions,
361            sorted,
362        } = self
363        {
364            if items.len() >= *m {
365                let mut map = BTreeMap::new();
366                let indexes = combinations(items, *m);
367                // `indexes` at this point is a Vec<Vec<usize>>, with the "n choose k" of items (m
368                // of n)
369                indexes
370                    .into_iter()
371                    // .inspect(|x| println!("--- orig --- {:?}", x))
372                    // we map each of the combinations of elements into a tuple of ([chosen items],
373                    // [conditions]). unfortunately, those items have potentially more than one
374                    // condition (think about ORs), so we also use `mix` to expand those, i.e. [[0],
375                    // [1, 2]] becomes [[0, 1], [0, 2]]. This is necessary to make sure that we
376                    // consider every possible options and check whether or not they are compatible.
377                    // since this step can turn one item of the iterator into multiple ones, we use
378                    // `flat_map()` to expand them out
379                    .flat_map(|i_vec| {
380                        mix(i_vec
381                            .iter()
382                            .map(|i| {
383                                conditions
384                                    .get(i)
385                                    .map(|set| set.clone().into_iter().collect())
386                                    .unwrap_or_default()
387                            })
388                            .collect())
389                        .into_iter()
390                        .map(|x| (i_vec.clone(), x))
391                        .collect::<Vec<(Vec<usize>, Vec<Condition>)>>()
392                    })
393                    // .inspect(|x| println!("flat {:?}", x))
394                    // try to fold all the conditions for this specific combination of
395                    // indexes/options. if they are not compatible, try_fold will be Err
396                    .map(|(key, val)| {
397                        (
398                            key,
399                            val.into_iter()
400                                .try_fold(Condition::default(), |acc, v| acc.merge(&v)),
401                        )
402                    })
403                    // .inspect(|x| println!("try_fold {:?}", x))
404                    // filter out all the incompatible combinations
405                    .filter(|(_, val)| val.is_ok())
406                    // .inspect(|x| println!("filter {:?}", x))
407                    // push them into the map
408                    .for_each(|(key, val)| {
409                        map.entry(key)
410                            .or_insert_with(HashSet::new)
411                            .insert(val.unwrap());
412                    });
413                // TODO: if the map is empty, the conditions are not compatible, return an error?
414                *self = Satisfaction::PartialComplete {
415                    n: *n,
416                    m: *m,
417                    items: items.clone(),
418                    conditions: map,
419                    sorted: *sorted,
420                };
421            }
422        }
423    }
424}
425
426impl From<bool> for Satisfaction {
427    fn from(other: bool) -> Self {
428        if other {
429            Satisfaction::Complete {
430                condition: Default::default(),
431            }
432        } else {
433            Satisfaction::None
434        }
435    }
436}
437
438/// Descriptor spending policy
439#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
440pub struct Policy {
441    /// Identifier for this policy node
442    pub id: String,
443
444    /// Type of this policy node
445    #[serde(flatten)]
446    pub item: SatisfiableItem,
447    /// How much a given PSBT already satisfies this policy node in terms of signatures
448    pub satisfaction: Satisfaction,
449    /// How the wallet's descriptor can satisfy this policy node
450    pub contribution: Satisfaction,
451}
452
453/// An extra condition that must be satisfied but that is out of control of the user
454/// TODO: use `bitcoin::LockTime` and `bitcoin::Sequence`
455#[derive(Hash, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Default, Serialize)]
456pub struct Condition {
457    /// Optional CheckSequenceVerify condition
458    #[serde(skip_serializing_if = "Option::is_none")]
459    pub csv: Option<Sequence>,
460    /// Optional timelock condition
461    #[serde(skip_serializing_if = "Option::is_none")]
462    pub timelock: Option<absolute::LockTime>,
463}
464
465impl Condition {
466    fn merge_nlocktime(
467        a: absolute::LockTime,
468        b: absolute::LockTime,
469    ) -> Result<absolute::LockTime, PolicyError> {
470        if !a.is_same_unit(b) {
471            Err(PolicyError::MixedTimelockUnits)
472        } else if a > b {
473            Ok(a)
474        } else {
475            Ok(b)
476        }
477    }
478
479    fn merge_nsequence(a: Sequence, b: Sequence) -> Result<Sequence, PolicyError> {
480        if a.is_time_locked() != b.is_time_locked() {
481            Err(PolicyError::MixedTimelockUnits)
482        } else {
483            Ok(max(a, b))
484        }
485    }
486
487    pub(crate) fn merge(mut self, other: &Condition) -> Result<Self, PolicyError> {
488        match (self.csv, other.csv) {
489            (Some(a), Some(b)) => self.csv = Some(Self::merge_nsequence(a, b)?),
490            (None, any) => self.csv = any,
491            _ => {}
492        }
493
494        match (self.timelock, other.timelock) {
495            (Some(a), Some(b)) => self.timelock = Some(Self::merge_nlocktime(a, b)?),
496            (None, any) => self.timelock = any,
497            _ => {}
498        }
499
500        Ok(self)
501    }
502
503    /// Returns `true` if there are no extra conditions to verify
504    pub fn is_null(&self) -> bool {
505        self.csv.is_none() && self.timelock.is_none()
506    }
507}
508
509/// Errors that can happen while extracting and manipulating policies
510#[derive(Debug, PartialEq, Eq)]
511pub enum PolicyError {
512    /// Not enough items are selected to satisfy a [`SatisfiableItem::Thresh`] or a
513    /// [`SatisfiableItem::Multisig`]
514    NotEnoughItemsSelected(String),
515    /// Index out of range for an item to satisfy a [`SatisfiableItem::Thresh`] or a
516    /// [`SatisfiableItem::Multisig`]
517    IndexOutOfRange(usize),
518    /// Can not add to an item that is [`Satisfaction::None`] or [`Satisfaction::Complete`]
519    AddOnLeaf,
520    /// Can not add to an item that is [`Satisfaction::PartialComplete`]
521    AddOnPartialComplete,
522    /// Can not merge CSV or timelock values unless both are less than or both are equal or greater
523    /// than 500_000_000
524    MixedTimelockUnits,
525    /// Incompatible conditions (not currently used)
526    IncompatibleConditions,
527}
528
529impl fmt::Display for PolicyError {
530    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
531        match self {
532            Self::NotEnoughItemsSelected(err) => write!(f, "Not enough items selected: {err}"),
533            Self::IndexOutOfRange(index) => write!(f, "Index out of range: {index}"),
534            Self::AddOnLeaf => write!(f, "Add on leaf"),
535            Self::AddOnPartialComplete => write!(f, "Add on partial complete"),
536            Self::MixedTimelockUnits => write!(f, "Mixed timelock units"),
537            Self::IncompatibleConditions => write!(f, "Incompatible conditions"),
538        }
539    }
540}
541
542#[cfg(feature = "std")]
543impl std::error::Error for PolicyError {}
544
545impl Policy {
546    fn new(item: SatisfiableItem) -> Self {
547        Policy {
548            id: item.id(),
549            item,
550            satisfaction: Satisfaction::None,
551            contribution: Satisfaction::None,
552        }
553    }
554
555    fn make_and(a: Option<Policy>, b: Option<Policy>) -> Result<Option<Policy>, PolicyError> {
556        match (a, b) {
557            (None, None) => Ok(None),
558            (Some(x), None) | (None, Some(x)) => Ok(Some(x)),
559            (Some(a), Some(b)) => Self::make_thresh(vec![a, b], 2),
560        }
561    }
562
563    fn make_or(a: Option<Policy>, b: Option<Policy>) -> Result<Option<Policy>, PolicyError> {
564        match (a, b) {
565            (None, None) => Ok(None),
566            (Some(x), None) | (None, Some(x)) => Ok(Some(x)),
567            (Some(a), Some(b)) => Self::make_thresh(vec![a, b], 1),
568        }
569    }
570
571    fn make_thresh(items: Vec<Policy>, threshold: usize) -> Result<Option<Policy>, PolicyError> {
572        if threshold == 0 {
573            return Ok(None);
574        }
575
576        let mut contribution = Satisfaction::Partial {
577            n: items.len(),
578            m: threshold,
579            items: vec![],
580            conditions: Default::default(),
581            sorted: None,
582        };
583        let mut satisfaction = contribution.clone();
584        for (index, item) in items.iter().enumerate() {
585            contribution.add(&item.contribution, index)?;
586            satisfaction.add(&item.satisfaction, index)?;
587        }
588
589        contribution.finalize();
590        satisfaction.finalize();
591
592        let mut policy: Policy = SatisfiableItem::Thresh { items, threshold }.into();
593        policy.contribution = contribution;
594        policy.satisfaction = satisfaction;
595
596        Ok(Some(policy))
597    }
598
599    fn make_multi<Ctx: ScriptContext + 'static, const MAX: usize>(
600        threshold: &Threshold<DescriptorPublicKey, MAX>,
601        signers: &SignersContainer,
602        build_sat: BuildSatisfaction,
603        sorted: bool,
604        secp: &SecpCtx,
605    ) -> Result<Option<Policy>, PolicyError> {
606        let parsed_keys = threshold.iter().map(|k| PkOrF::from_key(k, secp)).collect();
607
608        let mut contribution = Satisfaction::Partial {
609            n: threshold.n(),
610            m: threshold.k(),
611            items: vec![],
612            conditions: Default::default(),
613            sorted: Some(sorted),
614        };
615        let mut satisfaction = contribution.clone();
616
617        for (index, key) in threshold.iter().enumerate() {
618            if signers.find(signer_id(key, secp)).is_some() {
619                contribution.add(
620                    &Satisfaction::Complete {
621                        condition: Default::default(),
622                    },
623                    index,
624                )?;
625            }
626            if let Some(psbt) = build_sat.psbt() {
627                if Ctx::find_signature(psbt, key, secp) {
628                    satisfaction.add(
629                        &Satisfaction::Complete {
630                            condition: Default::default(),
631                        },
632                        index,
633                    )?;
634                }
635            }
636        }
637        satisfaction.finalize();
638        contribution.finalize();
639
640        let mut policy: Policy = SatisfiableItem::Multisig {
641            keys: parsed_keys,
642            threshold: threshold.k(),
643        }
644        .into();
645        policy.contribution = contribution;
646        policy.satisfaction = satisfaction;
647        Ok(Some(policy))
648    }
649
650    /// Return whether or not a specific path in the policy tree is required to unambiguously
651    /// create a transaction
652    ///
653    /// What this means is that for some spending policies the user should select which paths in
654    /// the tree it intends to satisfy while signing, because the transaction must be created
655    /// differently based on that.
656    pub fn requires_path(&self) -> bool {
657        self.get_condition(&BTreeMap::new()).is_err()
658    }
659
660    /// Return the conditions that are set by the spending policy for a given path in the
661    /// policy tree
662    pub fn get_condition(
663        &self,
664        path: &BTreeMap<String, Vec<usize>>,
665    ) -> Result<Condition, PolicyError> {
666        // if items.len() == threshold, selected can be omitted and we take all of them by default
667        let default = match &self.item {
668            SatisfiableItem::Thresh { items, threshold } if items.len() == *threshold => {
669                (0..*threshold).collect()
670            }
671            SatisfiableItem::Multisig { keys, .. } => (0..keys.len()).collect(),
672            _ => HashSet::new(),
673        };
674        let selected: HashSet<_> = match path.get(&self.id) {
675            Some(arr) => arr.iter().copied().collect(),
676            _ => default,
677        };
678
679        match &self.item {
680            SatisfiableItem::Thresh { items, threshold } => {
681                let mapped_req = items
682                    .iter()
683                    .map(|i| i.get_condition(path))
684                    .collect::<Vec<_>>();
685
686                // if all the requirements are null we don't care about `selected` because there
687                // are no requirements
688                if mapped_req
689                    .iter()
690                    .all(|cond| matches!(cond, Ok(c) if c.is_null()))
691                {
692                    return Ok(Condition::default());
693                }
694
695                // make sure all the indexes in the `selected` list are within range
696                for index in &selected {
697                    if *index >= items.len() {
698                        return Err(PolicyError::IndexOutOfRange(*index));
699                    }
700                }
701
702                // if we have something, make sure we have enough items. note that the user can set
703                // an empty value for this step in case of n-of-n, because `selected` is set to all
704                // the elements above
705                if selected.len() < *threshold {
706                    return Err(PolicyError::NotEnoughItemsSelected(self.id.clone()));
707                }
708
709                // check the selected items, see if there are conflicting requirements
710                mapped_req
711                    .into_iter()
712                    .enumerate()
713                    .filter(|(index, _)| selected.contains(index))
714                    .try_fold(Condition::default(), |acc, (_, cond)| acc.merge(&cond?))
715            }
716            SatisfiableItem::Multisig { keys, threshold } => {
717                if selected.len() < *threshold {
718                    return Err(PolicyError::NotEnoughItemsSelected(self.id.clone()));
719                }
720                if let Some(item) = selected.into_iter().find(|&i| i >= keys.len()) {
721                    return Err(PolicyError::IndexOutOfRange(item));
722                }
723
724                Ok(Condition::default())
725            }
726            SatisfiableItem::AbsoluteTimelock { value } => Ok(Condition {
727                csv: None,
728                timelock: Some(*value),
729            }),
730            SatisfiableItem::RelativeTimelock { value } => Ok(Condition {
731                csv: Some((*value).into()),
732                timelock: None,
733            }),
734            _ => Ok(Condition::default()),
735        }
736    }
737}
738
739impl From<SatisfiableItem> for Policy {
740    fn from(other: SatisfiableItem) -> Self {
741        Self::new(other)
742    }
743}
744
745fn signer_id(key: &DescriptorPublicKey, secp: &SecpCtx) -> SignerId {
746    // For consistency we always compute the key hash in "ecdsa" form (with the leading sign
747    // prefix) even if we are in a taproot descriptor. We just want some kind of unique identifier
748    // for a key, so it doesn't really matter how the identifier is computed.
749    match key {
750        DescriptorPublicKey::Single(SinglePub {
751            key: SinglePubKey::FullKey(pk),
752            ..
753        }) => pk.to_pubkeyhash(SigType::Ecdsa).into(),
754        DescriptorPublicKey::Single(SinglePub {
755            key: SinglePubKey::XOnly(pk),
756            ..
757        }) => pk.to_pubkeyhash(SigType::Ecdsa).into(),
758        DescriptorPublicKey::XPub(xpub) => xpub.root_fingerprint(secp).into(),
759        DescriptorPublicKey::MultiXPub(xpub) => xpub.root_fingerprint(secp).into(),
760    }
761}
762
763fn make_generic_signature<M: Fn() -> SatisfiableItem, F: Fn(&Psbt) -> bool>(
764    key: &DescriptorPublicKey,
765    signers: &SignersContainer,
766    build_sat: BuildSatisfaction,
767    secp: &SecpCtx,
768    make_policy: M,
769    find_sig: F,
770) -> Policy {
771    let mut policy: Policy = make_policy().into();
772
773    policy.contribution = if signers.find(signer_id(key, secp)).is_some() {
774        Satisfaction::Complete {
775            condition: Default::default(),
776        }
777    } else {
778        Satisfaction::None
779    };
780
781    if let Some(psbt) = build_sat.psbt() {
782        policy.satisfaction = if find_sig(psbt) {
783            Satisfaction::Complete {
784                condition: Default::default(),
785            }
786        } else {
787            Satisfaction::None
788        };
789    }
790
791    policy
792}
793
794fn generic_sig_in_psbt<
795    // C is for "check", it's a closure we use to *check* if a psbt input contains the signature
796    // for a specific key
797    C: Fn(&psbt::Input, &SinglePubKey) -> bool,
798    // E is for "extract", it extracts a key from the bip32 derivations found in the psbt input
799    E: Fn(&psbt::Input, Fingerprint) -> Option<SinglePubKey>,
800>(
801    psbt: &Psbt,
802    key: &DescriptorPublicKey,
803    secp: &SecpCtx,
804    check: C,
805    extract: E,
806) -> bool {
807    //TODO check signature validity
808    psbt.inputs.iter().all(|input| match key {
809        DescriptorPublicKey::Single(SinglePub { key, .. }) => check(input, key),
810        DescriptorPublicKey::XPub(xpub) => {
811            //TODO check actual derivation matches
812            match extract(input, xpub.root_fingerprint(secp)) {
813                Some(pubkey) => check(input, &pubkey),
814                None => false,
815            }
816        }
817        DescriptorPublicKey::MultiXPub(xpub) => {
818            //TODO check actual derivation matches
819            match extract(input, xpub.root_fingerprint(secp)) {
820                Some(pubkey) => check(input, &pubkey),
821                None => false,
822            }
823        }
824    })
825}
826
827trait SigExt: ScriptContext {
828    fn make_signature(
829        key: &DescriptorPublicKey,
830        signers: &SignersContainer,
831        build_sat: BuildSatisfaction,
832        secp: &SecpCtx,
833    ) -> Policy;
834
835    fn find_signature(psbt: &Psbt, key: &DescriptorPublicKey, secp: &SecpCtx) -> bool;
836}
837
838impl<T: ScriptContext + 'static> SigExt for T {
839    fn make_signature(
840        key: &DescriptorPublicKey,
841        signers: &SignersContainer,
842        build_sat: BuildSatisfaction,
843        secp: &SecpCtx,
844    ) -> Policy {
845        if T::as_enum().is_taproot() {
846            make_generic_signature(
847                key,
848                signers,
849                build_sat,
850                secp,
851                || SatisfiableItem::SchnorrSignature(PkOrF::from_key(key, secp)),
852                |psbt| Self::find_signature(psbt, key, secp),
853            )
854        } else {
855            make_generic_signature(
856                key,
857                signers,
858                build_sat,
859                secp,
860                || SatisfiableItem::EcdsaSignature(PkOrF::from_key(key, secp)),
861                |psbt| Self::find_signature(psbt, key, secp),
862            )
863        }
864    }
865
866    fn find_signature(psbt: &Psbt, key: &DescriptorPublicKey, secp: &SecpCtx) -> bool {
867        if T::as_enum().is_taproot() {
868            generic_sig_in_psbt(
869                psbt,
870                key,
871                secp,
872                |input, pk| {
873                    let pk = match pk {
874                        SinglePubKey::XOnly(pk) => pk,
875                        _ => return false,
876                    };
877
878                    if input.tap_internal_key == Some(*pk) && input.tap_key_sig.is_some() {
879                        true
880                    } else {
881                        input.tap_script_sigs.keys().any(|(sk, _)| sk == pk)
882                    }
883                },
884                |input, fing| {
885                    input
886                        .tap_key_origins
887                        .iter()
888                        .find(|(_, (_, (f, _)))| f == &fing)
889                        .map(|(pk, _)| SinglePubKey::XOnly(*pk))
890                },
891            )
892        } else {
893            generic_sig_in_psbt(
894                psbt,
895                key,
896                secp,
897                |input, pk| match pk {
898                    SinglePubKey::FullKey(pk) => input.partial_sigs.contains_key(pk),
899                    _ => false,
900                },
901                |input, fing| {
902                    input
903                        .bip32_derivation
904                        .iter()
905                        .find(|(_, (f, _))| f == &fing)
906                        .map(|(pk, _)| SinglePubKey::FullKey(PublicKey::new(*pk)))
907                },
908            )
909        }
910    }
911}
912
913impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublicKey, Ctx> {
914    fn extract_policy(
915        &self,
916        signers: &SignersContainer,
917        build_sat: BuildSatisfaction,
918        secp: &SecpCtx,
919    ) -> Result<Option<Policy>, Error> {
920        Ok(match &self.node {
921            // Leaves
922            Terminal::True | Terminal::False => None,
923            Terminal::PkK(pubkey) => Some(Ctx::make_signature(pubkey, signers, build_sat, secp)),
924            Terminal::PkH(pubkey_hash) => {
925                Some(Ctx::make_signature(pubkey_hash, signers, build_sat, secp))
926            }
927            Terminal::After(value) => {
928                let mut policy: Policy = SatisfiableItem::AbsoluteTimelock {
929                    value: (*value).into(),
930                }
931                .into();
932                policy.contribution = Satisfaction::Complete {
933                    condition: Condition {
934                        timelock: Some((*value).into()),
935                        csv: None,
936                    },
937                };
938                if let BuildSatisfaction::PsbtTimelocks {
939                    current_height,
940                    psbt,
941                    ..
942                } = build_sat
943                {
944                    let after = After::new(Some(current_height), false);
945                    let after_sat =
946                        Satisfier::<bitcoin::PublicKey>::check_after(&after, (*value).into());
947                    let inputs_sat = psbt_inputs_sat(psbt).all(|sat| {
948                        Satisfier::<bitcoin::PublicKey>::check_after(&sat, (*value).into())
949                    });
950                    if after_sat && inputs_sat {
951                        policy.satisfaction = policy.contribution.clone();
952                    }
953                }
954
955                Some(policy)
956            }
957            Terminal::Older(value) => {
958                let mut policy: Policy = SatisfiableItem::RelativeTimelock {
959                    value: (*value).into(),
960                }
961                .into();
962                policy.contribution = Satisfaction::Complete {
963                    condition: Condition {
964                        timelock: None,
965                        csv: Some((*value).into()),
966                    },
967                };
968                if let BuildSatisfaction::PsbtTimelocks {
969                    current_height,
970                    input_max_height,
971                    psbt,
972                } = build_sat
973                {
974                    let older = Older::new(Some(current_height), Some(input_max_height), false);
975                    let older_sat =
976                        Satisfier::<bitcoin::PublicKey>::check_older(&older, (*value).into());
977                    let inputs_sat = psbt_inputs_sat(psbt).all(|sat| {
978                        Satisfier::<bitcoin::PublicKey>::check_older(&sat, (*value).into())
979                    });
980                    if older_sat && inputs_sat {
981                        policy.satisfaction = policy.contribution.clone();
982                    }
983                }
984
985                Some(policy)
986            }
987            Terminal::Sha256(hash) => Some(SatisfiableItem::Sha256Preimage { hash: *hash }.into()),
988            Terminal::Hash256(hash) => {
989                Some(SatisfiableItem::Hash256Preimage { hash: *hash }.into())
990            }
991            Terminal::Ripemd160(hash) => {
992                Some(SatisfiableItem::Ripemd160Preimage { hash: *hash }.into())
993            }
994            Terminal::Hash160(hash) => {
995                Some(SatisfiableItem::Hash160Preimage { hash: *hash }.into())
996            }
997            Terminal::Multi(threshold) => Policy::make_multi::<Ctx, MAX_PUBKEYS_PER_MULTISIG>(
998                threshold, signers, build_sat, false, secp,
999            )?,
1000            Terminal::MultiA(threshold) => Policy::make_multi::<Ctx, MAX_PUBKEYS_IN_CHECKSIGADD>(
1001                threshold, signers, build_sat, false, secp,
1002            )?,
1003            // Identities
1004            Terminal::Alt(inner)
1005            | Terminal::Swap(inner)
1006            | Terminal::Check(inner)
1007            | Terminal::DupIf(inner)
1008            | Terminal::Verify(inner)
1009            | Terminal::NonZero(inner)
1010            | Terminal::ZeroNotEqual(inner) => inner.extract_policy(signers, build_sat, secp)?,
1011            // Complex policies
1012            Terminal::AndV(a, b) | Terminal::AndB(a, b) => Policy::make_and(
1013                a.extract_policy(signers, build_sat, secp)?,
1014                b.extract_policy(signers, build_sat, secp)?,
1015            )?,
1016            Terminal::AndOr(x, y, z) => Policy::make_or(
1017                Policy::make_and(
1018                    x.extract_policy(signers, build_sat, secp)?,
1019                    y.extract_policy(signers, build_sat, secp)?,
1020                )?,
1021                z.extract_policy(signers, build_sat, secp)?,
1022            )?,
1023            Terminal::OrB(a, b)
1024            | Terminal::OrD(a, b)
1025            | Terminal::OrC(a, b)
1026            | Terminal::OrI(a, b) => Policy::make_or(
1027                a.extract_policy(signers, build_sat, secp)?,
1028                b.extract_policy(signers, build_sat, secp)?,
1029            )?,
1030            Terminal::Thresh(threshold) => {
1031                let mut k = threshold.k();
1032                let nodes = threshold.data();
1033                let mapped: Vec<_> = nodes
1034                    .iter()
1035                    .map(|n| n.extract_policy(signers, build_sat, secp))
1036                    .collect::<Result<Vec<_>, _>>()?
1037                    .into_iter()
1038                    .flatten()
1039                    .collect();
1040
1041                if mapped.len() < nodes.len() {
1042                    k = match k.checked_sub(nodes.len() - mapped.len()) {
1043                        None => return Ok(None),
1044                        Some(x) => x,
1045                    };
1046                }
1047
1048                Policy::make_thresh(mapped, k)?
1049            }
1050
1051            // Unsupported
1052            Terminal::RawPkH(_) => None,
1053        })
1054    }
1055}
1056
1057fn psbt_inputs_sat(psbt: &Psbt) -> impl Iterator<Item = PsbtInputSatisfier> {
1058    (0..psbt.inputs.len()).map(move |i| PsbtInputSatisfier::new(psbt, i))
1059}
1060
1061/// Options to build the satisfaction field in the policy
1062#[derive(Debug, Clone, Copy)]
1063pub enum BuildSatisfaction<'a> {
1064    /// Don't generate `satisfaction` field
1065    None,
1066    /// Analyze the given PSBT to check for existing signatures
1067    Psbt(&'a Psbt),
1068    /// Like `Psbt` variant and also check for expired timelocks
1069    PsbtTimelocks {
1070        /// Given PSBT
1071        psbt: &'a Psbt,
1072        /// Current blockchain height
1073        current_height: u32,
1074        /// The highest confirmation height between the inputs
1075        /// CSV should consider different inputs, but we consider the worst condition for the tx as
1076        /// whole
1077        input_max_height: u32,
1078    },
1079}
1080impl<'a> BuildSatisfaction<'a> {
1081    fn psbt(&self) -> Option<&'a Psbt> {
1082        match self {
1083            BuildSatisfaction::None => None,
1084            BuildSatisfaction::Psbt(psbt) => Some(psbt),
1085            BuildSatisfaction::PsbtTimelocks { psbt, .. } => Some(psbt),
1086        }
1087    }
1088}
1089
1090impl ExtractPolicy for Descriptor<DescriptorPublicKey> {
1091    fn extract_policy(
1092        &self,
1093        signers: &SignersContainer,
1094        build_sat: BuildSatisfaction,
1095        secp: &SecpCtx,
1096    ) -> Result<Option<Policy>, Error> {
1097        fn make_sortedmulti<Ctx: ScriptContext + 'static>(
1098            keys: &SortedMultiVec<DescriptorPublicKey, Ctx>,
1099            signers: &SignersContainer,
1100            build_sat: BuildSatisfaction,
1101            secp: &SecpCtx,
1102        ) -> Result<Option<Policy>, Error> {
1103            let threshold = Threshold::new(keys.k(), keys.pks().to_vec())
1104                .expect("valid threshold and pks collection");
1105            Ok(Policy::make_multi::<Ctx, MAX_PUBKEYS_PER_MULTISIG>(
1106                &threshold, signers, build_sat, true, secp,
1107            )?)
1108        }
1109
1110        match self {
1111            Descriptor::Pkh(pk) => Ok(Some(miniscript::Legacy::make_signature(
1112                pk.as_inner(),
1113                signers,
1114                build_sat,
1115                secp,
1116            ))),
1117            Descriptor::Wpkh(pk) => Ok(Some(miniscript::Segwitv0::make_signature(
1118                pk.as_inner(),
1119                signers,
1120                build_sat,
1121                secp,
1122            ))),
1123            Descriptor::Sh(sh) => match sh.as_inner() {
1124                ShInner::Wpkh(pk) => Ok(Some(miniscript::Segwitv0::make_signature(
1125                    pk.as_inner(),
1126                    signers,
1127                    build_sat,
1128                    secp,
1129                ))),
1130                ShInner::Ms(ms) => Ok(ms.extract_policy(signers, build_sat, secp)?),
1131                ShInner::SortedMulti(ref keys) => make_sortedmulti(keys, signers, build_sat, secp),
1132                ShInner::Wsh(wsh) => match wsh.as_inner() {
1133                    WshInner::Ms(ms) => Ok(ms.extract_policy(signers, build_sat, secp)?),
1134                    WshInner::SortedMulti(ref keys) => {
1135                        make_sortedmulti(keys, signers, build_sat, secp)
1136                    }
1137                },
1138            },
1139            Descriptor::Wsh(wsh) => match wsh.as_inner() {
1140                WshInner::Ms(ms) => Ok(ms.extract_policy(signers, build_sat, secp)?),
1141                WshInner::SortedMulti(ref keys) => make_sortedmulti(keys, signers, build_sat, secp),
1142            },
1143            Descriptor::Bare(ms) => Ok(ms.as_inner().extract_policy(signers, build_sat, secp)?),
1144            Descriptor::Tr(tr) => {
1145                // If there's no tap tree, treat this as a single sig, otherwise build a `Thresh`
1146                // node with threshold = 1 and the key spend signature plus all the tree leaves
1147                let key_spend_sig =
1148                    miniscript::Tap::make_signature(tr.internal_key(), signers, build_sat, secp);
1149
1150                if tr.tap_tree().is_none() {
1151                    Ok(Some(key_spend_sig))
1152                } else {
1153                    let mut items = vec![key_spend_sig];
1154                    items.append(
1155                        &mut tr
1156                            .iter_scripts()
1157                            .filter_map(|(_, ms)| {
1158                                ms.extract_policy(signers, build_sat, secp).transpose()
1159                            })
1160                            .collect::<Result<Vec<_>, _>>()?,
1161                    );
1162
1163                    Ok(Policy::make_thresh(items, 1)?)
1164                }
1165            }
1166        }
1167    }
1168}
1169
1170#[cfg(test)]
1171mod test {
1172    use crate::descriptor;
1173    use crate::descriptor::{ExtractPolicy, IntoWalletDescriptor};
1174
1175    use super::*;
1176    use crate::descriptor::policy::SatisfiableItem::{EcdsaSignature, Multisig, Thresh};
1177    use crate::keys::{DescriptorKey, IntoDescriptorKey};
1178    use crate::wallet::signer::SignersContainer;
1179    use alloc::{string::ToString, sync::Arc};
1180    use assert_matches::assert_matches;
1181    use bitcoin::bip32;
1182    use bitcoin::secp256k1::Secp256k1;
1183    use bitcoin::Network;
1184    use core::str::FromStr;
1185
1186    const TPRV0_STR:&str = "tprv8ZgxMBicQKsPdZXrcHNLf5JAJWFAoJ2TrstMRdSKtEggz6PddbuSkvHKM9oKJyFgZV1B7rw8oChspxyYbtmEXYyg1AjfWbL3ho3XHDpHRZf";
1187    const TPRV1_STR:&str = "tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N";
1188
1189    const PATH: &str = "m/44'/1'/0'/0";
1190
1191    fn setup_keys<Ctx: ScriptContext>(
1192        tprv: &str,
1193        path: &str,
1194        secp: &SecpCtx,
1195    ) -> (DescriptorKey<Ctx>, DescriptorKey<Ctx>, Fingerprint) {
1196        let path = bip32::DerivationPath::from_str(path).unwrap();
1197        let tprv = bip32::Xpriv::from_str(tprv).unwrap();
1198        let tpub = bip32::Xpub::from_priv(secp, &tprv);
1199        let fingerprint = tprv.fingerprint(secp);
1200        let prvkey = (tprv, path.clone()).into_descriptor_key().unwrap();
1201        let pubkey = (tpub, path).into_descriptor_key().unwrap();
1202
1203        (prvkey, pubkey, fingerprint)
1204    }
1205
1206    // test ExtractPolicy trait for simple descriptors; wpkh(), sh(multi())
1207
1208    #[test]
1209    fn test_extract_policy_for_wpkh() {
1210        let secp = Secp256k1::new();
1211
1212        let (prvkey, pubkey, fingerprint) = setup_keys(TPRV0_STR, PATH, &secp);
1213        let desc = descriptor!(wpkh(pubkey)).unwrap();
1214        let (wallet_desc, keymap) = desc
1215            .into_wallet_descriptor(&secp, Network::Testnet)
1216            .unwrap();
1217        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
1218        let policy = wallet_desc
1219            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
1220            .unwrap()
1221            .unwrap();
1222
1223        assert_matches!(&policy.item, EcdsaSignature(PkOrF::Fingerprint(f)) if f == &fingerprint);
1224        assert_matches!(&policy.contribution, Satisfaction::None);
1225
1226        let desc = descriptor!(wpkh(prvkey)).unwrap();
1227        let (wallet_desc, keymap) = desc
1228            .into_wallet_descriptor(&secp, Network::Testnet)
1229            .unwrap();
1230        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
1231        let policy = wallet_desc
1232            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
1233            .unwrap()
1234            .unwrap();
1235
1236        assert_matches!(&policy.item, EcdsaSignature(PkOrF::Fingerprint(f)) if f == &fingerprint);
1237        assert_matches!(&policy.contribution, Satisfaction::Complete {condition} if condition.csv.is_none() && condition.timelock.is_none());
1238    }
1239
1240    // 2 pub keys descriptor, required 2 prv keys
1241    #[test]
1242    fn test_extract_policy_for_sh_multi_partial_0of2() {
1243        let secp = Secp256k1::new();
1244        let (_prvkey0, pubkey0, fingerprint0) = setup_keys(TPRV0_STR, PATH, &secp);
1245        let (_prvkey1, pubkey1, fingerprint1) = setup_keys(TPRV1_STR, PATH, &secp);
1246        let desc = descriptor!(sh(multi(2, pubkey0, pubkey1))).unwrap();
1247        let (wallet_desc, keymap) = desc
1248            .into_wallet_descriptor(&secp, Network::Testnet)
1249            .unwrap();
1250        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
1251        let policy = wallet_desc
1252            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
1253            .unwrap()
1254            .unwrap();
1255
1256        assert_matches!(&policy.item, Multisig { keys, threshold } if threshold == &2usize
1257            && keys[0] == PkOrF::Fingerprint(fingerprint0)
1258            && keys[1] == PkOrF::Fingerprint(fingerprint1)
1259        );
1260        // TODO should this be "Satisfaction::None" since we have no prv keys?
1261        // TODO should items and conditions not be empty?
1262        assert_matches!(&policy.contribution, Satisfaction::Partial { n, m, items, conditions, ..} if n == &2usize
1263            && m == &2usize
1264            && items.is_empty()
1265            && conditions.is_empty()
1266        );
1267    }
1268
1269    // 1 prv and 1 pub key descriptor, required 2 prv keys
1270    #[test]
1271    fn test_extract_policy_for_sh_multi_partial_1of2() {
1272        let secp = Secp256k1::new();
1273        let (prvkey0, _pubkey0, fingerprint0) = setup_keys(TPRV0_STR, PATH, &secp);
1274        let (_prvkey1, pubkey1, fingerprint1) = setup_keys(TPRV1_STR, PATH, &secp);
1275        let desc = descriptor!(sh(multi(2, prvkey0, pubkey1))).unwrap();
1276        let (wallet_desc, keymap) = desc
1277            .into_wallet_descriptor(&secp, Network::Testnet)
1278            .unwrap();
1279        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
1280        let policy = wallet_desc
1281            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
1282            .unwrap()
1283            .unwrap();
1284        assert_matches!(&policy.item, Multisig { keys, threshold } if threshold == &2usize
1285            && keys[0] == PkOrF::Fingerprint(fingerprint0)
1286            && keys[1] == PkOrF::Fingerprint(fingerprint1)
1287        );
1288
1289        assert_matches!(&policy.contribution, Satisfaction::Partial { n, m, items, conditions, ..} if n == &2usize
1290             && m == &2usize
1291             && items.len() == 1
1292             && conditions.contains_key(&0)
1293        );
1294    }
1295
1296    // 1 prv and 1 pub key descriptor, required 1 prv keys
1297    #[test]
1298    #[ignore] // see https://github.com/bitcoindevkit/bdk/issues/225
1299    fn test_extract_policy_for_sh_multi_complete_1of2() {
1300        let secp = Secp256k1::new();
1301
1302        let (_prvkey0, pubkey0, fingerprint0) = setup_keys(TPRV0_STR, PATH, &secp);
1303        let (prvkey1, _pubkey1, fingerprint1) = setup_keys(TPRV1_STR, PATH, &secp);
1304        let desc = descriptor!(sh(multi(1, pubkey0, prvkey1))).unwrap();
1305        let (wallet_desc, keymap) = desc
1306            .into_wallet_descriptor(&secp, Network::Testnet)
1307            .unwrap();
1308        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
1309        let policy = wallet_desc
1310            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
1311            .unwrap()
1312            .unwrap();
1313
1314        assert_matches!(&policy.item, Multisig { keys, threshold } if threshold == &1
1315            && keys[0] == PkOrF::Fingerprint(fingerprint0)
1316            && keys[1] == PkOrF::Fingerprint(fingerprint1)
1317        );
1318        assert_matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &2
1319             && m == &1
1320             && items.len() == 2
1321             && conditions.contains_key(&vec![0])
1322             && conditions.contains_key(&vec![1])
1323        );
1324    }
1325
1326    // 2 prv keys descriptor, required 2 prv keys
1327    #[test]
1328    fn test_extract_policy_for_sh_multi_complete_2of2() {
1329        let secp = Secp256k1::new();
1330
1331        let (prvkey0, _pubkey0, fingerprint0) = setup_keys(TPRV0_STR, PATH, &secp);
1332        let (prvkey1, _pubkey1, fingerprint1) = setup_keys(TPRV1_STR, PATH, &secp);
1333        let desc = descriptor!(sh(multi(2, prvkey0, prvkey1))).unwrap();
1334        let (wallet_desc, keymap) = desc
1335            .into_wallet_descriptor(&secp, Network::Testnet)
1336            .unwrap();
1337        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
1338        let policy = wallet_desc
1339            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
1340            .unwrap()
1341            .unwrap();
1342
1343        assert_matches!(&policy.item, Multisig { keys, threshold } if threshold == &2
1344            && keys[0] == PkOrF::Fingerprint(fingerprint0)
1345            && keys[1] == PkOrF::Fingerprint(fingerprint1)
1346        );
1347
1348        assert_matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &2
1349             && m == &2
1350             && items.len() == 2
1351             && conditions.contains_key(&vec![0,1])
1352        );
1353    }
1354
1355    // test ExtractPolicy trait with extended and single keys
1356
1357    #[test]
1358    fn test_extract_policy_for_single_wpkh() {
1359        let secp = Secp256k1::new();
1360
1361        let (prvkey, pubkey, fingerprint) = setup_keys(TPRV0_STR, PATH, &secp);
1362        let desc = descriptor!(wpkh(pubkey)).unwrap();
1363        let (wallet_desc, keymap) = desc
1364            .into_wallet_descriptor(&secp, Network::Testnet)
1365            .unwrap();
1366        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
1367        let policy = wallet_desc
1368            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
1369            .unwrap()
1370            .unwrap();
1371
1372        assert_matches!(&policy.item, EcdsaSignature(PkOrF::Fingerprint(f)) if f == &fingerprint);
1373        assert_matches!(&policy.contribution, Satisfaction::None);
1374
1375        let desc = descriptor!(wpkh(prvkey)).unwrap();
1376        let (wallet_desc, keymap) = desc
1377            .into_wallet_descriptor(&secp, Network::Testnet)
1378            .unwrap();
1379        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
1380        let policy = wallet_desc
1381            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
1382            .unwrap()
1383            .unwrap();
1384
1385        assert_matches!(policy.item, EcdsaSignature(PkOrF::Fingerprint(f)) if f == fingerprint);
1386        assert_matches!(policy.contribution, Satisfaction::Complete {condition} if condition.csv.is_none() && condition.timelock.is_none());
1387    }
1388
1389    // single key, 1 prv and 1 pub key descriptor, required 1 prv keys
1390    #[test]
1391    #[ignore] // see https://github.com/bitcoindevkit/bdk/issues/225
1392    fn test_extract_policy_for_single_wsh_multi_complete_1of2() {
1393        let secp = Secp256k1::new();
1394
1395        let (_prvkey0, pubkey0, fingerprint0) = setup_keys(TPRV0_STR, PATH, &secp);
1396        let (prvkey1, _pubkey1, fingerprint1) = setup_keys(TPRV1_STR, PATH, &secp);
1397        let desc = descriptor!(sh(multi(1, pubkey0, prvkey1))).unwrap();
1398        let (wallet_desc, keymap) = desc
1399            .into_wallet_descriptor(&secp, Network::Testnet)
1400            .unwrap();
1401        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
1402        let policy = wallet_desc
1403            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
1404            .unwrap()
1405            .unwrap();
1406
1407        assert_matches!(policy.item, Multisig { keys, threshold } if threshold == 1
1408            && keys[0] == PkOrF::Fingerprint(fingerprint0)
1409            && keys[1] == PkOrF::Fingerprint(fingerprint1)
1410        );
1411        assert_matches!(policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == 2
1412             && m == 1
1413             && items.len() == 2
1414             && conditions.contains_key(&vec![0])
1415             && conditions.contains_key(&vec![1])
1416        );
1417    }
1418
1419    // test ExtractPolicy trait with descriptors containing timelocks in a thresh()
1420
1421    #[test]
1422    #[ignore] // see https://github.com/bitcoindevkit/bdk/issues/225
1423    fn test_extract_policy_for_wsh_multi_timelock() {
1424        let secp = Secp256k1::new();
1425
1426        let (prvkey0, _pubkey0, _fingerprint0) = setup_keys(TPRV0_STR, PATH, &secp);
1427        let (_prvkey1, pubkey1, _fingerprint1) = setup_keys(TPRV1_STR, PATH, &secp);
1428        let sequence = 50;
1429        #[rustfmt::skip]
1430        let desc = descriptor!(wsh(thresh(
1431            2,
1432            pk(prvkey0),
1433            s:pk(pubkey1),
1434            s:d:v:older(sequence)
1435        )))
1436        .unwrap();
1437
1438        let (wallet_desc, keymap) = desc
1439            .into_wallet_descriptor(&secp, Network::Testnet)
1440            .unwrap();
1441        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
1442        let policy = wallet_desc
1443            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
1444            .unwrap()
1445            .unwrap();
1446
1447        assert_matches!(&policy.item, Thresh { items, threshold } if items.len() == 3 && threshold == &2);
1448
1449        assert_matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &3
1450             && m == &2
1451             && items.len() == 3
1452             && conditions.get(&vec![0,1]).unwrap().iter().next().unwrap().csv.is_none()
1453             && conditions.get(&vec![0,2]).unwrap().iter().next().unwrap().csv == Some(Sequence(sequence))
1454             && conditions.get(&vec![1,2]).unwrap().iter().next().unwrap().csv == Some(Sequence(sequence))
1455        );
1456    }
1457
1458    // - mixed timelocks should fail
1459
1460    #[test]
1461    #[ignore]
1462    fn test_extract_policy_for_wsh_mixed_timelocks() {
1463        let secp = Secp256k1::new();
1464        let (prvkey0, _pubkey0, _fingerprint0) = setup_keys(TPRV0_STR, PATH, &secp);
1465        let locktime_threshold = 500000000; // if less than this means block number, else block time in seconds
1466        let locktime_blocks = 100;
1467        let locktime_seconds = locktime_blocks + locktime_threshold;
1468        let desc = descriptor!(sh(and_v(
1469            v: pk(prvkey0),
1470            and_v(v: after(locktime_seconds), after(locktime_blocks))
1471        )))
1472        .unwrap();
1473        let (wallet_desc, keymap) = desc
1474            .into_wallet_descriptor(&secp, Network::Testnet)
1475            .unwrap();
1476        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
1477        let _policy = wallet_desc
1478            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
1479            .unwrap()
1480            .unwrap();
1481        // println!("desc policy = {:?}", policy); // TODO remove
1482        // TODO how should this fail with mixed timelocks?
1483    }
1484
1485    // - multiple timelocks of the same type should be correctly merged together
1486    #[test]
1487    #[ignore]
1488    fn test_extract_policy_for_multiple_same_timelocks() {
1489        let secp = Secp256k1::new();
1490        let (prvkey0, _pubkey0, _fingerprint0) = setup_keys(TPRV0_STR, PATH, &secp);
1491        let locktime_blocks0 = 100;
1492        let locktime_blocks1 = 200;
1493        let desc = descriptor!(sh(and_v(
1494            v: pk(prvkey0),
1495            and_v(v: after(locktime_blocks0), after(locktime_blocks1))
1496        )))
1497        .unwrap();
1498        let (wallet_desc, keymap) = desc
1499            .into_wallet_descriptor(&secp, Network::Testnet)
1500            .unwrap();
1501        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
1502        let _policy = wallet_desc
1503            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
1504            .unwrap()
1505            .unwrap();
1506        // println!("desc policy = {:?}", policy); // TODO remove
1507        // TODO how should this merge timelocks?
1508        let (prvkey1, _pubkey1, _fingerprint1) = setup_keys(TPRV0_STR, PATH, &secp);
1509        let locktime_seconds0 = 500000100;
1510        let locktime_seconds1 = 500000200;
1511        let desc = descriptor!(sh(and_v(
1512            v: pk(prvkey1),
1513            and_v(v: after(locktime_seconds0), after(locktime_seconds1))
1514        )))
1515        .unwrap();
1516        let (wallet_desc, keymap) = desc
1517            .into_wallet_descriptor(&secp, Network::Testnet)
1518            .unwrap();
1519        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
1520        let _policy = wallet_desc
1521            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
1522            .unwrap()
1523            .unwrap();
1524
1525        // println!("desc policy = {:?}", policy); // TODO remove
1526
1527        // TODO how should this merge timelocks?
1528    }
1529
1530    #[test]
1531    fn test_get_condition_multisig() {
1532        let secp = Secp256k1::new();
1533
1534        let (_, pk0, _) = setup_keys(TPRV0_STR, PATH, &secp);
1535        let (_, pk1, _) = setup_keys(TPRV1_STR, PATH, &secp);
1536
1537        let desc = descriptor!(wsh(multi(1, pk0, pk1))).unwrap();
1538        let (wallet_desc, keymap) = desc
1539            .into_wallet_descriptor(&secp, Network::Testnet)
1540            .unwrap();
1541        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
1542
1543        let policy = wallet_desc
1544            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
1545            .unwrap()
1546            .unwrap();
1547
1548        // no args, choose the default
1549        let no_args = policy.get_condition(&vec![].into_iter().collect());
1550        assert_eq!(no_args, Ok(Condition::default()));
1551
1552        // enough args
1553        let eq_thresh =
1554            policy.get_condition(&vec![(policy.id.clone(), vec![0])].into_iter().collect());
1555        assert_eq!(eq_thresh, Ok(Condition::default()));
1556
1557        // more args, it doesn't really change anything
1558        let gt_thresh =
1559            policy.get_condition(&vec![(policy.id.clone(), vec![0, 1])].into_iter().collect());
1560        assert_eq!(gt_thresh, Ok(Condition::default()));
1561
1562        // not enough args, error
1563        let lt_thresh =
1564            policy.get_condition(&vec![(policy.id.clone(), vec![])].into_iter().collect());
1565        assert_eq!(
1566            lt_thresh,
1567            Err(PolicyError::NotEnoughItemsSelected(policy.id.clone()))
1568        );
1569
1570        // index out of range
1571        let out_of_range =
1572            policy.get_condition(&vec![(policy.id.clone(), vec![5])].into_iter().collect());
1573        assert_eq!(out_of_range, Err(PolicyError::IndexOutOfRange(5)));
1574    }
1575
1576    const ALICE_TPRV_STR:&str = "tprv8ZgxMBicQKsPf6T5X327efHnvJDr45Xnb8W4JifNWtEoqXu9MRYS4v1oYe6DFcMVETxy5w3bqpubYRqvcVTqovG1LifFcVUuJcbwJwrhYzP";
1577    const BOB_TPRV_STR:&str = "tprv8ZgxMBicQKsPeinZ155cJAn117KYhbaN6MV3WeG6sWhxWzcvX1eg1awd4C9GpUN1ncLEM2rzEvunAg3GizdZD4QPPCkisTz99tXXB4wZArp";
1578    const CAROL_TPRV_STR:&str = "tprv8ZgxMBicQKsPdC3CicFifuLCEyVVdXVUNYorxUWj3iGZ6nimnLAYAY9SYB7ib8rKzRxrCKFcEytCt6szwd2GHnGPRCBLAEAoSVDefSNk4Bt";
1579    const ALICE_BOB_PATH: &str = "m/0'";
1580
1581    #[test]
1582    fn test_extract_satisfaction() {
1583        const ALICE_SIGNED_PSBT: &str = "cHNidP8BAFMBAAAAAZb0njwT2wRS3AumaaP3yb7T4MxOePpSWih4Nq+jWChMAQAAAAD/////Af4lAAAAAAAAF6kUXv2Fn+YemPP4PUpNR1ZbU16/eRCHAAAAAAABASuJJgAAAAAAACIAIERw5kTLo9DUH9QDJSClHQwPpC7VGJ+ZMDpa8U+2fzcYIgIDeAtjYQk/Vfu4db2+68hyMKjc38+kWl5sP5QH8L42ZstHMEQCIBj0jLjUeVYXNQ6cqB+gbtvuKMjV54wSgWlm1cfcgpHVAiBa3DtC9l/1Mt4IDCvR7mmwQd3eAP/m5++81euhJNSrgQEBBUdSIQN4C2NhCT9V+7h1vb7ryHIwqNzfz6RaXmw/lAfwvjZmyyEC+GE/y+LptI8xmiR6sOe998IGzybox0Qfz4+BQl1nmYhSriIGAvhhP8vi6bSPMZokerDnvffCBs8m6MdEH8+PgUJdZ5mIDBwu7j4AAACAAAAAACIGA3gLY2EJP1X7uHW9vuvIcjCo3N/PpFpebD+UB/C+NmbLDMkRfC4AAACAAAAAAAAA";
1584        const BOB_SIGNED_PSBT: &str =   "cHNidP8BAFMBAAAAAZb0njwT2wRS3AumaaP3yb7T4MxOePpSWih4Nq+jWChMAQAAAAD/////Af4lAAAAAAAAF6kUXv2Fn+YemPP4PUpNR1ZbU16/eRCHAAAAAAABASuJJgAAAAAAACIAIERw5kTLo9DUH9QDJSClHQwPpC7VGJ+ZMDpa8U+2fzcYIgIC+GE/y+LptI8xmiR6sOe998IGzybox0Qfz4+BQl1nmYhIMEUCIQD5zDtM5MwklurwJ5aW76RsO36Iqyu+6uMdVlhL6ws2GQIgesAiz4dbKS7UmhDsC/c1ezu0o6hp00UUtsCMfUZ4anYBAQVHUiEDeAtjYQk/Vfu4db2+68hyMKjc38+kWl5sP5QH8L42ZsshAvhhP8vi6bSPMZokerDnvffCBs8m6MdEH8+PgUJdZ5mIUq4iBgL4YT/L4um0jzGaJHqw5733wgbPJujHRB/Pj4FCXWeZiAwcLu4+AAAAgAAAAAAiBgN4C2NhCT9V+7h1vb7ryHIwqNzfz6RaXmw/lAfwvjZmywzJEXwuAAAAgAAAAAAAAA==";
1585        const ALICE_BOB_SIGNED_PSBT: &str =   "cHNidP8BAFMBAAAAAZb0njwT2wRS3AumaaP3yb7T4MxOePpSWih4Nq+jWChMAQAAAAD/////Af4lAAAAAAAAF6kUXv2Fn+YemPP4PUpNR1ZbU16/eRCHAAAAAAABASuJJgAAAAAAACIAIERw5kTLo9DUH9QDJSClHQwPpC7VGJ+ZMDpa8U+2fzcYIgIC+GE/y+LptI8xmiR6sOe998IGzybox0Qfz4+BQl1nmYhIMEUCIQD5zDtM5MwklurwJ5aW76RsO36Iqyu+6uMdVlhL6ws2GQIgesAiz4dbKS7UmhDsC/c1ezu0o6hp00UUtsCMfUZ4anYBIgIDeAtjYQk/Vfu4db2+68hyMKjc38+kWl5sP5QH8L42ZstHMEQCIBj0jLjUeVYXNQ6cqB+gbtvuKMjV54wSgWlm1cfcgpHVAiBa3DtC9l/1Mt4IDCvR7mmwQd3eAP/m5++81euhJNSrgQEBBUdSIQN4C2NhCT9V+7h1vb7ryHIwqNzfz6RaXmw/lAfwvjZmyyEC+GE/y+LptI8xmiR6sOe998IGzybox0Qfz4+BQl1nmYhSriIGAvhhP8vi6bSPMZokerDnvffCBs8m6MdEH8+PgUJdZ5mIDBwu7j4AAACAAAAAACIGA3gLY2EJP1X7uHW9vuvIcjCo3N/PpFpebD+UB/C+NmbLDMkRfC4AAACAAAAAAAEHAAEI2wQARzBEAiAY9Iy41HlWFzUOnKgfoG7b7ijI1eeMEoFpZtXH3IKR1QIgWtw7QvZf9TLeCAwr0e5psEHd3gD/5ufvvNXroSTUq4EBSDBFAiEA+cw7TOTMJJbq8CeWlu+kbDt+iKsrvurjHVZYS+sLNhkCIHrAIs+HWyku1JoQ7Av3NXs7tKOoadNFFLbAjH1GeGp2AUdSIQN4C2NhCT9V+7h1vb7ryHIwqNzfz6RaXmw/lAfwvjZmyyEC+GE/y+LptI8xmiR6sOe998IGzybox0Qfz4+BQl1nmYhSrgAA";
1586
1587        let secp = Secp256k1::new();
1588
1589        let (prvkey_alice, _, _) = setup_keys(ALICE_TPRV_STR, ALICE_BOB_PATH, &secp);
1590        let (prvkey_bob, _, _) = setup_keys(BOB_TPRV_STR, ALICE_BOB_PATH, &secp);
1591
1592        let desc = descriptor!(wsh(multi(2, prvkey_alice, prvkey_bob))).unwrap();
1593
1594        let (wallet_desc, keymap) = desc
1595            .into_wallet_descriptor(&secp, Network::Testnet)
1596            .unwrap();
1597
1598        let addr = wallet_desc
1599            .at_derivation_index(0)
1600            .unwrap()
1601            .address(Network::Testnet)
1602            .unwrap();
1603        assert_eq!(
1604            "tb1qg3cwv3xt50gdg875qvjjpfgaps86gtk4rz0ejvp6ttc5ldnlxuvqlcn0xk",
1605            addr.to_string()
1606        );
1607
1608        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
1609
1610        let psbt = Psbt::from_str(ALICE_SIGNED_PSBT).unwrap();
1611
1612        let policy_alice_psbt = wallet_desc
1613            .extract_policy(&signers_container, BuildSatisfaction::Psbt(&psbt), &secp)
1614            .unwrap()
1615            .unwrap();
1616        //println!("{}", serde_json::to_string(&policy_alice_psbt).unwrap());
1617
1618        assert_matches!(&policy_alice_psbt.satisfaction, Satisfaction::Partial { n, m, items, .. } if n == &2
1619             && m == &2
1620             && items == &vec![0]
1621        );
1622
1623        let psbt = Psbt::from_str(BOB_SIGNED_PSBT).unwrap();
1624        let policy_bob_psbt = wallet_desc
1625            .extract_policy(&signers_container, BuildSatisfaction::Psbt(&psbt), &secp)
1626            .unwrap()
1627            .unwrap();
1628        //println!("{}", serde_json::to_string(&policy_bob_psbt).unwrap());
1629
1630        assert_matches!(&policy_bob_psbt.satisfaction, Satisfaction::Partial { n, m, items, .. } if n == &2
1631             && m == &2
1632             && items == &vec![1]
1633        );
1634
1635        let psbt = Psbt::from_str(ALICE_BOB_SIGNED_PSBT).unwrap();
1636        let policy_alice_bob_psbt = wallet_desc
1637            .extract_policy(&signers_container, BuildSatisfaction::Psbt(&psbt), &secp)
1638            .unwrap()
1639            .unwrap();
1640        assert_matches!(&policy_alice_bob_psbt.satisfaction, Satisfaction::PartialComplete { n, m, items, .. } if n == &2
1641             && m == &2
1642             && items == &vec![0, 1]
1643        );
1644    }
1645
1646    #[rustfmt::skip]
1647    #[test]
1648    fn test_extract_satisfaction_timelock() {
1649        //const PSBT_POLICY_CONSIDER_TIMELOCK_NOT_EXPIRED: &str = "cHNidP8BAFMBAAAAAdld52uJFGT7Yde0YZdSVh2vL020Zm2exadH5R4GSNScAAAAAAD/////ATrcAAAAAAAAF6kUXv2Fn+YemPP4PUpNR1ZbU16/eRCHAAAAAAABASvI3AAAAAAAACIAILhzvvcBzw/Zfnc9ispRK0PCahxn1F6RHXTZAmw5tqNPAQVSdmNSsmlofCEDeAtjYQk/Vfu4db2+68hyMKjc38+kWl5sP5QH8L42Zsusk3whAvhhP8vi6bSPMZokerDnvffCBs8m6MdEH8+PgUJdZ5mIrJNShyIGAvhhP8vi6bSPMZokerDnvffCBs8m6MdEH8+PgUJdZ5mIDBwu7j4AAACAAAAAACIGA3gLY2EJP1X7uHW9vuvIcjCo3N/PpFpebD+UB/C+NmbLDMkRfC4AAACAAAAAAAAA";
1650        const PSBT_POLICY_CONSIDER_TIMELOCK_EXPIRED:     &str = "cHNidP8BAFMCAAAAAdld52uJFGT7Yde0YZdSVh2vL020Zm2exadH5R4GSNScAAAAAAACAAAAATrcAAAAAAAAF6kUXv2Fn+YemPP4PUpNR1ZbU16/eRCHAAAAAAABASvI3AAAAAAAACIAILhzvvcBzw/Zfnc9ispRK0PCahxn1F6RHXTZAmw5tqNPAQVSdmNSsmlofCEDeAtjYQk/Vfu4db2+68hyMKjc38+kWl5sP5QH8L42Zsusk3whAvhhP8vi6bSPMZokerDnvffCBs8m6MdEH8+PgUJdZ5mIrJNShyIGAvhhP8vi6bSPMZokerDnvffCBs8m6MdEH8+PgUJdZ5mIDBwu7j4AAACAAAAAACIGA3gLY2EJP1X7uHW9vuvIcjCo3N/PpFpebD+UB/C+NmbLDMkRfC4AAACAAAAAAAAA";
1651        const PSBT_POLICY_CONSIDER_TIMELOCK_EXPIRED_SIGNED: &str ="cHNidP8BAFMCAAAAAdld52uJFGT7Yde0YZdSVh2vL020Zm2exadH5R4GSNScAAAAAAACAAAAATrcAAAAAAAAF6kUXv2Fn+YemPP4PUpNR1ZbU16/eRCHAAAAAAABASvI3AAAAAAAACIAILhzvvcBzw/Zfnc9ispRK0PCahxn1F6RHXTZAmw5tqNPIgIDeAtjYQk/Vfu4db2+68hyMKjc38+kWl5sP5QH8L42ZstIMEUCIQCtZxNm6H3Ux3pnc64DSpgohMdBj+57xhFHcURYt2BpPAIgG3OnI7bcj/3GtWX1HHyYGSI7QGa/zq5YnsmK1Cw29NABAQVSdmNSsmlofCEDeAtjYQk/Vfu4db2+68hyMKjc38+kWl5sP5QH8L42Zsusk3whAvhhP8vi6bSPMZokerDnvffCBs8m6MdEH8+PgUJdZ5mIrJNShyIGAvhhP8vi6bSPMZokerDnvffCBs8m6MdEH8+PgUJdZ5mIDBwu7j4AAACAAAAAACIGA3gLY2EJP1X7uHW9vuvIcjCo3N/PpFpebD+UB/C+NmbLDMkRfC4AAACAAAAAAAEHAAEIoAQASDBFAiEArWcTZuh91Md6Z3OuA0qYKITHQY/ue8YRR3FEWLdgaTwCIBtzpyO23I/9xrVl9Rx8mBkiO0Bmv86uWJ7JitQsNvTQAQEBUnZjUrJpaHwhA3gLY2EJP1X7uHW9vuvIcjCo3N/PpFpebD+UB/C+NmbLrJN8IQL4YT/L4um0jzGaJHqw5733wgbPJujHRB/Pj4FCXWeZiKyTUocAAA==";
1652
1653        let secp = Secp256k1::new();
1654
1655        let (prvkey_alice, _, _) = setup_keys(ALICE_TPRV_STR, ALICE_BOB_PATH, &secp);
1656        let (prvkey_bob, _, _) = setup_keys(BOB_TPRV_STR, ALICE_BOB_PATH, &secp);
1657
1658        let desc =
1659            descriptor!(wsh(thresh(2,n:d:v:older(2),s:pk(prvkey_alice),s:pk(prvkey_bob)))).unwrap();
1660
1661        let (wallet_desc, keymap) = desc
1662            .into_wallet_descriptor(&secp, Network::Testnet)
1663            .unwrap();
1664        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
1665
1666        let addr = wallet_desc
1667            .at_derivation_index(0)
1668            .unwrap()
1669            .address(Network::Testnet)
1670            .unwrap();
1671        assert_eq!(
1672            "tb1qsydsey4hexagwkvercqsmes6yet0ndkyt6uzcphtqnygjd8hmzmsfxrv58",
1673            addr.to_string()
1674        );
1675
1676        let psbt = Psbt::from_str(PSBT_POLICY_CONSIDER_TIMELOCK_EXPIRED).unwrap();
1677
1678        let build_sat = BuildSatisfaction::PsbtTimelocks {
1679            psbt: &psbt,
1680            current_height: 10,
1681            input_max_height: 9,
1682        };
1683
1684        let policy = wallet_desc
1685            .extract_policy(&signers_container, build_sat, &secp)
1686            .unwrap()
1687            .unwrap();
1688        assert_matches!(&policy.satisfaction, Satisfaction::Partial { n, m, items, .. } if n == &3
1689             && m == &2
1690             && items.is_empty()
1691        );
1692        //println!("{}", serde_json::to_string(&policy).unwrap());
1693
1694        let build_sat_expired = BuildSatisfaction::PsbtTimelocks {
1695            psbt: &psbt,
1696            current_height: 12,
1697            input_max_height: 9,
1698        };
1699
1700        let policy_expired = wallet_desc
1701            .extract_policy(&signers_container, build_sat_expired, &secp)
1702            .unwrap()
1703            .unwrap();
1704        assert_matches!(&policy_expired.satisfaction, Satisfaction::Partial { n, m, items, .. } if n == &3
1705             && m == &2
1706             && items == &vec![0]
1707        );
1708        //println!("{}", serde_json::to_string(&policy_expired).unwrap());
1709
1710        let psbt_signed = Psbt::from_str(PSBT_POLICY_CONSIDER_TIMELOCK_EXPIRED_SIGNED).unwrap();
1711
1712        let build_sat_expired_signed = BuildSatisfaction::PsbtTimelocks {
1713            psbt: &psbt_signed,
1714            current_height: 12,
1715            input_max_height: 9,
1716        };
1717
1718        let policy_expired_signed = wallet_desc
1719            .extract_policy(&signers_container, build_sat_expired_signed, &secp)
1720            .unwrap()
1721            .unwrap();
1722        assert_matches!(&policy_expired_signed.satisfaction, Satisfaction::PartialComplete { n, m, items, .. } if n == &3
1723             && m == &2
1724             && items == &vec![0, 1]
1725        );
1726        //println!("{}", serde_json::to_string(&policy_expired_signed).unwrap());
1727    }
1728
1729    #[test]
1730    fn test_extract_pkh() {
1731        let secp = Secp256k1::new();
1732
1733        let (prvkey_alice, _, _) = setup_keys(ALICE_TPRV_STR, ALICE_BOB_PATH, &secp);
1734        let (prvkey_bob, _, _) = setup_keys(BOB_TPRV_STR, ALICE_BOB_PATH, &secp);
1735        let (prvkey_carol, _, _) = setup_keys(CAROL_TPRV_STR, ALICE_BOB_PATH, &secp);
1736
1737        let desc = descriptor!(wsh(c: andor(
1738            pk(prvkey_alice),
1739            pk_k(prvkey_bob),
1740            pk_h(prvkey_carol),
1741        )))
1742        .unwrap();
1743
1744        let (wallet_desc, keymap) = desc
1745            .into_wallet_descriptor(&secp, Network::Testnet)
1746            .unwrap();
1747        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
1748
1749        let policy = wallet_desc.extract_policy(&signers_container, BuildSatisfaction::None, &secp);
1750        assert!(policy.is_ok());
1751    }
1752
1753    #[test]
1754    fn test_extract_tr_key_spend() {
1755        let secp = Secp256k1::new();
1756
1757        let (prvkey, _, fingerprint) = setup_keys(ALICE_TPRV_STR, ALICE_BOB_PATH, &secp);
1758
1759        let desc = descriptor!(tr(prvkey)).unwrap();
1760        let (wallet_desc, keymap) = desc
1761            .into_wallet_descriptor(&secp, Network::Testnet)
1762            .unwrap();
1763        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
1764
1765        let policy = wallet_desc
1766            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
1767            .unwrap();
1768        assert_eq!(
1769            policy,
1770            Some(Policy {
1771                id: "48u0tz0n".to_string(),
1772                item: SatisfiableItem::SchnorrSignature(PkOrF::Fingerprint(fingerprint)),
1773                satisfaction: Satisfaction::None,
1774                contribution: Satisfaction::Complete {
1775                    condition: Condition::default()
1776                }
1777            })
1778        );
1779    }
1780
1781    #[test]
1782    fn test_extract_tr_script_spend() {
1783        let secp = Secp256k1::new();
1784
1785        let (alice_prv, _, alice_fing) = setup_keys(ALICE_TPRV_STR, ALICE_BOB_PATH, &secp);
1786        let (_, bob_pub, bob_fing) = setup_keys(BOB_TPRV_STR, ALICE_BOB_PATH, &secp);
1787
1788        let desc = descriptor!(tr(bob_pub, pk(alice_prv))).unwrap();
1789        let (wallet_desc, keymap) = desc
1790            .into_wallet_descriptor(&secp, Network::Testnet)
1791            .unwrap();
1792        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
1793
1794        let policy = wallet_desc
1795            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
1796            .unwrap()
1797            .unwrap();
1798
1799        assert_matches!(policy.item, SatisfiableItem::Thresh { ref items, threshold: 1 } if items.len() == 2);
1800        assert_matches!(policy.contribution, Satisfaction::PartialComplete { n: 2, m: 1, items, .. } if items == vec![1]);
1801
1802        let alice_sig = SatisfiableItem::SchnorrSignature(PkOrF::Fingerprint(alice_fing));
1803        let bob_sig = SatisfiableItem::SchnorrSignature(PkOrF::Fingerprint(bob_fing));
1804
1805        let thresh_items = match policy.item {
1806            SatisfiableItem::Thresh { items, .. } => items,
1807            _ => unreachable!(),
1808        };
1809
1810        assert_eq!(thresh_items[0].item, bob_sig);
1811        assert_eq!(thresh_items[1].item, alice_sig);
1812    }
1813
1814    #[test]
1815    fn test_extract_tr_satisfaction_key_spend() {
1816        const UNSIGNED_PSBT: &str = "cHNidP8BAFMBAAAAAUKgMCqtGLSiGYhsTols2UJ/VQQgQi/SXO38uXs2SahdAQAAAAD/////ARyWmAAAAAAAF6kU4R3W8CnGzZcSsaovTYu0X8vHt3WHAAAAAAABASuAlpgAAAAAACJRIEiEBFjbZa1xdjLfFjrKzuC1F1LeRyI/gL6IuGKNmUuSIRYnkGTDxwXMHP32fkDFoGJY28trxbkkVgR2z7jZa2pOJA0AyRF8LgAAAIADAAAAARcgJ5Bkw8cFzBz99n5AxaBiWNvLa8W5JFYEds+42WtqTiQAAA==";
1817        const SIGNED_PSBT: &str = "cHNidP8BAFMBAAAAAUKgMCqtGLSiGYhsTols2UJ/VQQgQi/SXO38uXs2SahdAQAAAAD/////ARyWmAAAAAAAF6kU4R3W8CnGzZcSsaovTYu0X8vHt3WHAAAAAAABASuAlpgAAAAAACJRIEiEBFjbZa1xdjLfFjrKzuC1F1LeRyI/gL6IuGKNmUuSARNAIsRvARpRxuyQosVA7guRQT9vXr+S25W2tnP2xOGBsSgq7A4RL8yrbvwDmNlWw9R0Nc/6t+IsyCyy7dD/lbUGgyEWJ5Bkw8cFzBz99n5AxaBiWNvLa8W5JFYEds+42WtqTiQNAMkRfC4AAACAAwAAAAEXICeQZMPHBcwc/fZ+QMWgYljby2vFuSRWBHbPuNlrak4kAAA=";
1818
1819        let unsigned_psbt = Psbt::from_str(UNSIGNED_PSBT).unwrap();
1820        let signed_psbt = Psbt::from_str(SIGNED_PSBT).unwrap();
1821
1822        let secp = Secp256k1::new();
1823
1824        let (_, pubkey, _) = setup_keys(ALICE_TPRV_STR, ALICE_BOB_PATH, &secp);
1825
1826        let desc = descriptor!(tr(pubkey)).unwrap();
1827        let (wallet_desc, _) = desc
1828            .into_wallet_descriptor(&secp, Network::Testnet)
1829            .unwrap();
1830
1831        let policy_unsigned = wallet_desc
1832            .extract_policy(
1833                &SignersContainer::default(),
1834                BuildSatisfaction::Psbt(&unsigned_psbt),
1835                &secp,
1836            )
1837            .unwrap()
1838            .unwrap();
1839        let policy_signed = wallet_desc
1840            .extract_policy(
1841                &SignersContainer::default(),
1842                BuildSatisfaction::Psbt(&signed_psbt),
1843                &secp,
1844            )
1845            .unwrap()
1846            .unwrap();
1847
1848        assert_eq!(policy_unsigned.satisfaction, Satisfaction::None);
1849        assert_eq!(
1850            policy_signed.satisfaction,
1851            Satisfaction::Complete {
1852                condition: Default::default()
1853            }
1854        );
1855    }
1856
1857    #[test]
1858    fn test_extract_tr_satisfaction_script_spend() {
1859        const UNSIGNED_PSBT: &str = "cHNidP8BAFMBAAAAAWZalxaErOL7P3WPIUc8DsjgE68S+ww+uqiqEI2SAwlPAAAAAAD/////AQiWmAAAAAAAF6kU4R3W8CnGzZcSsaovTYu0X8vHt3WHAAAAAAABASuAlpgAAAAAACJRINa6bLPZwp3/CYWoxyI3mLYcSC5f9LInAMUng94nspa2IhXBgiPY+kcolS1Hp0niOK/+7VHz6F+nsz8JVxnzWzkgToYjIHhGyuexxtRVKevRx4YwWR/W0r7LPHt6oS6DLlzyuYQarMAhFnhGyuexxtRVKevRx4YwWR/W0r7LPHt6oS6DLlzyuYQaLQH2onWFc3UR6I9ZhuHVeJCi5LNAf4APVd7mHn4BhdViHRwu7j4AAACAAgAAACEWgiPY+kcolS1Hp0niOK/+7VHz6F+nsz8JVxnzWzkgToYNAMkRfC4AAACAAgAAAAEXIIIj2PpHKJUtR6dJ4jiv/u1R8+hfp7M/CVcZ81s5IE6GARgg9qJ1hXN1EeiPWYbh1XiQouSzQH+AD1Xe5h5+AYXVYh0AAA==";
1860        const SIGNED_PSBT: &str = "cHNidP8BAFMBAAAAAWZalxaErOL7P3WPIUc8DsjgE68S+ww+uqiqEI2SAwlPAAAAAAD/////AQiWmAAAAAAAF6kU4R3W8CnGzZcSsaovTYu0X8vHt3WHAAAAAAABASuAlpgAAAAAACJRINa6bLPZwp3/CYWoxyI3mLYcSC5f9LInAMUng94nspa2AQcAAQhCAUALcP9w/+Ddly9DWdhHTnQ9uCDWLPZjR6vKbKePswW2Ee6W5KNfrklus/8z98n7BQ1U4vADHk0FbadeeL8rrbHlARNAC3D/cP/g3ZcvQ1nYR050Pbgg1iz2Y0erymynj7MFthHuluSjX65JbrP/M/fJ+wUNVOLwAx5NBW2nXni/K62x5UEUeEbK57HG1FUp69HHhjBZH9bSvss8e3qhLoMuXPK5hBr2onWFc3UR6I9ZhuHVeJCi5LNAf4APVd7mHn4BhdViHUAXNmWieJ80Fs+PMa2C186YOBPZbYG/ieEUkagMwzJ788SoCucNdp5wnxfpuJVygFhglDrXGzujFtC82PrMohwuIhXBgiPY+kcolS1Hp0niOK/+7VHz6F+nsz8JVxnzWzkgToYjIHhGyuexxtRVKevRx4YwWR/W0r7LPHt6oS6DLlzyuYQarMAhFnhGyuexxtRVKevRx4YwWR/W0r7LPHt6oS6DLlzyuYQaLQH2onWFc3UR6I9ZhuHVeJCi5LNAf4APVd7mHn4BhdViHRwu7j4AAACAAgAAACEWgiPY+kcolS1Hp0niOK/+7VHz6F+nsz8JVxnzWzkgToYNAMkRfC4AAACAAgAAAAEXIIIj2PpHKJUtR6dJ4jiv/u1R8+hfp7M/CVcZ81s5IE6GARgg9qJ1hXN1EeiPWYbh1XiQouSzQH+AD1Xe5h5+AYXVYh0AAA==";
1861
1862        let unsigned_psbt = Psbt::from_str(UNSIGNED_PSBT).unwrap();
1863        let signed_psbt = Psbt::from_str(SIGNED_PSBT).unwrap();
1864
1865        let secp = Secp256k1::new();
1866
1867        let (_, alice_pub, _) = setup_keys(ALICE_TPRV_STR, ALICE_BOB_PATH, &secp);
1868        let (_, bob_pub, _) = setup_keys(BOB_TPRV_STR, ALICE_BOB_PATH, &secp);
1869
1870        let desc = descriptor!(tr(bob_pub, pk(alice_pub))).unwrap();
1871        let (wallet_desc, _) = desc
1872            .into_wallet_descriptor(&secp, Network::Testnet)
1873            .unwrap();
1874
1875        let policy_unsigned = wallet_desc
1876            .extract_policy(
1877                &SignersContainer::default(),
1878                BuildSatisfaction::Psbt(&unsigned_psbt),
1879                &secp,
1880            )
1881            .unwrap()
1882            .unwrap();
1883        let policy_signed = wallet_desc
1884            .extract_policy(
1885                &SignersContainer::default(),
1886                BuildSatisfaction::Psbt(&signed_psbt),
1887                &secp,
1888            )
1889            .unwrap()
1890            .unwrap();
1891
1892        assert_matches!(policy_unsigned.item, SatisfiableItem::Thresh { ref items, threshold: 1 } if items.len() == 2);
1893        assert_matches!(policy_unsigned.satisfaction, Satisfaction::Partial { n: 2, m: 1, items, .. } if items.is_empty());
1894
1895        assert_matches!(policy_signed.item, SatisfiableItem::Thresh { ref items, threshold: 1 } if items.len() == 2);
1896        assert_matches!(policy_signed.satisfaction, Satisfaction::PartialComplete { n: 2, m: 1, items, .. } if items == vec![0, 1]);
1897
1898        let satisfied_items = match policy_signed.item {
1899            SatisfiableItem::Thresh { items, .. } => items,
1900            _ => unreachable!(),
1901        };
1902
1903        assert_eq!(
1904            satisfied_items[0].satisfaction,
1905            Satisfaction::Complete {
1906                condition: Default::default()
1907            }
1908        );
1909        assert_eq!(
1910            satisfied_items[1].satisfaction,
1911            Satisfaction::Complete {
1912                condition: Default::default()
1913            }
1914        );
1915    }
1916}