1use core::fmt;
2
3#[non_exhaustive]
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6pub enum DecodeError {
7 InputTooLarge,
9 TrailingBytes,
11 DecoderOverread,
13 Malformed,
15 ListTooLong,
17 NestingTooDeep,
19 AllocationExceeded,
21 ProofTooLarge,
23 ItemCountExceeded,
25 LengthOverflow,
27 OffsetOutOfBounds,
29}
30
31impl DecodeError {
32 #[must_use]
34 pub const fn code(self) -> &'static str {
35 match self {
36 Self::InputTooLarge => "ETH_CODEC_INPUT_TOO_LARGE",
37 Self::TrailingBytes => "ETH_CODEC_TRAILING_BYTES",
38 Self::DecoderOverread => "ETH_CODEC_DECODER_OVERREAD",
39 Self::Malformed => "ETH_CODEC_MALFORMED",
40 Self::ListTooLong => "ETH_CODEC_LIST_TOO_LONG",
41 Self::NestingTooDeep => "ETH_CODEC_NESTING_TOO_DEEP",
42 Self::AllocationExceeded => "ETH_CODEC_ALLOCATION_EXCEEDED",
43 Self::ProofTooLarge => "ETH_CODEC_PROOF_TOO_LARGE",
44 Self::ItemCountExceeded => "ETH_CODEC_ITEM_COUNT_EXCEEDED",
45 Self::LengthOverflow => "ETH_CODEC_LENGTH_OVERFLOW",
46 Self::OffsetOutOfBounds => "ETH_CODEC_OFFSET_OUT_OF_BOUNDS",
47 }
48 }
49
50 #[must_use]
52 pub const fn message(self) -> &'static str {
53 match self {
54 Self::InputTooLarge => "input exceeds the active decode byte limit",
55 Self::TrailingBytes => "decoded value did not consume the full input",
56 Self::DecoderOverread => "decoder consumed more bytes than were available",
57 Self::Malformed => "input is malformed for the selected codec",
58 Self::ListTooLong => "decoded list exceeds the active item limit",
59 Self::NestingTooDeep => "decoded structure exceeds the active nesting limit",
60 Self::AllocationExceeded => "decoder exceeded the active allocation limit",
61 Self::ProofTooLarge => "proof exceeds the active proof-node limit",
62 Self::ItemCountExceeded => "decoder exceeded the active cumulative item limit",
63 Self::LengthOverflow => "length or offset arithmetic overflowed",
64 Self::OffsetOutOfBounds => "offset or range is outside the input",
65 }
66 }
67
68 #[must_use]
70 pub const fn category(self) -> DecodeErrorCategory {
71 match self {
72 Self::InputTooLarge
73 | Self::ListTooLong
74 | Self::NestingTooDeep
75 | Self::AllocationExceeded
76 | Self::ProofTooLarge
77 | Self::ItemCountExceeded => DecodeErrorCategory::ResourceExhaustion,
78 Self::TrailingBytes
79 | Self::DecoderOverread
80 | Self::Malformed
81 | Self::LengthOverflow
82 | Self::OffsetOutOfBounds => DecodeErrorCategory::MalformedInput,
83 }
84 }
85
86 #[must_use]
89 pub const fn resource(self) -> Option<ResourceError> {
90 match self {
91 Self::InputTooLarge => Some(ResourceError::InputBytes),
92 Self::ListTooLong => Some(ResourceError::ListItems),
93 Self::NestingTooDeep => Some(ResourceError::NestingDepth),
94 Self::AllocationExceeded => Some(ResourceError::AllocationBytes),
95 Self::ProofTooLarge => Some(ResourceError::ProofNodes),
96 Self::ItemCountExceeded => Some(ResourceError::TotalItems),
97 Self::TrailingBytes
98 | Self::DecoderOverread
99 | Self::Malformed
100 | Self::LengthOverflow
101 | Self::OffsetOutOfBounds => None,
102 }
103 }
104}
105
106impl fmt::Display for DecodeError {
107 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
108 formatter.write_str(self.message())
109 }
110}
111
112#[cfg(feature = "std")]
113impl std::error::Error for DecodeError {}
114
115#[non_exhaustive]
117#[derive(Clone, Copy, Debug, Eq, PartialEq)]
118pub enum DecodeErrorCategory {
119 MalformedInput,
121 ResourceExhaustion,
123}
124
125#[non_exhaustive]
127#[derive(Clone, Copy, Debug, Eq, PartialEq)]
128pub enum ResourceError {
129 InputBytes,
131 ListItems,
133 NestingDepth,
135 AllocationBytes,
137 ProofNodes,
139 TotalItems,
141}
142
143impl ResourceError {
144 #[must_use]
146 pub const fn code(self) -> &'static str {
147 match self {
148 Self::InputBytes => "ETH_RESOURCE_INPUT_BYTES",
149 Self::ListItems => "ETH_RESOURCE_LIST_ITEMS",
150 Self::NestingDepth => "ETH_RESOURCE_NESTING_DEPTH",
151 Self::AllocationBytes => "ETH_RESOURCE_ALLOCATION_BYTES",
152 Self::ProofNodes => "ETH_RESOURCE_PROOF_NODES",
153 Self::TotalItems => "ETH_RESOURCE_TOTAL_ITEMS",
154 }
155 }
156
157 #[must_use]
159 pub const fn message(self) -> &'static str {
160 match self {
161 Self::InputBytes => "input byte budget exceeded",
162 Self::ListItems => "list item budget exceeded",
163 Self::NestingDepth => "nesting depth budget exceeded",
164 Self::AllocationBytes => "allocation byte budget exceeded",
165 Self::ProofNodes => "proof-node budget exceeded",
166 Self::TotalItems => "total item budget exceeded",
167 }
168 }
169}
170
171impl fmt::Display for ResourceError {
172 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
173 formatter.write_str(self.message())
174 }
175}
176
177#[cfg(feature = "std")]
178impl std::error::Error for ResourceError {}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183 extern crate std;
184 use std::string::ToString;
185
186 #[test]
187 fn decode_errors_have_stable_codes_and_messages() {
188 assert_eq!(DecodeError::Malformed.code(), "ETH_CODEC_MALFORMED");
189 assert_eq!(
190 DecodeError::Malformed.message(),
191 "input is malformed for the selected codec"
192 );
193 assert_eq!(
194 DecodeError::Malformed.category(),
195 DecodeErrorCategory::MalformedInput
196 );
197 assert_eq!(
198 DecodeError::Malformed.to_string(),
199 "input is malformed for the selected codec"
200 );
201 }
202
203 #[test]
204 fn resource_errors_are_classified_without_payloads() {
205 let error = DecodeError::AllocationExceeded;
206
207 assert_eq!(error.category(), DecodeErrorCategory::ResourceExhaustion);
208 assert_eq!(error.resource(), Some(ResourceError::AllocationBytes));
209 assert_eq!(
210 ResourceError::AllocationBytes.code(),
211 "ETH_RESOURCE_ALLOCATION_BYTES"
212 );
213 assert_eq!(
214 ResourceError::AllocationBytes.to_string(),
215 "allocation byte budget exceeded"
216 );
217 }
218
219 #[test]
220 fn new_resource_errors_are_classified() {
221 assert_eq!(
222 DecodeError::ProofTooLarge.resource(),
223 Some(ResourceError::ProofNodes)
224 );
225 assert_eq!(
226 DecodeError::ItemCountExceeded.resource(),
227 Some(ResourceError::TotalItems)
228 );
229 }
230
231 #[test]
232 fn arithmetic_errors_are_malformed_input() {
233 assert_eq!(
234 DecodeError::LengthOverflow.category(),
235 DecodeErrorCategory::MalformedInput
236 );
237 assert_eq!(DecodeError::OffsetOutOfBounds.resource(), None);
238 }
239}