use anyhow::{bail, ensure, Context, Result};
pub const MAX_RLP_DEPTH: usize = 16;
pub const MAX_RLP_ITEMS: usize = 200_000;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Rlp {
Bytes(Vec<u8>),
List(Vec<Rlp>),
}
impl Rlp {
pub fn empty_list() -> Rlp {
Rlp::List(Vec::new())
}
pub fn uint(n: u64) -> Rlp {
Rlp::Bytes(trim_leading_zeros(&n.to_be_bytes()).to_vec())
}
pub fn big_uint(be: &[u8]) -> Rlp {
Rlp::Bytes(trim_leading_zeros(be).to_vec())
}
pub fn bytes(b: &[u8]) -> Rlp {
Rlp::Bytes(b.to_vec())
}
pub fn string(s: &str) -> Rlp {
Rlp::Bytes(s.as_bytes().to_vec())
}
pub fn as_bytes(&self) -> Result<&[u8]> {
match self {
Rlp::Bytes(b) => Ok(b),
Rlp::List(_) => bail!("expected a byte string, found a list"),
}
}
pub fn as_list(&self) -> Result<&[Rlp]> {
match self {
Rlp::List(l) => Ok(l),
Rlp::Bytes(_) => bail!("expected a list, found a byte string"),
}
}
pub fn as_u64(&self) -> Result<u64> {
let b = self.as_bytes()?;
ensure!(
b.len() <= 8,
"integer of {} bytes is above 64 bits",
b.len()
);
ensure!(
b.first() != Some(&0),
"non-canonical integer (leading zero byte)"
);
Ok(b.iter().fold(0u64, |n, d| (n << 8) | u64::from(*d)))
}
pub fn as_big_uint(&self) -> Result<&[u8]> {
let b = self.as_bytes()?;
ensure!(
b.len() <= 32,
"integer of {} bytes is above 256 bits",
b.len()
);
ensure!(
b.first() != Some(&0),
"non-canonical integer (leading zero byte)"
);
Ok(b)
}
pub fn as_bool(&self) -> Result<bool> {
match self.as_u64()? {
0 => Ok(false),
1 => Ok(true),
n => bail!("boolean {n} is neither 0 nor 1"),
}
}
pub fn as_fixed<const N: usize>(&self) -> Result<[u8; N]> {
let b = self.as_bytes()?;
b.try_into()
.map_err(|_| anyhow::anyhow!("expected {N} bytes, found {}", b.len()))
}
pub fn encode(&self) -> Vec<u8> {
let mut out = Vec::new();
self.encode_into(&mut out);
out
}
fn encode_into(&self, out: &mut Vec<u8>) {
match self {
Rlp::Bytes(b) => {
if b.len() == 1 && b[0] < 0x80 {
out.push(b[0]);
} else {
put_length(out, 0x80, b.len());
out.extend_from_slice(b);
}
}
Rlp::List(items) => {
let mut payload = Vec::new();
for item in items {
item.encode_into(&mut payload);
}
put_length(out, 0xc0, payload.len());
out.extend_from_slice(&payload);
}
}
}
}
fn trim_leading_zeros(b: &[u8]) -> &[u8] {
let start = b.iter().position(|d| *d != 0).unwrap_or(b.len());
&b[start..]
}
fn put_length(out: &mut Vec<u8>, offset: u8, len: usize) {
if len <= 55 {
out.push(offset + len as u8);
} else {
let be = (len as u64).to_be_bytes();
let be = trim_leading_zeros(&be);
out.push(offset + 55 + be.len() as u8);
out.extend_from_slice(be);
}
}
pub fn decode(input: &[u8]) -> Result<Rlp> {
let (item, used) = decode_prefix(input)?;
ensure!(
used == input.len(),
"{} bytes trail the RLP item",
input.len() - used
);
Ok(item)
}
pub fn decode_prefix(input: &[u8]) -> Result<(Rlp, usize)> {
let mut items = 0usize;
decode_at(input, 0, &mut items)
}
fn header(input: &[u8]) -> Result<(bool, usize, usize)> {
let b = *input.first().context("RLP item is empty")?;
let (list, start, len) = match b {
0x00..=0x7f => return Ok((false, 0, 1)),
0x80..=0xb7 => (false, 1, usize::from(b - 0x80)),
0xc0..=0xf7 => (true, 1, usize::from(b - 0xc0)),
0xb8..=0xbf | 0xf8..=0xff => {
let list = b >= 0xf8;
let len_of_len = usize::from(b - if list { 0xf7 } else { 0xb7 });
let len_bytes = input
.get(1..1 + len_of_len)
.context("RLP length runs past the input")?;
ensure!(len_bytes[0] != 0, "non-canonical RLP length (leading zero)");
ensure!(
len_of_len <= 8,
"RLP length of {len_of_len} bytes is impossible"
);
let len = len_bytes.iter().fold(0u64, |n, d| (n << 8) | u64::from(*d));
ensure!(
len > 55,
"non-canonical RLP length: long form for {len} bytes"
);
let len = usize::try_from(len).context("RLP length above usize")?;
(list, 1 + len_of_len, len)
}
};
let end = start.checked_add(len).context("RLP length overflows")?;
ensure!(
end <= input.len(),
"RLP item declares {len} bytes, {} remain",
input.len() - start.min(input.len())
);
if !list && start == 1 && len == 1 {
ensure!(
input[1] >= 0x80,
"non-canonical RLP: single byte {:#04x} with a length prefix",
input[1]
);
}
Ok((list, start, len))
}
fn decode_at(input: &[u8], depth: usize, items: &mut usize) -> Result<(Rlp, usize)> {
*items += 1;
ensure!(
*items <= MAX_RLP_ITEMS,
"RLP holds more than {MAX_RLP_ITEMS} items"
);
let (list, start, len) = header(input)?;
let payload = &input[start..start + len];
if !list {
if start == 0 {
return Ok((Rlp::Bytes(vec![input[0]]), 1));
}
return Ok((Rlp::Bytes(payload.to_vec()), start + len));
}
ensure!(
depth < MAX_RLP_DEPTH,
"RLP nesting deeper than {MAX_RLP_DEPTH} levels"
);
let mut out = Vec::new();
let mut pos = 0;
while pos < payload.len() {
let (item, used) = decode_at(&payload[pos..], depth + 1, items)?;
out.push(item);
pos += used;
}
Ok((Rlp::List(out), start + len))
}
pub fn split_code(frame: &[u8]) -> Result<(u64, &[u8])> {
let (list, start, len) = header(frame)?;
ensure!(!list, "message code is a list");
let code_bytes = if start == 0 {
&frame[..1]
} else {
&frame[start..start + len]
};
let code = Rlp::Bytes(code_bytes.to_vec())
.as_u64()
.context("message code")?;
let used = if start == 0 { 1 } else { start + len };
Ok((code, &frame[used..]))
}