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