diff --git a/Cargo.toml b/Cargo.toml index 155db8f84..e3eaedd30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,6 @@ core = { path = "./crates/core" } tracing = "0.1" tracing-subscriber = "0.3.0" -serde = "1.0.203" +serde = {version = "1.0.203", features = ["derive"]} serde_json = "1.0.117" libmdbx = { version = "0.5.0", features = ["orm"] } diff --git a/crates/core/src/rlp/encode.rs b/crates/core/src/rlp/encode.rs index 821f77502..bfd026f2e 100644 --- a/crates/core/src/rlp/encode.rs +++ b/crates/core/src/rlp/encode.rs @@ -1,6 +1,6 @@ -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - +use crate::U256; use bytes::BufMut; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use tinyvec::ArrayVec; pub trait RLPEncode { @@ -188,6 +188,15 @@ impl RLPEncode for String { } } +impl RLPEncode for U256 { + fn encode(&self, buf: &mut dyn BufMut) { + let leading_zeros_in_bytes: usize = (self.leading_zeros() / 8) as usize; + let mut bytes: [u8; 32] = [0; 32]; + self.to_big_endian(&mut bytes); + bytes[leading_zeros_in_bytes..].encode(buf) + } +} + impl RLPEncode for Vec { fn encode(&self, buf: &mut dyn BufMut) { if self.is_empty() { @@ -214,6 +223,24 @@ impl RLPEncode for Vec { } } +impl RLPEncode for (T, S) { + fn encode(&self, buf: &mut dyn BufMut) { + let total_len = self.0.length() + self.1.length(); + if total_len < 56 { + buf.put_u8(0xc0 + total_len as u8); + } else { + let mut bytes = ArrayVec::<[u8; 8]>::new(); + bytes.extend_from_slice(&total_len.to_be_bytes()); + let start = bytes.iter().position(|&x| x != 0).unwrap(); + let len = bytes.len() - start; + buf.put_u8(0xf7 + len as u8); + buf.put_slice(&bytes[start..]); + } + self.0.encode(buf); + self.1.encode(buf); + } +} + impl RLPEncode for Ipv4Addr { fn encode(&self, buf: &mut dyn BufMut) { self.octets().encode(buf) @@ -289,7 +316,7 @@ impl RLPEncode for ethereum_types::Signature { mod tests { use std::net::IpAddr; - use ethereum_types::Address; + use ethereum_types::{Address, U256}; use hex_literal::hex; use super::RLPEncode; @@ -535,4 +562,32 @@ mod tests { let expected = hex!("94ef2d6d194084c2de36e0dabfce45d046b37d1106"); assert_eq!(encoded, expected); } + + #[test] + fn can_encode_u256() { + let mut encoded = Vec::new(); + U256::from(1).encode(&mut encoded); + assert_eq!(encoded, vec![1]); + + let mut encoded = Vec::new(); + U256::from(128).encode(&mut encoded); + assert_eq!(encoded, vec![0x80 + 1, 128]); + + let mut encoded = Vec::new(); + U256::max_value().encode(&mut encoded); + let bytes = [0xff; 32]; + let mut expected: Vec = bytes.into(); + expected.insert(0, 0x80 + 32); + assert_eq!(encoded, expected); + } + + #[test] + fn can_encode_tuple() { + // TODO: check if works for tuples with total length greater than 55 bytes + let tuple: (u8, u8) = (0x01, 0x02); + let mut encoded = Vec::new(); + tuple.encode(&mut encoded); + let expected = vec![0xc0 + 2, 0x01, 0x02]; + assert_eq!(encoded, expected); + } } diff --git a/crates/core/src/types/block.rs b/crates/core/src/types/block.rs index c2b08805e..1762c3044 100644 --- a/crates/core/src/types/block.rs +++ b/crates/core/src/types/block.rs @@ -58,3 +58,111 @@ impl RLPEncode for BlockHeader { self.parent_beacon_block_root.encode(buf); } } + +// The body of a block on the chain +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Body { + transactions: Vec, + ommers: Vec, + withdrawals: Vec, +} + +impl RLPEncode for Body { + fn encode(&self, buf: &mut dyn bytes::BufMut) { + self.transactions.encode(buf); + self.ommers.encode(buf); + self.withdrawals.encode(buf); + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Withdrawal { + index: u64, + validator_index: u64, + address: Address, + amount: U256, +} + +impl RLPEncode for Withdrawal { + fn encode(&self, buf: &mut dyn bytes::BufMut) { + self.index.encode(buf); + self.validator_index.encode(buf); + self.address.encode(buf); + self.amount.encode(buf); + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Transaction { + LegacyTransaction(LegacyTransaction), + EIP1559Transaction(EIP1559Transaction), +} + +impl RLPEncode for Transaction { + fn encode(&self, buf: &mut dyn bytes::BufMut) { + match self { + Transaction::LegacyTransaction(t) => t.encode(buf), + Transaction::EIP1559Transaction(t) => t.encode(buf), + }; + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct LegacyTransaction { + nonce: U256, + gas_price: u64, + gas: u64, + to: Address, + value: U256, + data: Bytes, + v: U256, + r: U256, + s: U256, +} + +impl RLPEncode for LegacyTransaction { + fn encode(&self, buf: &mut dyn bytes::BufMut) { + self.nonce.encode(buf); + self.gas_price.encode(buf); + self.gas.encode(buf); + self.to.encode(buf); + self.value.encode(buf); + self.data.encode(buf); + self.v.encode(buf); + self.r.encode(buf); + self.s.encode(buf); + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct EIP1559Transaction { + chain_id: u64, + signer_nonce: U256, + max_priority_fee_per_gas: u64, + max_fee_per_gas: u64, + gas_limit: u64, + destination: Address, + amount: u64, + payload: Bytes, + access_list: Vec<(Address, Vec)>, + signature_y_parity: bool, + signature_r: U256, + signature_s: U256, +} + +impl RLPEncode for EIP1559Transaction { + fn encode(&self, buf: &mut dyn bytes::BufMut) { + self.chain_id.encode(buf); + self.signer_nonce.encode(buf); + self.max_priority_fee_per_gas.encode(buf); + self.max_fee_per_gas.encode(buf); + self.gas_limit.encode(buf); + self.destination.encode(buf); + self.amount.encode(buf); + self.payload.encode(buf); + self.access_list.encode(buf); + self.signature_y_parity.encode(buf); + self.signature_r.encode(buf); + self.signature_s.encode(buf); + } +} diff --git a/crates/storage/src/block.rs b/crates/storage/src/block.rs index 46a1482a4..76c351134 100644 --- a/crates/storage/src/block.rs +++ b/crates/storage/src/block.rs @@ -15,3 +15,19 @@ impl Decodable for BlockHeaderRLP { Ok(BlockHeaderRLP(b.to_vec())) } } + +pub struct BlockBodyRLP(Vec); + +impl Encodable for BlockBodyRLP { + type Encoded = Vec; + + fn encode(self) -> Self::Encoded { + self.0 + } +} + +impl Decodable for BlockBodyRLP { + fn decode(b: &[u8]) -> anyhow::Result { + Ok(BlockBodyRLP(b.to_vec())) + } +} diff --git a/crates/storage/src/lib.rs b/crates/storage/src/lib.rs index 067d79eaf..01a2a030c 100644 --- a/crates/storage/src/lib.rs +++ b/crates/storage/src/lib.rs @@ -1,6 +1,6 @@ mod block; -use block::BlockHeaderRLP; +use block::{BlockBodyRLP, BlockHeaderRLP}; use core::types::BlockNumber; use libmdbx::{ orm::{table, Database}, @@ -13,11 +13,16 @@ table!( /// Block headers table. ( Headers ) BlockNumber => BlockHeaderRLP ); - +table!( + /// Block bodies table. + ( Bodies ) BlockNumber => BlockBodyRLP +); /// Initializes a new database with the provided path. If the path is `None`, the database /// will be temporary. pub fn init_db(path: Option>) -> Database { - let tables = [table_info!(Headers)].into_iter().collect(); + let tables = [table_info!(Headers), table_info!(Bodies)] + .into_iter() + .collect(); let path = path.map(|p| p.as_ref().to_path_buf()); Database::create(path, &tables).unwrap() }