pub mod decode;
pub mod encode;
pub mod gui_schema;
pub mod inspect;
pub mod vectors;
pub mod verify;
use std::str::FromStr;
use bitcoin::bip32::{DerivationPath, Fingerprint, Xpub};
use bitcoin::hashes::{Hash, sha256};
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 parse_xpub(s: &str) -> Result<Xpub> {
Xpub::from_str(s).map_err(|e| CliError::UsageError(format!("invalid xpub {s:?}: {e}")))
}
pub fn derive_stub_from_md1(md1_str: &str) -> Result<[u8; 4]> {
let descriptor = md_codec::decode_md1_string(md1_str)?;
let (bytecode_bytes, _bit_len) = md_codec::encode_payload(&descriptor)?;
let hash = sha256::Hash::hash(&bytecode_bytes);
let stub: [u8; 4] = hash.as_byte_array()[..4].try_into().expect("4-byte slice");
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 = line.trim();
if !s.is_empty() {
out.push(s.to_string());
}
}
} else if a == "-" {
} else {
out.push(a.clone());
}
}
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"
}
}