1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! ms-codec error taxonomy. Variants mirror SPEC §4 decoder validity rules
//! plus the encoder-side validation surface from SPEC §3.5 / §3.5.1.
use std::fmt;
/// ms-codec error type.
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
/// Upstream codex32 parse / checksum failure (delegated from rust-codex32).
Codex32(codex32::Error),
/// HRP was not "ms" (SPEC §4 rule 2).
WrongHrp {
/// The HRP that was observed.
got: String,
},
/// Threshold was not 0 (SPEC §4 rule 3).
ThresholdNotZero {
/// The threshold-position byte (ASCII digit) that was observed.
got: u8,
},
/// Share-index was not 's' — BIP-93 requires 's' for threshold=0 (SPEC §4 rule 4).
ShareIndexNotSecret {
/// The share-index character that was observed.
got: char,
},
/// Tag bytes were not in the codex32 alphabet (SPEC §4 rule 5).
TagInvalidAlphabet {
/// The 4-byte id-field bytes that failed alphabet validation.
got: [u8; 4],
},
/// Tag was structurally valid but not in RESERVED_TAG_TABLE (SPEC §4 rule 6).
UnknownTag {
/// The 4-byte tag that was not recognized.
got: [u8; 4],
},
/// Tag was in RESERVED_TAG_TABLE but reserved-not-emitted in v0.1 (SPEC §4 rule 7,
/// SPEC §3.5.1 encoder symmetry).
ReservedTagNotEmittedInV01 {
/// The 4-byte reserved tag (one of seed/xprv/mnem/prvk in v0.1).
got: [u8; 4],
},
/// Reserved-prefix byte was not 0x00 (SPEC §4 rule 8).
ReservedPrefixViolation {
/// The non-zero prefix byte that was observed.
got: u8,
},
/// Total string length was outside the v0.1 emittable set (SPEC §4 rule 9).
UnexpectedStringLength {
/// The total string length that was observed.
got: usize,
/// The set of v0.1-emittable lengths.
allowed: &'static [usize],
},
/// Payload byte length did not match the tag's spec (SPEC §3.5, §4 rule 10).
PayloadLengthMismatch {
/// The 4-byte tag whose length set was checked against.
tag: [u8; 4],
/// The set of valid byte lengths for this tag.
expected: &'static [usize],
/// The observed payload byte length (after stripping the prefix byte).
got: usize,
},
/// BCH error-correction (`bch_decode`) reported the input is uncorrectable
/// — the number of symbol errors exceeds the regular code's `t = 4`
/// correction capacity (singleton bound `d = 8`). Surfaced by
/// [`crate::decode_with_correction`] when `bch_decode::decode_regular_errors`
/// returns `None`, or when a post-correction re-verification step fails
/// (catches pathological 5+-error patterns that fool the decoder into
/// producing a "consistent" but invalid locator). Added v0.2.0 per plan
/// §1 D29 + §2.B.2.
///
/// `bound = 8` is the BCH(93,80,8) singleton bound. ms1 is single-chunk
/// only — no `chunk_index` field (cf. md-codec's `TooManyErrors` which
/// carries chunk-set context).
TooManyErrors {
/// Singleton bound for the BCH regular code (always 8).
bound: u8,
},
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Codex32(e) => write!(f, "codex32 parse error: {:?}", e),
Error::WrongHrp { got } => write!(f, "wrong HRP: got {:?}, expected \"ms\"", got),
Error::ThresholdNotZero { got } => {
write!(
f,
"threshold not 0 (got '{}'); v0.1 is single-string only",
*got as char
)
}
Error::ShareIndexNotSecret { got } => {
write!(
f,
"share-index not 's' (got '{}'); BIP-93 requires 's' for threshold=0",
got
)
}
Error::TagInvalidAlphabet { got } => {
write!(f, "tag bytes not in codex32 alphabet: {:?}", got)
}
Error::UnknownTag { got } => write!(
f,
"unknown tag {:?}; not a member of RESERVED_TAG_TABLE",
std::str::from_utf8(got).unwrap_or("<non-utf8>")
),
Error::ReservedTagNotEmittedInV01 { got } => write!(
f,
"tag {:?} reserved-not-emitted in v0.1; deferred to v0.2+",
std::str::from_utf8(got).unwrap_or("<non-utf8>")
),
Error::ReservedPrefixViolation { got } => {
write!(f, "reserved-prefix byte was 0x{:02x}, expected 0x00", got)
}
Error::UnexpectedStringLength { got, allowed } => {
write!(f, "string length {} outside v0.1 set {:?}", got, allowed)
}
Error::PayloadLengthMismatch { tag, expected, got } => write!(
f,
"tag {:?} payload length {} not in expected set {:?}",
std::str::from_utf8(tag).unwrap_or("<non-utf8>"),
got,
expected
),
Error::TooManyErrors { bound } => {
write!(f, "more than {} errors; uncorrectable", bound)
}
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
// codex32::Error doesn't impl std::error::Error in v0.1.0; chain stops here.
None
}
}
impl From<codex32::Error> for Error {
fn from(e: codex32::Error) -> Self {
Error::Codex32(e)
}
}
/// Result alias for ms-codec.
pub type Result<T> = std::result::Result<T, Error>;