use md_codec::encode::{Descriptor, encode_payload};
use md_codec::origin_path::{OriginPath, PathComponent, PathDecl, PathDeclPaths};
use md_codec::tag::Tag;
use md_codec::tlv::TlvSection;
use md_codec::tree::{Body, Node};
use md_codec::use_site_path::{Alternative, UseSitePath};
fn bip84_path() -> OriginPath {
OriginPath {
components: vec![
PathComponent {
hardened: true,
value: 84,
},
PathComponent {
hardened: true,
value: 0,
},
PathComponent {
hardened: true,
value: 0,
},
],
}
}
fn bip48_type_2_path() -> OriginPath {
OriginPath {
components: vec![
PathComponent {
hardened: true,
value: 48,
},
PathComponent {
hardened: true,
value: 0,
},
PathComponent {
hardened: true,
value: 0,
},
PathComponent {
hardened: true,
value: 2,
},
],
}
}
fn valid_compressed_pubkey() -> [u8; 33] {
let mut out = [0u8; 33];
out[0] = 0x02;
let x: [u8; 32] = [
0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B,
0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8,
0x17, 0x98,
];
out[1..].copy_from_slice(&x);
out
}
fn make_xpub(seed: u8) -> [u8; 65] {
let mut x = [0u8; 65];
for b in x[0..32].iter_mut() {
*b = seed;
}
x[32..65].copy_from_slice(&valid_compressed_pubkey());
x
}
fn wpkh_at_0() -> Node {
Node {
tag: Tag::Wpkh,
body: Body::KeyArg { index: 0 },
}
}
fn wsh_multi_2of2() -> Node {
Node {
tag: Tag::Wsh,
body: Body::Children(vec![Node {
tag: Tag::Multi,
body: Body::MultiKeys {
k: 2,
indices: vec![0, 1],
},
}]),
}
}
fn wsh_sortedmulti_2of8() -> Node {
Node {
tag: Tag::Wsh,
body: Body::Children(vec![Node {
tag: Tag::SortedMulti,
body: Body::MultiKeys {
k: 2,
indices: vec![0, 1, 2, 3, 4, 5, 6, 7],
},
}]),
}
}
fn wallet_policy_wpkh_golden() -> Descriptor {
let mut tlv = TlvSection::new_empty();
tlv.pubkeys = Some(vec![(0u8, make_xpub(0x11))]);
Descriptor {
n: 1,
path_decl: PathDecl {
n: 1,
paths: PathDeclPaths::Shared(bip84_path()),
},
use_site_path: UseSitePath::standard_multipath(),
tree: wpkh_at_0(),
tlv,
}
}
const WALLET_POLICY_WPKH_BYTES_HEX: &str = concat!(
"200ef5210800600550408888888888888888888888888888888888888888888888888888888",
"88888888813cdf333f7cee5dd62ad0314ae7438583814dfe6d96e7146cacf940ad8b7c0bcc0"
);
const WALLET_POLICY_WPKH_TOTAL_BITS: usize = 597;
fn use_site_override_golden() -> Descriptor {
let mut tlv = TlvSection::new_empty();
tlv.use_site_path_overrides = Some(vec![(
1u8,
UseSitePath {
multipath: Some(vec![
Alternative {
hardened: false,
value: 2,
},
Alternative {
hardened: false,
value: 3,
},
]),
wildcard_hardened: false,
},
)]);
Descriptor {
n: 2,
path_decl: PathDecl {
n: 2,
paths: PathDeclPaths::Shared(bip48_type_2_path()),
},
use_site_path: UseSitePath::standard_multipath(),
tree: wsh_multi_2of2(),
tlv,
}
}
const USE_SITE_OVERRIDE_BYTES_HEX: &str = "2052d84212a00182182140b4c0a160";
const USE_SITE_OVERRIDE_TOTAL_BITS: usize = 116;
fn origin_override_golden() -> Descriptor {
Descriptor {
n: 2,
path_decl: PathDecl {
n: 2,
paths: PathDeclPaths::Divergent(vec![bip48_type_2_path(), bip84_path()]),
},
use_site_path: UseSitePath::standard_multipath(),
tree: wsh_multi_2of2(),
tlv: TlvSection::new_empty(),
}
}
const ORIGIN_OVERRIDE_BYTES_HEX: &str = "a052d842128ef521080060860850";
const ORIGIN_OVERRIDE_TOTAL_BITS: usize = 108;
fn wsh_sortedmulti_2chunk_golden() -> Descriptor {
let mut tlv = TlvSection::new_empty();
tlv.fingerprints = Some(vec![
(0u8, [0x01, 0x02, 0x03, 0x04]),
(1u8, [0x02, 0x03, 0x04, 0x05]),
(2u8, [0x03, 0x04, 0x05, 0x06]),
(3u8, [0x04, 0x05, 0x06, 0x07]),
(4u8, [0x05, 0x06, 0x07, 0x08]),
(5u8, [0x06, 0x07, 0x08, 0x09]),
(6u8, [0x07, 0x08, 0x09, 0x0a]),
(7u8, [0x08, 0x09, 0x0a, 0x0b]),
]);
Descriptor {
n: 8,
path_decl: PathDecl {
n: 8,
paths: PathDeclPaths::Shared(OriginPath { components: vec![] }),
},
use_site_path: UseSitePath::standard_multipath(),
tree: wsh_sortedmulti_2of8(),
tlv,
}
}
const WSH_SORTEDMULTI_2CHUNK_CHUNK_0: &str =
"md1fujf0qsppcgqpsgwzwpfewuxvvqqgzqvzzqsrqsz5qcyq5srf0tn67rd3va";
const WSH_SORTEDMULTI_2CHUNK_CHUNK_1: &str =
"md1fujf0qsgvcyq5rq0q9qcrs3gxquyqns8pqys4cgpy9qkqt2cxjz4rtdyjk";
#[test]
fn wallet_policy_wpkh_bytes_hex_frozen() {
let d = wallet_policy_wpkh_golden();
let (bytes, total_bits) = encode_payload(&d).expect("encode wallet-policy wpkh golden");
assert_eq!(
hex::encode(&bytes),
WALLET_POLICY_WPKH_BYTES_HEX,
"wallet-policy wpkh wire bytes drifted from the frozen historical golden"
);
assert_eq!(total_bits, WALLET_POLICY_WPKH_TOTAL_BITS);
}
#[test]
fn use_site_override_bytes_hex_frozen() {
let d = use_site_override_golden();
let (bytes, total_bits) = encode_payload(&d).expect("encode use-site-override golden");
assert_eq!(
hex::encode(&bytes),
USE_SITE_OVERRIDE_BYTES_HEX,
"use-site-override wire bytes drifted from the frozen historical golden"
);
assert_eq!(total_bits, USE_SITE_OVERRIDE_TOTAL_BITS);
}
#[test]
fn origin_override_bytes_hex_frozen() {
let d = origin_override_golden();
let (bytes, total_bits) = encode_payload(&d).expect("encode origin-override golden");
assert_eq!(
hex::encode(&bytes),
ORIGIN_OVERRIDE_BYTES_HEX,
"origin-override (Divergent) wire bytes drifted from the frozen historical golden"
);
assert_eq!(total_bits, ORIGIN_OVERRIDE_TOTAL_BITS);
}
#[test]
fn wsh_sortedmulti_2chunk_chunk_strings_frozen() {
let d = wsh_sortedmulti_2chunk_golden();
let chunks = md_codec::chunk::split(&d).expect("split wsh_sortedmulti_2chunk golden");
assert_eq!(
chunks,
vec![
WSH_SORTEDMULTI_2CHUNK_CHUNK_0.to_string(),
WSH_SORTEDMULTI_2CHUNK_CHUNK_1.to_string(),
],
"2-chunk split STRINGS drifted from the frozen historical golden \
(this closes the D1 chunk-split-boundary mutation gap — a \
.bytes.hex-only golden cannot RED a split-boundary regression \
because split/reassemble round-trips regardless of where the \
boundary falls)"
);
}