diff --git a/Cargo.lock b/Cargo.lock index a1fc1a92d..904d1f124 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7717,7 +7717,6 @@ dependencies = [ name = "reth-primitives" version = "1.0.0-rc.2" dependencies = [ - "alloy-consensus", "alloy-eips", "alloy-genesis", "alloy-primitives", @@ -7726,7 +7725,6 @@ dependencies = [ "alloy-trie", "arbitrary", "assert_matches", - "byteorder", "bytes", "c-kzg", "criterion", @@ -7769,6 +7767,7 @@ dependencies = [ "alloy-rlp", "alloy-rpc-types-eth", "arbitrary", + "byteorder", "bytes", "derive_more", "modular-bitfield", diff --git a/crates/primitives-traits/Cargo.toml b/crates/primitives-traits/Cargo.toml index 871d9ea31..050f84c03 100644 --- a/crates/primitives-traits/Cargo.toml +++ b/crates/primitives-traits/Cargo.toml @@ -14,7 +14,7 @@ workspace = true [dependencies] reth-codecs.workspace = true -alloy-consensus.workspace = true +alloy-consensus = { workspace = true, features = ["serde"] } alloy-eips.workspace = true alloy-genesis.workspace = true alloy-primitives.workspace = true @@ -22,11 +22,12 @@ alloy-rlp.workspace = true alloy-rpc-types-eth = { workspace = true, optional = true } derive_more.workspace = true -revm-primitives.workspace = true +revm-primitives = { workspace = true, features = ["serde"] } # misc thiserror-no-std = { workspace = true, default-features = false } roaring = "0.10.2" +byteorder = "1" # required by reth-codecs modular-bitfield.workspace = true @@ -51,6 +52,7 @@ default = ["std"] std = ["thiserror-no-std/std"] test-utils = ["arbitrary"] arbitrary = [ + "alloy-consensus/arbitrary", "dep:arbitrary", "dep:proptest", "dep:proptest-derive" diff --git a/crates/primitives-traits/src/account.rs b/crates/primitives-traits/src/account.rs index 3c3bb3033..df32be1dc 100644 --- a/crates/primitives-traits/src/account.rs +++ b/crates/primitives-traits/src/account.rs @@ -1,7 +1,12 @@ use alloy_consensus::constants::KECCAK_EMPTY; use alloy_genesis::GenesisAccount; -use alloy_primitives::{keccak256, B256, U256}; +use alloy_primitives::{keccak256, Bytes, B256, U256}; +use byteorder::{BigEndian, ReadBytesExt}; +use bytes::Buf; +use derive_more::Deref; use reth_codecs::{main_codec, Compact}; +use revm_primitives::{Bytecode as RevmBytecode, JumpTable}; +use serde::{Deserialize, Serialize}; /// An Ethereum account. #[main_codec] @@ -45,3 +50,148 @@ impl Account { self.bytecode_hash.unwrap_or(KECCAK_EMPTY) } } + +/// Bytecode for an account. +/// +/// A wrapper around [`revm::primitives::Bytecode`][RevmBytecode] with encoding/decoding support. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, Deref)] +pub struct Bytecode(pub RevmBytecode); + +impl Bytecode { + /// Create new bytecode from raw bytes. + /// + /// No analysis will be performed. + pub fn new_raw(bytes: Bytes) -> Self { + Self(RevmBytecode::new_raw(bytes)) + } +} + +impl Compact for Bytecode { + fn to_compact(self, buf: &mut B) -> usize + where + B: bytes::BufMut + AsMut<[u8]>, + { + let bytecode = &self.0.bytecode()[..]; + buf.put_u32(bytecode.len() as u32); + buf.put_slice(bytecode); + let len = match &self.0 { + RevmBytecode::LegacyRaw(_) => { + buf.put_u8(0); + 1 + } + // `1` has been removed. + RevmBytecode::LegacyAnalyzed(analyzed) => { + buf.put_u8(2); + buf.put_u64(analyzed.original_len() as u64); + let map = analyzed.jump_table().as_slice(); + buf.put_slice(map); + 1 + 8 + map.len() + } + RevmBytecode::Eof(_) => { + // buf.put_u8(3); + // TODO(EOF) + todo!("EOF") + } + }; + len + bytecode.len() + 4 + } + + // # Panics + // + // A panic will be triggered if a bytecode variant of 1 or greater than 2 is passed from the + // database. + fn from_compact(mut buf: &[u8], _: usize) -> (Self, &[u8]) { + let len = buf.read_u32::().expect("could not read bytecode length"); + let bytes = Bytes::from(buf.copy_to_bytes(len as usize)); + let variant = buf.read_u8().expect("could not read bytecode variant"); + let decoded = match variant { + 0 => Self(RevmBytecode::new_raw(bytes)), + 1 => unreachable!("Junk data in database: checked Bytecode variant was removed"), + 2 => Self(unsafe { + RevmBytecode::new_analyzed( + bytes, + buf.read_u64::().unwrap() as usize, + JumpTable::from_slice(buf), + ) + }), + // TODO(EOF) + 3 => todo!("EOF"), + _ => unreachable!("Junk data in database: unknown Bytecode variant"), + }; + (decoded, &[]) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_primitives::{hex_literal::hex, B256, U256}; + use revm_primitives::LegacyAnalyzedBytecode; + + #[test] + fn test_account() { + let mut buf = vec![]; + let mut acc = Account::default(); + let len = acc.to_compact(&mut buf); + assert_eq!(len, 2); + + acc.balance = U256::from(2); + let len = acc.to_compact(&mut buf); + assert_eq!(len, 3); + + acc.nonce = 2; + let len = acc.to_compact(&mut buf); + assert_eq!(len, 4); + } + + #[test] + fn test_empty_account() { + let mut acc = Account { nonce: 0, balance: U256::ZERO, bytecode_hash: None }; + // Nonce 0, balance 0, and bytecode hash set to None is considered empty. + assert!(acc.is_empty()); + + acc.bytecode_hash = Some(KECCAK_EMPTY); + // Nonce 0, balance 0, and bytecode hash set to KECCAK_EMPTY is considered empty. + assert!(acc.is_empty()); + + acc.balance = U256::from(2); + // Non-zero balance makes it non-empty. + assert!(!acc.is_empty()); + + acc.balance = U256::ZERO; + acc.nonce = 10; + // Non-zero nonce makes it non-empty. + assert!(!acc.is_empty()); + + acc.nonce = 0; + acc.bytecode_hash = Some(B256::from(U256::ZERO)); + // Non-empty bytecode hash makes it non-empty. + assert!(!acc.is_empty()); + } + + #[test] + fn test_bytecode() { + let mut buf = vec![]; + let bytecode = Bytecode::new_raw(Bytes::default()); + let len = bytecode.to_compact(&mut buf); + assert_eq!(len, 5); + + let mut buf = vec![]; + let bytecode = Bytecode::new_raw(Bytes::from(&hex!("ffff"))); + let len = bytecode.to_compact(&mut buf); + assert_eq!(len, 7); + + let mut buf = vec![]; + let bytecode = Bytecode(RevmBytecode::LegacyAnalyzed(LegacyAnalyzedBytecode::new( + Bytes::from(&hex!("ffff")), + 2, + JumpTable::from_slice(&[0]), + ))); + let len = bytecode.clone().to_compact(&mut buf); + assert_eq!(len, 16); + + let (decoded, remainder) = Bytecode::from_compact(&buf, len); + assert_eq!(decoded, bytecode); + assert!(remainder.is_empty()); + } +} diff --git a/crates/primitives-traits/src/lib.rs b/crates/primitives-traits/src/lib.rs index 3a352f292..1517090d6 100644 --- a/crates/primitives-traits/src/lib.rs +++ b/crates/primitives-traits/src/lib.rs @@ -19,11 +19,17 @@ pub mod constants; /// Minimal account pub mod account; -pub use account::Account; +pub use account::{Account, Bytecode}; mod integer_list; pub use integer_list::IntegerList; +pub mod request; +pub use request::{Request, Requests}; + +mod withdrawal; +pub use withdrawal::{Withdrawal, Withdrawals}; + /// Common header types pub mod header; #[cfg(any(test, feature = "arbitrary", feature = "test-utils"))] diff --git a/crates/primitives/src/request.rs b/crates/primitives-traits/src/request.rs similarity index 97% rename from crates/primitives/src/request.rs rename to crates/primitives-traits/src/request.rs index e3b5f220b..99c2375e2 100644 --- a/crates/primitives/src/request.rs +++ b/crates/primitives-traits/src/request.rs @@ -1,6 +1,6 @@ //! EIP-7685 requests. -use crate::Request; +pub use alloy_consensus::Request; use alloy_eips::eip7685::{Decodable7685, Encodable7685}; use alloy_rlp::{Decodable, Encodable}; use derive_more::{Deref, DerefMut, From, IntoIterator}; diff --git a/crates/primitives/src/withdrawal.rs b/crates/primitives-traits/src/withdrawal.rs similarity index 99% rename from crates/primitives/src/withdrawal.rs rename to crates/primitives-traits/src/withdrawal.rs index cfd0de226..d9285f6bc 100644 --- a/crates/primitives/src/withdrawal.rs +++ b/crates/primitives-traits/src/withdrawal.rs @@ -68,7 +68,7 @@ impl Withdrawals { #[cfg(test)] mod tests { use super::*; - use crate::Address; + use alloy_primitives::Address; use alloy_rlp::{RlpDecodable, RlpEncodable}; use proptest::proptest; diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 37b67fe46..29db6c993 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -22,7 +22,6 @@ reth-chainspec.workspace = true revm-primitives = { workspace = true, features = ["serde"] } # ethereum -alloy-consensus = { workspace = true, features = ["serde"] } alloy-primitives = { workspace = true, features = ["rand", "rlp"] } alloy-rlp = { workspace = true, features = ["arrayvec"] } alloy-rpc-types = { workspace = true, optional = true } @@ -40,7 +39,6 @@ c-kzg = { workspace = true, features = ["serde"], optional = true } # misc bytes.workspace = true -byteorder = "1" derive_more.workspace = true modular-bitfield.workspace = true once_cell.workspace = true @@ -62,7 +60,6 @@ revm-primitives = { workspace = true, features = ["arbitrary"] } nybbles = { workspace = true, features = ["arbitrary"] } alloy-trie = { workspace = true, features = ["arbitrary"] } alloy-eips = { workspace = true, features = ["arbitrary"] } -alloy-consensus = { workspace = true, features = ["arbitrary"] } assert_matches.workspace = true arbitrary = { workspace = true, features = ["derive"] } @@ -94,7 +91,6 @@ arbitrary = [ "reth-ethereum-forks/arbitrary", "nybbles/arbitrary", "alloy-trie/arbitrary", - "alloy-consensus/arbitrary", "alloy-eips/arbitrary", "dep:arbitrary", "dep:proptest", diff --git a/crates/primitives/src/account.rs b/crates/primitives/src/account.rs deleted file mode 100644 index 0fa55108e..000000000 --- a/crates/primitives/src/account.rs +++ /dev/null @@ -1,154 +0,0 @@ -use crate::revm_primitives::{Bytecode as RevmBytecode, Bytes}; -use byteorder::{BigEndian, ReadBytesExt}; -use bytes::Buf; -use derive_more::Deref; -use reth_codecs::Compact; -use revm_primitives::JumpTable; -use serde::{Deserialize, Serialize}; - -pub use reth_primitives_traits::Account; - -/// Bytecode for an account. -/// -/// A wrapper around [`revm::primitives::Bytecode`][RevmBytecode] with encoding/decoding support. -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, Deref)] -pub struct Bytecode(pub RevmBytecode); - -impl Bytecode { - /// Create new bytecode from raw bytes. - /// - /// No analysis will be performed. - pub fn new_raw(bytes: Bytes) -> Self { - Self(RevmBytecode::new_raw(bytes)) - } -} - -impl Compact for Bytecode { - fn to_compact(self, buf: &mut B) -> usize - where - B: bytes::BufMut + AsMut<[u8]>, - { - let bytecode = &self.0.bytecode()[..]; - buf.put_u32(bytecode.len() as u32); - buf.put_slice(bytecode); - let len = match &self.0 { - RevmBytecode::LegacyRaw(_) => { - buf.put_u8(0); - 1 - } - // `1` has been removed. - RevmBytecode::LegacyAnalyzed(analyzed) => { - buf.put_u8(2); - buf.put_u64(analyzed.original_len() as u64); - let map = analyzed.jump_table().as_slice(); - buf.put_slice(map); - 1 + 8 + map.len() - } - RevmBytecode::Eof(_) => { - // buf.put_u8(3); - // TODO(EOF) - todo!("EOF") - } - }; - len + bytecode.len() + 4 - } - - // # Panics - // - // A panic will be triggered if a bytecode variant of 1 or greater than 2 is passed from the - // database. - fn from_compact(mut buf: &[u8], _: usize) -> (Self, &[u8]) { - let len = buf.read_u32::().expect("could not read bytecode length"); - let bytes = Bytes::from(buf.copy_to_bytes(len as usize)); - let variant = buf.read_u8().expect("could not read bytecode variant"); - let decoded = match variant { - 0 => Self(RevmBytecode::new_raw(bytes)), - 1 => unreachable!("Junk data in database: checked Bytecode variant was removed"), - 2 => Self(unsafe { - RevmBytecode::new_analyzed( - bytes, - buf.read_u64::().unwrap() as usize, - JumpTable::from_slice(buf), - ) - }), - // TODO(EOF) - 3 => todo!("EOF"), - _ => unreachable!("Junk data in database: unknown Bytecode variant"), - }; - (decoded, &[]) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{hex_literal::hex, B256, KECCAK_EMPTY, U256}; - use revm_primitives::LegacyAnalyzedBytecode; - - #[test] - fn test_account() { - let mut buf = vec![]; - let mut acc = Account::default(); - let len = acc.to_compact(&mut buf); - assert_eq!(len, 2); - - acc.balance = U256::from(2); - let len = acc.to_compact(&mut buf); - assert_eq!(len, 3); - - acc.nonce = 2; - let len = acc.to_compact(&mut buf); - assert_eq!(len, 4); - } - - #[test] - fn test_empty_account() { - let mut acc = Account { nonce: 0, balance: U256::ZERO, bytecode_hash: None }; - // Nonce 0, balance 0, and bytecode hash set to None is considered empty. - assert!(acc.is_empty()); - - acc.bytecode_hash = Some(KECCAK_EMPTY); - // Nonce 0, balance 0, and bytecode hash set to KECCAK_EMPTY is considered empty. - assert!(acc.is_empty()); - - acc.balance = U256::from(2); - // Non-zero balance makes it non-empty. - assert!(!acc.is_empty()); - - acc.balance = U256::ZERO; - acc.nonce = 10; - // Non-zero nonce makes it non-empty. - assert!(!acc.is_empty()); - - acc.nonce = 0; - acc.bytecode_hash = Some(B256::from(U256::ZERO)); - // Non-empty bytecode hash makes it non-empty. - assert!(!acc.is_empty()); - } - - #[test] - fn test_bytecode() { - let mut buf = vec![]; - let bytecode = Bytecode::new_raw(Bytes::default()); - let len = bytecode.to_compact(&mut buf); - assert_eq!(len, 5); - - let mut buf = vec![]; - let bytecode = Bytecode::new_raw(Bytes::from(&hex!("ffff"))); - let len = bytecode.to_compact(&mut buf); - assert_eq!(len, 7); - - let mut buf = vec![]; - let bytecode = Bytecode(RevmBytecode::LegacyAnalyzed(LegacyAnalyzedBytecode::new( - Bytes::from(&hex!("ffff")), - 2, - JumpTable::from_slice(&[0]), - ))); - let len = bytecode.clone().to_compact(&mut buf); - assert_eq!(len, 16); - - let (decoded, remainder) = Bytecode::from_compact(&buf, len); - assert_eq!(decoded, bytecode); - assert!(remainder.is_empty()); - } -} diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index 08c261a39..3bd41e2cd 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -1,5 +1,5 @@ use crate::{ - Address, Bytes, GotExpected, Header, Requests, SealedHeader, TransactionSigned, + Address, Bytes, GotExpected, Header, SealedHeader, TransactionSigned, TransactionSignedEcRecovered, Withdrawals, B256, }; pub use alloy_eips::eip1898::{ @@ -12,6 +12,7 @@ use proptest::prelude::prop_compose; use reth_codecs::derive_arbitrary; #[cfg(any(test, feature = "arbitrary"))] pub use reth_primitives_traits::test_utils::{generate_valid_header, valid_header_strategy}; +use reth_primitives_traits::Requests; use serde::{Deserialize, Serialize}; #[cfg(not(feature = "std"))] diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index e5b0f4a6d..e1cc48c00 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -22,7 +22,6 @@ #[cfg(not(feature = "std"))] extern crate alloc; -mod account; #[cfg(feature = "alloy-compat")] mod alloy_compat; pub mod basefee; @@ -37,14 +36,11 @@ pub mod header; mod log; pub mod proofs; mod receipt; -mod request; /// Helpers for working with revm pub mod revm; pub use reth_static_file_types as static_file; mod storage; pub mod transaction; -mod withdrawal; -pub use account::{Account, Bytecode}; #[cfg(any(test, feature = "arbitrary"))] pub use block::{generate_valid_header, valid_header_strategy}; pub use block::{ @@ -64,7 +60,7 @@ pub use log::{logs_bloom, Log}; pub use receipt::{ gas_spent_by_transactions, Receipt, ReceiptWithBloom, ReceiptWithBloomRef, Receipts, }; -pub use request::Requests; +pub use reth_primitives_traits::{Account, Bytecode, Request, Requests, Withdrawal, Withdrawals}; pub use static_file::StaticFileSegment; pub use storage::StorageEntry; @@ -85,11 +81,8 @@ pub use transaction::{ LEGACY_TX_TYPE_ID, }; -pub use withdrawal::{Withdrawal, Withdrawals}; - // Re-exports pub use self::ruint::UintTryTo; -pub use alloy_consensus::Request; pub use alloy_primitives::{ self, address, b256, bloom, bytes, bytes::{Buf, BufMut, BytesMut},