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 UnexpectedList,
17 UnexpectedScalar,
19 ListTooLong,
21 NestingTooDeep,
23 AllocationExceeded,
25 ProofTooLarge,
27 ItemCountExceeded,
29 UnreviewedDeploymentPolicy,
31 LengthOverflow,
33 OffsetOutOfBounds,
35}
36
37impl DecodeError {
38 #[must_use]
40 pub const fn code(self) -> &'static str {
41 match self {
42 Self::InputTooLarge => "ETH_CODEC_INPUT_TOO_LARGE",
43 Self::TrailingBytes => "ETH_CODEC_TRAILING_BYTES",
44 Self::DecoderOverread => "ETH_CODEC_DECODER_OVERREAD",
45 Self::Malformed => "ETH_CODEC_MALFORMED",
46 Self::UnexpectedList => "ETH_CODEC_UNEXPECTED_LIST",
47 Self::UnexpectedScalar => "ETH_CODEC_UNEXPECTED_SCALAR",
48 Self::ListTooLong => "ETH_CODEC_LIST_TOO_LONG",
49 Self::NestingTooDeep => "ETH_CODEC_NESTING_TOO_DEEP",
50 Self::AllocationExceeded => "ETH_CODEC_ALLOCATION_EXCEEDED",
51 Self::ProofTooLarge => "ETH_CODEC_PROOF_TOO_LARGE",
52 Self::ItemCountExceeded => "ETH_CODEC_ITEM_COUNT_EXCEEDED",
53 Self::UnreviewedDeploymentPolicy => "ETH_CODEC_UNREVIEWED_DEPLOYMENT_POLICY",
54 Self::LengthOverflow => "ETH_CODEC_LENGTH_OVERFLOW",
55 Self::OffsetOutOfBounds => "ETH_CODEC_OFFSET_OUT_OF_BOUNDS",
56 }
57 }
58
59 #[must_use]
61 pub const fn message(self) -> &'static str {
62 match self {
63 Self::InputTooLarge => "input exceeds the active decode byte limit",
64 Self::TrailingBytes => "decoded value did not consume the full input",
65 Self::DecoderOverread => "decoder consumed more bytes than were available",
66 Self::Malformed => "input is malformed for the selected codec",
67 Self::UnexpectedList => "RLP list encountered where scalar was required",
68 Self::UnexpectedScalar => "RLP scalar encountered where list was required",
69 Self::ListTooLong => "decoded list exceeds the active item limit",
70 Self::NestingTooDeep => "decoded structure exceeds the active nesting limit",
71 Self::AllocationExceeded => "decoder exceeded the active allocation limit",
72 Self::ProofTooLarge => "proof exceeds the active proof-node limit",
73 Self::ItemCountExceeded => "decoder exceeded the active cumulative item limit",
74 Self::UnreviewedDeploymentPolicy => {
75 "deployment decode policy must be reviewed and tightened"
76 }
77 Self::LengthOverflow => "length or offset arithmetic overflowed",
78 Self::OffsetOutOfBounds => "offset or range is outside the input",
79 }
80 }
81
82 #[must_use]
84 pub const fn category(self) -> DecodeErrorCategory {
85 match self {
86 Self::InputTooLarge
87 | Self::ListTooLong
88 | Self::NestingTooDeep
89 | Self::AllocationExceeded
90 | Self::ProofTooLarge
91 | Self::ItemCountExceeded
92 | Self::UnreviewedDeploymentPolicy => DecodeErrorCategory::ResourceExhaustion,
93 Self::TrailingBytes
94 | Self::DecoderOverread
95 | Self::Malformed
96 | Self::UnexpectedList
97 | Self::UnexpectedScalar
98 | Self::LengthOverflow
99 | Self::OffsetOutOfBounds => DecodeErrorCategory::MalformedInput,
100 }
101 }
102
103 #[must_use]
106 pub const fn resource(self) -> Option<ResourceError> {
107 match self {
108 Self::InputTooLarge => Some(ResourceError::InputBytes),
109 Self::ListTooLong => Some(ResourceError::ListItems),
110 Self::NestingTooDeep => Some(ResourceError::NestingDepth),
111 Self::AllocationExceeded => Some(ResourceError::AllocationBytes),
112 Self::ProofTooLarge => Some(ResourceError::ProofNodes),
113 Self::ItemCountExceeded => Some(ResourceError::TotalItems),
114 Self::UnreviewedDeploymentPolicy => Some(ResourceError::DeploymentPolicy),
115 Self::TrailingBytes
116 | Self::DecoderOverread
117 | Self::Malformed
118 | Self::UnexpectedList
119 | Self::UnexpectedScalar
120 | Self::LengthOverflow
121 | Self::OffsetOutOfBounds => None,
122 }
123 }
124}
125
126impl fmt::Display for DecodeError {
127 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
128 formatter.write_str(self.message())
129 }
130}
131
132#[cfg(feature = "std")]
133impl std::error::Error for DecodeError {}
134
135#[non_exhaustive]
137#[derive(Clone, Copy, Debug, Eq, PartialEq)]
138pub enum DecodeErrorCategory {
139 MalformedInput,
141 ResourceExhaustion,
143}
144
145#[non_exhaustive]
147#[derive(Clone, Copy, Debug, Eq, PartialEq)]
148pub enum ResourceError {
149 InputBytes,
151 ListItems,
153 NestingDepth,
155 AllocationBytes,
157 ProofNodes,
159 TotalItems,
161 DeploymentPolicy,
163}
164
165impl ResourceError {
166 #[must_use]
168 pub const fn code(self) -> &'static str {
169 match self {
170 Self::InputBytes => "ETH_RESOURCE_INPUT_BYTES",
171 Self::ListItems => "ETH_RESOURCE_LIST_ITEMS",
172 Self::NestingDepth => "ETH_RESOURCE_NESTING_DEPTH",
173 Self::AllocationBytes => "ETH_RESOURCE_ALLOCATION_BYTES",
174 Self::ProofNodes => "ETH_RESOURCE_PROOF_NODES",
175 Self::TotalItems => "ETH_RESOURCE_TOTAL_ITEMS",
176 Self::DeploymentPolicy => "ETH_RESOURCE_DEPLOYMENT_POLICY",
177 }
178 }
179
180 #[must_use]
182 pub const fn message(self) -> &'static str {
183 match self {
184 Self::InputBytes => "input byte budget exceeded",
185 Self::ListItems => "list item budget exceeded",
186 Self::NestingDepth => "nesting depth budget exceeded",
187 Self::AllocationBytes => "allocation byte budget exceeded",
188 Self::ProofNodes => "proof-node budget exceeded",
189 Self::TotalItems => "total item budget exceeded",
190 Self::DeploymentPolicy => "deployment policy review required",
191 }
192 }
193}
194
195impl fmt::Display for ResourceError {
196 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
197 formatter.write_str(self.message())
198 }
199}
200
201#[cfg(feature = "std")]
202impl std::error::Error for ResourceError {}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207 extern crate std;
208 use std::string::ToString;
209
210 #[test]
211 fn decode_errors_have_stable_codes_and_messages() {
212 assert_eq!(DecodeError::Malformed.code(), "ETH_CODEC_MALFORMED");
213 assert_eq!(
214 DecodeError::Malformed.message(),
215 "input is malformed for the selected codec"
216 );
217 assert_eq!(
218 DecodeError::Malformed.category(),
219 DecodeErrorCategory::MalformedInput
220 );
221 assert_eq!(
222 DecodeError::Malformed.to_string(),
223 "input is malformed for the selected codec"
224 );
225 }
226
227 #[test]
228 fn resource_errors_are_classified_without_payloads() {
229 let error = DecodeError::AllocationExceeded;
230
231 assert_eq!(error.category(), DecodeErrorCategory::ResourceExhaustion);
232 assert_eq!(error.resource(), Some(ResourceError::AllocationBytes));
233 assert_eq!(
234 ResourceError::AllocationBytes.code(),
235 "ETH_RESOURCE_ALLOCATION_BYTES"
236 );
237 assert_eq!(
238 ResourceError::AllocationBytes.to_string(),
239 "allocation byte budget exceeded"
240 );
241 }
242
243 #[test]
244 fn new_resource_errors_are_classified() {
245 assert_eq!(
246 DecodeError::ProofTooLarge.resource(),
247 Some(ResourceError::ProofNodes)
248 );
249 assert_eq!(
250 DecodeError::ItemCountExceeded.resource(),
251 Some(ResourceError::TotalItems)
252 );
253 assert_eq!(
254 DecodeError::UnreviewedDeploymentPolicy.resource(),
255 Some(ResourceError::DeploymentPolicy)
256 );
257 }
258
259 #[test]
260 fn arithmetic_errors_are_malformed_input() {
261 assert_eq!(
262 DecodeError::LengthOverflow.category(),
263 DecodeErrorCategory::MalformedInput
264 );
265 assert_eq!(DecodeError::OffsetOutOfBounds.resource(), None);
266 }
267
268 #[test]
269 fn unexpected_list_is_malformed_input() {
270 assert_eq!(
271 DecodeError::UnexpectedList.code(),
272 "ETH_CODEC_UNEXPECTED_LIST"
273 );
274 assert_eq!(
275 DecodeError::UnexpectedList.category(),
276 DecodeErrorCategory::MalformedInput
277 );
278 assert_eq!(DecodeError::UnexpectedList.resource(), None);
279 }
280
281 #[test]
282 fn unexpected_scalar_is_malformed_input() {
283 assert_eq!(
284 DecodeError::UnexpectedScalar.code(),
285 "ETH_CODEC_UNEXPECTED_SCALAR"
286 );
287 assert_eq!(
288 DecodeError::UnexpectedScalar.category(),
289 DecodeErrorCategory::MalformedInput
290 );
291 assert_eq!(DecodeError::UnexpectedScalar.resource(), None);
292 }
293}