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