Skip to main content

md_codec/
test_vectors.rs

1//! Canonical `md` test-vector corpus.
2//!
3//! Used by `md-codec`'s own integration tests, by `md-cli`'s `vectors`
4//! subcommand, and by `md-cli`'s `tests/json_snapshots.rs` /
5//! `tests/template_roundtrip.rs`. Single source of truth: any vector
6//! addition / removal / rename happens here.
7//!
8//! `Vector` is `#[non_exhaustive]` so future fields can be added without a
9//! breaking-change bump: external consumers construct nothing — they only
10//! read `MANIFEST` entries.
11
12/// One entry of the canonical test-vector corpus.
13#[non_exhaustive]
14pub struct Vector {
15    /// Vector identifier — used in test failure messages and as a stable
16    /// handle for cross-suite filtering. Convention: snake_case mirroring
17    /// the wallet-policy template's distinguishing structure.
18    pub name: &'static str,
19    /// BIP-388 wallet-policy template string the vector encodes. Parsed
20    /// by `parse::template`; round-tripped through `encode` and `decode`.
21    pub template: &'static str,
22    /// `(@N, xpub)` pairs binding each `@N` placeholder in `template`. Empty
23    /// when the vector exercises template-only paths (no key binding).
24    pub keys: &'static [(u8, &'static str)],
25    /// `(@N, 4-byte master fingerprint)` pairs aligned with `keys`. Empty
26    /// when the vector does not exercise fingerprint round-tripping.
27    pub fingerprints: &'static [(u8, [u8; 4])],
28    /// When true, force the encoder onto the chunked wire path even if the
29    /// payload would fit in a single chunk. Exercises chunk-boundary logic
30    /// without padding the template artificially.
31    pub force_chunked: bool,
32}
33
34/// The canonical 10-entry corpus.
35///
36/// `tr_with_leaf` and `sh_wpkh` are intentionally omitted: their round-trip
37/// via the v0.14+ codec is asymmetric (encode requires explicit origin;
38/// decode strips canonical 86'/0'/0' resp. 49'/0'/0'). Coverage for those
39/// wrappers is preserved by `parse::template` unit tests
40/// (`tr_with_one_leaf`, `sh_wpkh_nested`).
41#[rustfmt::skip]
42pub const MANIFEST: &[Vector] = &[
43    Vector { name: "wpkh_basic",         template: "wpkh(@0/<0;1>/*)",                                   keys: &[], fingerprints: &[], force_chunked: false },
44    Vector { name: "pkh_basic",          template: "pkh(@0/<0;1>/*)",                                    keys: &[], fingerprints: &[], force_chunked: false },
45    Vector { name: "wsh_multi_2of2",     template: "wsh(multi(2,@0/<0;1>/*,@1/<0;1>/*))",                keys: &[], fingerprints: &[], force_chunked: false },
46    Vector { name: "wsh_multi_2of3",     template: "wsh(multi(2,@0/<0;1>/*,@1/<0;1>/*,@2/<0;1>/*))",     keys: &[], fingerprints: &[], force_chunked: false },
47    Vector { name: "wsh_sortedmulti",    template: "wsh(sortedmulti(2,@0/<0;1>/*,@1/<0;1>/*,@2/<0;1>/*))", keys: &[], fingerprints: &[], force_chunked: false },
48    Vector { name: "tr_keyonly",         template: "tr(@0/<0;1>/*)",                                     keys: &[], fingerprints: &[], force_chunked: false },
49    Vector { name: "sh_wsh_multi",       template: "sh(wsh(multi(2,@0/<0;1>/*,@1/<0;1>/*)))",            keys: &[], fingerprints: &[], force_chunked: false },
50    Vector { name: "wsh_divergent_paths", template: "wsh(multi(2,@0/<0;1>/*,@1/<2;3>/*))",               keys: &[], fingerprints: &[], force_chunked: false },
51    Vector { name: "wsh_with_fingerprints", template: "wsh(multi(2,@0/<0;1>/*,@1/<0;1>/*))",
52        keys: &[],
53        fingerprints: &[(0, [0xDE,0xAD,0xBE,0xEF]), (1, [0xCA,0xFE,0xBA,0xBE])],
54        force_chunked: false },
55    Vector { name: "wsh_multi_chunked",  template: "wsh(multi(3,@0/<0;1>/*,@1/<0;1>/*,@2/<0;1>/*))",     keys: &[], fingerprints: &[], force_chunked: true },
56];