1use crate::alloc::string::ToString;
18use crate::collections::BTreeMap;
19use alloc::string::String;
20use alloc::vec::Vec;
21
22use bitcoin::bip32::{ChildNumber, DerivationPath, Fingerprint, KeySource, Xpub};
23use bitcoin::{key::XOnlyPublicKey, secp256k1, PublicKey};
24use bitcoin::{psbt, taproot};
25use bitcoin::{Network, TxOut};
26
27use miniscript::descriptor::{
28 DefiniteDescriptorKey, DescriptorMultiXKey, DescriptorSecretKey, DescriptorType,
29 DescriptorXKey, InnerXKey, KeyMap, SinglePubKey, Wildcard,
30};
31pub use miniscript::{
32 Descriptor, DescriptorPublicKey, Legacy, Miniscript, ScriptContext, Segwitv0,
33};
34use miniscript::{ForEachKey, MiniscriptKey, TranslatePk};
35
36use crate::descriptor::policy::BuildSatisfaction;
37
38pub mod checksum;
39#[doc(hidden)]
40pub mod dsl;
41pub mod error;
42pub mod policy;
43pub mod template;
44
45pub use self::checksum::calc_checksum;
46pub use self::error::Error as DescriptorError;
47pub use self::policy::Policy;
48use self::template::DescriptorTemplateOut;
49use crate::keys::{IntoDescriptorKey, KeyError};
50use crate::wallet::signer::SignersContainer;
51use crate::wallet::utils::SecpCtx;
52
53pub type ExtendedDescriptor = Descriptor<DescriptorPublicKey>;
55
56pub type DerivedDescriptor = Descriptor<DefiniteDescriptorKey>;
58
59pub type HdKeyPaths = BTreeMap<secp256k1::PublicKey, KeySource>;
65
66pub type TapKeyOrigins = BTreeMap<XOnlyPublicKey, (Vec<taproot::TapLeafHash>, KeySource)>;
72
73pub trait IntoWalletDescriptor {
76 fn into_wallet_descriptor(
78 self,
79 secp: &SecpCtx,
80 network: Network,
81 ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError>;
82}
83
84impl IntoWalletDescriptor for &str {
85 fn into_wallet_descriptor(
86 self,
87 secp: &SecpCtx,
88 network: Network,
89 ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
90 let descriptor = match self.split_once('#') {
91 Some((desc, original_checksum)) => {
92 let checksum = calc_checksum(desc)?;
93 if original_checksum != checksum {
94 return Err(DescriptorError::InvalidDescriptorChecksum);
95 }
96 desc
97 }
98 None => self,
99 };
100
101 ExtendedDescriptor::parse_descriptor(secp, descriptor)?
102 .into_wallet_descriptor(secp, network)
103 }
104}
105
106impl IntoWalletDescriptor for &String {
107 fn into_wallet_descriptor(
108 self,
109 secp: &SecpCtx,
110 network: Network,
111 ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
112 self.as_str().into_wallet_descriptor(secp, network)
113 }
114}
115
116impl IntoWalletDescriptor for String {
117 fn into_wallet_descriptor(
118 self,
119 secp: &SecpCtx,
120 network: Network,
121 ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
122 self.as_str().into_wallet_descriptor(secp, network)
123 }
124}
125
126impl IntoWalletDescriptor for ExtendedDescriptor {
127 fn into_wallet_descriptor(
128 self,
129 secp: &SecpCtx,
130 network: Network,
131 ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
132 (self, KeyMap::default()).into_wallet_descriptor(secp, network)
133 }
134}
135
136impl IntoWalletDescriptor for (ExtendedDescriptor, KeyMap) {
137 fn into_wallet_descriptor(
138 self,
139 secp: &SecpCtx,
140 network: Network,
141 ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
142 use crate::keys::DescriptorKey;
143
144 struct Translator<'s, 'd> {
145 secp: &'s SecpCtx,
146 descriptor: &'d ExtendedDescriptor,
147 network: Network,
148 }
149
150 impl miniscript::Translator<DescriptorPublicKey, String, DescriptorError> for Translator<'_, '_> {
151 fn pk(&mut self, pk: &DescriptorPublicKey) -> Result<String, DescriptorError> {
152 let secp = &self.secp;
153
154 let (_, _, networks) = if self.descriptor.is_taproot() {
155 let descriptor_key: DescriptorKey<miniscript::Tap> =
156 pk.clone().into_descriptor_key()?;
157 descriptor_key.extract(secp)?
158 } else if self.descriptor.is_witness() {
159 let descriptor_key: DescriptorKey<miniscript::Segwitv0> =
160 pk.clone().into_descriptor_key()?;
161 descriptor_key.extract(secp)?
162 } else {
163 let descriptor_key: DescriptorKey<miniscript::Legacy> =
164 pk.clone().into_descriptor_key()?;
165 descriptor_key.extract(secp)?
166 };
167
168 if networks.contains(&self.network) {
169 Ok(Default::default())
170 } else {
171 Err(DescriptorError::Key(KeyError::InvalidNetwork))
172 }
173 }
174 fn sha256(
175 &mut self,
176 _sha256: &<DescriptorPublicKey as MiniscriptKey>::Sha256,
177 ) -> Result<String, DescriptorError> {
178 Ok(Default::default())
179 }
180 fn hash256(
181 &mut self,
182 _hash256: &<DescriptorPublicKey as MiniscriptKey>::Hash256,
183 ) -> Result<String, DescriptorError> {
184 Ok(Default::default())
185 }
186 fn ripemd160(
187 &mut self,
188 _ripemd160: &<DescriptorPublicKey as MiniscriptKey>::Ripemd160,
189 ) -> Result<String, DescriptorError> {
190 Ok(Default::default())
191 }
192 fn hash160(
193 &mut self,
194 _hash160: &<DescriptorPublicKey as MiniscriptKey>::Hash160,
195 ) -> Result<String, DescriptorError> {
196 Ok(Default::default())
197 }
198 }
199
200 use miniscript::TranslateErr;
202 match self.0.translate_pk(&mut Translator {
203 secp,
204 network,
205 descriptor: &self.0,
206 }) {
207 Ok(_) => {}
208 Err(TranslateErr::TranslatorErr(e)) => return Err(e),
209 Err(TranslateErr::OuterError(e)) => return Err(e.into()),
210 }
211
212 Ok(self)
213 }
214}
215
216impl IntoWalletDescriptor for DescriptorTemplateOut {
217 fn into_wallet_descriptor(
218 self,
219 _secp: &SecpCtx,
220 network: Network,
221 ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
222 struct Translator {
223 network: Network,
224 }
225
226 impl miniscript::Translator<DescriptorPublicKey, DescriptorPublicKey, DescriptorError>
227 for Translator
228 {
229 fn pk(
230 &mut self,
231 pk: &DescriptorPublicKey,
232 ) -> Result<DescriptorPublicKey, DescriptorError> {
233 let pk = match pk {
239 DescriptorPublicKey::XPub(ref xpub) => {
240 let mut xpub = xpub.clone();
241 xpub.xkey.network = self.network.into();
242
243 DescriptorPublicKey::XPub(xpub)
244 }
245 other => other.clone(),
246 };
247
248 Ok(pk)
249 }
250 miniscript::translate_hash_clone!(
251 DescriptorPublicKey,
252 DescriptorPublicKey,
253 DescriptorError
254 );
255 }
256
257 let (desc, keymap, networks) = self;
258
259 if !networks.contains(&network) {
260 return Err(DescriptorError::Key(KeyError::InvalidNetwork));
261 }
262
263 use miniscript::TranslateErr;
265 let translated = match desc.translate_pk(&mut Translator { network }) {
266 Ok(descriptor) => descriptor,
267 Err(TranslateErr::TranslatorErr(e)) => return Err(e),
268 Err(TranslateErr::OuterError(e)) => return Err(e.into()),
269 };
270 let fixed_keymap = keymap
272 .into_iter()
273 .map(|(mut k, mut v)| {
274 match (&mut k, &mut v) {
275 (DescriptorPublicKey::XPub(xpub), DescriptorSecretKey::XPrv(xprv)) => {
276 xpub.xkey.network = network.into();
277 xprv.xkey.network = network.into();
278 }
279 (_, DescriptorSecretKey::Single(key)) => {
280 key.key.network = network.into();
281 }
282 _ => {}
283 }
284
285 (k, v)
286 })
287 .collect();
288
289 Ok((translated, fixed_keymap))
290 }
291}
292
293pub(crate) fn check_wallet_descriptor(
295 descriptor: &Descriptor<DescriptorPublicKey>,
296) -> Result<(), DescriptorError> {
297 let descriptor_contains_hardened_steps = descriptor.for_any_key(|k| {
299 if let DescriptorPublicKey::XPub(DescriptorXKey {
300 derivation_path,
301 wildcard,
302 ..
303 }) = k
304 {
305 return *wildcard == Wildcard::Hardened
306 || derivation_path.into_iter().any(ChildNumber::is_hardened);
307 }
308
309 false
310 });
311 if descriptor_contains_hardened_steps {
312 return Err(DescriptorError::HardenedDerivationXpub);
313 }
314
315 if descriptor.is_multipath() {
316 return Err(DescriptorError::Miniscript(
317 miniscript::Error::BadDescriptor(
318 "`check_wallet_descriptor` must not contain multipath keys".to_string(),
319 ),
320 ));
321 }
322
323 descriptor.sanity_check()?;
326
327 Ok(())
328}
329
330#[doc(hidden)]
331pub trait CheckMiniscript<Ctx: miniscript::ScriptContext> {
333 fn check_miniscript(&self) -> Result<(), miniscript::Error>;
334}
335
336impl<Ctx: miniscript::ScriptContext, Pk: miniscript::MiniscriptKey> CheckMiniscript<Ctx>
337 for miniscript::Miniscript<Pk, Ctx>
338{
339 fn check_miniscript(&self) -> Result<(), miniscript::Error> {
340 Ctx::check_global_validity(self)?;
341
342 Ok(())
343 }
344}
345
346pub trait ExtractPolicy {
348 fn extract_policy(
350 &self,
351 signers: &SignersContainer,
352 psbt: BuildSatisfaction,
353 secp: &SecpCtx,
354 ) -> Result<Option<Policy>, DescriptorError>;
355}
356
357pub(crate) trait XKeyUtils {
358 fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint;
359}
360
361impl<T> XKeyUtils for DescriptorMultiXKey<T>
362where
363 T: InnerXKey,
364{
365 fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint {
366 match self.origin {
367 Some((fingerprint, _)) => fingerprint,
368 None => self.xkey.xkey_fingerprint(secp),
369 }
370 }
371}
372
373impl<T> XKeyUtils for DescriptorXKey<T>
374where
375 T: InnerXKey,
376{
377 fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint {
378 match self.origin {
379 Some((fingerprint, _)) => fingerprint,
380 None => self.xkey.xkey_fingerprint(secp),
381 }
382 }
383}
384
385pub(crate) trait DescriptorMeta {
386 fn is_witness(&self) -> bool;
387 fn is_taproot(&self) -> bool;
388 fn get_extended_keys(&self) -> Vec<DescriptorXKey<Xpub>>;
389 fn derive_from_hd_keypaths(
390 &self,
391 hd_keypaths: &HdKeyPaths,
392 secp: &SecpCtx,
393 ) -> Option<DerivedDescriptor>;
394 fn derive_from_tap_key_origins(
395 &self,
396 tap_key_origins: &TapKeyOrigins,
397 secp: &SecpCtx,
398 ) -> Option<DerivedDescriptor>;
399 fn derive_from_psbt_key_origins(
400 &self,
401 key_origins: BTreeMap<Fingerprint, (&DerivationPath, SinglePubKey)>,
402 secp: &SecpCtx,
403 ) -> Option<DerivedDescriptor>;
404 fn derive_from_psbt_input(
405 &self,
406 psbt_input: &psbt::Input,
407 utxo: Option<TxOut>,
408 secp: &SecpCtx,
409 ) -> Option<DerivedDescriptor>;
410}
411
412impl DescriptorMeta for ExtendedDescriptor {
413 fn is_witness(&self) -> bool {
414 matches!(
415 self.desc_type(),
416 DescriptorType::Wpkh
417 | DescriptorType::ShWpkh
418 | DescriptorType::Wsh
419 | DescriptorType::ShWsh
420 | DescriptorType::ShWshSortedMulti
421 | DescriptorType::WshSortedMulti
422 )
423 }
424
425 fn is_taproot(&self) -> bool {
426 self.desc_type() == DescriptorType::Tr
427 }
428
429 fn get_extended_keys(&self) -> Vec<DescriptorXKey<Xpub>> {
430 let mut answer = Vec::new();
431
432 self.for_each_key(|pk| {
433 if let DescriptorPublicKey::XPub(xpub) = pk {
434 answer.push(xpub.clone());
435 }
436
437 true
438 });
439
440 answer
441 }
442
443 fn derive_from_psbt_key_origins(
444 &self,
445 key_origins: BTreeMap<Fingerprint, (&DerivationPath, SinglePubKey)>,
446 secp: &SecpCtx,
447 ) -> Option<DerivedDescriptor> {
448 let verify_key =
450 |xpub: &DescriptorXKey<Xpub>, path: &DerivationPath, expected: &SinglePubKey| {
451 let derived = xpub
452 .xkey
453 .derive_pub(secp, path)
454 .expect("The path should never contain hardened derivation steps")
455 .public_key;
456
457 match expected {
458 SinglePubKey::FullKey(pk) if &PublicKey::new(derived) == pk => true,
459 SinglePubKey::XOnly(pk) if &XOnlyPublicKey::from(derived) == pk => true,
460 _ => false,
461 }
462 };
463
464 let mut path_found = None;
465
466 self.for_any_key(|key| {
468 if let DescriptorPublicKey::XPub(xpub) = key {
469 let root_fingerprint = xpub.root_fingerprint(secp);
475 let derive_path = key_origins
476 .get_key_value(&root_fingerprint)
477 .and_then(|(fingerprint, (path, expected))| {
478 xpub.matches(&(*fingerprint, (*path).clone()), secp)
479 .zip(Some((path, expected)))
480 })
481 .and_then(|(prefix, (full_path, expected))| {
482 let derive_path = full_path
483 .into_iter()
484 .skip(prefix.into_iter().count())
485 .cloned()
486 .collect::<DerivationPath>();
487
488 if verify_key(
494 xpub,
495 &xpub.derivation_path.extend(derive_path.clone()),
496 expected,
497 ) {
498 Some(derive_path)
499 } else {
500 None
501 }
502 });
503
504 match derive_path {
505 Some(path) if xpub.wildcard != Wildcard::None && path.len() == 1 => {
506 if let ChildNumber::Normal { index } = path[0] {
508 path_found = Some(index);
509 return true;
510 }
511 }
512 Some(path) if xpub.wildcard == Wildcard::None && path.is_empty() => {
513 path_found = Some(0);
514 return true;
515 }
516 _ => {}
517 }
518 }
519
520 false
521 });
522
523 path_found.map(|path| {
524 self.at_derivation_index(path)
525 .expect("We ignore hardened wildcards")
526 })
527 }
528
529 fn derive_from_hd_keypaths(
530 &self,
531 hd_keypaths: &HdKeyPaths,
532 secp: &SecpCtx,
533 ) -> Option<DerivedDescriptor> {
534 let key_origins = hd_keypaths
536 .iter()
537 .map(|(pk, (fingerprint, path))| {
538 (
539 *fingerprint,
540 (path, SinglePubKey::FullKey(PublicKey::new(*pk))),
541 )
542 })
543 .collect();
544 self.derive_from_psbt_key_origins(key_origins, secp)
545 }
546
547 fn derive_from_tap_key_origins(
548 &self,
549 tap_key_origins: &TapKeyOrigins,
550 secp: &SecpCtx,
551 ) -> Option<DerivedDescriptor> {
552 let key_origins = tap_key_origins
554 .iter()
555 .map(|(pk, (_, (fingerprint, path)))| (*fingerprint, (path, SinglePubKey::XOnly(*pk))))
556 .collect();
557 self.derive_from_psbt_key_origins(key_origins, secp)
558 }
559
560 fn derive_from_psbt_input(
561 &self,
562 psbt_input: &psbt::Input,
563 utxo: Option<TxOut>,
564 secp: &SecpCtx,
565 ) -> Option<DerivedDescriptor> {
566 if let Some(derived) = self.derive_from_hd_keypaths(&psbt_input.bip32_derivation, secp) {
567 return Some(derived);
568 }
569 if let Some(derived) = self.derive_from_tap_key_origins(&psbt_input.tap_key_origins, secp) {
570 return Some(derived);
571 }
572 if self.has_wildcard() {
573 return None;
575 }
576
577 let descriptor = self.at_derivation_index(0).expect("0 is not hardened");
578 match descriptor.desc_type() {
579 DescriptorType::Pkh
581 | DescriptorType::Wpkh
582 | DescriptorType::ShWpkh
583 | DescriptorType::Tr
584 if utxo.is_some()
585 && descriptor.script_pubkey() == utxo.as_ref().unwrap().script_pubkey =>
586 {
587 Some(descriptor)
588 }
589 DescriptorType::Bare | DescriptorType::Sh | DescriptorType::ShSortedMulti
590 if psbt_input.redeem_script.is_some()
591 && &descriptor.explicit_script().unwrap()
592 == psbt_input.redeem_script.as_ref().unwrap() =>
593 {
594 Some(descriptor)
595 }
596 DescriptorType::Wsh
597 | DescriptorType::ShWsh
598 | DescriptorType::ShWshSortedMulti
599 | DescriptorType::WshSortedMulti
600 if psbt_input.witness_script.is_some()
601 && &descriptor.explicit_script().unwrap()
602 == psbt_input.witness_script.as_ref().unwrap() =>
603 {
604 Some(descriptor)
605 }
606 _ => None,
607 }
608 }
609}
610
611#[cfg(test)]
612mod test {
613 use alloc::string::ToString;
614 use core::str::FromStr;
615
616 use assert_matches::assert_matches;
617 use bitcoin::hex::FromHex;
618 use bitcoin::secp256k1::Secp256k1;
619 use bitcoin::{bip32, Psbt};
620 use bitcoin::{NetworkKind, ScriptBuf};
621
622 use super::*;
623 use crate::psbt::PsbtUtils;
624
625 #[test]
626 fn test_derive_from_psbt_input_wpkh_wif() {
627 let descriptor = Descriptor::<DescriptorPublicKey>::from_str(
628 "wpkh(02b4632d08485ff1df2db55b9dafd23347d1c47a457072a1e87be26896549a8737)",
629 )
630 .unwrap();
631 let psbt = Psbt::deserialize(
632 &Vec::<u8>::from_hex(
633 "70736274ff010052010000000162307be8e431fbaff807cdf9cdc3fde44d7402\
634 11bc8342c31ffd6ec11fe35bcc0100000000ffffffff01328601000000000016\
635 001493ce48570b55c42c2af816aeaba06cfee1224fae000000000001011fa086\
636 01000000000016001493ce48570b55c42c2af816aeaba06cfee1224fae010304\
637 010000000000",
638 )
639 .unwrap(),
640 )
641 .unwrap();
642
643 assert!(descriptor
644 .derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
645 .is_some());
646 }
647
648 #[test]
649 fn test_derive_from_psbt_input_pkh_tpub() {
650 let descriptor = Descriptor::<DescriptorPublicKey>::from_str(
651 "pkh([0f056943/44h/0h/0h]tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd/10/*)",
652 )
653 .unwrap();
654 let psbt = Psbt::deserialize(
655 &Vec::<u8>::from_hex(
656 "70736274ff010053010000000145843b86be54a3cd8c9e38444e1162676c00df\
657 e7964122a70df491ea12fd67090100000000ffffffff01c19598000000000017\
658 a91432bb94283282f72b2e034709e348c44d5a4db0ef8700000000000100f902\
659 0000000001010167e99c0eb67640f3a1b6805f2d8be8238c947f8aaf49eb0a9c\
660 bee6a42c984200000000171600142b29a22019cca05b9c2b2d283a4c4489e1cf\
661 9f8ffeffffff02a01dced06100000017a914e2abf033cadbd74f0f4c74946201\
662 decd20d5c43c8780969800000000001976a9148b0fce5fb1264e599a65387313\
663 3c95478b902eb288ac02473044022015d9211576163fa5b001e84dfa3d44efd9\
664 86b8f3a0d3d2174369288b2b750906022048dacc0e5d73ae42512fd2b97e2071\
665 a8d0bce443b390b1fe0b8128fe70ec919e01210232dad1c5a67dcb0116d407e2\
666 52584228ab7ec00e8b9779d0c3ffe8114fc1a7d2c80600000103040100000022\
667 0603433b83583f8c4879b329dd08bbc7da935e4cc02f637ff746e05f0466ffb2\
668 a6a2180f0569432c00008000000080000000800a000000000000000000",
669 )
670 .unwrap(),
671 )
672 .unwrap();
673
674 assert!(descriptor
675 .derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
676 .is_some());
677 }
678
679 #[test]
680 fn test_derive_from_psbt_input_wsh() {
681 let descriptor = Descriptor::<DescriptorPublicKey>::from_str(
682 "wsh(and_v(v:pk(03b6633fef2397a0a9de9d7b6f23aef8368a6e362b0581f0f0af70d5ecfd254b14),older(6)))",
683 )
684 .unwrap();
685 let psbt = Psbt::deserialize(
686 &Vec::<u8>::from_hex(
687 "70736274ff01005302000000011c8116eea34408ab6529223c9a176606742207\
688 67a1ff1d46a6e3c4a88243ea6e01000000000600000001109698000000000017\
689 a914ad105f61102e0d01d7af40d06d6a5c3ae2f7fde387000000000001012b80\
690 969800000000002200203ca72f106a72234754890ca7640c43f65d2174e44d33\
691 336030f9059345091044010304010000000105252103b6633fef2397a0a9de9d\
692 7b6f23aef8368a6e362b0581f0f0af70d5ecfd254b14ad56b20000",
693 )
694 .unwrap(),
695 )
696 .unwrap();
697
698 assert!(descriptor
699 .derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
700 .is_some());
701 }
702
703 #[test]
704 fn test_derive_from_psbt_input_sh() {
705 let descriptor = Descriptor::<DescriptorPublicKey>::from_str(
706 "sh(and_v(v:pk(021403881a5587297818fcaf17d239cefca22fce84a45b3b1d23e836c4af671dbb),after(630000)))",
707 )
708 .unwrap();
709 let psbt = Psbt::deserialize(
710 &Vec::<u8>::from_hex(
711 "70736274ff0100530100000001bc8c13df445dfadcc42afa6dc841f85d22b01d\
712 a6270ebf981740f4b7b1d800390000000000feffffff01ba9598000000000017\
713 a91457b148ba4d3e5fa8608a8657875124e3d1c9390887f09c0900000100e002\
714 0000000001016ba1bbe05cc93574a0d611ec7d93ad0ab6685b28d0cd80e8a82d\
715 debb326643c90100000000feffffff02809698000000000017a914d9a6e8c455\
716 8e16c8253afe53ce37ad61cf4c38c487403504cf6100000017a9144044fb6e0b\
717 757dfc1b34886b6a95aef4d3db137e870247304402202a9b72d939bcde8ba2a1\
718 e0980597e47af4f5c152a78499143c3d0a78ac2286a602207a45b1df9e93b8c9\
719 6f09f5c025fe3e413ca4b905fe65ee55d32a3276439a9b8f012102dc1fcc2636\
720 4da1aa718f03d8d9bd6f2ff410ed2cf1245a168aa3bcc995ac18e0a806000001\
721 03040100000001042821021403881a5587297818fcaf17d239cefca22fce84a4\
722 5b3b1d23e836c4af671dbbad03f09c09b10000",
723 )
724 .unwrap(),
725 )
726 .unwrap();
727
728 assert!(descriptor
729 .derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
730 .is_some());
731 }
732
733 #[test]
734 fn test_to_wallet_descriptor_fixup_networks() {
735 use crate::keys::{any_network, IntoDescriptorKey};
736
737 let secp = Secp256k1::new();
738
739 let xprv = bip32::Xpriv::from_str("xprv9s21ZrQH143K3c3gF1DUWpWNr2SG2XrG8oYPpqYh7hoWsJy9NjabErnzriJPpnGHyKz5NgdXmq1KVbqS1r4NXdCoKitWg5e86zqXHa8kxyB").unwrap();
740 let path = bip32::DerivationPath::from_str("m/0").unwrap();
741
742 let key = (xprv, path.clone()).into_descriptor_key().unwrap();
745 let key = key.override_valid_networks(any_network());
747
748 let desc = crate::descriptor!(wpkh(key)).unwrap();
750 let (wallet_desc, keymap) = desc
752 .into_wallet_descriptor(&secp, Network::Testnet)
753 .unwrap();
754
755 let mut xprv_testnet = xprv;
756 xprv_testnet.network = NetworkKind::Test;
757
758 let xpub_testnet = bip32::Xpub::from_priv(&secp, &xprv_testnet);
759 let desc_pubkey = DescriptorPublicKey::XPub(DescriptorXKey {
760 xkey: xpub_testnet,
761 origin: None,
762 derivation_path: path,
763 wildcard: Wildcard::Unhardened,
764 });
765
766 assert_eq!(wallet_desc.to_string(), "wpkh(tpubD6NzVbkrYhZ4XtJzoDja5snUjBNQRP5B3f4Hyn1T1x6PVPxzzVjvw6nJx2D8RBCxog9GEVjZoyStfepTz7TtKoBVdkCtnc7VCJh9dD4RAU9/0/*)#a3svx0ha");
767 assert_eq!(
768 keymap
769 .get(&desc_pubkey)
770 .map(|key| key.to_public(&secp).unwrap()),
771 Some(desc_pubkey)
772 );
773 }
774
775 #[test]
777 fn test_descriptor_from_str_with_checksum() {
778 let secp = Secp256k1::new();
779
780 let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#tqz0nc62"
781 .into_wallet_descriptor(&secp, Network::Testnet);
782 assert!(desc.is_ok());
783
784 let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
785 .into_wallet_descriptor(&secp, Network::Testnet);
786 assert!(desc.is_ok());
787
788 let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)#67ju93jw"
789 .into_wallet_descriptor(&secp, Network::Testnet);
790 assert!(desc.is_ok());
791
792 let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)"
793 .into_wallet_descriptor(&secp, Network::Testnet);
794 assert!(desc.is_ok());
795
796 let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#67ju93jw"
797 .into_wallet_descriptor(&secp, Network::Testnet);
798 assert_matches!(desc, Err(DescriptorError::InvalidDescriptorChecksum));
799
800 let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#67ju93jw"
801 .into_wallet_descriptor(&secp, Network::Testnet);
802 assert_matches!(desc, Err(DescriptorError::InvalidDescriptorChecksum));
803 }
804
805 #[test]
807 fn test_descriptor_from_str_with_keys_network() {
808 let secp = Secp256k1::new();
809
810 let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
811 .into_wallet_descriptor(&secp, Network::Testnet);
812 assert!(desc.is_ok());
813
814 let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
815 .into_wallet_descriptor(&secp, Network::Testnet4);
816 assert!(desc.is_ok());
817
818 let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
819 .into_wallet_descriptor(&secp, Network::Regtest);
820 assert!(desc.is_ok());
821
822 let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)"
823 .into_wallet_descriptor(&secp, Network::Testnet);
824 assert!(desc.is_ok());
825
826 let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)"
827 .into_wallet_descriptor(&secp, Network::Regtest);
828 assert!(desc.is_ok());
829
830 let desc = "sh(wpkh(02864bb4ad00cefa806098a69e192bbda937494e69eb452b87bb3f20f6283baedb))"
831 .into_wallet_descriptor(&secp, Network::Testnet);
832 assert!(desc.is_ok());
833
834 let desc = "sh(wpkh(02864bb4ad00cefa806098a69e192bbda937494e69eb452b87bb3f20f6283baedb))"
835 .into_wallet_descriptor(&secp, Network::Bitcoin);
836 assert!(desc.is_ok());
837
838 let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
839 .into_wallet_descriptor(&secp, Network::Bitcoin);
840 assert_matches!(desc, Err(DescriptorError::Key(KeyError::InvalidNetwork)));
841
842 let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)"
843 .into_wallet_descriptor(&secp, Network::Bitcoin);
844 assert_matches!(desc, Err(DescriptorError::Key(KeyError::InvalidNetwork)));
845 }
846
847 #[test]
849 fn test_descriptor_from_str_from_output_of_macro() {
850 let secp = Secp256k1::new();
851
852 let tpub = bip32::Xpub::from_str("tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK").unwrap();
853 let path = bip32::DerivationPath::from_str("m/1/2").unwrap();
854 let key = (tpub, path).into_descriptor_key().unwrap();
855
856 let desc = crate::descriptor!(wpkh(key)).unwrap();
858
859 let (wallet_desc, _) = desc
860 .into_wallet_descriptor(&secp, Network::Testnet)
861 .unwrap();
862 let wallet_desc_str = wallet_desc.to_string();
863 assert_eq!(wallet_desc_str, "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)#67ju93jw");
864
865 let (wallet_desc2, _) = wallet_desc_str
866 .into_wallet_descriptor(&secp, Network::Testnet)
867 .unwrap();
868 assert_eq!(wallet_desc, wallet_desc2)
869 }
870
871 #[test]
872 fn test_check_wallet_descriptor() {
873 let secp = Secp256k1::new();
874
875 let descriptor = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0'/1/2/*)";
876 let (descriptor, _) = descriptor
877 .into_wallet_descriptor(&secp, Network::Testnet)
878 .expect("must parse");
879 let result = check_wallet_descriptor(&descriptor);
880
881 assert_matches!(result, Err(DescriptorError::HardenedDerivationXpub));
882
883 let descriptor = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/<0;1>/*)";
885 let (descriptor, _) = descriptor
886 .into_wallet_descriptor(&secp, Network::Testnet)
887 .expect("must parse");
888 let result = check_wallet_descriptor(&descriptor);
889
890 assert_matches!(
891 result,
892 Err(DescriptorError::Miniscript(
893 miniscript::Error::BadDescriptor(_)
894 ))
895 );
896
897 let descriptor = "wsh(multi(2,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0/*,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0/*))";
899 let (descriptor, _) = descriptor
900 .into_wallet_descriptor(&secp, Network::Testnet)
901 .expect("must parse");
902 let result = check_wallet_descriptor(&descriptor);
903
904 assert!(result.is_err());
905 }
906
907 #[test]
908 fn test_sh_wsh_sortedmulti_redeemscript() {
909 use miniscript::psbt::PsbtInputExt;
910
911 let secp = Secp256k1::new();
912
913 let descriptor = "sh(wsh(sortedmulti(3,tpubDEsqS36T4DVsKJd9UH8pAKzrkGBYPLEt9jZMwpKtzh1G6mgYehfHt9WCgk7MJG5QGSFWf176KaBNoXbcuFcuadAFKxDpUdMDKGBha7bY3QM/0/*,tpubDF3cpwfs7fMvXXuoQbohXtLjNM6ehwYT287LWtmLsd4r77YLg6MZg4vTETx5MSJ2zkfigbYWu31VA2Z2Vc1cZugCYXgS7FQu6pE8V6TriEH/0/*,tpubDE1SKfcW76Tb2AASv5bQWMuScYNAdoqLHoexw13sNDXwmUhQDBbCD3QAedKGLhxMrWQdMDKENzYtnXPDRvexQPNuDrLj52wAjHhNEm8sJ4p/0/*,tpubDFLc6oXwJmhm3FGGzXkfJNTh2KitoY3WhmmQvuAjMhD8YbyWn5mAqckbxXfm2etM3p5J6JoTpSrMqRSTfMLtNW46poDaEZJ1kjd3csRSjwH/0/*,tpubDEWD9NBeWP59xXmdqSNt4VYdtTGwbpyP8WS962BuqpQeMZmX9Pur14dhXdZT5a7wR1pK6dPtZ9fP5WR493hPzemnBvkfLLYxnUjAKj1JCQV/0/*,tpubDEHyZkkwd7gZWCTgQuYQ9C4myF2hMEmyHsBCCmLssGqoqUxeT3gzohF5uEVURkf9TtmeepJgkSUmteac38FwZqirjApzNX59XSHLcwaTZCH/0/*,tpubDEqLouCekwnMUWN486kxGzD44qVgeyuqHyxUypNEiQt5RnUZNJe386TKPK99fqRV1vRkZjYAjtXGTECz98MCsdLcnkM67U6KdYRzVubeCgZ/0/*)))";
914 let (descriptor, _) = descriptor
915 .into_wallet_descriptor(&secp, Network::Testnet)
916 .unwrap();
917 check_wallet_descriptor(&descriptor).expect("descriptor");
918
919 let descriptor = descriptor.at_derivation_index(0).unwrap();
920
921 let script = ScriptBuf::from_hex("5321022f533b667e2ea3b36e21961c9fe9dca340fbe0af5210173a83ae0337ab20a57621026bb53a98e810bd0ee61a0ed1164ba6c024786d76554e793e202dc6ce9c78c4ea2102d5b8a7d66a41ffdb6f4c53d61994022e886b4f45001fb158b95c9164d45f8ca3210324b75eead2c1f9c60e8adeb5e7009fec7a29afcdb30d829d82d09562fe8bae8521032d34f8932200833487bd294aa219dcbe000b9f9b3d824799541430009f0fa55121037468f8ea99b6c64788398b5ad25480cad08f4b0d65be54ce3a55fd206b5ae4722103f72d3d96663b0ea99b0aeb0d7f273cab11a8de37885f1dddc8d9112adb87169357ae").unwrap();
922
923 let mut psbt_input = psbt::Input::default();
924 psbt_input
925 .update_with_descriptor_unchecked(&descriptor)
926 .unwrap();
927
928 assert_eq!(psbt_input.redeem_script, Some(script.to_p2wsh()));
929 assert_eq!(psbt_input.witness_script, Some(script));
930 }
931
932 #[test]
933 fn test_into_wallet_descriptor_multi() -> anyhow::Result<()> {
934 let secp = Secp256k1::new();
936
937 let descriptor_str = "wpkh([9a6a2580/84'/1'/0']tpubDDnGNapGEY6AZAdQbfRJgMg9fvz8pUBrLwvyvUqEgcUfgzM6zc2eVK4vY9x9L5FJWdX8WumXuLEDV5zDZnTfbn87vLe9XceCFwTu9so9Kks/<0;1>/*)";
939 let (descriptor, _key_map) = descriptor_str
940 .into_wallet_descriptor(&secp, Network::Testnet)
941 .expect("should parse multipath tpub");
942
943 assert!(descriptor.is_multipath());
944
945 let descriptor_str = "wpkh([9a6a2580/84'/0'/0']xpub6DEzNop46vmxR49zYWFnMwmEfawSNmAMf6dLH5YKDY463twtvw1XD7ihwJRLPRGZJz799VPFzXHpZu6WdhT29WnaeuChS6aZHZPFmqczR5K/<0;1>/*)";
947 let res = descriptor_str.into_wallet_descriptor(&secp, Network::Testnet);
948
949 assert!(matches!(
950 res,
951 Err(DescriptorError::Key(KeyError::InvalidNetwork))
952 ));
953
954 let descriptor_str = "wpkh([9a6a2580/84'/0'/0']xpub6DEzNop46vmxR49zYWFnMwmEfawSNmAMf6dLH5YKDY463twtvw1XD7ihwJRLPRGZJz799VPFzXHpZu6WdhT29WnaeuChS6aZHZPFmqczR5K/<0;1>/*)";
956 let (descriptor, _key_map) = descriptor_str
957 .into_wallet_descriptor(&secp, Network::Bitcoin)
958 .expect("should parse multipath xpub");
959
960 assert!(descriptor.is_multipath());
961
962 let descriptor_str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/<0;1>/*)";
965 assert!(matches!(
966 Descriptor::parse_descriptor(&secp, descriptor_str),
967 Err(miniscript::Error::Unexpected(..)),
968 ));
969 let _ = descriptor_str
970 .into_wallet_descriptor(&secp, Network::Testnet)
971 .unwrap_err();
972
973 Ok(())
974 }
975}