use std::process::Command;
use assert_cmd::cargo::CommandCargoExt;
const V1_XPUB: &str = "xpub6Den8YwXbKQvkwukmx7Uukicw4qDgMEPuuUkhMp3Rn557YSN2uVQnCMQNSfgDtennU9nES3Wbbmz1LAPBydhNpED8NU4mf1SFF41hM7vFrc";
const V1_FP_HEX: &str = "aabbccdd";
const V1_PATH: &str = "m/48'/0'/0'/2'";
const PKH_BASIC_TEMPLATE_MD1: &str = "md1yqpqqxzq2qwfv8urt848e";
const EXPECTED_TEMPLATE_STUB: [u8; 4] = [0x55, 0x9e, 0x64, 0xb2];
const POLICY_STUB_FOR_TEMPLATE: [u8; 4] = [0x3d, 0x19, 0x0a, 0xf3];
const KEYED_WALLET_POLICY_MD1: &str = "md1yqpqqxzqksdatd7au25zzzgfpyysjzgfpyysjzgfpyysjzgfpyysjzgfpyysjzgfpyysjzggp8n0nx0muaewav2ksx99wwsu9swq5mlndjmn3gm9vl9q2mzmup0xqvgudy3j07cxwl";
const EXPECTED_KEYED_POLICY_STUB: [u8; 4] = [0x2a, 0xed, 0x98, 0x0d];
#[test]
fn encode_from_keyless_template_md1_uses_template_id_stub() {
let mut cmd = Command::cargo_bin("mk").expect("mk binary");
let out = cmd
.args([
"encode",
"--xpub",
V1_XPUB,
"--origin-fingerprint",
V1_FP_HEX,
"--origin-path",
V1_PATH,
"--from-md1",
PKH_BASIC_TEMPLATE_MD1,
"--group-size",
"0",
])
.output()
.expect("invoke mk encode");
assert!(out.status.success(), "mk encode failed: {out:?}");
let stdout = String::from_utf8(out.stdout).unwrap();
let strings: Vec<String> = stdout.lines().map(str::to_string).collect();
assert!(!strings.is_empty(), "no mk1 strings on stdout");
let refs: Vec<&str> = strings.iter().map(|s| s.as_str()).collect();
let card = mk_codec::decode(&refs).expect("decode emitted strings");
assert_eq!(card.policy_id_stubs.len(), 1);
assert_eq!(
card.policy_id_stubs[0], EXPECTED_TEMPLATE_STUB,
"keyless template md1 must use the WalletDescriptorTemplateId stub"
);
assert_ne!(
card.policy_id_stubs[0], POLICY_STUB_FOR_TEMPLATE,
"must NOT fall back to the unconditional WalletPolicyId stub for a template"
);
}
#[test]
fn verify_from_keyless_template_md1_matches_template_id_stub() {
let mut enc = Command::cargo_bin("mk").expect("mk binary");
let out = enc
.args([
"encode",
"--xpub",
V1_XPUB,
"--origin-fingerprint",
V1_FP_HEX,
"--origin-path",
V1_PATH,
"--from-md1",
PKH_BASIC_TEMPLATE_MD1,
"--group-size",
"0",
])
.output()
.expect("invoke mk encode");
assert!(out.status.success(), "mk encode failed: {out:?}");
let stdout = String::from_utf8(out.stdout).unwrap();
let strings: Vec<String> = stdout.lines().map(str::to_string).collect();
assert!(!strings.is_empty());
let mut ver = Command::cargo_bin("mk").expect("mk binary");
let mut args: Vec<String> = vec!["verify".into()];
args.extend(strings.iter().cloned());
args.push("--from-md1".into());
args.push(PKH_BASIC_TEMPLATE_MD1.into());
let vout = ver.args(&args).output().expect("invoke mk verify");
assert!(
vout.status.success(),
"mk verify --from-md1 (template) must exit 0; stderr={}",
String::from_utf8_lossy(&vout.stderr)
);
let mut enc2 = Command::cargo_bin("mk").expect("mk binary");
let out2 = enc2
.args([
"encode",
"--xpub",
V1_XPUB,
"--origin-fingerprint",
V1_FP_HEX,
"--origin-path",
V1_PATH,
"--policy-id-stub",
"3d190af3",
"--group-size",
"0",
])
.output()
.expect("invoke mk encode (policy-stub card)");
assert!(
out2.status.success(),
"mk encode (policy-stub) failed: {out2:?}"
);
let stdout2 = String::from_utf8(out2.stdout).unwrap();
let strings2: Vec<String> = stdout2.lines().map(str::to_string).collect();
let mut ver2 = Command::cargo_bin("mk").expect("mk binary");
let mut args2: Vec<String> = vec!["verify".into()];
args2.extend(strings2.iter().cloned());
args2.push("--from-md1".into());
args2.push(PKH_BASIC_TEMPLATE_MD1.into());
let vout2 = ver2
.args(&args2)
.output()
.expect("invoke mk verify (mismatch)");
assert_eq!(
vout2.status.code(),
Some(4),
"policy-id-stamped card must MISMATCH the template-id derivation; stderr={}",
String::from_utf8_lossy(&vout2.stderr)
);
}
#[test]
fn encode_from_keyed_wallet_policy_md1_uses_policy_id_stub() {
let mut cmd = Command::cargo_bin("mk").expect("mk binary");
let out = cmd
.args([
"encode",
"--xpub",
V1_XPUB,
"--origin-fingerprint",
V1_FP_HEX,
"--origin-path",
V1_PATH,
"--from-md1",
KEYED_WALLET_POLICY_MD1,
"--group-size",
"0",
])
.output()
.expect("invoke mk encode");
assert!(out.status.success(), "mk encode failed: {out:?}");
let stdout = String::from_utf8(out.stdout).unwrap();
let strings: Vec<String> = stdout.lines().map(str::to_string).collect();
assert!(!strings.is_empty());
let refs: Vec<&str> = strings.iter().map(|s| s.as_str()).collect();
let card = mk_codec::decode(&refs).expect("decode emitted strings");
assert_eq!(card.policy_id_stubs.len(), 1);
assert_eq!(
card.policy_id_stubs[0], EXPECTED_KEYED_POLICY_STUB,
"keyed wallet-policy md1 must keep using the WalletPolicyId stub"
);
assert_ne!(
card.policy_id_stubs[0], EXPECTED_TEMPLATE_STUB,
"keyed md1 must NOT switch to the template-id stub"
);
}