1use core::iter::FromIterator;
18
19use bitcoin::hashes::{hash160, ripemd160, sha256};
20use bitcoin::key::XOnlyPublicKey;
21use bitcoin::script::PushBytesBuf;
22use bitcoin::taproot::{ControlBlock, LeafVersion, TapLeafHash};
23use bitcoin::{absolute, bip32, psbt, relative, ScriptBuf, WitnessVersion};
24
25use crate::descriptor::{self, Descriptor, DescriptorType, KeyMap};
26use crate::miniscript::hash256;
27use crate::miniscript::satisfy::{Placeholder, Satisfier, SchnorrSigType};
28use crate::prelude::*;
29use crate::util::witness_size;
30use crate::{DefiniteDescriptorKey, DescriptorPublicKey, Error, MiniscriptKey, ToPublicKey};
31
32pub trait AssetProvider<Pk: MiniscriptKey> {
44 fn provider_lookup_ecdsa_sig(&self, _: &Pk) -> bool { false }
46
47 fn provider_lookup_tap_key_spend_sig(&self, _: &Pk) -> Option<usize> { None }
49
50 fn provider_lookup_tap_leaf_script_sig(&self, _: &Pk, _: &TapLeafHash) -> Option<usize> { None }
53
54 fn provider_lookup_tap_control_block_map(
56 &self,
57 ) -> Option<&BTreeMap<ControlBlock, (bitcoin::ScriptBuf, LeafVersion)>> {
58 None
59 }
60
61 fn provider_lookup_raw_pkh_pk(&self, _: &hash160::Hash) -> Option<bitcoin::PublicKey> { None }
63
64 fn provider_lookup_raw_pkh_x_only_pk(&self, _: &hash160::Hash) -> Option<XOnlyPublicKey> {
66 None
67 }
68
69 fn provider_lookup_raw_pkh_ecdsa_sig(&self, _: &hash160::Hash) -> Option<bitcoin::PublicKey> {
75 None
76 }
77
78 fn provider_lookup_raw_pkh_tap_leaf_script_sig(
84 &self,
85 _: &(hash160::Hash, TapLeafHash),
86 ) -> Option<(XOnlyPublicKey, usize)> {
87 None
88 }
89
90 fn provider_lookup_sha256(&self, _: &Pk::Sha256) -> bool { false }
92
93 fn provider_lookup_hash256(&self, _: &Pk::Hash256) -> bool { false }
95
96 fn provider_lookup_ripemd160(&self, _: &Pk::Ripemd160) -> bool { false }
98
99 fn provider_lookup_hash160(&self, _: &Pk::Hash160) -> bool { false }
101
102 fn check_older(&self, _: relative::LockTime) -> bool { false }
104
105 fn check_after(&self, _: absolute::LockTime) -> bool { false }
107}
108
109#[cfg(feature = "std")]
111pub struct LoggerAssetProvider<'a>(pub &'a Assets);
112
113#[cfg(feature = "std")]
114macro_rules! impl_log_method {
115 ( $name:ident, $( <$ctx:ident: ScriptContext > )? $( $arg:ident : $ty:ty, )* -> $ret_ty:ty ) => {
116 fn $name $( <$ctx: ScriptContext> )? ( &self, $( $arg:$ty ),* ) -> $ret_ty {
117 let ret = (self.0).$name $( ::<$ctx> )*( $( $arg ),* );
118 dbg!(stringify!( $name ), ( $( $arg ),* ), &ret);
119
120 ret
121 }
122 }
123}
124
125#[cfg(feature = "std")]
126impl<'a> AssetProvider<DefiniteDescriptorKey> for LoggerAssetProvider<'a> {
127 impl_log_method!(provider_lookup_ecdsa_sig, pk: &DefiniteDescriptorKey, -> bool);
128 impl_log_method!(provider_lookup_tap_key_spend_sig, pk: &DefiniteDescriptorKey, -> Option<usize>);
129 impl_log_method!(provider_lookup_tap_leaf_script_sig, pk: &DefiniteDescriptorKey, leaf_hash: &TapLeafHash, -> Option<usize>);
130 impl_log_method!(provider_lookup_tap_control_block_map, -> Option<&BTreeMap<ControlBlock, (bitcoin::ScriptBuf, LeafVersion)>>);
131 impl_log_method!(provider_lookup_raw_pkh_pk, hash: &hash160::Hash, -> Option<bitcoin::PublicKey>);
132 impl_log_method!(provider_lookup_raw_pkh_x_only_pk, hash: &hash160::Hash, -> Option<XOnlyPublicKey>);
133 impl_log_method!(provider_lookup_raw_pkh_ecdsa_sig, hash: &hash160::Hash, -> Option<bitcoin::PublicKey>);
134 impl_log_method!(provider_lookup_raw_pkh_tap_leaf_script_sig, hash: &(hash160::Hash, TapLeafHash), -> Option<(XOnlyPublicKey, usize)>);
135 impl_log_method!(provider_lookup_sha256, hash: &sha256::Hash, -> bool);
136 impl_log_method!(provider_lookup_hash256, hash: &hash256::Hash, -> bool);
137 impl_log_method!(provider_lookup_ripemd160, hash: &ripemd160::Hash, -> bool);
138 impl_log_method!(provider_lookup_hash160, hash: &hash160::Hash, -> bool);
139 impl_log_method!(check_older, s: relative::LockTime, -> bool);
140 impl_log_method!(check_after, t: absolute::LockTime, -> bool);
141}
142
143impl<T, Pk> AssetProvider<Pk> for T
144where
145 T: Satisfier<Pk>,
146 Pk: MiniscriptKey + ToPublicKey,
147{
148 fn provider_lookup_ecdsa_sig(&self, pk: &Pk) -> bool {
149 Satisfier::lookup_ecdsa_sig(self, pk).is_some()
150 }
151
152 fn provider_lookup_tap_key_spend_sig(&self, _: &Pk) -> Option<usize> {
153 Satisfier::lookup_tap_key_spend_sig(self).map(|s| s.to_vec().len())
154 }
155
156 fn provider_lookup_tap_leaf_script_sig(
157 &self,
158 pk: &Pk,
159 leaf_hash: &TapLeafHash,
160 ) -> Option<usize> {
161 Satisfier::lookup_tap_leaf_script_sig(self, pk, leaf_hash).map(|s| s.to_vec().len())
162 }
163
164 fn provider_lookup_tap_control_block_map(
165 &self,
166 ) -> Option<&BTreeMap<ControlBlock, (bitcoin::ScriptBuf, LeafVersion)>> {
167 Satisfier::lookup_tap_control_block_map(self)
168 }
169
170 fn provider_lookup_raw_pkh_pk(&self, hash: &hash160::Hash) -> Option<bitcoin::PublicKey> {
171 Satisfier::lookup_raw_pkh_pk(self, hash)
172 }
173
174 fn provider_lookup_raw_pkh_x_only_pk(&self, hash: &hash160::Hash) -> Option<XOnlyPublicKey> {
175 Satisfier::lookup_raw_pkh_x_only_pk(self, hash)
176 }
177
178 fn provider_lookup_raw_pkh_ecdsa_sig(
179 &self,
180 hash: &hash160::Hash,
181 ) -> Option<bitcoin::PublicKey> {
182 Satisfier::lookup_raw_pkh_ecdsa_sig(self, hash).map(|(pk, _)| pk)
183 }
184
185 fn provider_lookup_raw_pkh_tap_leaf_script_sig(
186 &self,
187 hash: &(hash160::Hash, TapLeafHash),
188 ) -> Option<(XOnlyPublicKey, usize)> {
189 Satisfier::lookup_raw_pkh_tap_leaf_script_sig(self, hash)
190 .map(|(pk, sig)| (pk, sig.to_vec().len()))
191 }
192
193 fn provider_lookup_sha256(&self, hash: &Pk::Sha256) -> bool {
194 Satisfier::lookup_sha256(self, hash).is_some()
195 }
196
197 fn provider_lookup_hash256(&self, hash: &Pk::Hash256) -> bool {
198 Satisfier::lookup_hash256(self, hash).is_some()
199 }
200
201 fn provider_lookup_ripemd160(&self, hash: &Pk::Ripemd160) -> bool {
202 Satisfier::lookup_ripemd160(self, hash).is_some()
203 }
204
205 fn provider_lookup_hash160(&self, hash: &Pk::Hash160) -> bool {
206 Satisfier::lookup_hash160(self, hash).is_some()
207 }
208
209 fn check_older(&self, s: relative::LockTime) -> bool { Satisfier::check_older(self, s) }
210
211 fn check_after(&self, l: absolute::LockTime) -> bool { Satisfier::check_after(self, l) }
212}
213
214#[derive(Debug, Clone)]
219pub struct Plan {
220 pub(crate) template: Vec<Placeholder<DefiniteDescriptorKey>>,
222 pub absolute_timelock: Option<absolute::LockTime>,
224 pub relative_timelock: Option<relative::LockTime>,
226
227 pub(crate) descriptor: Descriptor<DefiniteDescriptorKey>,
228}
229
230impl Plan {
231 pub fn witness_template(&self) -> &Vec<Placeholder<DefiniteDescriptorKey>> { &self.template }
233
234 pub fn witness_version(&self) -> Option<WitnessVersion> {
236 self.descriptor.desc_type().segwit_version()
237 }
238
239 pub fn satisfaction_weight(&self) -> usize { self.witness_size() + self.scriptsig_size() * 4 }
242
243 pub fn scriptsig_size(&self) -> usize {
245 match (self.descriptor.desc_type().segwit_version(), self.descriptor.desc_type()) {
246 (None, _) => witness_size(self.template.as_ref()),
248 (Some(WitnessVersion::V1), _) => 1,
250 (_, DescriptorType::ShWpkh) => 1 + 1 + 1 + 20,
252 (_, DescriptorType::ShWsh) | (_, DescriptorType::ShWshSortedMulti) => 1 + 1 + 1 + 32,
254 _ => 1,
256 }
257 }
258
259 pub fn witness_size(&self) -> usize {
261 if self.descriptor.desc_type().segwit_version().is_some() {
262 witness_size(self.template.as_ref())
263 } else {
264 0 }
267 }
268
269 pub fn satisfy<Sat: Satisfier<DefiniteDescriptorKey>>(
271 &self,
272 stfr: &Sat,
273 ) -> Result<(Vec<Vec<u8>>, ScriptBuf), Error> {
274 use bitcoin::blockdata::script::Builder;
275
276 let stack = self
277 .template
278 .iter()
279 .map(|placeholder| placeholder.satisfy_self(stfr))
280 .collect::<Option<Vec<Vec<u8>>>>()
281 .ok_or(Error::CouldNotSatisfy)?;
282
283 Ok(match self.descriptor.desc_type() {
284 DescriptorType::Bare
285 | DescriptorType::Sh
286 | DescriptorType::Pkh
287 | DescriptorType::ShSortedMulti => (
288 vec![],
289 stack
290 .into_iter()
291 .fold(Builder::new(), |builder, item| {
292 use core::convert::TryFrom;
293 let bytes = PushBytesBuf::try_from(item)
294 .expect("All the possible placeholders can be made into PushBytesBuf");
295 builder.push_slice(bytes)
296 })
297 .into_script(),
298 ),
299 DescriptorType::Wpkh
300 | DescriptorType::Wsh
301 | DescriptorType::WshSortedMulti
302 | DescriptorType::Tr => (stack, ScriptBuf::new()),
303 DescriptorType::ShWsh | DescriptorType::ShWshSortedMulti | DescriptorType::ShWpkh => {
304 (stack, self.descriptor.unsigned_script_sig())
305 }
306 })
307 }
308
309 pub fn update_psbt_input(&self, input: &mut psbt::Input) {
315 if let Descriptor::Tr(tr) = &self.descriptor {
316 enum SpendType {
317 KeySpend { internal_key: XOnlyPublicKey },
318 ScriptSpend { leaf_hash: TapLeafHash },
319 }
320
321 #[derive(Default)]
322 struct TrDescriptorData {
323 tap_script: Option<ScriptBuf>,
324 control_block: Option<ControlBlock>,
325 spend_type: Option<SpendType>,
326 key_origins: BTreeMap<XOnlyPublicKey, bip32::KeySource>,
327 }
328
329 let spend_info = tr.spend_info();
330 input.tap_merkle_root = spend_info.merkle_root();
331
332 let data = self
333 .template
334 .iter()
335 .fold(TrDescriptorData::default(), |mut data, item| {
336 match item {
337 Placeholder::TapScript(script) => data.tap_script = Some(script.clone()),
338 Placeholder::TapControlBlock(cb) => data.control_block = Some(cb.clone()),
339 Placeholder::SchnorrSigPk(pk, sig_type, _) => {
340 let raw_pk = pk.to_x_only_pubkey();
341
342 match (&data.spend_type, sig_type) {
343 (None, SchnorrSigType::KeySpend { .. }) => data.spend_type = Some(SpendType::KeySpend { internal_key: raw_pk }),
345 (None, SchnorrSigType::ScriptSpend { leaf_hash }) => data.spend_type = Some(SpendType::ScriptSpend { leaf_hash: *leaf_hash }),
346
347 (Some(SpendType::KeySpend {..}), SchnorrSigType::ScriptSpend { .. }) | (Some(SpendType::ScriptSpend {..}), SchnorrSigType::KeySpend{..}) => unreachable!("Mixed taproot key-spend and script-spend placeholders in the same plan"),
350
351 _ => {},
352 }
353
354 for path in pk.full_derivation_paths() {
355 data.key_origins.insert(raw_pk, (pk.master_fingerprint(), path));
356 }
357 }
358 Placeholder::SchnorrSigPkHash(_, tap_leaf_hash, _) => {
359 data.spend_type = Some(SpendType::ScriptSpend { leaf_hash: *tap_leaf_hash });
360 }
361 _ => {}
362 }
363
364 data
365 });
366
367 let leaf_hash = match data.spend_type {
370 Some(SpendType::KeySpend { internal_key }) => {
371 input.tap_internal_key = Some(internal_key);
372 None
373 }
374 Some(SpendType::ScriptSpend { leaf_hash }) => Some(leaf_hash),
375 _ => None,
376 };
377 for (pk, key_source) in data.key_origins {
378 input
379 .tap_key_origins
380 .entry(pk)
381 .and_modify(|(leaf_hashes, _)| {
382 if let Some(lh) = leaf_hash {
383 if leaf_hashes.iter().all(|&i| i != lh) {
384 leaf_hashes.push(lh);
385 }
386 }
387 })
388 .or_insert_with(|| (vec![], key_source));
389 }
390 if let (Some(tap_script), Some(control_block)) = (data.tap_script, data.control_block) {
391 input
392 .tap_scripts
393 .insert(control_block, (tap_script, LeafVersion::TapScript));
394 }
395 } else {
396 for item in &self.template {
397 if let Placeholder::EcdsaSigPk(pk) = item {
398 let public_key = pk.to_public_key().inner;
399 let master_fingerprint = pk.master_fingerprint();
400 for derivation_path in pk.full_derivation_paths() {
401 input
402 .bip32_derivation
403 .insert(public_key, (master_fingerprint, derivation_path));
404 }
405 }
406 }
407
408 match &self.descriptor {
409 Descriptor::Bare(_) | Descriptor::Pkh(_) | Descriptor::Wpkh(_) => {}
410 Descriptor::Sh(sh) => match sh.as_inner() {
411 descriptor::ShInner::Wsh(wsh) => {
412 input.witness_script = Some(wsh.inner_script());
413 input.redeem_script = Some(wsh.inner_script().to_p2wsh());
414 }
415 descriptor::ShInner::Wpkh(..) => input.redeem_script = Some(sh.inner_script()),
416 descriptor::ShInner::SortedMulti(_) | descriptor::ShInner::Ms(_) => {
417 input.redeem_script = Some(sh.inner_script())
418 }
419 },
420 Descriptor::Wsh(wsh) => input.witness_script = Some(wsh.inner_script()),
421 Descriptor::Tr(_) => unreachable!("Tr is dealt with separately"),
422 }
423 }
424 }
425}
426
427#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
428pub struct CanSign {
432 pub ecdsa: bool,
434 pub taproot: TaprootCanSign,
436}
437
438impl Default for CanSign {
439 fn default() -> Self { CanSign { ecdsa: true, taproot: TaprootCanSign::default() } }
440}
441
442#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
443pub struct TaprootCanSign {
447 pub key_spend: bool,
449 pub script_spend: TaprootAvailableLeaves,
451 pub sighash_default: bool,
453}
454
455impl TaprootCanSign {
456 fn sig_len(&self) -> usize {
457 match self.sighash_default {
458 true => 64,
459 false => 65,
460 }
461 }
462}
463
464impl Default for TaprootCanSign {
465 fn default() -> Self {
466 TaprootCanSign {
467 key_spend: true,
468 script_spend: TaprootAvailableLeaves::Any,
469 sighash_default: true,
470 }
471 }
472}
473
474#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
475pub enum TaprootAvailableLeaves {
477 None,
479 Any,
481 Single(TapLeafHash),
483 Many(Vec<TapLeafHash>),
485}
486
487impl TaprootAvailableLeaves {
488 fn is_available(&self, lh: &TapLeafHash) -> bool {
489 use TaprootAvailableLeaves::*;
490
491 match self {
492 None => false,
493 Any => true,
494 Single(v) => v == lh,
495 Many(list) => list.contains(lh),
496 }
497 }
498}
499
500#[derive(Debug, Default)]
502pub struct Assets {
503 pub keys: BTreeSet<(bip32::KeySource, CanSign)>,
509 pub sha256_preimages: BTreeSet<sha256::Hash>,
511 pub hash256_preimages: BTreeSet<hash256::Hash>,
513 pub ripemd160_preimages: BTreeSet<ripemd160::Hash>,
515 pub hash160_preimages: BTreeSet<hash160::Hash>,
517 pub absolute_timelock: Option<absolute::LockTime>,
519 pub relative_timelock: Option<relative::LockTime>,
521}
522
523fn is_key_direct_child_of(
529 pk: &DefiniteDescriptorKey,
530 derivation_path: &bip32::DerivationPath,
531) -> bool {
532 for pk_derivation_path in pk.full_derivation_paths() {
533 if &pk_derivation_path == derivation_path {
534 return true;
535 }
536
537 let definite_path_len = pk_derivation_path.len();
538 if derivation_path.as_ref() == &pk_derivation_path[..(definite_path_len - 1)] {
539 return true;
540 }
541 }
542
543 false
544}
545
546impl Assets {
547 pub(crate) fn has_ecdsa_key(&self, pk: &DefiniteDescriptorKey) -> bool {
548 self.keys.iter().any(|(keysource, can_sign)| {
549 can_sign.ecdsa
550 && pk.master_fingerprint() == keysource.0
551 && is_key_direct_child_of(pk, &keysource.1)
552 })
553 }
554
555 pub(crate) fn has_taproot_internal_key(&self, pk: &DefiniteDescriptorKey) -> Option<usize> {
556 self.keys.iter().find_map(|(keysource, can_sign)| {
557 if !can_sign.taproot.key_spend
558 || pk.master_fingerprint() != keysource.0
559 || !is_key_direct_child_of(pk, &keysource.1)
560 {
561 None
562 } else {
563 Some(can_sign.taproot.sig_len())
564 }
565 })
566 }
567
568 pub(crate) fn has_taproot_script_key(
569 &self,
570 pk: &DefiniteDescriptorKey,
571 tap_leaf_hash: &TapLeafHash,
572 ) -> Option<usize> {
573 self.keys.iter().find_map(|(keysource, can_sign)| {
574 if !can_sign.taproot.script_spend.is_available(tap_leaf_hash)
575 || pk.master_fingerprint() != keysource.0
576 || !is_key_direct_child_of(pk, &keysource.1)
577 {
578 None
579 } else {
580 Some(can_sign.taproot.sig_len())
581 }
582 })
583 }
584}
585
586impl AssetProvider<DefiniteDescriptorKey> for Assets {
587 fn provider_lookup_ecdsa_sig(&self, pk: &DefiniteDescriptorKey) -> bool {
588 self.has_ecdsa_key(pk)
589 }
590
591 fn provider_lookup_tap_key_spend_sig(&self, pk: &DefiniteDescriptorKey) -> Option<usize> {
592 self.has_taproot_internal_key(pk)
593 }
594
595 fn provider_lookup_tap_leaf_script_sig(
596 &self,
597 pk: &DefiniteDescriptorKey,
598 tap_leaf_hash: &TapLeafHash,
599 ) -> Option<usize> {
600 self.has_taproot_script_key(pk, tap_leaf_hash)
601 }
602
603 fn provider_lookup_sha256(&self, hash: &sha256::Hash) -> bool {
604 self.sha256_preimages.contains(hash)
605 }
606
607 fn provider_lookup_hash256(&self, hash: &hash256::Hash) -> bool {
608 self.hash256_preimages.contains(hash)
609 }
610
611 fn provider_lookup_ripemd160(&self, hash: &ripemd160::Hash) -> bool {
612 self.ripemd160_preimages.contains(hash)
613 }
614
615 fn provider_lookup_hash160(&self, hash: &hash160::Hash) -> bool {
616 self.hash160_preimages.contains(hash)
617 }
618
619 fn check_older(&self, s: relative::LockTime) -> bool {
620 if let Some(timelock) = self.relative_timelock {
621 s.is_implied_by(timelock)
622 } else {
623 false
624 }
625 }
626
627 fn check_after(&self, l: absolute::LockTime) -> bool {
628 if let Some(timelock) = self.absolute_timelock {
629 l.is_implied_by(timelock)
630 } else {
631 false
632 }
633 }
634}
635
636impl FromIterator<DescriptorPublicKey> for Assets {
637 fn from_iter<I: IntoIterator<Item = DescriptorPublicKey>>(iter: I) -> Self {
638 let mut keys = BTreeSet::new();
639 for pk in iter {
640 for deriv_path in pk.full_derivation_paths() {
641 keys.insert(((pk.master_fingerprint(), deriv_path), CanSign::default()));
642 }
643 }
644 Assets { keys, ..Default::default() }
645 }
646}
647
648pub trait IntoAssets {
650 fn into_assets(self) -> Assets;
652}
653
654impl IntoAssets for KeyMap {
655 fn into_assets(self) -> Assets { Assets::from_iter(self.into_iter().map(|(k, _)| k)) }
656}
657
658impl IntoAssets for DescriptorPublicKey {
659 fn into_assets(self) -> Assets { vec![self].into_assets() }
660}
661
662impl IntoAssets for Vec<DescriptorPublicKey> {
663 fn into_assets(self) -> Assets { Assets::from_iter(self) }
664}
665
666impl IntoAssets for sha256::Hash {
667 fn into_assets(self) -> Assets {
668 Assets { sha256_preimages: vec![self].into_iter().collect(), ..Default::default() }
669 }
670}
671
672impl IntoAssets for hash256::Hash {
673 fn into_assets(self) -> Assets {
674 Assets { hash256_preimages: vec![self].into_iter().collect(), ..Default::default() }
675 }
676}
677
678impl IntoAssets for ripemd160::Hash {
679 fn into_assets(self) -> Assets {
680 Assets { ripemd160_preimages: vec![self].into_iter().collect(), ..Default::default() }
681 }
682}
683
684impl IntoAssets for hash160::Hash {
685 fn into_assets(self) -> Assets {
686 Assets { hash160_preimages: vec![self].into_iter().collect(), ..Default::default() }
687 }
688}
689
690impl IntoAssets for Assets {
691 fn into_assets(self) -> Assets { self }
692}
693
694impl Assets {
695 pub fn new() -> Self { Self::default() }
697
698 #[allow(clippy::should_implement_trait)] pub fn add<A: IntoAssets>(mut self, asset: A) -> Self {
701 self.append(asset.into_assets());
702 self
703 }
704
705 pub fn older(mut self, seq: relative::LockTime) -> Self {
707 self.relative_timelock = Some(seq);
708 self
709 }
710
711 pub fn after(mut self, lt: absolute::LockTime) -> Self {
713 self.absolute_timelock = Some(lt);
714 self
715 }
716
717 fn append(&mut self, b: Self) {
718 self.keys.extend(b.keys);
719 self.sha256_preimages.extend(b.sha256_preimages);
720 self.hash256_preimages.extend(b.hash256_preimages);
721 self.ripemd160_preimages.extend(b.ripemd160_preimages);
722 self.hash160_preimages.extend(b.hash160_preimages);
723
724 self.relative_timelock = b.relative_timelock.or(self.relative_timelock);
725 self.absolute_timelock = b.absolute_timelock.or(self.absolute_timelock);
726 }
727}
728
729#[cfg(test)]
730mod test {
731 use std::str::FromStr;
732
733 use bitcoin::bip32::Xpub;
734
735 use super::*;
736 use crate::*;
737
738 #[allow(clippy::type_complexity)]
739 fn test_inner(
740 desc: &str,
741 keys: Vec<DescriptorPublicKey>,
742 hashes: Vec<hash160::Hash>,
743 tests: Vec<(
745 Vec<usize>,
746 Vec<usize>,
747 Option<relative::LockTime>,
748 Option<absolute::LockTime>,
749 Option<usize>,
750 )>,
751 ) {
752 let desc = Descriptor::<DefiniteDescriptorKey>::from_str(desc).unwrap();
753
754 for (key_indexes, hash_indexes, older, after, expected) in tests {
755 let mut assets = Assets::new();
756 if let Some(seq) = older {
757 assets = assets.older(seq);
758 }
759 if let Some(locktime) = after {
760 assets = assets.after(locktime);
761 }
762 for ki in key_indexes {
763 assets = assets.add(keys[ki].clone());
764 }
765 for hi in hash_indexes {
766 assets = assets.add(hashes[hi]);
767 }
768
769 let result = desc.clone().plan(&assets);
770 assert_eq!(
771 result.as_ref().ok().map(|plan| plan.satisfaction_weight()),
772 expected,
773 "{:#?}",
774 result
775 );
776 }
777 }
778
779 #[test]
780 fn test_or() {
781 let keys = vec![
782 DescriptorPublicKey::from_str(
783 "02c2fd50ceae468857bb7eb32ae9cd4083e6c7e42fbbec179d81134b3e3830586c",
784 )
785 .unwrap(),
786 DescriptorPublicKey::from_str(
787 "0257f4a2816338436cccabc43aa724cf6e69e43e84c3c8a305212761389dd73a8a",
788 )
789 .unwrap(),
790 ];
791 let hashes = vec![];
792 let desc = format!("wsh(t:or_c(pk({}),v:pkh({})))", keys[0], keys[1]);
793
794 let tests = vec![
796 (vec![], vec![], None, None, None),
797 (vec![0], vec![], None, None, Some(4 + 1 + 73)),
798 (vec![0, 1], vec![], None, None, Some(4 + 1 + 73)),
799 ];
800
801 test_inner(&desc, keys, hashes, tests);
802 }
803
804 #[test]
805 fn test_and() {
806 let keys = vec![
807 DescriptorPublicKey::from_str(
808 "02c2fd50ceae468857bb7eb32ae9cd4083e6c7e42fbbec179d81134b3e3830586c",
809 )
810 .unwrap(),
811 DescriptorPublicKey::from_str(
812 "0257f4a2816338436cccabc43aa724cf6e69e43e84c3c8a305212761389dd73a8a",
813 )
814 .unwrap(),
815 ];
816 let hashes = vec![];
817 let desc = format!("wsh(and_v(v:pk({}),pk({})))", keys[0], keys[1]);
818
819 let tests = vec![
821 (vec![], vec![], None, None, None),
822 (vec![0], vec![], None, None, None),
823 (vec![0, 1], vec![], None, None, Some(4 + 1 + 73 * 2)),
824 ];
825
826 test_inner(&desc, keys, hashes, tests);
827 }
828
829 #[test]
830 fn test_multi() {
831 let keys = vec![
832 DescriptorPublicKey::from_str(
833 "02c2fd50ceae468857bb7eb32ae9cd4083e6c7e42fbbec179d81134b3e3830586c",
834 )
835 .unwrap(),
836 DescriptorPublicKey::from_str(
837 "0257f4a2816338436cccabc43aa724cf6e69e43e84c3c8a305212761389dd73a8a",
838 )
839 .unwrap(),
840 DescriptorPublicKey::from_str(
841 "03500a2b48b0f66c8183cc0d6645ab21cc19c7fad8a33ff04d41c3ece54b0bc1c5",
842 )
843 .unwrap(),
844 DescriptorPublicKey::from_str(
845 "033ad2d191da4f39512adbaac320cae1f12f298386a4e9d43fd98dec7cf5db2ac9",
846 )
847 .unwrap(),
848 ];
849 let hashes = vec![];
850 let desc = format!("wsh(multi(3,{},{},{},{}))", keys[0], keys[1], keys[2], keys[3]);
851
852 let tests = vec![
853 (vec![], vec![], None, None, None),
854 (vec![0, 1], vec![], None, None, None),
855 (vec![0, 1, 3], vec![], None, None, Some(4 + 1 + 73 * 3 + 1)),
857 ];
858
859 test_inner(&desc, keys, hashes, tests);
860 }
861
862 #[test]
863 fn test_thresh() {
864 use bitcoin::Sequence;
866 let keys = vec![
867 DescriptorPublicKey::from_str(
868 "02c2fd50ceae468857bb7eb32ae9cd4083e6c7e42fbbec179d81134b3e3830586c",
869 )
870 .unwrap(),
871 DescriptorPublicKey::from_str(
872 "0257f4a2816338436cccabc43aa724cf6e69e43e84c3c8a305212761389dd73a8a",
873 )
874 .unwrap(),
875 ];
876 let hashes = vec![];
877 let desc = format!("wsh(thresh(2,pk({}),s:pk({}),snl:older(144)))", keys[0], keys[1]);
878
879 let tests = vec![
880 (vec![], vec![], None, None, None),
881 (
882 vec![],
883 vec![],
884 Some(Sequence(1000).to_relative_lock_time().unwrap()),
885 None,
886 None,
887 ),
888 (vec![0], vec![], None, None, None),
889 (
891 vec![0],
892 vec![],
893 Some(Sequence(1000).to_relative_lock_time().unwrap()),
894 None,
895 Some(80),
896 ),
897 (vec![0, 1], vec![], None, None, Some(153)),
899 (
901 vec![0, 1],
902 vec![],
903 Some(Sequence(1000).to_relative_lock_time().unwrap()),
904 None,
905 Some(80),
906 ),
907 (
909 vec![0, 1],
910 vec![],
911 Some(
912 Sequence::from_512_second_intervals(10)
913 .to_relative_lock_time()
914 .unwrap(),
915 ),
916 None,
917 Some(153),
918 ), ];
920
921 test_inner(&desc, keys.clone(), hashes.clone(), tests);
922
923 let desc = format!("wsh(thresh(2,pk({}),s:pk({}),snl:after(144)))", keys[0], keys[1]);
924
925 let tests = vec![
926 (
928 vec![0],
929 vec![],
930 None,
931 Some(absolute::LockTime::from_height(1000).unwrap()),
932 Some(80),
933 ),
934 (
936 vec![0, 1],
937 vec![],
938 None,
939 Some(absolute::LockTime::from_time(500_001_000).unwrap()),
940 Some(153),
941 ), ];
943
944 test_inner(&desc, keys, hashes, tests);
945 }
946
947 #[test]
948 fn test_taproot() {
949 let keys = vec![
950 DescriptorPublicKey::from_str(
951 "02c2fd50ceae468857bb7eb32ae9cd4083e6c7e42fbbec179d81134b3e3830586c",
952 )
953 .unwrap(),
954 DescriptorPublicKey::from_str(
955 "0257f4a2816338436cccabc43aa724cf6e69e43e84c3c8a305212761389dd73a8a",
956 )
957 .unwrap(),
958 DescriptorPublicKey::from_str(
959 "03500a2b48b0f66c8183cc0d6645ab21cc19c7fad8a33ff04d41c3ece54b0bc1c5",
960 )
961 .unwrap(),
962 DescriptorPublicKey::from_str(
963 "033ad2d191da4f39512adbaac320cae1f12f298386a4e9d43fd98dec7cf5db2ac9",
964 )
965 .unwrap(),
966 DescriptorPublicKey::from_str(
967 "023fc33527afab09fa97135f2180bcd22ce637b1d2fbcb2db748b1f2c33f45b2b4",
968 )
969 .unwrap(),
970 ];
971 let hashes = vec![];
972 let desc = format!(
982 "tr({},{{pk({}),{{multi_a(1,{},{}),and_v(v:pk({}),after(10))}}}})",
983 keys[0], keys[1], keys[2], keys[3], keys[4]
984 );
985
986 let internal_key_sat_weight = Some(70);
988 let first_leaf_sat_weight = Some(169);
992 let second_leaf_sat_weight = Some(238);
1000 let third_leaf_sat_weight = Some(203);
1006
1007 let tests = vec![
1008 (vec![], vec![], None, None, None),
1010 (vec![0], vec![], None, None, internal_key_sat_weight),
1012 (vec![1], vec![], None, None, first_leaf_sat_weight),
1014 (vec![2], vec![], None, None, second_leaf_sat_weight),
1016 (vec![2, 3], vec![], None, None, second_leaf_sat_weight),
1018 (
1020 vec![4],
1021 vec![],
1022 None,
1023 Some(absolute::LockTime::from_height(10).unwrap()),
1024 third_leaf_sat_weight,
1025 ),
1026 (vec![4], vec![], None, Some(absolute::LockTime::from_height(9).unwrap()), None),
1029 (
1032 vec![4],
1033 vec![],
1034 None,
1035 Some(absolute::LockTime::from_time(1296000000).unwrap()),
1036 None,
1037 ),
1038 (vec![4], vec![], None, None, None),
1041 (vec![0, 1, 2, 3, 4], vec![], None, None, internal_key_sat_weight),
1043 (vec![1, 2, 3, 4], vec![], None, None, first_leaf_sat_weight),
1045 (vec![2, 3, 4], vec![], None, None, second_leaf_sat_weight),
1047 (
1049 vec![2, 3, 4],
1050 vec![],
1051 None,
1052 Some(absolute::LockTime::from_consensus(11)),
1053 third_leaf_sat_weight,
1054 ),
1055 ];
1056
1057 test_inner(&desc, keys, hashes, tests);
1058 }
1059
1060 #[test]
1061 fn test_hash() {
1062 let keys = vec![DescriptorPublicKey::from_str(
1063 "02c2fd50ceae468857bb7eb32ae9cd4083e6c7e42fbbec179d81134b3e3830586c",
1064 )
1065 .unwrap()];
1066 let hashes = vec![hash160::Hash::from_slice(&[0; 20]).unwrap()];
1067 let desc = format!("wsh(and_v(v:pk({}),hash160({})))", keys[0], hashes[0]);
1068
1069 let tests = vec![
1070 (vec![], vec![], None, None, None),
1072 (vec![0], vec![], None, None, None),
1074 (vec![], vec![0], None, None, None),
1076 (vec![0], vec![0], None, None, Some(111)),
1079 ];
1080
1081 test_inner(&desc, keys, hashes, tests);
1082 }
1083
1084 #[test]
1085 fn test_plan_update_psbt_tr() {
1086 let root_xpub = Xpub::from_str("xpub661MyMwAqRbcFkPHucMnrGNzDwb6teAX1RbKQmqtEF8kK3Z7LZ59qafCjB9eCRLiTVG3uxBxgKvRgbubRhqSKXnGGb1aoaqLrpMBDrVxga8").unwrap();
1088 let fingerprint = root_xpub.fingerprint();
1089 let xpub = format!("[{}/86'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ", fingerprint);
1090 let desc =
1091 format!("tr({}/0/0,{{pkh({}/0/1),multi_a(2,{}/1/0,{}/1/1)}})", xpub, xpub, xpub, xpub);
1092
1093 let desc = Descriptor::from_str(&desc).unwrap();
1094
1095 let internal_key = DescriptorPublicKey::from_str(&format!("{}/0/0", xpub)).unwrap();
1096 let first_branch = DescriptorPublicKey::from_str(&format!("{}/0/1", xpub)).unwrap();
1097 let second_branch = DescriptorPublicKey::from_str(&format!("{}/1/*", xpub)).unwrap(); let mut psbt_input = bitcoin::psbt::Input::default();
1100 let assets = Assets::new().add(internal_key);
1101 desc.clone()
1102 .plan(&assets)
1103 .unwrap()
1104 .update_psbt_input(&mut psbt_input);
1105 assert!(psbt_input.tap_internal_key.is_some(), "Internal key is missing");
1106 assert!(psbt_input.tap_merkle_root.is_some(), "Merkle root is missing");
1107 assert_eq!(psbt_input.tap_key_origins.len(), 1, "Unexpected number of tap_key_origins");
1108 assert_eq!(psbt_input.tap_scripts.len(), 0, "Unexpected number of tap_scripts");
1109
1110 let mut psbt_input = bitcoin::psbt::Input::default();
1111 let assets = Assets::new().add(first_branch);
1112 desc.clone()
1113 .plan(&assets)
1114 .unwrap()
1115 .update_psbt_input(&mut psbt_input);
1116 assert!(psbt_input.tap_internal_key.is_none(), "Internal key is present");
1117 assert!(psbt_input.tap_merkle_root.is_some(), "Merkle root is missing");
1118 assert_eq!(psbt_input.tap_key_origins.len(), 1, "Unexpected number of tap_key_origins");
1119 assert_eq!(psbt_input.tap_scripts.len(), 1, "Unexpected number of tap_scripts");
1120
1121 let mut psbt_input = bitcoin::psbt::Input::default();
1122 let assets = Assets::new().add(second_branch);
1123 desc.plan(&assets)
1124 .unwrap()
1125 .update_psbt_input(&mut psbt_input);
1126 assert!(psbt_input.tap_internal_key.is_none(), "Internal key is present");
1127 assert!(psbt_input.tap_merkle_root.is_some(), "Merkle root is missing");
1128 assert_eq!(psbt_input.tap_key_origins.len(), 2, "Unexpected number of tap_key_origins");
1129 assert_eq!(psbt_input.tap_scripts.len(), 1, "Unexpected number of tap_scripts");
1130 }
1131
1132 #[test]
1133 fn test_plan_update_psbt_segwit() {
1134 let root_xpub = Xpub::from_str("xpub661MyMwAqRbcFkPHucMnrGNzDwb6teAX1RbKQmqtEF8kK3Z7LZ59qafCjB9eCRLiTVG3uxBxgKvRgbubRhqSKXnGGb1aoaqLrpMBDrVxga8").unwrap();
1136 let fingerprint = root_xpub.fingerprint();
1137 let xpub = format!("[{}/86'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ", fingerprint);
1138 let desc = format!("wsh(multi(2,{}/1/0,{}/1/1))", xpub, xpub);
1139
1140 let desc = Descriptor::from_str(&desc).unwrap();
1141
1142 let asset_key = DescriptorPublicKey::from_str(&format!("{}/1/*", xpub)).unwrap(); let mut psbt_input = bitcoin::psbt::Input::default();
1145 let assets = Assets::new().add(asset_key);
1146 desc.plan(&assets)
1147 .unwrap()
1148 .update_psbt_input(&mut psbt_input);
1149 assert!(psbt_input.witness_script.is_some(), "Witness script missing");
1150 assert!(psbt_input.redeem_script.is_none(), "Redeem script present");
1151 assert_eq!(psbt_input.bip32_derivation.len(), 2, "Unexpected number of bip32_derivation");
1152 }
1153}