Skip to main content

eth_valkyoth_codec/rlp/
encode.rs

1use core::convert::TryFrom;
2
3use crate::{DecodeAccumulator, DecodeError, DecodeLimits, checked_len_add};
4
5use super::{
6    LENGTH_RADIX, LONG_LIST_OFFSET, LONG_STRING_OFFSET, RlpInteger, RlpItem, RlpList, RlpScalar,
7    SHORT_LIST_OFFSET, SHORT_STRING_LIMIT, SHORT_STRING_OFFSET,
8    integer::validate_rlp_integer_payload, list::validate_list_payload,
9};
10
11/// Returns the encoded RLP byte length for a scalar byte-string payload.
12pub fn encoded_rlp_scalar_len(payload: &[u8]) -> Result<usize, DecodeError> {
13    encoded_payload_len(payload, ScalarSingleByte::Enabled)
14}
15
16/// Returns the encoded RLP byte length for a canonical integer payload.
17///
18/// The empty payload represents zero. Non-empty payloads must be shortest-form
19/// unsigned big-endian bytes and therefore cannot start with `0x00`.
20pub fn encoded_rlp_integer_len(payload: &[u8]) -> Result<usize, DecodeError> {
21    validate_rlp_integer_payload(payload)?;
22    encoded_rlp_scalar_len(payload)
23}
24
25/// Returns the encoded RLP byte length for a list payload.
26///
27/// The payload must be the concatenated encoded child items of the list.
28pub fn encoded_rlp_list_len(payload: &[u8], limits: DecodeLimits) -> Result<usize, DecodeError> {
29    validate_encoded_list_payload(payload, limits)?;
30    encoded_payload_len(payload, ScalarSingleByte::Disabled)
31}
32
33/// Canonically encodes a scalar byte-string payload into `output`.
34///
35/// `output` is not modified unless this function returns `Ok`.
36/// Returns the number of bytes written.
37pub fn encode_rlp_scalar(payload: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
38    encode_payload(
39        payload,
40        output,
41        SHORT_STRING_OFFSET,
42        LONG_STRING_OFFSET,
43        ScalarSingleByte::Enabled,
44    )
45}
46
47/// Canonically encodes an Ethereum integer payload into `output`.
48///
49/// `output` is not modified unless this function returns `Ok`.
50/// Returns the number of bytes written.
51pub fn encode_rlp_integer(payload: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
52    validate_rlp_integer_payload(payload)?;
53    encode_rlp_scalar(payload, output)
54}
55
56/// Canonically encodes a list payload into `output`.
57///
58/// The payload must already contain the concatenated encoded child items.
59/// `output` is not modified unless this function returns `Ok`.
60/// Returns the number of bytes written.
61pub fn encode_rlp_list_payload(
62    payload: &[u8],
63    limits: DecodeLimits,
64    output: &mut [u8],
65) -> Result<usize, DecodeError> {
66    validate_encoded_list_payload(payload, limits)?;
67    encode_rlp_list_payload_unchecked(payload, output)
68}
69
70fn encode_rlp_list_payload_unchecked(
71    payload: &[u8],
72    output: &mut [u8],
73) -> Result<usize, DecodeError> {
74    encode_payload(
75        payload,
76        output,
77        SHORT_LIST_OFFSET,
78        LONG_LIST_OFFSET,
79        ScalarSingleByte::Disabled,
80    )
81}
82
83/// Re-encodes a decoded canonical scalar into `output`.
84///
85/// `output` is not modified unless this function returns `Ok`.
86/// Returns the number of bytes written.
87pub fn encode_decoded_scalar(
88    scalar: RlpScalar<'_>,
89    output: &mut [u8],
90) -> Result<usize, DecodeError> {
91    encode_rlp_scalar(scalar.payload(), output)
92}
93
94/// Re-encodes a decoded canonical integer into `output`.
95///
96/// `output` is not modified unless this function returns `Ok`.
97/// Returns the number of bytes written.
98pub fn encode_decoded_integer(
99    integer: RlpInteger<'_>,
100    output: &mut [u8],
101) -> Result<usize, DecodeError> {
102    encode_rlp_integer(integer.payload(), output)
103}
104
105/// Re-encodes a decoded canonical list into `output`.
106///
107/// `output` is not modified unless this function returns `Ok`.
108/// Returns the number of bytes written.
109pub fn encode_decoded_list(list: RlpList<'_>, output: &mut [u8]) -> Result<usize, DecodeError> {
110    encode_rlp_list_payload_unchecked(list.payload(), output)
111}
112
113/// Re-encodes a decoded canonical RLP item into `output`.
114///
115/// `output` is not modified unless this function returns `Ok`.
116/// Returns the number of bytes written.
117pub fn encode_decoded_item(item: RlpItem<'_>, output: &mut [u8]) -> Result<usize, DecodeError> {
118    match item {
119        RlpItem::Scalar(scalar) => encode_decoded_scalar(scalar, output),
120        RlpItem::List(list) => encode_decoded_list(list, output),
121    }
122}
123
124#[derive(Clone, Copy)]
125enum ScalarSingleByte {
126    Enabled,
127    Disabled,
128}
129
130fn encoded_payload_len(
131    payload: &[u8],
132    scalar_single_byte: ScalarSingleByte,
133) -> Result<usize, DecodeError> {
134    let payload_len = payload.len();
135    if matches!(scalar_single_byte, ScalarSingleByte::Enabled)
136        && payload_len == 1
137        && payload.first().is_some_and(|byte| *byte <= 0x7f)
138    {
139        return Ok(1);
140    }
141    if payload_len <= SHORT_STRING_LIMIT {
142        return checked_len_add(1, payload_len);
143    }
144    let len_of_len = length_of_length(payload_len)?;
145    checked_len_add(checked_len_add(1, len_of_len)?, payload_len)
146}
147
148fn encode_payload(
149    payload: &[u8],
150    output: &mut [u8],
151    short_offset: u8,
152    long_offset: u8,
153    scalar_single_byte: ScalarSingleByte,
154) -> Result<usize, DecodeError> {
155    let required_len = encoded_payload_len(payload, scalar_single_byte)?;
156    if output.len() < required_len {
157        return Err(DecodeError::OffsetOutOfBounds);
158    }
159
160    if matches!(scalar_single_byte, ScalarSingleByte::Enabled)
161        && payload.len() == 1
162        && payload.first().is_some_and(|byte| *byte <= 0x7f)
163    {
164        let byte = *payload.first().ok_or(DecodeError::OffsetOutOfBounds)?;
165        write_byte(output, byte)?;
166        return Ok(1);
167    }
168
169    let header_len = if payload.len() <= SHORT_STRING_LIMIT {
170        write_short_header(output, short_offset, payload.len())?
171    } else {
172        write_long_header(output, long_offset, payload.len())?
173    };
174    write_payload(output, header_len, payload)?;
175    checked_len_add(header_len, payload.len())
176}
177
178fn write_short_header(
179    output: &mut [u8],
180    short_offset: u8,
181    payload_len: usize,
182) -> Result<usize, DecodeError> {
183    let payload_len = u8::try_from(payload_len).map_err(|_| DecodeError::LengthOverflow)?;
184    let prefix = short_offset
185        .checked_add(payload_len)
186        .ok_or(DecodeError::LengthOverflow)?;
187    write_byte(output, prefix)?;
188    Ok(1)
189}
190
191fn write_long_header(
192    output: &mut [u8],
193    long_offset: u8,
194    payload_len: usize,
195) -> Result<usize, DecodeError> {
196    let len_of_len = length_of_length(payload_len)?;
197    let len_of_len_byte = u8::try_from(len_of_len).map_err(|_| DecodeError::LengthOverflow)?;
198    let prefix = long_offset
199        .checked_add(len_of_len_byte)
200        .ok_or(DecodeError::LengthOverflow)?;
201    let header_len = checked_len_add(1, len_of_len)?;
202    write_byte(output, prefix)?;
203
204    let len_bytes = payload_len.to_be_bytes();
205    let start = len_bytes
206        .len()
207        .checked_sub(len_of_len)
208        .ok_or(DecodeError::LengthOverflow)?;
209    let source = len_bytes
210        .get(start..)
211        .ok_or(DecodeError::OffsetOutOfBounds)?;
212    let target = output
213        .get_mut(1..header_len)
214        .ok_or(DecodeError::OffsetOutOfBounds)?;
215    target.copy_from_slice(source);
216    Ok(header_len)
217}
218
219fn write_byte(output: &mut [u8], byte: u8) -> Result<(), DecodeError> {
220    let target = output.first_mut().ok_or(DecodeError::OffsetOutOfBounds)?;
221    *target = byte;
222    Ok(())
223}
224
225fn write_payload(output: &mut [u8], header_len: usize, payload: &[u8]) -> Result<(), DecodeError> {
226    let end = checked_len_add(header_len, payload.len())?;
227    let target = output
228        .get_mut(header_len..end)
229        .ok_or(DecodeError::OffsetOutOfBounds)?;
230    target.copy_from_slice(payload);
231    Ok(())
232}
233
234fn length_of_length(payload_len: usize) -> Result<usize, DecodeError> {
235    if payload_len <= SHORT_STRING_LIMIT {
236        return Err(DecodeError::Malformed);
237    }
238
239    let mut value = payload_len;
240    let mut len = 0usize;
241    while value > 0 {
242        len = checked_len_add(len, 1)?;
243        value = value
244            .checked_div(LENGTH_RADIX)
245            // LENGTH_RADIX is the nonzero constant 256, so this branch is
246            // unreachable unless the constant changes.
247            .ok_or(DecodeError::LengthOverflow)?;
248    }
249    Ok(len)
250}
251
252fn validate_encoded_list_payload(
253    payload: &[u8],
254    limits: DecodeLimits,
255) -> Result<usize, DecodeError> {
256    let mut accumulator: DecodeAccumulator = limits.accumulator();
257    accumulator.check_input_len(payload.len())?;
258    validate_list_payload(payload, &mut accumulator)
259}