pub mod address;
pub mod decode;
pub mod derive;
pub mod derive_support;
pub mod encode;
pub mod gui_schema;
pub mod inspect;
pub mod repair;
pub mod vectors;
pub mod verify;
use std::str::FromStr;
use bitcoin::bip32::{DerivationPath, Fingerprint, Xpub};
use crate::error::{CliError, Result};
pub fn parse_stub_hex(s: &str) -> Result<[u8; 4]> {
let bytes = hex::decode(s)
.map_err(|e| CliError::UsageError(format!("--policy-id-stub: invalid hex {s:?}: {e}")))?;
if bytes.len() != 4 {
return Err(CliError::UsageError(format!(
"--policy-id-stub: expected 4 bytes (8 hex chars), got {} bytes ({s:?})",
bytes.len()
)));
}
Ok([bytes[0], bytes[1], bytes[2], bytes[3]])
}
pub fn parse_fingerprint(s: &str) -> Result<Fingerprint> {
let bytes = hex::decode(s).map_err(|e| {
CliError::UsageError(format!("--origin-fingerprint: invalid hex {s:?}: {e}"))
})?;
if bytes.len() != 4 {
return Err(CliError::UsageError(format!(
"--origin-fingerprint: expected 4 bytes (8 hex chars), got {} bytes",
bytes.len()
)));
}
Ok(Fingerprint::from([bytes[0], bytes[1], bytes[2], bytes[3]]))
}
pub fn parse_derivation_path(s: &str) -> Result<DerivationPath> {
DerivationPath::from_str(s).map_err(|e| {
CliError::UsageError(format!("--origin-path: invalid derivation path {s:?}: {e}"))
})
}
pub fn derive_stub_from_md1(md1_str: &str) -> Result<[u8; 4]> {
let descriptor = md_codec::decode_md1_string(md1_str)?;
let id_bytes = if descriptor.is_wallet_policy() {
*md_codec::compute_wallet_policy_id(&descriptor)?.as_bytes()
} else {
*md_codec::compute_wallet_descriptor_template_id(&descriptor)?.as_bytes()
};
let mut stub = [0u8; 4];
stub.copy_from_slice(&id_bytes[..4]);
Ok(stub)
}
pub fn fmt_stub(stub: &[u8; 4]) -> String {
hex::encode(stub)
}
pub fn fmt_fingerprint(fp: &Fingerprint) -> String {
hex::encode(fp.to_bytes())
}
pub fn read_mk1_strings(args: &[String]) -> Result<Vec<String>> {
let mut out = Vec::with_capacity(args.len());
let mut consumed_stdin = false;
for a in args {
if a == "-" && !consumed_stdin {
consumed_stdin = true;
let mut buf = String::new();
std::io::Read::read_to_string(&mut std::io::stdin(), &mut buf)?;
for line in buf.lines() {
let s = crate::format::strip_display_separators(line);
if !s.is_empty() {
out.push(s);
}
}
} else if a == "-" {
} else {
out.push(crate::format::strip_display_separators(a));
}
}
if out.is_empty() {
return Err(CliError::UsageError(
"expected at least one mk1 string (positional or via stdin with '-')".into(),
));
}
Ok(out)
}
pub fn classify_code_variant(s: &str) -> &'static str {
if s.len() <= 96 + "mk1".len() {
"regular"
} else {
"long"
}
}
pub fn parse_xpub_normalized(s: &str, origin_path: Option<&DerivationPath>) -> Result<Xpub> {
let (xpub, variant) = crate::slip132::detect_and_normalize(s)?;
if let Some(v) = variant {
eprintln!(
"note: --xpub was a SLIP-0132 {}; normalized to canonical {} — script type is conveyed by the origin path, not the key prefix",
v.label(),
v.canonical_label()
);
if let Some(path) = origin_path {
if !v.path_matches(path) {
return Err(CliError::UsageError(v.mismatch_help(path)));
}
}
}
Ok(xpub)
}