Skip to main content

mk_codec/
consts.rs

1//! Locked constants for `mk1` per `design/SPEC_mk_v0_1.md` v0.1.
2//!
3//! All values are closure-locked (see
4//! `docs/superpowers/specs/2026-04-29-mk1-open-questions-closure-design.md`).
5//! Reproducer for the NUMS-derived target constants is documented in
6//! the BIP draft's "Why new target constants?" section.
7
8/// HRP for `mk1` strings (BIP 173 separator `1` follows: prefix is `mk1`).
9pub const HRP: &str = "mk";
10
11/// Domain string for NUMS-derived target constants (closure Q-1).
12///
13/// The string itself is the audit trail: any reader can recompute the
14/// SHA-256 and verify the constants follow from it.
15pub const NUMS_DOMAIN: &[u8] = b"shibbolethnumskey";
16
17/// Top 65 bits of `SHA-256(NUMS_DOMAIN)`. Regular-code target residue.
18pub const MK_REGULAR_CONST: u128 = 0x1062435f91072fa5c;
19
20/// Top 75 bits of `SHA-256(NUMS_DOMAIN)`. Long-code target residue.
21pub const MK_LONG_CONST: u128 = 0x41890d7e441cbe97273;
22
23/// Maximum components in an explicit-path encoding (closure Q-3).
24///
25/// Real BIP-style derivations top out at 6 (BIP 48 multisig is 4); 10
26/// gives margin without locking out plausibly real paths.
27pub const MAX_PATH_COMPONENTS: u8 = 10;
28
29/// Single-string regular-code payload bytes.
30pub const SINGLE_STRING_REGULAR_BYTES: usize = 48;
31
32/// Single-string long-code payload bytes.
33pub const SINGLE_STRING_LONG_BYTES: usize = 56;
34
35/// Chunked-fragment regular-code payload bytes per chunk.
36pub const CHUNKED_FRAGMENT_REGULAR_BYTES: usize = 45;
37
38/// Chunked-fragment long-code payload bytes per chunk.
39pub const CHUNKED_FRAGMENT_LONG_BYTES: usize = 53;
40
41/// Maximum chunks per card.
42pub const MAX_CHUNKS: u8 = 32;
43
44/// Cross-chunk integrity hash size in bytes.
45pub const CROSS_CHUNK_HASH_BYTES: usize = 4;
46
47/// Family-stable generator string (closure Q-10) for vector-corpus
48/// SHA-256 anchoring. Patch-version bumps don't roll the token; minor-
49/// or major-version bumps do.
50pub const GENERATOR_FAMILY: &str = "mk-codec 0.2";
51
52/// Compact-73 xpub byte size (closure Q-7).
53pub const XPUB_COMPACT_BYTES: usize = 73;
54
55/// Policy ID stub size in bytes (closure Q-2).
56pub const POLICY_ID_STUB_BYTES: usize = 4;
57
58/// Origin fingerprint size in bytes.
59pub const ORIGIN_FINGERPRINT_BYTES: usize = 4;
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64    use bitcoin::hashes::{Hash, sha256};
65
66    /// Verifies that the locked hex constants reproduce from the
67    /// documented derivation rule. Catches accidental drift if either
68    /// the domain string or the constants are edited without updating
69    /// the other.
70    #[test]
71    fn nums_constants_reproduce_from_domain() {
72        let digest = sha256::Hash::hash(NUMS_DOMAIN);
73        let bytes = digest.as_byte_array();
74        // Stage the leading 128 bits of the 256-bit digest as a
75        // big-endian u128.
76        let hi: u128 = u128::from_be_bytes(bytes[0..16].try_into().unwrap());
77
78        // Top 65 bits: shift the leading 128 bits right by (128 - 65).
79        let derived_regular = hi >> 63;
80        assert_eq!(
81            derived_regular, MK_REGULAR_CONST,
82            "MK_REGULAR_CONST drift from SHA-256(NUMS_DOMAIN) top-65-bits",
83        );
84
85        // Top 75 bits: shift right by (128 - 75).
86        let derived_long = hi >> 53;
87        assert_eq!(
88            derived_long, MK_LONG_CONST,
89            "MK_LONG_CONST drift from SHA-256(NUMS_DOMAIN) top-75-bits",
90        );
91    }
92
93    #[test]
94    fn nums_string_differs_from_md1() {
95        assert_ne!(
96            NUMS_DOMAIN, b"shibbolethnums",
97            "mk1 NUMS string MUST differ from md1's per closure D-10",
98        );
99    }
100
101    #[test]
102    fn capacity_constants_match_spec() {
103        // Sanity: confirms the four capacity numbers carry the values
104        // pinned in SPEC §2.4 / BIP §"Length envelope".
105        assert_eq!(SINGLE_STRING_REGULAR_BYTES, 48);
106        assert_eq!(SINGLE_STRING_LONG_BYTES, 56);
107        assert_eq!(CHUNKED_FRAGMENT_REGULAR_BYTES, 45);
108        assert_eq!(CHUNKED_FRAGMENT_LONG_BYTES, 53);
109        assert_eq!(MAX_CHUNKS, 32);
110    }
111
112    #[test]
113    fn xpub_compact_size_is_73() {
114        // 4 (version) + 4 (parent_fingerprint) + 32 (chain_code) + 33 (public_key) = 73.
115        assert_eq!(XPUB_COMPACT_BYTES, 4 + 4 + 32 + 33);
116    }
117
118    #[test]
119    fn path_cap_is_ten() {
120        // closure Q-3 lock; not 32.
121        assert_eq!(MAX_PATH_COMPONENTS, 10);
122    }
123}