use crate::error::CliError;
use md_codec::encode::Descriptor;
use std::fmt::Write as _;
pub fn descriptor_to_template(d: &Descriptor) -> Result<String, CliError> {
Ok(md_codec::descriptor_to_template(d)?)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parse::template::parse_template;
#[test]
fn roundtrip_wpkh_singlepath() {
let t = "wpkh(@0/*)";
let d = parse_template(t, &[], &[]).unwrap();
assert_eq!(descriptor_to_template(&d).unwrap(), t);
}
#[test]
fn roundtrip_wsh_multi_2of2() {
let t = "wsh(multi(2,@0/<0;1>/*,@1/<0;1>/*))";
let d = parse_template(t, &[], &[]).unwrap();
assert_eq!(descriptor_to_template(&d).unwrap(), t);
}
#[test]
fn roundtrip_sh_wpkh() {
let t = "sh(wpkh(@0/<0;1>/*))";
let d = parse_template(t, &[], &[]).unwrap();
assert_eq!(descriptor_to_template(&d).unwrap(), t);
}
#[test]
fn roundtrip_tr_keyonly() {
let t = "tr(@0/<0;1>/*)";
let d = parse_template(t, &[], &[]).unwrap();
assert_eq!(descriptor_to_template(&d).unwrap(), t);
}
#[test]
fn roundtrip_tr_and_v_verify_older_inheritance() {
let t = "tr(@0/<0;1>/*,and_v(v:pk(@1/<0;1>/*),older(144)))";
let d = parse_template(t, &[], &[]).unwrap();
assert_eq!(descriptor_to_template(&d).unwrap(), t);
}
#[test]
fn roundtrip_tr_or_d_recovery_pattern() {
let t = "tr(@0/<0;1>/*,or_d(pk(@1/<0;1>/*),and_v(v:pk(@2/<0;1>/*),older(144))))";
let d = parse_template(t, &[], &[]).unwrap();
assert_eq!(descriptor_to_template(&d).unwrap(), t);
}
#[test]
fn roundtrip_tr_or_i_disjunction() {
let t = "tr(@0/<0;1>/*,or_i(pk(@1/<0;1>/*),pk(@2/<0;1>/*)))";
let d = parse_template(t, &[], &[]).unwrap();
assert_eq!(descriptor_to_template(&d).unwrap(), t);
}
#[test]
fn roundtrip_tr_and_or_ternary() {
let t = "tr(@0/<0;1>/*,andor(pk(@1/<0;1>/*),pk(@2/<0;1>/*),pk(@3/<0;1>/*)))";
let d = parse_template(t, &[], &[]).unwrap();
assert_eq!(descriptor_to_template(&d).unwrap(), t);
}
#[test]
fn roundtrip_tr_and_v_after_absolute_timelock() {
let t = "tr(@0/<0;1>/*,and_v(v:pk(@1/<0;1>/*),after(700000)))";
let d = parse_template(t, &[], &[]).unwrap();
assert_eq!(descriptor_to_template(&d).unwrap(), t);
}
#[test]
fn roundtrip_tr_and_v_sha256_hash_lock() {
let t = "tr(@0/<0;1>/*,and_v(v:pk(@1/<0;1>/*),sha256(0000000000000000000000000000000000000000000000000000000000000001)))";
let d = parse_template(t, &[], &[]).unwrap();
assert_eq!(descriptor_to_template(&d).unwrap(), t);
}
#[test]
fn roundtrip_tr_and_v_hash160_hash_lock() {
let t = "tr(@0/<0;1>/*,and_v(v:pk(@1/<0;1>/*),hash160(0000000000000000000000000000000000000001)))";
let d = parse_template(t, &[], &[]).unwrap();
assert_eq!(descriptor_to_template(&d).unwrap(), t);
}
#[test]
fn roundtrip_wsh_thresh_with_non_key_fragment_child() {
let t =
"wsh(thresh(2,pk(@0/<0;1>/*),s:pk(@1/<0;1>/*),snj:and_v(v:pk(@2/<0;1>/*),older(144))))";
let d = parse_template(t, &[], &[]).unwrap();
assert_eq!(descriptor_to_template(&d).unwrap(), t);
}
#[test]
fn roundtrip_wsh_and_b_with_swap_wrapper() {
let t = "wsh(and_b(pk(@0/<0;1>/*),s:pk(@1/<0;1>/*)))";
let d = parse_template(t, &[], &[]).unwrap();
assert_eq!(descriptor_to_template(&d).unwrap(), t);
}
#[test]
fn roundtrip_wsh_or_b_with_swap_wrapper() {
let t = "wsh(or_b(pk(@0/<0;1>/*),s:pk(@1/<0;1>/*)))";
let d = parse_template(t, &[], &[]).unwrap();
assert_eq!(descriptor_to_template(&d).unwrap(), t);
}
#[test]
fn roundtrip_tr_t_or_c_desugars_to_and_v_with_true() {
let input = "tr(@0/<0;1>/*,t:or_c(pk(@1/<0;1>/*),v:pk(@2/<0;1>/*)))";
let canonical = "tr(@0/<0;1>/*,and_v(or_c(pk(@1/<0;1>/*),v:pk(@2/<0;1>/*)),1))";
let d = parse_template(input, &[], &[]).unwrap();
assert_eq!(descriptor_to_template(&d).unwrap(), canonical);
}
}
use md_codec::chunk::ChunkHeader;
use md_codec::identity::{Md1EncodingId, WalletDescriptorTemplateId, WalletPolicyId};
pub fn fmt_md1_id(id: &Md1EncodingId) -> String {
let bytes = id.as_bytes();
let mut s = String::with_capacity(64);
for b in bytes {
write!(s, "{b:02x}").unwrap();
}
s
}
pub fn fmt_template_id(id: &WalletDescriptorTemplateId) -> String {
let bytes = id.as_bytes();
let mut s = String::with_capacity(64);
for b in bytes {
write!(s, "{b:02x}").unwrap();
}
s
}
pub fn fmt_policy_id(id: &WalletPolicyId) -> String {
let bytes = id.as_bytes();
let mut s = String::with_capacity(32);
for b in bytes {
write!(s, "{b:02x}").unwrap();
}
s
}
pub fn fmt_policy_id_fingerprint(id: &WalletPolicyId) -> String {
let b = id.as_bytes();
format!("0x{:02x}{:02x}{:02x}{:02x}", b[0], b[1], b[2], b[3])
}
#[allow(dead_code)] pub fn fmt_chunk_header(h: &ChunkHeader) -> String {
format!(
"chunk-set-id=0x{:05x}, count={}, index={}",
h.chunk_set_id, h.count, h.index
)
}
#[cfg(test)]
mod hash_tests {
use super::*;
#[test]
fn policy_id_fingerprint_format() {
let bytes = [
0x9E, 0x1D, 0x72, 0xB6, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
let id = WalletPolicyId::new(bytes);
assert_eq!(fmt_policy_id_fingerprint(&id), "0x9e1d72b6");
}
}