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