1use crate::canonicalize::{ExpandedKey, expand_per_at_n};
12use crate::derive::xpub_from_tlv_bytes;
13use crate::encode::Descriptor;
14use crate::error::Error;
15use crate::origin_path::OriginPath;
16use crate::tag::Tag;
17use crate::tree::{Body, Node};
18use crate::use_site_path::UseSitePath;
19
20use bitcoin::bip32::{ChildNumber, DerivationPath, Fingerprint};
21use miniscript::descriptor::{
22 DerivPaths, DescriptorMultiXKey, DescriptorPublicKey, DescriptorXKey, SinglePub, SinglePubKey,
23 Wildcard,
24};
25use miniscript::miniscript::limits::{MAX_PUBKEYS_IN_CHECKSIGADD, MAX_PUBKEYS_PER_MULTISIG};
26use miniscript::{
27 AbsLockTime, Legacy, Miniscript, RelLockTime, ScriptContext, Segwitv0, Tap, Terminal, Threshold,
28};
29use std::str::FromStr;
30use std::sync::Arc;
31
32use crate::nums::NUMS_H_POINT_X_ONLY_HEX;
33
34pub fn to_miniscript_descriptor(
52 d: &Descriptor,
53 chain: u32,
54) -> Result<miniscript::Descriptor<DescriptorPublicKey>, Error> {
55 let expanded = expand_per_at_n(d)?;
56 let mut keys: Vec<DescriptorPublicKey> = Vec::with_capacity(expanded.len());
57 for e in &expanded {
58 keys.push(build_descriptor_public_key(e, &e.use_site_path, chain)?);
63 }
64 node_to_descriptor(&d.tree, &keys)
65}
66
67pub fn has_hardened_use_site(d: &Descriptor) -> bool {
87 use_site_is_hardened(&d.use_site_path)
88 || d.tlv
89 .use_site_path_overrides
90 .as_deref()
91 .unwrap_or(&[])
92 .iter()
93 .any(|(_, usp)| use_site_is_hardened(usp))
94}
95
96fn use_site_is_hardened(u: &UseSitePath) -> bool {
99 u.wildcard_hardened
100 || u.multipath
101 .as_deref()
102 .unwrap_or(&[])
103 .iter()
104 .any(|a| a.hardened)
105}
106
107type DescriptorOrigin = Option<(Fingerprint, DerivationPath)>;
112
113fn assemble_origin_and_xkey(
117 e: &ExpandedKey,
118) -> Result<(DescriptorOrigin, bitcoin::bip32::Xpub), Error> {
119 let xpub_bytes = e.xpub.ok_or(Error::MissingPubkey { idx: e.idx })?;
120 let xkey = xpub_from_tlv_bytes(e.idx, &xpub_bytes)?;
121 let origin = e.fingerprint.map(|fp| {
122 (
123 Fingerprint::from(fp),
124 origin_path_to_derivation(&e.origin_path),
125 )
126 });
127 Ok((origin, xkey))
128}
129
130fn wildcard_for(use_site: &UseSitePath) -> Wildcard {
134 if use_site.wildcard_hardened {
135 Wildcard::Hardened
136 } else {
137 Wildcard::Unhardened
138 }
139}
140
141fn build_descriptor_public_key(
145 e: &ExpandedKey,
146 use_site: &UseSitePath,
147 chain: u32,
148) -> Result<DescriptorPublicKey, Error> {
149 let (origin, xkey) = assemble_origin_and_xkey(e)?;
150
151 let derivation_path = use_site_to_derivation_path(use_site, chain)?;
154
155 Ok(DescriptorPublicKey::XPub(DescriptorXKey {
156 origin,
157 xkey,
158 derivation_path,
159 wildcard: wildcard_for(use_site),
160 }))
161}
162
163fn build_descriptor_multi_public_key(e: &ExpandedKey) -> Result<DescriptorPublicKey, Error> {
176 let (origin, xkey) = assemble_origin_and_xkey(e)?;
177 let use_site = &e.use_site_path;
178 let wildcard = wildcard_for(use_site);
179
180 match &use_site.multipath {
181 Some(alts) => {
182 let paths: Vec<DerivationPath> = alts
183 .iter()
184 .map(|a| {
185 let child = if a.hardened {
186 ChildNumber::from_hardened_idx(a.value)
187 .unwrap_or(ChildNumber::Hardened { index: a.value })
188 } else {
189 ChildNumber::Normal { index: a.value }
190 };
191 DerivationPath::from(vec![child])
192 })
193 .collect();
194 let derivation_paths = DerivPaths::new(paths)
195 .ok_or_else(|| failed(format!("@{} multipath group is empty", e.idx)))?;
196 Ok(DescriptorPublicKey::MultiXPub(DescriptorMultiXKey {
197 origin,
198 xkey,
199 derivation_paths,
200 wildcard,
201 }))
202 }
203 None => Ok(DescriptorPublicKey::XPub(DescriptorXKey {
204 origin,
205 xkey,
206 derivation_path: DerivationPath::master(),
207 wildcard,
208 })),
209 }
210}
211
212pub fn to_miniscript_descriptor_multipath(
242 d: &Descriptor,
243) -> Result<miniscript::Descriptor<DescriptorPublicKey>, Error> {
244 let expanded = expand_per_at_n(d)?;
245 let mut keys: Vec<DescriptorPublicKey> = Vec::with_capacity(expanded.len());
246 for e in &expanded {
247 keys.push(build_descriptor_multi_public_key(e)?);
248 }
249 node_to_descriptor(&d.tree, &keys)
250}
251
252fn origin_path_to_derivation(p: &OriginPath) -> DerivationPath {
254 let children: Vec<ChildNumber> = p
255 .components
256 .iter()
257 .map(|c| {
258 if c.hardened {
259 ChildNumber::from_hardened_idx(c.value)
260 .unwrap_or(ChildNumber::Hardened { index: c.value })
261 } else {
262 ChildNumber::from_normal_idx(c.value)
263 .unwrap_or(ChildNumber::Normal { index: c.value })
264 }
265 })
266 .collect();
267 DerivationPath::from(children)
268}
269
270fn use_site_to_derivation_path(u: &UseSitePath, chain: u32) -> Result<DerivationPath, Error> {
275 let mut comps: Vec<ChildNumber> = Vec::new();
276 if let Some(alts) = &u.multipath {
277 let alt = alts
278 .get(chain as usize)
279 .ok_or(Error::ChainIndexOutOfRange {
280 chain,
281 alt_count: alts.len(),
282 })?;
283 if alt.hardened {
284 return Err(Error::HardenedPublicDerivation);
285 }
286 comps.push(ChildNumber::Normal { index: alt.value });
287 }
288 Ok(DerivationPath::from(comps))
289}
290
291fn node_to_descriptor(
293 node: &Node,
294 keys: &[DescriptorPublicKey],
295) -> Result<miniscript::Descriptor<DescriptorPublicKey>, Error> {
296 match (&node.tag, &node.body) {
297 (Tag::Pkh, Body::KeyArg { index }) => {
298 let pk = lookup_key(keys, *index)?;
299 miniscript::Descriptor::new_pkh(pk).map_err(|e| failed(e.to_string()))
300 }
301 (Tag::Wpkh, Body::KeyArg { index }) => {
302 let pk = lookup_key(keys, *index)?;
303 miniscript::Descriptor::new_wpkh(pk).map_err(|e| failed(e.to_string()))
304 }
305 (Tag::Sh, Body::Children(children)) if children.len() == 1 => {
306 sh_inner_to_descriptor(&children[0], keys)
307 }
308 (Tag::Wsh, Body::Children(children)) if children.len() == 1 => {
309 wsh_inner_to_descriptor(&children[0], keys)
310 }
311 (
312 Tag::Tr,
313 Body::Tr {
314 is_nums,
315 key_index,
316 tree,
317 },
318 ) => {
319 let internal_key = if *is_nums {
320 build_nums_internal_key()?
321 } else {
322 lookup_key(keys, *key_index)?
323 };
324 let script_tree = if let Some(t) = tree {
325 Some(tree_to_taptree(t, keys)?)
326 } else {
327 None
328 };
329 miniscript::Descriptor::new_tr(internal_key, script_tree)
330 .map_err(|e| failed(e.to_string()))
331 }
332 _ => Err(failed(format!(
333 "unsupported top-level tag {:?} with body shape",
334 node.tag
335 ))),
336 }
337}
338
339fn build_nums_internal_key() -> Result<DescriptorPublicKey, Error> {
342 let x_only = bitcoin::secp256k1::XOnlyPublicKey::from_str(NUMS_H_POINT_X_ONLY_HEX)
343 .map_err(|e| failed(format!("NUMS x-only parse: {e}")))?;
344 Ok(DescriptorPublicKey::Single(SinglePub {
345 origin: None,
346 key: SinglePubKey::XOnly(x_only),
347 }))
348}
349
350fn wsh_inner_to_descriptor(
353 inner: &Node,
354 keys: &[DescriptorPublicKey],
355) -> Result<miniscript::Descriptor<DescriptorPublicKey>, Error> {
356 if let (Tag::SortedMulti, Body::MultiKeys { k, indices }) = (&inner.tag, &inner.body) {
357 let thresh = build_multi_threshold::<{ MAX_PUBKEYS_PER_MULTISIG }>(
358 *k,
359 indices,
360 keys,
361 "wsh-sortedmulti",
362 )?;
363 return miniscript::Descriptor::new_wsh_sortedmulti(thresh)
364 .map_err(|e| failed(e.to_string()));
365 }
366 let ms = node_to_miniscript::<Segwitv0>(inner, keys)?;
367 miniscript::Descriptor::new_wsh(ms).map_err(|e| failed(e.to_string()))
368}
369
370fn sh_inner_to_descriptor(
374 inner: &Node,
375 keys: &[DescriptorPublicKey],
376) -> Result<miniscript::Descriptor<DescriptorPublicKey>, Error> {
377 match (&inner.tag, &inner.body) {
378 (Tag::Wsh, Body::Children(grand)) if grand.len() == 1 => {
379 let grandchild = &grand[0];
380 if let (Tag::SortedMulti, Body::MultiKeys { k, indices }) =
381 (&grandchild.tag, &grandchild.body)
382 {
383 let thresh = build_multi_threshold::<{ MAX_PUBKEYS_PER_MULTISIG }>(
384 *k,
385 indices,
386 keys,
387 "sh-wsh-sortedmulti",
388 )?;
389 return miniscript::Descriptor::new_sh_wsh_sortedmulti(thresh)
390 .map_err(|e| failed(e.to_string()));
391 }
392 let ms = node_to_miniscript::<Segwitv0>(grandchild, keys)?;
393 miniscript::Descriptor::new_sh_wsh(ms).map_err(|e| failed(e.to_string()))
394 }
395 (Tag::Wpkh, Body::KeyArg { index }) => {
396 let pk = lookup_key(keys, *index)?;
397 miniscript::Descriptor::new_sh_wpkh(pk).map_err(|e| failed(e.to_string()))
398 }
399 (Tag::SortedMulti, Body::MultiKeys { k, indices }) => {
400 let thresh = build_multi_threshold::<{ MAX_PUBKEYS_PER_MULTISIG }>(
401 *k,
402 indices,
403 keys,
404 "sh-sortedmulti",
405 )?;
406 miniscript::Descriptor::new_sh_sortedmulti(thresh).map_err(|e| failed(e.to_string()))
407 }
408 _ => {
409 let ms = node_to_miniscript::<Legacy>(inner, keys)?;
410 miniscript::Descriptor::new_sh(ms).map_err(|e| failed(e.to_string()))
411 }
412 }
413}
414
415fn tree_to_taptree(
417 node: &Node,
418 keys: &[DescriptorPublicKey],
419) -> Result<miniscript::descriptor::TapTree<DescriptorPublicKey>, Error> {
420 if let (Tag::TapTree, Body::Children(children)) = (&node.tag, &node.body) {
421 if children.len() != 2 {
422 return Err(failed(format!(
423 "Tag::TapTree expected 2 children, got {}",
424 children.len()
425 )));
426 }
427 let l = tree_to_taptree(&children[0], keys)?;
428 let r = tree_to_taptree(&children[1], keys)?;
429 return miniscript::descriptor::TapTree::combine(l, r)
430 .map_err(|e| failed(format!("TapTree depth: {e}")));
431 }
432 let ms = node_to_miniscript::<Tap>(node, keys)?;
436 Ok(miniscript::descriptor::TapTree::leaf(Arc::new(ms)))
437}
438
439fn node_to_miniscript<Ctx>(
441 node: &Node,
442 keys: &[DescriptorPublicKey],
443) -> Result<Miniscript<DescriptorPublicKey, Ctx>, Error>
444where
445 Ctx: ScriptContext,
446{
447 let term: Terminal<DescriptorPublicKey, Ctx> = match (&node.tag, &node.body) {
448 (Tag::PkK, Body::KeyArg { index }) => {
449 let pk = lookup_key(keys, *index)?;
454 let inner = Miniscript::from_ast(Terminal::PkK(pk)).map_err(into_failed)?;
455 Terminal::Check(Arc::new(inner))
456 }
457 (Tag::PkH, Body::KeyArg { index }) => {
458 let pk = lookup_key(keys, *index)?;
459 let inner = Miniscript::from_ast(Terminal::PkH(pk)).map_err(into_failed)?;
460 Terminal::Check(Arc::new(inner))
461 }
462 (Tag::Check, Body::Children(children)) => {
463 arity_eq(node.tag, children.len(), 1)?;
464 if matches!(
476 (&children[0].tag, &children[0].body),
477 (Tag::PkK | Tag::PkH, Body::KeyArg { .. })
478 ) {
479 return node_to_miniscript::<Ctx>(&children[0], keys);
480 }
481 Terminal::Check(Arc::new(node_to_miniscript::<Ctx>(&children[0], keys)?))
482 }
483 (Tag::Verify, Body::Children(children)) => {
484 arity_eq(node.tag, children.len(), 1)?;
485 Terminal::Verify(Arc::new(node_to_miniscript::<Ctx>(&children[0], keys)?))
486 }
487 (Tag::Swap, Body::Children(children)) => {
488 arity_eq(node.tag, children.len(), 1)?;
489 Terminal::Swap(Arc::new(node_to_miniscript::<Ctx>(&children[0], keys)?))
490 }
491 (Tag::Alt, Body::Children(children)) => {
492 arity_eq(node.tag, children.len(), 1)?;
493 Terminal::Alt(Arc::new(node_to_miniscript::<Ctx>(&children[0], keys)?))
494 }
495 (Tag::DupIf, Body::Children(children)) => {
496 arity_eq(node.tag, children.len(), 1)?;
497 Terminal::DupIf(Arc::new(node_to_miniscript::<Ctx>(&children[0], keys)?))
498 }
499 (Tag::NonZero, Body::Children(children)) => {
500 arity_eq(node.tag, children.len(), 1)?;
501 Terminal::NonZero(Arc::new(node_to_miniscript::<Ctx>(&children[0], keys)?))
502 }
503 (Tag::ZeroNotEqual, Body::Children(children)) => {
504 arity_eq(node.tag, children.len(), 1)?;
505 Terminal::ZeroNotEqual(Arc::new(node_to_miniscript::<Ctx>(&children[0], keys)?))
506 }
507 (Tag::AndV, Body::Children(children)) => {
508 arity_eq(node.tag, children.len(), 2)?;
509 let l = node_to_miniscript::<Ctx>(&children[0], keys)?;
510 let r = node_to_miniscript::<Ctx>(&children[1], keys)?;
511 Terminal::AndV(Arc::new(l), Arc::new(r))
512 }
513 (Tag::AndB, Body::Children(children)) => {
514 arity_eq(node.tag, children.len(), 2)?;
515 let l = node_to_miniscript::<Ctx>(&children[0], keys)?;
516 let r = node_to_miniscript::<Ctx>(&children[1], keys)?;
517 Terminal::AndB(Arc::new(l), Arc::new(r))
518 }
519 (Tag::AndOr, Body::Children(children)) => {
520 arity_eq(node.tag, children.len(), 3)?;
521 let a = node_to_miniscript::<Ctx>(&children[0], keys)?;
522 let b = node_to_miniscript::<Ctx>(&children[1], keys)?;
523 let c = node_to_miniscript::<Ctx>(&children[2], keys)?;
524 Terminal::AndOr(Arc::new(a), Arc::new(b), Arc::new(c))
525 }
526 (Tag::OrB, Body::Children(children)) => {
527 arity_eq(node.tag, children.len(), 2)?;
528 let l = node_to_miniscript::<Ctx>(&children[0], keys)?;
529 let r = node_to_miniscript::<Ctx>(&children[1], keys)?;
530 Terminal::OrB(Arc::new(l), Arc::new(r))
531 }
532 (Tag::OrC, Body::Children(children)) => {
533 arity_eq(node.tag, children.len(), 2)?;
534 let l = node_to_miniscript::<Ctx>(&children[0], keys)?;
535 let r = node_to_miniscript::<Ctx>(&children[1], keys)?;
536 Terminal::OrC(Arc::new(l), Arc::new(r))
537 }
538 (Tag::OrD, Body::Children(children)) => {
539 arity_eq(node.tag, children.len(), 2)?;
540 let l = node_to_miniscript::<Ctx>(&children[0], keys)?;
541 let r = node_to_miniscript::<Ctx>(&children[1], keys)?;
542 Terminal::OrD(Arc::new(l), Arc::new(r))
543 }
544 (Tag::OrI, Body::Children(children)) => {
545 arity_eq(node.tag, children.len(), 2)?;
546 let l = node_to_miniscript::<Ctx>(&children[0], keys)?;
547 let r = node_to_miniscript::<Ctx>(&children[1], keys)?;
548 Terminal::OrI(Arc::new(l), Arc::new(r))
549 }
550 (Tag::Thresh, Body::Variable { k, children }) => {
551 let mut subs: Vec<Arc<Miniscript<DescriptorPublicKey, Ctx>>> =
552 Vec::with_capacity(children.len());
553 for c in children {
554 subs.push(Arc::new(node_to_miniscript::<Ctx>(c, keys)?));
555 }
556 let thresh =
557 Threshold::<_, 0>::new(*k as usize, subs).map_err(|e| failed(e.to_string()))?;
558 Terminal::Thresh(thresh)
559 }
560 (Tag::Multi, Body::MultiKeys { k, indices }) => {
561 let thresh =
566 build_multi_threshold::<{ MAX_PUBKEYS_PER_MULTISIG }>(*k, indices, keys, "multi")?;
567 Terminal::Multi(thresh)
568 }
569 (Tag::MultiA, Body::MultiKeys { k, indices }) => {
570 let thresh = build_multi_threshold::<{ MAX_PUBKEYS_IN_CHECKSIGADD }>(
571 *k, indices, keys, "multi_a",
572 )?;
573 Terminal::MultiA(thresh)
574 }
575 (Tag::SortedMulti, Body::MultiKeys { .. }) => {
576 return Err(failed(
577 "Tag::SortedMulti must be the sole child of wsh/sh; cannot appear as a miniscript leaf"
578 .to_string(),
579 ));
580 }
581 (Tag::SortedMultiA, Body::MultiKeys { .. }) => {
582 return Err(failed(
583 "Tag::SortedMultiA must be a tap-leaf root child; rust-miniscript v13 has no Terminal::SortedMultiA fragment"
584 .to_string(),
585 ));
586 }
587 (Tag::After, Body::Timelock(v)) => {
588 let lt = AbsLockTime::from_consensus(*v).map_err(|e| failed(e.to_string()))?;
589 Terminal::After(lt)
590 }
591 (Tag::Older, Body::Timelock(v)) => {
592 let lt = RelLockTime::from_consensus(*v).map_err(|e| failed(e.to_string()))?;
593 Terminal::Older(lt)
594 }
595 (Tag::Sha256, Body::Hash256Body(h)) => {
596 let hash = sha256_from_bytes(h)?;
597 Terminal::Sha256(hash)
598 }
599 (Tag::Hash256, Body::Hash256Body(h)) => {
600 let hash = hash256_from_bytes(h)?;
601 Terminal::Hash256(hash)
602 }
603 (Tag::Ripemd160, Body::Hash160Body(h)) => {
604 let hash = ripemd160_from_bytes(h)?;
605 Terminal::Ripemd160(hash)
606 }
607 (Tag::Hash160, Body::Hash160Body(h)) => {
608 let hash = hash160_from_bytes(h)?;
609 Terminal::Hash160(hash)
610 }
611 (Tag::RawPkH, Body::Hash160Body(_)) => {
612 return Err(failed(
613 "Tag::RawPkH is not constructible through miniscript's public API".to_string(),
614 ));
615 }
616 (Tag::False, Body::Empty) => Terminal::False,
617 (Tag::True, Body::Empty) => Terminal::True,
618 (Tag::TapTree, _) => {
619 return Err(failed(
620 "Tag::TapTree is a tap-tree internal node, not a miniscript leaf".to_string(),
621 ));
622 }
623 (Tag::Tr, _) | (Tag::Wsh, _) | (Tag::Sh, _) | (Tag::Wpkh, _) | (Tag::Pkh, _) => {
624 return Err(failed(format!(
625 "top-level wrapper {:?} cannot appear inside a miniscript context",
626 node.tag
627 )));
628 }
629 _ => {
630 return Err(failed(format!(
631 "tag {:?} unsupported with body shape",
632 node.tag
633 )));
634 }
635 };
636 Miniscript::from_ast(term).map_err(into_failed)
637}
638
639fn lookup_key(keys: &[DescriptorPublicKey], idx: u8) -> Result<DescriptorPublicKey, Error> {
640 keys.get(idx as usize)
641 .cloned()
642 .ok_or_else(|| failed(format!("@{idx} out of range")))
643}
644
645fn build_multi_threshold<const MAX: usize>(
646 k: u8,
647 indices: &[u8],
648 keys: &[DescriptorPublicKey],
649 label: &str,
650) -> Result<Threshold<DescriptorPublicKey, MAX>, Error> {
651 let pks: Vec<DescriptorPublicKey> = indices
652 .iter()
653 .map(|i| lookup_key(keys, *i))
654 .collect::<Result<_, _>>()?;
655 Threshold::<DescriptorPublicKey, MAX>::new(k as usize, pks)
656 .map_err(|e| failed(format!("{label} threshold: {e}")))
657}
658
659fn arity_eq(tag: Tag, got: usize, expected: usize) -> Result<(), Error> {
660 if got != expected {
661 return Err(failed(format!(
662 "{tag:?} expected {expected} children, got {got}"
663 )));
664 }
665 Ok(())
666}
667
668fn failed(detail: String) -> Error {
669 Error::AddressDerivationFailed { detail }
670}
671
672fn into_failed(e: miniscript::Error) -> Error {
673 failed(e.to_string())
674}
675
676fn sha256_from_bytes(h: &[u8; 32]) -> Result<bitcoin::hashes::sha256::Hash, Error> {
679 use bitcoin::hashes::Hash;
680 Ok(bitcoin::hashes::sha256::Hash::from_byte_array(*h))
681}
682
683fn hash256_from_bytes(h: &[u8; 32]) -> Result<miniscript::hash256::Hash, Error> {
684 use bitcoin::hashes::Hash;
685 Ok(miniscript::hash256::Hash::from_byte_array(*h))
686}
687
688fn ripemd160_from_bytes(h: &[u8; 20]) -> Result<bitcoin::hashes::ripemd160::Hash, Error> {
689 use bitcoin::hashes::Hash;
690 Ok(bitcoin::hashes::ripemd160::Hash::from_byte_array(*h))
691}
692
693fn hash160_from_bytes(h: &[u8; 20]) -> Result<bitcoin::hashes::hash160::Hash, Error> {
694 use bitcoin::hashes::Hash;
695 Ok(bitcoin::hashes::hash160::Hash::from_byte_array(*h))
696}