1use 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#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
74#[serde(rename_all = "snake_case")]
75pub enum PkOrF {
76 Pubkey(PublicKey),
78 XOnlyPubkey(XOnlyPublicKey),
80 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#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
105#[serde(tag = "type", rename_all = "UPPERCASE")]
106pub enum SatisfiableItem {
107 EcdsaSignature(PkOrF),
110 SchnorrSignature(PkOrF),
112 Sha256Preimage {
114 hash: sha256::Hash,
116 },
117 Hash256Preimage {
119 hash: hash256::Hash,
121 },
122 Ripemd160Preimage {
124 hash: ripemd160::Hash,
126 },
127 Hash160Preimage {
129 hash: hash160::Hash,
131 },
132 AbsoluteTimelock {
134 value: absolute::LockTime,
136 },
137 RelativeTimelock {
139 value: Sequence,
141 },
142 Multisig {
144 keys: Vec<PkOrF>,
146 threshold: usize,
148 },
149
150 Thresh {
153 items: Vec<Policy>,
155 threshold: usize,
157 },
158}
159
160impl SatisfiableItem {
161 pub fn is_leaf(&self) -> bool {
163 !matches!(
164 self,
165 SatisfiableItem::Thresh {
166 items: _,
167 threshold: _,
168 }
169 )
170 }
171
172 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
237pub type ConditionMap = BTreeMap<usize, HashSet<Condition>>;
239pub 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#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
259#[serde(tag = "type", rename_all = "UPPERCASE")]
260pub enum Satisfaction {
261 Partial {
263 n: usize,
265 m: usize,
267 items: Vec<usize>,
269 #[serde(skip_serializing_if = "Option::is_none")]
270 sorted: Option<bool>,
272 #[serde(skip_serializing_if = "BTreeMap::is_empty")]
273 conditions: ConditionMap,
275 },
276 PartialComplete {
278 n: usize,
280 m: usize,
282 items: Vec<usize>,
284 #[serde(skip_serializing_if = "Option::is_none")]
285 sorted: Option<bool>,
287 #[serde(
288 serialize_with = "serialize_folded_cond_map",
289 skip_serializing_if = "BTreeMap::is_empty"
290 )]
291 conditions: FoldedConditionMap,
293 },
294
295 Complete {
297 condition: Condition,
299 },
300 None,
302}
303
304impl Satisfaction {
305 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 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 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 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
367 .into_iter()
368 .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 .map(|(key, val)| {
390 (
391 key,
392 val.into_iter()
393 .try_fold(Condition::default(), |acc, v| acc.merge(&v)),
394 )
395 })
396 .filter(|(_, val)| val.is_ok())
399 .for_each(|(key, val)| {
402 map.entry(key)
403 .or_insert_with(HashSet::new)
404 .insert(val.unwrap());
405 });
406 *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#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
433pub struct Policy {
434 pub id: String,
436
437 #[serde(flatten)]
439 pub item: SatisfiableItem,
440 pub satisfaction: Satisfaction,
442 pub contribution: Satisfaction,
444}
445
446#[derive(Hash, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Default, Serialize)]
449pub struct Condition {
450 #[serde(skip_serializing_if = "Option::is_none")]
452 pub csv: Option<Sequence>,
453 #[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 pub fn is_null(&self) -> bool {
498 self.csv.is_none() && self.timelock.is_none()
499 }
500}
501
502#[derive(Debug, PartialEq, Eq)]
504pub enum PolicyError {
505 NotEnoughItemsSelected(String),
507 IndexOutOfRange(usize),
509 AddOnLeaf,
511 AddOnPartialComplete,
513 MixedTimelockUnits,
515 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 pub fn requires_path(&self) -> bool {
653 self.get_condition(&BTreeMap::new()).is_err()
654 }
655
656 pub fn get_condition(
659 &self,
660 path: &BTreeMap<String, Vec<usize>>,
661 ) -> Result<Condition, PolicyError> {
662 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 mapped_req
685 .iter()
686 .all(|cond| matches!(cond, Ok(c) if c.is_null()))
687 {
688 return Ok(Condition::default());
689 }
690
691 for index in &selected {
693 if *index >= items.len() {
694 return Err(PolicyError::IndexOutOfRange(*index));
695 }
696 }
697
698 if selected.len() < *threshold {
702 return Err(PolicyError::NotEnoughItemsSelected(self.id.clone()));
703 }
704
705 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 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: Fn(&psbt::Input, &SinglePubKey) -> bool,
794 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 psbt.inputs.iter().all(|input| match key {
805 DescriptorPublicKey::Single(SinglePub { key, .. }) => check(input, key),
806 DescriptorPublicKey::XPub(xpub) => {
807 match extract(input, xpub.root_fingerprint(secp)) {
809 Some(pubkey) => check(input, &pubkey),
810 None => false,
811 }
812 }
813 DescriptorPublicKey::MultiXPub(xpub) => {
814 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 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 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 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 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#[derive(Debug, Clone, Copy)]
1050pub enum BuildSatisfaction<'a> {
1051 None,
1053 Psbt(&'a Psbt),
1055 PsbtTimelocks {
1057 psbt: &'a Psbt,
1059 current_height: u32,
1061 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 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]
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 #[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 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 #[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 #[test]
1287 #[ignore] 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 #[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]
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 #[test]
1380 #[ignore] 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]
1411 #[ignore] 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 #[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; 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); }
1473
1474 #[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); 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); }
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 let no_args = policy.get_condition(&vec![].into_iter().collect());
1539 assert_eq!(no_args, Ok(Condition::default()));
1540
1541 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 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 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 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 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 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_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 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 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 }
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}