Skip to main content

eth_valkyoth_codec/
rlp.rs

1mod integer;
2mod list;
3
4#[cfg(test)]
5mod tests;
6
7use crate::{
8    DecodeAccumulator, DecodeError, DecodeLimits, checked_len_add, require_exact_consumption,
9    require_range_in_bounds,
10};
11
12pub use integer::{
13    MAX_RLP_U256_BYTES, RlpInteger, decode_rlp_integer, decode_rlp_integer_partial, decode_rlp_u64,
14    decode_rlp_u128, decode_rlp_u256_bytes,
15};
16pub use list::{
17    MAX_RLP_LIST_TRAVERSAL_DEPTH, RlpItem, RlpList, RlpListForm, RlpListItems, decode_rlp_list,
18    decode_rlp_list_partial,
19};
20
21pub(super) const SHORT_STRING_OFFSET: u8 = 0x80;
22/// Base for computing `len_of_len` in long-string encoding.
23///
24/// This is the last short-string prefix, not the first long-string prefix.
25pub(super) const LONG_STRING_OFFSET: u8 = 0xb7;
26pub(super) const SHORT_LIST_OFFSET: u8 = 0xc0;
27/// Base for computing `len_of_len` in long-list encoding.
28///
29/// This is the last short-list prefix, not the first long-list prefix.
30pub(super) const LONG_LIST_OFFSET: u8 = 0xf7;
31pub(super) const SHORT_STRING_LIMIT: usize = 55;
32pub(super) const LENGTH_RADIX: usize = 256;
33
34/// Canonical RLP scalar form used by the decoder.
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
36pub enum RlpScalarForm {
37    /// A single byte in `0x00..=0x7f`, encoded as itself.
38    SingleByte,
39    /// A byte string with a one-byte RLP header.
40    ShortString,
41    /// A byte string with a length-of-length RLP header.
42    LongString,
43}
44
45/// Borrowed RLP scalar byte string.
46#[derive(Clone, Copy, Debug, Eq, PartialEq)]
47pub struct RlpScalar<'a> {
48    payload: &'a [u8],
49    encoded_len: usize,
50    header_len: usize,
51    form: RlpScalarForm,
52}
53
54impl<'a> RlpScalar<'a> {
55    /// Returns the decoded scalar payload bytes.
56    #[must_use]
57    pub const fn payload(self) -> &'a [u8] {
58        self.payload
59    }
60
61    /// Returns the total encoded item length in bytes.
62    #[must_use]
63    pub const fn encoded_len(self) -> usize {
64        self.encoded_len
65    }
66
67    /// Returns the RLP header length in bytes.
68    #[must_use]
69    pub const fn header_len(self) -> usize {
70        self.header_len
71    }
72
73    /// Returns the canonical scalar form that was decoded.
74    #[must_use]
75    pub const fn form(self) -> RlpScalarForm {
76        self.form
77    }
78
79    /// Returns true when the decoded payload is empty.
80    #[must_use]
81    pub const fn is_empty(self) -> bool {
82        self.payload.is_empty()
83    }
84}
85
86/// Decodes exactly one canonical RLP scalar byte string.
87pub fn decode_rlp_scalar<'a>(
88    input: &'a [u8],
89    limits: DecodeLimits,
90) -> Result<RlpScalar<'a>, DecodeError> {
91    let mut accumulator = limits.accumulator();
92    let scalar = decode_rlp_scalar_partial(input, &mut accumulator)?;
93    require_exact_consumption(scalar.encoded_len, input.len())?;
94    Ok(scalar)
95}
96
97/// Decodes one canonical RLP scalar byte string from the start of `input`.
98///
99/// Warning: this intentionally accepts trailing bytes. Use
100/// [`decode_rlp_scalar`] when the full input must be consumed.
101///
102/// The input-length budget check applies to the full `input` slice, not only
103/// the consumed scalar bytes. Callers that decode from a larger outer buffer
104/// must pre-slice before calling this helper.
105///
106/// This helper does not reject trailing bytes. It is intended for nested
107/// decoders that need to consume one item while sharing cumulative budgets.
108pub fn decode_rlp_scalar_partial<'a>(
109    input: &'a [u8],
110    accumulator: &mut DecodeAccumulator,
111) -> Result<RlpScalar<'a>, DecodeError> {
112    accumulator.check_input_len(input.len())?;
113    accumulator.account_items(1)?;
114
115    let prefix = *input.first().ok_or(DecodeError::Malformed)?;
116    match prefix {
117        0x00..=0x7f => decode_single_byte(input),
118        SHORT_STRING_OFFSET..=LONG_STRING_OFFSET => decode_short_string(input, prefix),
119        0xb8..=0xbf => decode_long_string(input, prefix),
120        SHORT_LIST_OFFSET..=0xff => Err(DecodeError::UnexpectedList),
121    }
122}
123
124fn decode_single_byte(input: &[u8]) -> Result<RlpScalar<'_>, DecodeError> {
125    let payload = input.get(..1).ok_or(DecodeError::OffsetOutOfBounds)?;
126    Ok(RlpScalar {
127        payload,
128        encoded_len: 1,
129        header_len: 0,
130        form: RlpScalarForm::SingleByte,
131    })
132}
133
134pub(super) fn decode_short_string(input: &[u8], prefix: u8) -> Result<RlpScalar<'_>, DecodeError> {
135    let payload_len = usize::from(prefix.saturating_sub(SHORT_STRING_OFFSET));
136    let payload = payload(input, 1, payload_len)?;
137    if payload_len == 1 && payload.first().is_some_and(|byte| *byte <= 0x7f) {
138        return Err(DecodeError::Malformed);
139    }
140    Ok(RlpScalar {
141        payload,
142        encoded_len: checked_len_add(1, payload_len)?,
143        header_len: 1,
144        form: RlpScalarForm::ShortString,
145    })
146}
147
148pub(super) fn decode_long_string(input: &[u8], prefix: u8) -> Result<RlpScalar<'_>, DecodeError> {
149    let len_of_len = usize::from(prefix.saturating_sub(LONG_STRING_OFFSET));
150    let payload_len = parse_payload_len(input, 1, len_of_len)?;
151    if payload_len <= SHORT_STRING_LIMIT {
152        return Err(DecodeError::Malformed);
153    }
154    let header_len = checked_len_add(1, len_of_len)?;
155    let payload = payload(input, header_len, payload_len)?;
156    Ok(RlpScalar {
157        payload,
158        encoded_len: checked_len_add(header_len, payload_len)?,
159        header_len,
160        form: RlpScalarForm::LongString,
161    })
162}
163
164pub(super) fn parse_payload_len(
165    input: &[u8],
166    offset: usize,
167    len: usize,
168) -> Result<usize, DecodeError> {
169    let end = require_range_in_bounds(offset, len, input.len())?;
170    let bytes = input
171        .get(offset..end)
172        .ok_or(DecodeError::OffsetOutOfBounds)?;
173    if bytes.first().is_some_and(|byte| *byte == 0) {
174        return Err(DecodeError::Malformed);
175    }
176
177    let mut value = 0usize;
178    for byte in bytes {
179        value = value
180            .checked_mul(LENGTH_RADIX)
181            .ok_or(DecodeError::LengthOverflow)?;
182        value = value
183            .checked_add(usize::from(*byte))
184            .ok_or(DecodeError::LengthOverflow)?;
185    }
186    Ok(value)
187}
188
189pub(super) fn payload(input: &[u8], offset: usize, len: usize) -> Result<&[u8], DecodeError> {
190    let end = require_range_in_bounds(offset, len, input.len())?;
191    input.get(offset..end).ok_or(DecodeError::OffsetOutOfBounds)
192}