1use crate::DecodeError;
2
3#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5pub struct DecodeLimits {
6 pub max_input_bytes: usize,
8 pub max_list_items: usize,
10 pub max_nesting_depth: usize,
12 pub max_total_allocation: usize,
14 pub max_proof_nodes: usize,
16 pub max_total_items: usize,
18}
19
20impl DecodeLimits {
21 pub const TEST_FIXTURE: Self = Self {
26 max_input_bytes: 1 << 20,
27 max_list_items: 4096,
28 max_nesting_depth: 64,
29 max_total_allocation: 1 << 20,
30 max_proof_nodes: 1024,
31 max_total_items: 8192,
32 };
33
34 pub const DEPLOYMENT_TEMPLATE: Self = Self {
40 max_input_bytes: 2 << 20,
41 max_list_items: 16_384,
42 max_nesting_depth: 64,
43 max_total_allocation: 4 << 20,
44 max_proof_nodes: 4096,
45 max_total_items: 65_536,
46 };
47
48 pub fn check_input_len(self, len: usize) -> Result<(), DecodeError> {
50 if len > self.max_input_bytes {
51 return Err(DecodeError::InputTooLarge);
52 }
53 Ok(())
54 }
55
56 pub fn check_list_count(self, count: usize) -> Result<(), DecodeError> {
58 if count > self.max_list_items {
59 return Err(DecodeError::ListTooLong);
60 }
61 Ok(())
62 }
63
64 pub fn check_nesting_depth(self, depth: usize) -> Result<(), DecodeError> {
66 if depth > self.max_nesting_depth {
67 return Err(DecodeError::NestingTooDeep);
68 }
69 Ok(())
70 }
71
72 pub fn check_single_allocation_limit(self, size: usize) -> Result<(), DecodeError> {
78 if size > self.max_total_allocation {
79 return Err(DecodeError::AllocationExceeded);
80 }
81 Ok(())
82 }
83
84 pub fn check_proof_node_count(self, count: usize) -> Result<(), DecodeError> {
89 if count > self.max_proof_nodes {
90 return Err(DecodeError::ProofTooLarge);
91 }
92 Ok(())
93 }
94
95 pub fn check_item_count(self, count: usize) -> Result<(), DecodeError> {
100 if count > self.max_total_items {
101 return Err(DecodeError::ItemCountExceeded);
102 }
103 Ok(())
104 }
105
106 #[must_use]
108 pub const fn accumulator(self) -> DecodeAccumulator {
109 DecodeAccumulator {
110 limits: self,
111 total_allocated: 0,
112 total_items: 0,
113 proof_nodes: 0,
114 }
115 }
116}
117
118#[derive(Debug, Eq, PartialEq)]
120pub struct DecodeAccumulator {
121 limits: DecodeLimits,
122 total_allocated: usize,
123 total_items: usize,
124 proof_nodes: usize,
125}
126
127impl DecodeAccumulator {
128 #[must_use]
130 pub const fn limits(&self) -> DecodeLimits {
131 self.limits
132 }
133
134 #[must_use]
136 pub const fn total_allocated(&self) -> usize {
137 self.total_allocated
138 }
139
140 #[must_use]
142 pub const fn total_items(&self) -> usize {
143 self.total_items
144 }
145
146 #[must_use]
148 pub const fn proof_nodes(&self) -> usize {
149 self.proof_nodes
150 }
151
152 pub fn check_input_len(&self, len: usize) -> Result<(), DecodeError> {
154 self.limits.check_input_len(len)
155 }
156
157 pub fn check_list_count(&self, count: usize) -> Result<(), DecodeError> {
159 self.limits.check_list_count(count)
160 }
161
162 pub fn check_nesting_depth(&self, depth: usize) -> Result<(), DecodeError> {
164 self.limits.check_nesting_depth(depth)
165 }
166
167 pub fn check_allocation(&mut self, size: usize) -> Result<(), DecodeError> {
169 let new_total = self
170 .total_allocated
171 .checked_add(size)
172 .ok_or(DecodeError::AllocationExceeded)?;
173 if new_total > self.limits.max_total_allocation {
174 return Err(DecodeError::AllocationExceeded);
175 }
176 self.total_allocated = new_total;
177 Ok(())
178 }
179
180 pub fn account_items(&mut self, count: usize) -> Result<(), DecodeError> {
182 let new_total = self
183 .total_items
184 .checked_add(count)
185 .ok_or(DecodeError::ItemCountExceeded)?;
186 if new_total > self.limits.max_total_items {
187 return Err(DecodeError::ItemCountExceeded);
188 }
189 self.total_items = new_total;
190 Ok(())
191 }
192
193 pub fn account_proof_nodes(&mut self, count: usize) -> Result<(), DecodeError> {
195 let new_total = self
196 .proof_nodes
197 .checked_add(count)
198 .ok_or(DecodeError::ProofTooLarge)?;
199 if new_total > self.limits.max_proof_nodes {
200 return Err(DecodeError::ProofTooLarge);
201 }
202 self.proof_nodes = new_total;
203 Ok(())
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[test]
212 fn rejects_oversized_input() {
213 let limits = DecodeLimits {
214 max_input_bytes: 2,
215 ..DecodeLimits::TEST_FIXTURE
216 };
217 assert_eq!(limits.check_input_len(3), Err(DecodeError::InputTooLarge));
218 }
219
220 #[test]
221 fn rejects_oversized_list() {
222 let limits = DecodeLimits {
223 max_list_items: 2,
224 ..DecodeLimits::TEST_FIXTURE
225 };
226 assert_eq!(limits.check_list_count(3), Err(DecodeError::ListTooLong));
227 }
228
229 #[test]
230 fn rejects_excessive_nesting_depth() {
231 let limits = DecodeLimits {
232 max_nesting_depth: 2,
233 ..DecodeLimits::TEST_FIXTURE
234 };
235 assert_eq!(
236 limits.check_nesting_depth(3),
237 Err(DecodeError::NestingTooDeep)
238 );
239 }
240
241 #[test]
242 fn rejects_excessive_allocation() {
243 let limits = DecodeLimits {
244 max_total_allocation: 2,
245 ..DecodeLimits::TEST_FIXTURE
246 };
247 assert_eq!(
248 limits.check_single_allocation_limit(3),
249 Err(DecodeError::AllocationExceeded)
250 );
251 }
252
253 #[test]
254 fn rejects_excessive_proof_nodes() {
255 let limits = DecodeLimits {
256 max_proof_nodes: 2,
257 ..DecodeLimits::TEST_FIXTURE
258 };
259 assert_eq!(
260 limits.check_proof_node_count(3),
261 Err(DecodeError::ProofTooLarge)
262 );
263 }
264
265 #[test]
266 fn rejects_excessive_total_items() {
267 let limits = DecodeLimits {
268 max_total_items: 2,
269 ..DecodeLimits::TEST_FIXTURE
270 };
271 assert_eq!(
272 limits.check_item_count(3),
273 Err(DecodeError::ItemCountExceeded)
274 );
275 }
276
277 #[test]
278 fn fixture_and_deployment_template_limits_are_distinct() {
279 let production = DecodeLimits::DEPLOYMENT_TEMPLATE;
280 let fixture = DecodeLimits::TEST_FIXTURE;
281
282 assert!(production.max_input_bytes > fixture.max_input_bytes);
283 assert!(production.max_total_allocation > fixture.max_total_allocation);
284 assert!(production.max_proof_nodes > fixture.max_proof_nodes);
285 assert!(production.max_total_items > fixture.max_total_items);
286 }
287
288 #[test]
289 fn accumulator_rejects_cumulative_allocation_over_budget() {
290 let limits = DecodeLimits {
291 max_total_allocation: 4,
292 ..DecodeLimits::TEST_FIXTURE
293 };
294 let mut accumulator = limits.accumulator();
295
296 assert_eq!(accumulator.check_allocation(3), Ok(()));
297 assert_eq!(accumulator.total_allocated(), 3);
298 assert_eq!(
299 accumulator.check_allocation(2),
300 Err(DecodeError::AllocationExceeded)
301 );
302 assert_eq!(accumulator.total_allocated(), 3);
303 }
304
305 #[test]
306 fn accumulator_rejects_allocation_counter_overflow() {
307 let limits = DecodeLimits {
308 max_total_allocation: usize::MAX,
309 ..DecodeLimits::TEST_FIXTURE
310 };
311 let mut accumulator = limits.accumulator();
312
313 assert_eq!(accumulator.check_allocation(usize::MAX), Ok(()));
314 assert_eq!(
315 accumulator.check_allocation(1),
316 Err(DecodeError::AllocationExceeded)
317 );
318 }
319
320 #[test]
321 fn accumulator_rejects_cumulative_items_over_budget() {
322 let limits = DecodeLimits {
323 max_total_items: 4,
324 ..DecodeLimits::TEST_FIXTURE
325 };
326 let mut accumulator = limits.accumulator();
327
328 assert_eq!(accumulator.account_items(3), Ok(()));
329 assert_eq!(accumulator.total_items(), 3);
330 assert_eq!(
331 accumulator.account_items(2),
332 Err(DecodeError::ItemCountExceeded)
333 );
334 assert_eq!(accumulator.total_items(), 3);
335 }
336
337 #[test]
338 fn accumulator_rejects_cumulative_proof_nodes_over_budget() {
339 let limits = DecodeLimits {
340 max_proof_nodes: 4,
341 ..DecodeLimits::TEST_FIXTURE
342 };
343 let mut accumulator = limits.accumulator();
344
345 assert_eq!(accumulator.account_proof_nodes(3), Ok(()));
346 assert_eq!(accumulator.proof_nodes(), 3);
347 assert_eq!(
348 accumulator.account_proof_nodes(2),
349 Err(DecodeError::ProofTooLarge)
350 );
351 assert_eq!(accumulator.proof_nodes(), 3);
352 }
353}