use crate::types::*; use arrayvec::ArrayVec; use auto_impl::auto_impl; use bytes::{BufMut, Bytes, BytesMut}; use core::borrow::Borrow; fn zeroless_view(v: &impl AsRef<[u8]>) -> &[u8] { let v = v.as_ref(); &v[v.iter().take_while(|&&b| b == 0).count()..] } impl Header { pub fn encode(&self, out: &mut dyn BufMut) { if self.payload_length < 56 { let code = if self.list { EMPTY_LIST_CODE } else { EMPTY_STRING_CODE }; out.put_u8(code + self.payload_length as u8); } else { let len_be = self.payload_length.to_be_bytes(); let len_be = zeroless_view(&len_be); let code = if self.list { 0xF7 } else { 0xB7 }; out.put_u8(code + len_be.len() as u8); out.put_slice(len_be); } } } pub const fn length_of_length(payload_length: usize) -> usize { if payload_length < 56 { 1 } else { 1 + 8 - payload_length.leading_zeros() as usize / 8 } } #[doc(hidden)] pub const fn const_add(a: usize, b: usize) -> usize { a + b } #[doc(hidden)] pub unsafe trait MaxEncodedLen: Encodable {} #[doc(hidden)] pub unsafe trait MaxEncodedLenAssoc: Encodable { const LEN: usize; } /// Use this to define length of an encoded entity /// /// # Safety /// Invalid value can cause the encoder to crash. #[macro_export] macro_rules! impl_max_encoded_len { ($t:ty, $len:block) => { unsafe impl MaxEncodedLen<{ $len }> for $t {} unsafe impl MaxEncodedLenAssoc for $t { const LEN: usize = $len; } }; } #[auto_impl(&)] #[cfg_attr(feature = "alloc", auto_impl(Box, Arc))] pub trait Encodable { fn encode(&self, out: &mut dyn BufMut); fn length(&self) -> usize { let mut out = BytesMut::new(); self.encode(&mut out); out.len() } } impl<'a> Encodable for &'a [u8] { fn length(&self) -> usize { let mut len = self.len(); if self.len() != 1 || self[0] >= EMPTY_STRING_CODE { len += length_of_length(self.len()); } len } fn encode(&self, out: &mut dyn BufMut) { if self.len() != 1 || self[0] >= EMPTY_STRING_CODE { Header { list: false, payload_length: self.len() }.encode(out); } out.put_slice(self); } } impl Encodable for [u8; LEN] { fn length(&self) -> usize { (self as &[u8]).length() } fn encode(&self, out: &mut dyn BufMut) { (self as &[u8]).encode(out) } } unsafe impl MaxEncodedLenAssoc for [u8; LEN] { const LEN: usize = LEN + length_of_length(LEN); } macro_rules! encodable_uint { ($t:ty) => { #[allow(clippy::cmp_owned)] impl Encodable for $t { fn length(&self) -> usize { if *self < <$t>::from(EMPTY_STRING_CODE) { 1 } else { 1 + (<$t>::BITS as usize / 8) - (self.leading_zeros() as usize / 8) } } fn encode(&self, out: &mut dyn BufMut) { if *self == 0 { out.put_u8(EMPTY_STRING_CODE); } else if *self < <$t>::from(EMPTY_STRING_CODE) { out.put_u8(u8::try_from(*self).unwrap()); } else { let be = self.to_be_bytes(); let be = zeroless_view(&be); out.put_u8(EMPTY_STRING_CODE + be.len() as u8); out.put_slice(be); } } } }; } macro_rules! max_encoded_len_uint { ($t:ty) => { impl_max_encoded_len!($t, { length_of_length(<$t>::MAX.to_be_bytes().len()) + <$t>::MAX.to_be_bytes().len() }); }; } encodable_uint!(usize); max_encoded_len_uint!(usize); encodable_uint!(u8); max_encoded_len_uint!(u8); encodable_uint!(u16); max_encoded_len_uint!(u16); encodable_uint!(u32); max_encoded_len_uint!(u32); encodable_uint!(u64); max_encoded_len_uint!(u64); encodable_uint!(u128); max_encoded_len_uint!(u128); impl Encodable for bool { fn length(&self) -> usize { (*self as u8).length() } fn encode(&self, out: &mut dyn BufMut) { (*self as u8).encode(out) } } impl_max_encoded_len!(bool, { ::LEN }); #[cfg(feature = "ethnum")] mod ethnum_support { use super::*; encodable_uint!(ethnum::U256); impl_max_encoded_len!(ethnum::U256, { length_of_length(32) + 32 }); } #[cfg(feature = "ethereum-types")] mod ethereum_types_support { use super::*; use ethereum_types::*; macro_rules! fixed_hash_impl { ($t:ty) => { impl Encodable for $t { fn length(&self) -> usize { self.0.length() } fn encode(&self, out: &mut dyn bytes::BufMut) { self.0.encode(out) } } impl_max_encoded_len!($t, { length_of_length(<$t>::len_bytes()) + <$t>::len_bytes() }); }; } fixed_hash_impl!(H64); fixed_hash_impl!(H128); fixed_hash_impl!(H160); fixed_hash_impl!(H256); fixed_hash_impl!(H512); fixed_hash_impl!(H520); //TODO fixed_hash_impl!(Bloom); macro_rules! fixed_uint_impl { ($t:ty, $n_bytes:tt) => { impl Encodable for $t { fn length(&self) -> usize { if *self < <$t>::from(EMPTY_STRING_CODE) { 1 } else { 1 + $n_bytes - (self.leading_zeros() as usize / 8) } } fn encode(&self, out: &mut dyn bytes::BufMut) { let mut temp_arr = [0u8; $n_bytes]; self.to_big_endian(&mut temp_arr[..]); // cut the leading zeros after converting to big endian let sliced = &temp_arr[(self.leading_zeros() / 8) as usize..]; sliced.encode(out); } } }; } fixed_uint_impl!(U64, 8); fixed_uint_impl!(U128, 16); fixed_uint_impl!(U256, 32); fixed_uint_impl!(U512, 64); } macro_rules! slice_impl { ($t:ty) => { impl $crate::Encodable for $t { fn length(&self) -> usize { (&self[..]).length() } fn encode(&self, out: &mut dyn bytes::BufMut) { (&self[..]).encode(out) } } }; } #[cfg(feature = "alloc")] mod alloc_support { use super::*; extern crate alloc; impl Encodable for ::alloc::vec::Vec where T: Encodable, { fn length(&self) -> usize { list_length(self) } fn encode(&self, out: &mut dyn BufMut) { encode_list(self, out) } } impl Encodable for ::alloc::string::String { fn encode(&self, out: &mut dyn BufMut) { self.as_bytes().encode(out); } fn length(&self) -> usize { self.as_bytes().length() } } } slice_impl!(Bytes); slice_impl!(BytesMut); fn rlp_list_header(v: &[K]) -> Header where E: Encodable + ?Sized, K: Borrow, { let mut h = Header { list: true, payload_length: 0 }; for x in v { h.payload_length += x.borrow().length(); } h } pub fn list_length(v: &[K]) -> usize where E: Encodable, K: Borrow, { let payload_length = rlp_list_header(v).payload_length; length_of_length(payload_length) + payload_length } pub fn encode_list(v: &[K], out: &mut dyn BufMut) where E: Encodable + ?Sized, K: Borrow, { let h = rlp_list_header(v); h.encode(out); for x in v { x.borrow().encode(out); } } pub fn encode_fixed_size, const LEN: usize>(v: &E) -> ArrayVec { let mut out = ArrayVec::from([0_u8; LEN]); let mut s = out.as_mut_slice(); v.encode(&mut s); let final_len = LEN - s.len(); out.truncate(final_len); out } #[cfg(test)] mod tests { extern crate alloc; use super::*; use alloc::vec; use bytes::BytesMut; use hex_literal::hex; fn encoded(t: T) -> BytesMut { let mut out = BytesMut::new(); t.encode(&mut out); out } fn encoded_list(t: &[T]) -> BytesMut { let mut out1 = BytesMut::new(); encode_list(t, &mut out1); let v = t.to_vec(); assert_eq!(out1.len(), v.length()); let mut out2 = BytesMut::new(); v.encode(&mut out2); assert_eq!(out1, out2); out1 } #[test] fn rlp_strings() { assert_eq!(encoded(hex!(""))[..], hex!("80")[..]); assert_eq!(encoded(hex!("7B"))[..], hex!("7b")[..]); assert_eq!(encoded(hex!("80"))[..], hex!("8180")[..]); assert_eq!(encoded(hex!("ABBA"))[..], hex!("82abba")[..]); } fn u8_fixtures() -> impl IntoIterator { vec![ (0, &hex!("80")[..]), (1, &hex!("01")[..]), (0x7F, &hex!("7F")[..]), (0x80, &hex!("8180")[..]), ] } fn c>( it: impl IntoIterator, ) -> impl Iterator { it.into_iter().map(|(k, v)| (k.into(), v)) } fn u16_fixtures() -> impl IntoIterator { c(u8_fixtures()).chain(vec![(0x400, &hex!("820400")[..])]) } fn u32_fixtures() -> impl IntoIterator { c(u16_fixtures()) .chain(vec![(0xFFCCB5, &hex!("83ffccb5")[..]), (0xFFCCB5DD, &hex!("84ffccb5dd")[..])]) } fn u64_fixtures() -> impl IntoIterator { c(u32_fixtures()).chain(vec![ (0xFFCCB5DDFF, &hex!("85ffccb5ddff")[..]), (0xFFCCB5DDFFEE, &hex!("86ffccb5ddffee")[..]), (0xFFCCB5DDFFEE14, &hex!("87ffccb5ddffee14")[..]), (0xFFCCB5DDFFEE1483, &hex!("88ffccb5ddffee1483")[..]), ]) } fn u128_fixtures() -> impl IntoIterator { c(u64_fixtures()).chain(vec![( 0x10203E405060708090A0B0C0D0E0F2, &hex!("8f10203e405060708090a0b0c0d0e0f2")[..], )]) } #[cfg(feature = "ethnum")] fn u256_fixtures() -> impl IntoIterator { c(u128_fixtures()).chain(vec![( ethnum::U256::from_str_radix( "0100020003000400050006000700080009000A0B4B000C000D000E01", 16, ) .unwrap(), &hex!("9c0100020003000400050006000700080009000a0b4b000c000d000e01")[..], )]) } #[cfg(feature = "ethereum-types")] fn eth_u64_fixtures() -> impl IntoIterator { c(u64_fixtures()).chain(vec![ ( ethereum_types::U64::from_str_radix("FFCCB5DDFF", 16).unwrap(), &hex!("85ffccb5ddff")[..], ), ( ethereum_types::U64::from_str_radix("FFCCB5DDFFEE", 16).unwrap(), &hex!("86ffccb5ddffee")[..], ), ( ethereum_types::U64::from_str_radix("FFCCB5DDFFEE14", 16).unwrap(), &hex!("87ffccb5ddffee14")[..], ), ( ethereum_types::U64::from_str_radix("FFCCB5DDFFEE1483", 16).unwrap(), &hex!("88ffccb5ddffee1483")[..], ), ]) } #[cfg(feature = "ethereum-types")] fn eth_u128_fixtures() -> impl IntoIterator { c(u128_fixtures()).chain(vec![( ethereum_types::U128::from_str_radix("10203E405060708090A0B0C0D0E0F2", 16).unwrap(), &hex!("8f10203e405060708090a0b0c0d0e0f2")[..], )]) } #[cfg(feature = "ethereum-types")] fn eth_u256_fixtures() -> impl IntoIterator { c(u128_fixtures()).chain(vec![( ethereum_types::U256::from_str_radix( "0100020003000400050006000700080009000A0B4B000C000D000E01", 16, ) .unwrap(), &hex!("9c0100020003000400050006000700080009000a0b4b000c000d000e01")[..], )]) } #[cfg(feature = "ethereum-types")] fn eth_u512_fixtures() -> impl IntoIterator { c(eth_u256_fixtures()).chain(vec![( ethereum_types::U512::from_str_radix( "0100020003000400050006000700080009000A0B4B000C000D000E010100020003000400050006000700080009000A0B4B000C000D000E01", 16, ) .unwrap(), &hex!("b8380100020003000400050006000700080009000A0B4B000C000D000E010100020003000400050006000700080009000A0B4B000C000D000E01")[..], )]) } macro_rules! uint_rlp_test { ($fixtures:expr) => { for (input, output) in $fixtures { assert_eq!(encoded(input), output); } }; } #[test] fn rlp_uints() { uint_rlp_test!(u8_fixtures()); uint_rlp_test!(u16_fixtures()); uint_rlp_test!(u32_fixtures()); uint_rlp_test!(u64_fixtures()); uint_rlp_test!(u128_fixtures()); #[cfg(feature = "ethnum")] uint_rlp_test!(u256_fixtures()); } #[cfg(feature = "ethereum-types")] #[test] fn rlp_eth_uints() { uint_rlp_test!(eth_u64_fixtures()); uint_rlp_test!(eth_u128_fixtures()); uint_rlp_test!(eth_u256_fixtures()); uint_rlp_test!(eth_u512_fixtures()); } #[test] fn rlp_list() { assert_eq!(encoded_list::(&[]), &hex!("c0")[..]); assert_eq!(encoded_list(&[0xFFCCB5_u64, 0xFFC0B5_u64]), &hex!("c883ffccb583ffc0b5")[..]); } }