1use crate::{
2 DecodeAccumulator, DecodeError, DecodeLimits, checked_len_add, require_exact_consumption,
3 require_range_in_bounds,
4};
5
6const SHORT_STRING_OFFSET: u8 = 0x80;
7const LONG_STRING_OFFSET: u8 = 0xb7;
8const SHORT_LIST_OFFSET: u8 = 0xc0;
9const SHORT_STRING_LIMIT: usize = 55;
10const LENGTH_RADIX: usize = 256;
11
12#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub enum RlpScalarForm {
15 SingleByte,
17 ShortString,
19 LongString,
21}
22
23#[derive(Clone, Copy, Debug, Eq, PartialEq)]
25pub struct RlpScalar<'a> {
26 payload: &'a [u8],
27 encoded_len: usize,
28 header_len: usize,
29 form: RlpScalarForm,
30}
31
32impl<'a> RlpScalar<'a> {
33 #[must_use]
35 pub const fn payload(self) -> &'a [u8] {
36 self.payload
37 }
38
39 #[must_use]
41 pub const fn encoded_len(self) -> usize {
42 self.encoded_len
43 }
44
45 #[must_use]
47 pub const fn header_len(self) -> usize {
48 self.header_len
49 }
50
51 #[must_use]
53 pub const fn form(self) -> RlpScalarForm {
54 self.form
55 }
56
57 #[must_use]
59 pub const fn is_empty(self) -> bool {
60 self.payload.is_empty()
61 }
62}
63
64pub fn decode_rlp_scalar<'a>(
66 input: &'a [u8],
67 limits: DecodeLimits,
68) -> Result<RlpScalar<'a>, DecodeError> {
69 let mut accumulator = limits.accumulator();
70 let scalar = decode_rlp_scalar_partial(input, &mut accumulator)?;
71 require_exact_consumption(scalar.encoded_len, input.len())?;
72 Ok(scalar)
73}
74
75pub fn decode_rlp_scalar_partial<'a>(
83 input: &'a [u8],
84 accumulator: &mut DecodeAccumulator,
85) -> Result<RlpScalar<'a>, DecodeError> {
86 accumulator.check_input_len(input.len())?;
87 accumulator.account_items(1)?;
88
89 let prefix = *input.first().ok_or(DecodeError::Malformed)?;
90 match prefix {
91 0x00..=0x7f => decode_single_byte(input),
92 SHORT_STRING_OFFSET..=LONG_STRING_OFFSET => decode_short_string(input, prefix),
93 0xb8..=0xbf => decode_long_string(input, prefix),
94 SHORT_LIST_OFFSET..=0xff => Err(DecodeError::UnexpectedList),
95 }
96}
97
98fn decode_single_byte(input: &[u8]) -> Result<RlpScalar<'_>, DecodeError> {
99 let payload = input.get(..1).ok_or(DecodeError::OffsetOutOfBounds)?;
100 Ok(RlpScalar {
101 payload,
102 encoded_len: 1,
103 header_len: 0,
104 form: RlpScalarForm::SingleByte,
105 })
106}
107
108fn decode_short_string(input: &[u8], prefix: u8) -> Result<RlpScalar<'_>, DecodeError> {
109 let payload_len = usize::from(prefix.saturating_sub(SHORT_STRING_OFFSET));
111 let payload = payload(input, 1, payload_len)?;
112 if payload_len == 1 && payload.first().is_some_and(|byte| *byte <= 0x7f) {
113 return Err(DecodeError::Malformed);
114 }
115 Ok(RlpScalar {
116 payload,
117 encoded_len: checked_len_add(1, payload_len)?,
118 header_len: 1,
119 form: RlpScalarForm::ShortString,
120 })
121}
122
123fn decode_long_string(input: &[u8], prefix: u8) -> Result<RlpScalar<'_>, DecodeError> {
124 let len_of_len = usize::from(prefix.saturating_sub(LONG_STRING_OFFSET));
126 let payload_len = parse_payload_len(input, 1, len_of_len)?;
127 if payload_len <= SHORT_STRING_LIMIT {
128 return Err(DecodeError::Malformed);
129 }
130 let header_len = checked_len_add(1, len_of_len)?;
131 let payload = payload(input, header_len, payload_len)?;
132 Ok(RlpScalar {
133 payload,
134 encoded_len: checked_len_add(header_len, payload_len)?,
135 header_len,
136 form: RlpScalarForm::LongString,
137 })
138}
139
140fn parse_payload_len(input: &[u8], offset: usize, len: usize) -> Result<usize, DecodeError> {
141 let end = require_range_in_bounds(offset, len, input.len())?;
142 let bytes = input
143 .get(offset..end)
144 .ok_or(DecodeError::OffsetOutOfBounds)?;
145 if bytes.first().is_some_and(|byte| *byte == 0) {
146 return Err(DecodeError::Malformed);
147 }
148
149 let mut value = 0usize;
150 for byte in bytes {
151 value = value
152 .checked_mul(LENGTH_RADIX)
153 .ok_or(DecodeError::LengthOverflow)?;
154 value = value
155 .checked_add(usize::from(*byte))
156 .ok_or(DecodeError::LengthOverflow)?;
157 }
158 Ok(value)
159}
160
161fn payload(input: &[u8], offset: usize, len: usize) -> Result<&[u8], DecodeError> {
162 let end = require_range_in_bounds(offset, len, input.len())?;
163 input.get(offset..end).ok_or(DecodeError::OffsetOutOfBounds)
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169 extern crate std;
170 use std::vec;
171
172 #[test]
173 fn decodes_single_byte_scalar() {
174 assert_eq!(
175 decode_rlp_scalar(&[0x7f], DecodeLimits::TEST_FIXTURE),
176 Ok(RlpScalar {
177 payload: &[0x7f],
178 encoded_len: 1,
179 header_len: 0,
180 form: RlpScalarForm::SingleByte,
181 })
182 );
183 }
184
185 #[test]
186 fn decodes_empty_string() {
187 assert_eq!(
188 decode_rlp_scalar(&[0x80], DecodeLimits::TEST_FIXTURE),
189 Ok(RlpScalar {
190 payload: &[],
191 encoded_len: 1,
192 header_len: 1,
193 form: RlpScalarForm::ShortString,
194 })
195 );
196 }
197
198 #[test]
199 fn decodes_short_string() {
200 assert_eq!(
201 decode_rlp_scalar(&[0x83, b'd', b'o', b'g'], DecodeLimits::TEST_FIXTURE),
202 Ok(RlpScalar {
203 payload: b"dog",
204 encoded_len: 4,
205 header_len: 1,
206 form: RlpScalarForm::ShortString,
207 })
208 );
209 }
210
211 #[test]
212 fn decodes_multi_byte_payload() {
213 assert_eq!(
214 decode_rlp_scalar(&[0x82, 0x04, 0x00], DecodeLimits::TEST_FIXTURE),
215 Ok(RlpScalar {
216 payload: &[0x04, 0x00],
217 encoded_len: 3,
218 header_len: 1,
219 form: RlpScalarForm::ShortString,
220 })
221 );
222 }
223
224 #[test]
225 fn decodes_official_scalar_examples() {
226 let lorem = b"Lorem ipsum dolor sit amet, consectetur adipisicing elit";
227 let mut long = vec![0xb8, 0x38];
228 long.extend_from_slice(lorem);
229
230 let cases: &[(&[u8], &[u8], RlpScalarForm)] = &[
231 (
232 &[0x83, b'd', b'o', b'g'],
233 b"dog",
234 RlpScalarForm::ShortString,
235 ),
236 (&[0x80], b"", RlpScalarForm::ShortString),
237 (&[0x00], &[0x00], RlpScalarForm::SingleByte),
238 (&[0x0f], &[0x0f], RlpScalarForm::SingleByte),
239 (
240 &[0x82, 0x04, 0x00],
241 &[0x04, 0x00],
242 RlpScalarForm::ShortString,
243 ),
244 (long.as_slice(), lorem, RlpScalarForm::LongString),
245 ];
246
247 for (input, expected_payload, expected_form) in cases {
248 assert!(matches!(
249 decode_rlp_scalar(input, DecodeLimits::TEST_FIXTURE),
250 Ok(scalar)
251 if scalar.payload() == *expected_payload
252 && scalar.encoded_len() == input.len()
253 && scalar.form() == *expected_form
254 ));
255 }
256 }
257
258 #[test]
259 fn decodes_long_string() {
260 let mut input = vec![0xb8, 56];
261 input.extend_from_slice(&[b'a'; 56]);
262
263 assert!(matches!(
264 decode_rlp_scalar(&input, DecodeLimits::TEST_FIXTURE),
265 Ok(scalar)
266 if scalar.payload().len() == 56
267 && scalar.encoded_len() == 58
268 && scalar.header_len() == 2
269 && scalar.form() == RlpScalarForm::LongString
270 ));
271 }
272
273 #[test]
274 fn partial_decoder_leaves_trailing_bytes_to_caller() {
275 let mut accumulator = DecodeLimits::TEST_FIXTURE.accumulator();
276
277 assert!(matches!(
278 decode_rlp_scalar_partial(&[0x83, b'd', b'o', b'g', 0x80], &mut accumulator),
279 Ok(scalar) if scalar.payload() == b"dog" && scalar.encoded_len() == 4
280 ));
281 assert_eq!(accumulator.total_items(), 1);
282 }
283
284 #[test]
285 fn exact_decoder_rejects_trailing_bytes() {
286 assert_eq!(
287 decode_rlp_scalar(&[0x83, b'd', b'o', b'g', 0x80], DecodeLimits::TEST_FIXTURE),
288 Err(DecodeError::TrailingBytes)
289 );
290 }
291
292 #[test]
293 fn rejects_empty_input() {
294 assert_eq!(
295 decode_rlp_scalar(&[], DecodeLimits::TEST_FIXTURE),
296 Err(DecodeError::Malformed)
297 );
298 }
299
300 #[test]
301 fn rejects_short_string_missing_payload() {
302 assert_eq!(
303 decode_rlp_scalar(&[0x82, 0x04], DecodeLimits::TEST_FIXTURE),
304 Err(DecodeError::OffsetOutOfBounds)
305 );
306 }
307
308 #[test]
309 fn rejects_noncanonical_single_byte_string() {
310 assert_eq!(
311 decode_rlp_scalar(&[0x81, 0x7f], DecodeLimits::TEST_FIXTURE),
312 Err(DecodeError::Malformed)
313 );
314 }
315
316 #[test]
317 fn rejects_long_string_missing_length_bytes() {
318 assert_eq!(
319 decode_rlp_scalar(&[0xb9, 0x01], DecodeLimits::TEST_FIXTURE),
320 Err(DecodeError::OffsetOutOfBounds)
321 );
322 }
323
324 #[test]
325 fn rejects_long_string_missing_payload() {
326 assert_eq!(
327 decode_rlp_scalar(&[0xb8, 56, b'a'], DecodeLimits::TEST_FIXTURE),
328 Err(DecodeError::OffsetOutOfBounds)
329 );
330 }
331
332 #[test]
333 fn rejects_long_string_with_leading_zero_length() {
334 let mut input = vec![0xb9, 0, 56];
335 input.extend_from_slice(&[b'a'; 56]);
336
337 assert_eq!(
338 decode_rlp_scalar(&input, DecodeLimits::TEST_FIXTURE),
339 Err(DecodeError::Malformed)
340 );
341 }
342
343 #[test]
344 fn rejects_long_string_length_overflow() {
345 let input = [0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
346
347 assert_eq!(
348 decode_rlp_scalar(&input, DecodeLimits::TEST_FIXTURE),
349 Err(DecodeError::LengthOverflow)
350 );
351 }
352
353 #[test]
354 fn rejects_long_string_used_for_short_payload() {
355 let mut input = vec![0xb8, 55];
356 input.extend_from_slice(&[b'a'; 55]);
357
358 assert_eq!(
359 decode_rlp_scalar(&input, DecodeLimits::TEST_FIXTURE),
360 Err(DecodeError::Malformed)
361 );
362 }
363
364 #[test]
365 fn rejects_list_prefix_for_scalar_decoder() {
366 assert_eq!(
367 decode_rlp_scalar(&[0xc0], DecodeLimits::TEST_FIXTURE),
368 Err(DecodeError::UnexpectedList)
369 );
370 }
371
372 #[test]
373 fn enforces_input_budget() {
374 let limits = DecodeLimits {
375 max_input_bytes: 1,
376 ..DecodeLimits::TEST_FIXTURE
377 };
378
379 assert_eq!(
380 decode_rlp_scalar(&[0x81, 0x80], limits),
381 Err(DecodeError::InputTooLarge)
382 );
383 }
384
385 #[test]
386 fn enforces_item_budget() {
387 let limits = DecodeLimits {
388 max_total_items: 0,
389 ..DecodeLimits::TEST_FIXTURE
390 };
391
392 assert_eq!(
393 decode_rlp_scalar(&[0x80], limits),
394 Err(DecodeError::ItemCountExceeded)
395 );
396 }
397}