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