diff --git a/Cargo.toml b/Cargo.toml
index a845b314c5c..55895ad15c6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,7 +3,6 @@ members = [
"ethrex",
"crates/consensus",
"crates/core",
- "crates/evm",
"crates/net",
"crates/rpc",
"crates/storage",
@@ -18,7 +17,6 @@ edition = "2021"
[workspace.dependencies]
ethrex-consensus = { path = "./crates/consensus" }
ethrex-core = { path = "./crates/core" }
-ethrex-evm = { path = "./crates/evm" }
ethrex-net = { path = "./crates/net" }
ethrex-rpc = { path = "./crates/rpc" }
ethrex-storage = { path = "./crates/storage" }
@@ -29,6 +27,6 @@ tracing-subscriber = "0.3.0"
serde = { version = "1.0.203", features = ["derive"] }
serde_json = "1.0.117"
libmdbx = { version = "0.5.0", features = ["orm"] }
+bytes = "1.6.0"
tokio = { version = "1.38.0", features = ["full"] }
-bytes = { version = "1.6.0", features = ["serde"] }
thiserror = "1.0.61"
diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml
index 8915a056773..40feadcdec2 100644
--- a/crates/core/Cargo.toml
+++ b/crates/core/Cargo.toml
@@ -12,8 +12,13 @@ serde.workspace = true
serde_json.workspace = true
thiserror.workspace = true
keccak-hash = "0.10.0"
-patricia-merkle-tree = { git = "https://github.com/lambdaclass/merkle_patricia_tree.git" }
sha3 = "0.10.8"
+secp256k1 = { version = "0.29", default-features = false, features = [
+ "global-context",
+ "recovery",
+] }
+revm = { version = "10.0.0", features = ["serde", "std", "serde-json"] }
+patricia-merkle-tree = { git = "https://github.com/lambdaclass/merkle_patricia_tree.git" }
bytes.workspace = true
[dev-dependencies]
diff --git a/crates/core/src/evm/execution_result.rs b/crates/core/src/evm/execution_result.rs
new file mode 100644
index 00000000000..42c638eff3f
--- /dev/null
+++ b/crates/core/src/evm/execution_result.rs
@@ -0,0 +1,86 @@
+use bytes::Bytes;
+use ethereum_types::Address;
+use revm::primitives::result::Output as RevmOutput;
+use revm::primitives::result::SuccessReason as RevmSuccessReason;
+use revm::primitives::ExecutionResult as RevmExecutionResult;
+
+pub enum ExecutionResult {
+ Success {
+ reason: SuccessReason,
+ gas_used: u64,
+ gas_refunded: u64,
+ output: Output,
+ },
+ /// Reverted by `REVERT` opcode
+ Revert { gas_used: u64, output: Bytes },
+ /// Reverted for other reasons, spends all gas.
+ Halt {
+ reason: String,
+ /// Halting will spend all the gas, which will be equal to gas_limit.
+ gas_used: u64,
+ },
+}
+
+pub enum SuccessReason {
+ Stop,
+ Return,
+ SelfDestruct,
+ EofReturnContract,
+}
+
+pub enum Output {
+ Call(Bytes),
+ Create(Bytes, Option
),
+}
+
+impl From for ExecutionResult {
+ fn from(val: RevmExecutionResult) -> Self {
+ match val {
+ RevmExecutionResult::Success {
+ reason,
+ gas_used,
+ gas_refunded,
+ logs: _,
+ output,
+ } => ExecutionResult::Success {
+ reason: match reason {
+ RevmSuccessReason::Stop => SuccessReason::Stop,
+ RevmSuccessReason::Return => SuccessReason::Return,
+ RevmSuccessReason::SelfDestruct => SuccessReason::SelfDestruct,
+ RevmSuccessReason::EofReturnContract => SuccessReason::EofReturnContract,
+ },
+ gas_used,
+ gas_refunded,
+ output: match output {
+ RevmOutput::Call(bytes) => Output::Call(bytes.0),
+ RevmOutput::Create(bytes, addr) => Output::Create(
+ bytes.0,
+ addr.map(|addr| Address::from_slice(addr.0.as_ref())),
+ ),
+ },
+ },
+ RevmExecutionResult::Revert { gas_used, output } => ExecutionResult::Revert {
+ gas_used,
+ output: output.0,
+ },
+ RevmExecutionResult::Halt { reason, gas_used } => ExecutionResult::Halt {
+ reason: format!("{:?}", reason),
+ gas_used,
+ },
+ }
+ }
+}
+
+impl ExecutionResult {
+ pub fn is_success(&self) -> bool {
+ matches!(
+ self,
+ ExecutionResult::Success {
+ reason: _,
+ gas_used: _,
+ gas_refunded: _,
+ output: _
+ }
+ )
+ }
+}
diff --git a/crates/core/src/evm/mod.rs b/crates/core/src/evm/mod.rs
new file mode 100644
index 00000000000..7e4b8fa1b41
--- /dev/null
+++ b/crates/core/src/evm/mod.rs
@@ -0,0 +1,115 @@
+mod execution_result;
+
+use crate::types::TxKind;
+
+use super::{
+ types::{Account, BlockHeader, Transaction},
+ Address,
+};
+use revm::{
+ inspector_handle_register,
+ inspectors::TracerEip3155,
+ primitives::{BlockEnv, Bytecode, TxEnv, U256},
+ CacheState, Evm,
+};
+use std::collections::HashMap;
+// Rename imported types for clarity
+use revm::primitives::AccountInfo as RevmAccountInfo;
+use revm::primitives::Address as RevmAddress;
+use revm::primitives::TxKind as RevmTxKind;
+// Export needed types
+pub use execution_result::*;
+pub use revm::primitives::SpecId;
+
+pub fn execute_tx(
+ tx: &Transaction,
+ header: &BlockHeader,
+ pre: &HashMap, // TODO: Modify this type when we have a defined State structure
+ spec_id: SpecId,
+) -> ExecutionResult {
+ let block_env = block_env(header);
+ let tx_env = tx_env(tx);
+ let cache_state = cache_state(pre);
+ let mut state = revm::db::State::builder()
+ .with_cached_prestate(cache_state)
+ .with_bundle_update()
+ .build();
+ let mut evm = Evm::builder()
+ .with_db(&mut state)
+ .with_block_env(block_env)
+ .with_tx_env(tx_env)
+ .with_spec_id(spec_id)
+ .reset_handler()
+ .with_external_context(TracerEip3155::new(Box::new(std::io::stderr())).without_summary())
+ .append_handler_register(inspector_handle_register)
+ .build();
+ let tx_result = evm.transact().unwrap();
+ tx_result.result.into()
+}
+
+fn cache_state(pre: &HashMap) -> CacheState {
+ let mut cache_state = revm::CacheState::new(false);
+ for (address, account) in pre {
+ let acc_info = RevmAccountInfo {
+ balance: U256::from_limbs(account.info.balance.0),
+ code_hash: account.info.code_hash.0.into(),
+ code: Some(Bytecode::new_raw(account.code.clone().into())),
+ nonce: account.info.nonce,
+ };
+
+ let mut storage = HashMap::new();
+ for (k, v) in &account.storage {
+ storage.insert(U256::from_be_bytes(k.0), U256::from_be_bytes(v.0));
+ }
+
+ cache_state.insert_account_with_storage(address.to_fixed_bytes().into(), acc_info, storage);
+ }
+ cache_state
+}
+
+fn block_env(header: &BlockHeader) -> BlockEnv {
+ BlockEnv {
+ number: U256::from(header.number),
+ coinbase: RevmAddress(header.coinbase.0.into()),
+ timestamp: U256::from(header.timestamp),
+ gas_limit: U256::from(header.gas_limit),
+ basefee: U256::from(header.base_fee_per_gas),
+ difficulty: U256::from_limbs(header.difficulty.0),
+ prevrandao: Some(header.prev_randao.as_fixed_bytes().into()),
+ ..Default::default()
+ }
+}
+
+fn tx_env(tx: &Transaction) -> TxEnv {
+ TxEnv {
+ caller: RevmAddress(tx.sender().0.into()),
+ gas_limit: tx.gas_limit(),
+ gas_price: U256::from(tx.gas_price()),
+ transact_to: tx.to().into(),
+ value: U256::from_limbs(tx.value().0),
+ data: tx.data().clone().into(),
+ nonce: Some(tx.nonce()),
+ chain_id: tx.chain_id(),
+ access_list: tx
+ .access_list()
+ .into_iter()
+ .map(|(addr, list)| {
+ (
+ RevmAddress(addr.0.into()),
+ list.into_iter().map(|a| U256::from_be_bytes(a.0)).collect(),
+ )
+ })
+ .collect(),
+ gas_priority_fee: tx.max_priority_fee().map(U256::from),
+ ..Default::default()
+ }
+}
+
+impl From for RevmTxKind {
+ fn from(val: TxKind) -> Self {
+ match val {
+ TxKind::Call(address) => RevmTxKind::Call(address.0.into()),
+ TxKind::Create => RevmTxKind::Create,
+ }
+ }
+}
diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs
index 7a5c7dc0aa0..83614d610dc 100644
--- a/crates/core/src/lib.rs
+++ b/crates/core/src/lib.rs
@@ -1,4 +1,5 @@
pub mod rlp;
pub use ethereum_types::*;
+pub mod evm;
pub mod serde_utils;
pub mod types;
diff --git a/crates/core/src/types/account.rs b/crates/core/src/types/account.rs
index 2a9604b39b2..17d5b1dd680 100644
--- a/crates/core/src/types/account.rs
+++ b/crates/core/src/types/account.rs
@@ -36,7 +36,7 @@ impl From for Account {
}
}
-fn code_hash(code: &Bytes) -> H256 {
+pub fn code_hash(code: &Bytes) -> H256 {
keccak_hash::keccak(code.as_ref())
}
diff --git a/crates/core/src/types/block.rs b/crates/core/src/types/block.rs
index a7fd404f9b7..971f742362e 100644
--- a/crates/core/src/types/block.rs
+++ b/crates/core/src/types/block.rs
@@ -1,40 +1,39 @@
use crate::{
- rlp::{
- constants::RLP_NULL, decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError,
- structs::Encoder,
- },
+ rlp::{encode::RLPEncode, structs::Encoder},
Address, H256, U256,
};
use bytes::Bytes;
use patricia_merkle_tree::PatriciaMerkleTree;
use sha3::Keccak256;
+use super::Transaction;
+
pub type BlockNumber = u64;
pub type Bloom = [u8; 256];
/// Header part of a block on the chain.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BlockHeader {
- parent_hash: H256,
- ommers_hash: H256,
- coinbase: Address,
- state_root: H256,
- transactions_root: H256,
- receipt_root: H256,
- logs_bloom: Bloom,
- difficulty: U256,
- number: BlockNumber,
- gas_limit: u64,
- gas_used: u64,
- timestamp: u64,
- extra_data: Bytes,
- prev_randao: H256,
- nonce: u64,
- base_fee_per_gas: u64,
- withdrawals_root: H256,
- blob_gas_used: u64,
- excess_blob_gas: u64,
- parent_beacon_block_root: H256,
+ pub parent_hash: H256,
+ pub ommers_hash: H256, // ommer = uncle
+ pub coinbase: Address,
+ pub state_root: H256,
+ pub transactions_root: H256,
+ pub receipt_root: H256,
+ pub logs_bloom: Bloom,
+ pub difficulty: U256,
+ pub number: BlockNumber,
+ pub gas_limit: u64,
+ pub gas_used: u64,
+ pub timestamp: u64,
+ pub extra_data: Bytes,
+ pub prev_randao: H256,
+ pub nonce: u64,
+ pub base_fee_per_gas: u64,
+ pub withdrawals_root: H256,
+ pub blob_gas_used: u64,
+ pub excess_blob_gas: u64,
+ pub parent_beacon_block_root: H256,
}
impl RLPEncode for BlockHeader {
@@ -67,7 +66,7 @@ impl RLPEncode for BlockHeader {
// The body of a block on the chain
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BlockBody {
- transactions: Vec,
+ pub transactions: Vec,
// TODO: ommers list is always empty, so we can remove it
ommers: Vec,
withdrawals: Vec,
@@ -135,176 +134,3 @@ impl RLPEncode for Withdrawal {
.finish();
}
}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum Transaction {
- LegacyTransaction(LegacyTransaction),
- EIP1559Transaction(EIP1559Transaction),
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub enum TxType {
- Legacy = 0x00,
- EIP2930 = 0x01,
- EIP1559 = 0x02,
- EIP4844 = 0x03,
-}
-
-impl Transaction {
- pub fn encode_with_type(&self, buf: &mut dyn bytes::BufMut) {
- // tx_type || RLP(tx) if tx_type != 0
- // RLP(tx) else
- match self {
- // Legacy transactions don't have a prefix
- Transaction::LegacyTransaction(_) => {}
- _ => buf.put_u8(self.tx_type() as u8),
- }
-
- self.encode(buf);
- }
-
- pub fn tx_type(&self) -> TxType {
- match self {
- Transaction::LegacyTransaction(_) => TxType::Legacy,
- Transaction::EIP1559Transaction(_) => TxType::EIP1559,
- }
- }
-}
-
-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),
- };
- }
-}
-
-/// The transaction's kind: call or create.
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum TxKind {
- Call(Address),
- Create,
-}
-
-impl RLPEncode for TxKind {
- fn encode(&self, buf: &mut dyn bytes::BufMut) {
- match self {
- Self::Call(address) => address.encode(buf),
- Self::Create => buf.put_u8(RLP_NULL),
- }
- }
-}
-
-impl RLPDecode for TxKind {
- fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
- let first_byte = rlp.first().ok_or(RLPDecodeError::InvalidLength)?;
- if *first_byte == RLP_NULL {
- return Ok((Self::Create, &rlp[1..]));
- }
- Address::decode_unfinished(rlp).map(|(t, rest)| (Self::Call(t), rest))
- }
-}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct LegacyTransaction {
- nonce: u64,
- gas_price: U256,
- gas: u64,
- /// The recipient of the transaction.
- /// Create transactions contain a [`null`](RLP_NULL) value in this field.
- to: TxKind,
- value: U256,
- data: Bytes,
- v: U256,
- r: U256,
- s: U256,
-}
-
-impl RLPEncode for LegacyTransaction {
- fn encode(&self, buf: &mut dyn bytes::BufMut) {
- Encoder::new(buf)
- .encode_field(&self.nonce)
- .encode_field(&self.gas_price)
- .encode_field(&self.gas)
- .encode_field(&self.to)
- .encode_field(&self.value)
- .encode_field(&self.data)
- .encode_field(&self.v)
- .encode_field(&self.r)
- .encode_field(&self.s)
- .finish();
- }
-}
-
-#[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) {
- Encoder::new(buf)
- .encode_field(&self.chain_id)
- .encode_field(&self.signer_nonce)
- .encode_field(&self.max_priority_fee_per_gas)
- .encode_field(&self.max_fee_per_gas)
- .encode_field(&self.gas_limit)
- .encode_field(&self.destination)
- .encode_field(&self.amount)
- .encode_field(&self.payload)
- .encode_field(&self.access_list)
- .encode_field(&self.signature_y_parity)
- .encode_field(&self.signature_r)
- .encode_field(&self.signature_s)
- .finish();
- }
-}
-
-#[cfg(test)]
-mod tests {
- use hex_literal::hex;
-
- use super::{BlockBody, LegacyTransaction};
- use crate::{
- types::{Transaction, TxKind},
- U256,
- };
-
- #[test]
- fn test_compute_transactions_root() {
- let mut body = BlockBody::empty();
- let tx = LegacyTransaction {
- nonce: 0,
- gas_price: 0x0a.into(),
- gas: 0x05f5e100,
- to: TxKind::Call(hex!("1000000000000000000000000000000000000000").into()),
- value: 0.into(),
- data: Default::default(),
- v: U256::from(0x1b),
- r: U256::from(hex!(
- "7e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37"
- )),
- s: U256::from(hex!(
- "5f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509b"
- )),
- };
- body.transactions.push(Transaction::LegacyTransaction(tx));
- let expected_root =
- hex!("8151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcb");
- let result = body.compute_transactions_root();
-
- assert_eq!(result, expected_root.into());
- }
-}
diff --git a/crates/core/src/types/mod.rs b/crates/core/src/types/mod.rs
index 92fa004e0fe..2006464f251 100644
--- a/crates/core/src/types/mod.rs
+++ b/crates/core/src/types/mod.rs
@@ -2,8 +2,10 @@ mod account;
mod block;
mod genesis;
mod receipt;
+mod transaction;
pub use account::*;
pub use block::*;
pub use genesis::*;
pub use receipt::*;
+pub use transaction::*;
diff --git a/crates/core/src/types/transaction.rs b/crates/core/src/types/transaction.rs
new file mode 100644
index 00000000000..cf1e2dc7721
--- /dev/null
+++ b/crates/core/src/types/transaction.rs
@@ -0,0 +1,318 @@
+use bytes::Bytes;
+use ethereum_types::{Address, H256, U256};
+use secp256k1::{ecdsa::RecoveryId, Message, SECP256K1};
+use sha3::{Digest, Keccak256};
+
+use crate::rlp::{
+ constants::RLP_NULL, decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError,
+ structs::Encoder,
+};
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum Transaction {
+ LegacyTransaction(LegacyTransaction),
+ EIP1559Transaction(EIP1559Transaction),
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct LegacyTransaction {
+ pub nonce: u64,
+ pub gas_price: u64,
+ pub gas: u64,
+ /// The recipient of the transaction.
+ /// Create transactions contain a [`null`](RLP_NULL) value in this field.
+ pub to: TxKind,
+ pub value: U256,
+ pub data: Bytes,
+ pub v: U256,
+ pub r: U256,
+ pub s: U256,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct EIP1559Transaction {
+ pub chain_id: u64,
+ pub signer_nonce: u64,
+ pub max_priority_fee_per_gas: u64,
+ pub max_fee_per_gas: u64,
+ pub gas_limit: u64,
+ pub destination: Address,
+ pub amount: U256,
+ pub payload: Bytes,
+ pub access_list: Vec<(Address, Vec)>,
+ pub signature_y_parity: bool,
+ pub signature_r: U256,
+ pub signature_s: U256,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum TxType {
+ Legacy = 0x00,
+ EIP2930 = 0x01,
+ EIP1559 = 0x02,
+ EIP4844 = 0x03,
+}
+
+impl Transaction {
+ pub fn encode_with_type(&self, buf: &mut dyn bytes::BufMut) {
+ // tx_type || RLP(tx) if tx_type != 0
+ // RLP(tx) else
+ match self {
+ // Legacy transactions don't have a prefix
+ Transaction::LegacyTransaction(_) => {}
+ _ => buf.put_u8(self.tx_type() as u8),
+ }
+
+ self.encode(buf);
+ }
+
+ pub fn tx_type(&self) -> TxType {
+ match self {
+ Transaction::LegacyTransaction(_) => TxType::Legacy,
+ Transaction::EIP1559Transaction(_) => TxType::EIP1559,
+ }
+ }
+}
+
+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),
+ };
+ }
+}
+
+/// The transaction's kind: call or create.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum TxKind {
+ Call(Address),
+ Create,
+}
+
+impl RLPEncode for TxKind {
+ fn encode(&self, buf: &mut dyn bytes::BufMut) {
+ match self {
+ Self::Call(address) => address.encode(buf),
+ Self::Create => buf.put_u8(RLP_NULL),
+ }
+ }
+}
+
+impl RLPDecode for TxKind {
+ fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
+ let first_byte = rlp.first().ok_or(RLPDecodeError::InvalidLength)?;
+ if *first_byte == RLP_NULL {
+ return Ok((Self::Create, &rlp[1..]));
+ }
+ Address::decode_unfinished(rlp).map(|(t, rest)| (Self::Call(t), rest))
+ }
+}
+
+impl RLPEncode for LegacyTransaction {
+ fn encode(&self, buf: &mut dyn bytes::BufMut) {
+ Encoder::new(buf)
+ .encode_field(&self.nonce)
+ .encode_field(&self.gas_price)
+ .encode_field(&self.gas)
+ .encode_field(&self.to)
+ .encode_field(&self.value)
+ .encode_field(&self.data)
+ .encode_field(&self.v)
+ .encode_field(&self.r)
+ .encode_field(&self.s)
+ .finish();
+ }
+}
+
+impl RLPEncode for EIP1559Transaction {
+ fn encode(&self, buf: &mut dyn bytes::BufMut) {
+ Encoder::new(buf)
+ .encode_field(&self.chain_id)
+ .encode_field(&self.signer_nonce)
+ .encode_field(&self.max_priority_fee_per_gas)
+ .encode_field(&self.max_fee_per_gas)
+ .encode_field(&self.gas_limit)
+ .encode_field(&self.destination)
+ .encode_field(&self.amount)
+ .encode_field(&self.payload)
+ .encode_field(&self.access_list)
+ .encode_field(&self.signature_y_parity)
+ .encode_field(&self.signature_r)
+ .encode_field(&self.signature_s)
+ .finish();
+ }
+}
+
+impl Transaction {
+ pub fn sender(&self) -> Address {
+ match self {
+ Transaction::LegacyTransaction(tx) => {
+ let signature_y_parity = match self.chain_id() {
+ Some(chain_id) => tx.v.as_u64().saturating_sub(35 + chain_id * 2) != 0,
+ None => tx.v.as_u64().saturating_sub(27) != 0,
+ };
+ let mut buf = vec![];
+ Encoder::new(&mut buf)
+ .encode_field(&tx.nonce)
+ .encode_field(&tx.gas_price)
+ .encode_field(&tx.gas)
+ .encode_field(&tx.to)
+ .encode_field(&tx.value)
+ .encode_field(&tx.data)
+ .finish();
+ recover_address(&tx.r, &tx.s, signature_y_parity, &Bytes::from(buf))
+ }
+ Transaction::EIP1559Transaction(tx) => {
+ let mut buf = vec![];
+ Encoder::new(&mut buf)
+ .encode_field(&tx.signer_nonce)
+ // TODO: The following two fields are not part of EIP1559Transaction, other fields were used instead
+ // consider adding them
+ .encode_field(&tx.max_fee_per_gas) // gas_price
+ .encode_field(&tx.gas_limit) // gas
+ .encode_field(&tx.destination)
+ .encode_field(&tx.amount)
+ .encode_field(&tx.payload)
+ .encode_field(&tx.chain_id)
+ .encode_field(&0_u64)
+ .encode_field(&0_u64)
+ .finish();
+ recover_address(
+ &tx.signature_r,
+ &tx.signature_s,
+ tx.signature_y_parity,
+ &Bytes::from(buf),
+ )
+ }
+ }
+ }
+
+ pub fn gas_limit(&self) -> u64 {
+ match self {
+ Transaction::LegacyTransaction(tx) => tx.gas,
+ Transaction::EIP1559Transaction(tx) => tx.gas_limit,
+ }
+ }
+
+ pub fn gas_price(&self) -> u64 {
+ match self {
+ Transaction::LegacyTransaction(tx) => tx.gas_price,
+ Transaction::EIP1559Transaction(tx) => tx.max_fee_per_gas,
+ }
+ }
+
+ pub fn to(&self) -> TxKind {
+ match self {
+ Transaction::LegacyTransaction(tx) => tx.to.clone(),
+ Transaction::EIP1559Transaction(tx) => TxKind::Call(tx.destination),
+ }
+ }
+
+ pub fn value(&self) -> U256 {
+ match self {
+ Transaction::LegacyTransaction(tx) => tx.value,
+ Transaction::EIP1559Transaction(tx) => tx.amount,
+ }
+ }
+
+ pub fn max_priority_fee(&self) -> Option {
+ match self {
+ Transaction::LegacyTransaction(_tx) => None,
+ Transaction::EIP1559Transaction(tx) => Some(tx.max_priority_fee_per_gas),
+ }
+ }
+
+ pub fn chain_id(&self) -> Option {
+ match self {
+ Transaction::LegacyTransaction(_tx) => None,
+ Transaction::EIP1559Transaction(tx) => Some(tx.chain_id),
+ }
+ }
+
+ pub fn access_list(&self) -> Vec<(Address, Vec)> {
+ match self {
+ Transaction::LegacyTransaction(_tx) => Vec::new(),
+ Transaction::EIP1559Transaction(tx) => tx.access_list.clone(),
+ }
+ }
+
+ pub fn nonce(&self) -> u64 {
+ match self {
+ Transaction::LegacyTransaction(tx) => tx.nonce,
+ Transaction::EIP1559Transaction(tx) => tx.signer_nonce,
+ }
+ }
+
+ pub fn data(&self) -> &Bytes {
+ match self {
+ Transaction::LegacyTransaction(tx) => &tx.data,
+ Transaction::EIP1559Transaction(tx) => &tx.payload,
+ }
+ }
+}
+
+fn recover_address(
+ signature_r: &U256,
+ signature_s: &U256,
+ signature_y_parity: bool,
+ message: &Bytes,
+) -> Address {
+ // Create signature
+ let mut signature_bytes = [0; 64];
+ signature_r.to_big_endian(&mut signature_bytes[0..32]);
+ signature_s.to_big_endian(&mut signature_bytes[32..]);
+ let signature = secp256k1::ecdsa::RecoverableSignature::from_compact(
+ &signature_bytes,
+ RecoveryId::from_i32(signature_y_parity as i32).unwrap(), // cannot fail
+ )
+ .unwrap();
+ // Hash message
+ let msg_digest: [u8; 32] = Keccak256::new_with_prefix(message.as_ref())
+ .finalize()
+ .into();
+ // Recover public key
+ let public = SECP256K1
+ .recover_ecdsa(&Message::from_digest(msg_digest), &signature)
+ .unwrap();
+ // Hash public key to obtain address
+ let hash = Keccak256::new_with_prefix(&public.serialize_uncompressed()[1..]).finalize();
+ Address::from_slice(&hash[12..])
+}
+
+#[cfg(test)]
+mod tests {
+ use hex_literal::hex;
+
+ use super::LegacyTransaction;
+ use crate::{
+ types::{BlockBody, Transaction, TxKind},
+ U256,
+ };
+
+ #[test]
+ fn test_compute_transactions_root() {
+ let mut body = BlockBody::empty();
+ let tx = LegacyTransaction {
+ nonce: 0,
+ gas_price: 0x0a,
+ gas: 0x05f5e100,
+ to: TxKind::Call(hex!("1000000000000000000000000000000000000000").into()),
+ value: 0.into(),
+ data: Default::default(),
+ v: U256::from(0x1b),
+ r: U256::from(hex!(
+ "7e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37"
+ )),
+ s: U256::from(hex!(
+ "5f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509b"
+ )),
+ };
+ body.transactions.push(Transaction::LegacyTransaction(tx));
+ let expected_root =
+ hex!("8151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcb");
+ let result = body.compute_transactions_root();
+
+ assert_eq!(result, expected_root.into());
+ }
+}
diff --git a/crates/evm/Cargo.toml b/crates/evm/Cargo.toml
deleted file mode 100644
index decaee9fe36..00000000000
--- a/crates/evm/Cargo.toml
+++ /dev/null
@@ -1,8 +0,0 @@
-[package]
-name = "ethrex-evm"
-version = "0.1.0"
-edition = "2021"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
diff --git a/crates/evm/src/lib.rs b/crates/evm/src/lib.rs
deleted file mode 100644
index 7d12d9af819..00000000000
--- a/crates/evm/src/lib.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-pub fn add(left: usize, right: usize) -> usize {
- left + right
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn it_works() {
- let result = add(2, 2);
- assert_eq!(result, 4);
- }
-}
diff --git a/ef_tests/Cargo.toml b/ef_tests/Cargo.toml
index 4f7db70e0ec..e75da1e54cd 100644
--- a/ef_tests/Cargo.toml
+++ b/ef_tests/Cargo.toml
@@ -4,7 +4,8 @@ version.workspace = true
edition.workspace = true
[dependencies]
-revm = { version = "9.0.0", features = ["serde", "std", "serde-json"] }
ethrex-core.workspace = true
serde.workspace = true
serde_json.workspace = true
+bytes.workspace = true
+hex = "0.4.3"
diff --git a/ef_tests/src/evm.rs b/ef_tests/src/evm.rs
deleted file mode 100644
index 5c5f29c2148..00000000000
--- a/ef_tests/src/evm.rs
+++ /dev/null
@@ -1,94 +0,0 @@
-use std::{collections::HashMap, io::stderr};
-
-use ethrex_core::{Address, U256};
-use revm::{
- inspector_handle_register,
- inspectors::TracerEip3155,
- primitives::{
- keccak256, AccountInfo, Bytecode, Env, ExecutionResult, FixedBytes, SpecId, TransactTo,
- U256 as AlloyU256,
- },
- Evm,
-};
-
-use crate::types::{Account, Header, Transaction};
-
-pub fn execute_transaction(
- block: &Header,
- transaction: &Transaction,
- pre: HashMap,
-) -> ExecutionResult {
- let mut env = Box::::default();
-
- env.block.number = to_alloy_bytes(block.number);
- env.block.coinbase = block.coinbase.to_fixed_bytes().into();
- env.block.timestamp = to_alloy_bytes(block.timestamp);
- env.block.gas_limit = to_alloy_bytes(block.gas_limit);
- env.block.basefee = AlloyU256::ZERO;
- env.block.difficulty = AlloyU256::ZERO;
- env.block.prevrandao = Some(block.mix_hash.as_fixed_bytes().into());
-
- env.tx.caller = transaction.sender.to_fixed_bytes().into();
-
- env.tx.gas_price = to_alloy_bytes(
- transaction
- .gas_price
- .or(transaction.max_fee_per_gas)
- .unwrap_or_default(),
- );
- env.tx.gas_priority_fee = transaction.max_priority_fee_per_gas.map(to_alloy_bytes);
-
- let spec_id = SpecId::CANCUN;
-
- env.tx.gas_limit = transaction.gas_limit.as_u64();
-
- env.tx.data = transaction.data.clone();
- env.tx.value = to_alloy_bytes(transaction.value);
-
- env.tx.transact_to = TransactTo::Call(transaction.to.to_fixed_bytes().into());
-
- let mut cache_state = revm::CacheState::new(false);
- for (address, info) in pre {
- let acc_info = AccountInfo {
- balance: to_alloy_bytes(info.balance),
- code_hash: keccak256(&info.code),
- code: Some(Bytecode::new_raw(info.code)),
- nonce: info.nonce.as_u64(),
- };
-
- let mut storage = HashMap::new();
- for (k, v) in info.storage {
- storage.insert(to_alloy_bytes(k), to_alloy_bytes(v));
- }
-
- cache_state.insert_account_with_storage(address.to_fixed_bytes().into(), acc_info, storage);
- }
-
- let cache = cache_state.clone();
- let mut state = revm::db::State::builder()
- .with_cached_prestate(cache)
- .with_bundle_update()
- .build();
- let evm = Evm::builder()
- .with_db(&mut state)
- .modify_env(|e| e.clone_from(&env))
- .with_spec_id(spec_id)
- .build();
-
- let mut evm = evm
- .modify()
- .reset_handler_with_external_context(
- TracerEip3155::new(Box::new(stderr())).without_summary(),
- )
- .append_handler_register(inspector_handle_register)
- .build();
-
- evm.transact_commit().unwrap()
-}
-
-fn to_alloy_bytes(eth_byte: U256) -> AlloyU256 {
- let mut bytes = [0u8; 32];
- eth_byte.to_big_endian(&mut bytes);
- let fixed_bytes: FixedBytes<32> = bytes.into();
- fixed_bytes.into()
-}
diff --git a/ef_tests/src/lib.rs b/ef_tests/src/lib.rs
index 1dc5a8894f7..cd408564ea0 100644
--- a/ef_tests/src/lib.rs
+++ b/ef_tests/src/lib.rs
@@ -1,2 +1 @@
-pub mod evm;
pub mod types;
diff --git a/ef_tests/src/types.rs b/ef_tests/src/types.rs
index 4f7ca078a9c..b0b4e9bb618 100644
--- a/ef_tests/src/types.rs
+++ b/ef_tests/src/types.rs
@@ -1,7 +1,10 @@
-use ethrex_core::{Address, Bloom, H256, U256, U64};
-
-use revm::primitives::Bytes;
-use serde::{Deserialize, Serialize};
+use bytes::Bytes;
+use ethrex_core::types::{
+ code_hash, Account as EthrexAccount, AccountInfo, EIP1559Transaction, LegacyTransaction,
+ Transaction as EthrexTransacion,
+};
+use ethrex_core::{types::BlockHeader, Address, Bloom, H256, U256, U64};
+use serde::{Deserialize, Deserializer, Serialize};
use std::collections::HashMap;
#[derive(Debug, Deserialize)]
@@ -23,6 +26,7 @@ pub struct TestUnit {
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
pub struct Account {
pub balance: U256,
+ #[serde(deserialize_with = "deser_hex_str")]
pub code: Bytes,
pub nonce: U256,
pub storage: HashMap,
@@ -96,6 +100,7 @@ pub struct Block {
pub struct Transaction {
#[serde(rename = "type")]
pub transaction_type: Option,
+ #[serde(deserialize_with = "deser_hex_str")]
pub data: Bytes,
pub gas_limit: U256,
pub gas_price: Option,
@@ -112,3 +117,125 @@ pub struct Transaction {
pub sender: Address,
pub to: Address,
}
+
+// Conversions between EFtests & Ethrex types
+
+impl From for BlockHeader {
+ fn from(val: Header) -> Self {
+ BlockHeader {
+ parent_hash: val.parent_hash,
+ ommers_hash: val.uncle_hash,
+ coinbase: val.coinbase,
+ state_root: val.state_root,
+ transactions_root: val.transactions_trie,
+ receipt_root: val.receipt_trie,
+ logs_bloom: val.bloom.into(),
+ difficulty: val.difficulty,
+ number: val.number.as_u64(),
+ gas_limit: val.gas_limit.as_u64(),
+ gas_used: val.gas_used.as_u64(),
+ timestamp: val.timestamp.as_u64(),
+ extra_data: val.extra_data,
+ prev_randao: val.mix_hash,
+ nonce: val.nonce.as_u64(),
+ base_fee_per_gas: val.base_fee_per_gas.unwrap().as_u64(),
+ withdrawals_root: val.withdrawals_root.unwrap(),
+ blob_gas_used: val.blob_gas_used.unwrap().as_u64(),
+ excess_blob_gas: val.excess_blob_gas.unwrap().as_u64(),
+ parent_beacon_block_root: val.parent_beacon_block_root.unwrap(),
+ }
+ }
+}
+
+impl From for EthrexTransacion {
+ fn from(val: Transaction) -> Self {
+ match val.transaction_type {
+ Some(tx_type) => match tx_type.as_u64() {
+ 2 => EthrexTransacion::EIP1559Transaction(val.into()),
+ _ => unimplemented!(),
+ },
+ None => EthrexTransacion::LegacyTransaction(val.into()),
+ }
+ }
+}
+
+impl From for EIP1559Transaction {
+ fn from(val: Transaction) -> Self {
+ EIP1559Transaction {
+ // Note: gas_price is not used in this conversion as it is not part of EIP1559Transaction, this could be a problem
+ chain_id: val.chain_id.map(|id| id.as_u64()).unwrap_or(1 /*mainnet*/), // TODO: Consider converting this into Option
+ signer_nonce: val.nonce.as_u64(),
+ max_priority_fee_per_gas: val.max_priority_fee_per_gas.unwrap_or_default().as_u64(), // TODO: Consider converting this into Option
+ max_fee_per_gas: val
+ .max_fee_per_gas
+ .unwrap_or(val.gas_price.unwrap_or_default())
+ .as_u64(), // TODO: Consider converting this into Option
+ gas_limit: val.gas_limit.as_u64(),
+ destination: val.to,
+ amount: val.value,
+ payload: val.data,
+ access_list: val
+ .access_list
+ .unwrap_or_default()
+ .into_iter()
+ .map(|item| (item.address, item.storage_keys))
+ .collect(),
+ signature_y_parity: val.v.as_u64().saturating_sub(27) != 0,
+ signature_r: val.r,
+ signature_s: val.s,
+ }
+ }
+}
+
+impl From for LegacyTransaction {
+ fn from(val: Transaction) -> Self {
+ LegacyTransaction {
+ nonce: val.nonce.as_u64(),
+ gas_price: val.gas_price.unwrap_or_default().as_u64(), // TODO: Consider converting this into Option
+ gas: val.gas_limit.as_u64(),
+ to: ethrex_core::types::TxKind::Call(val.to),
+ value: val.value,
+ data: val.data,
+ v: val.v,
+ r: val.r,
+ s: val.s,
+ }
+ }
+}
+
+impl From for EthrexAccount {
+ fn from(val: Account) -> Self {
+ EthrexAccount {
+ info: AccountInfo {
+ code_hash: code_hash(&val.code),
+ balance: val.balance,
+ nonce: val.nonce.as_u64(),
+ },
+ code: val.code,
+ storage: val
+ .storage
+ .into_iter()
+ .map(|(k, v)| {
+ let mut k_bytes = [0; 32];
+ let mut v_bytes = [0; 32];
+ k.to_big_endian(&mut k_bytes);
+ v.to_big_endian(&mut v_bytes);
+ (H256(k_bytes), H256(v_bytes))
+ })
+ .collect(),
+ }
+ }
+}
+
+// Serde utils
+use serde::de::Error;
+
+pub fn deser_hex_str<'de, D>(d: D) -> Result
+where
+ D: Deserializer<'de>,
+{
+ let value = String::deserialize(d)?;
+ let bytes =
+ hex::decode(value.trim_start_matches("0x")).map_err(|e| D::Error::custom(e.to_string()))?;
+ Ok(Bytes::from(bytes))
+}
diff --git a/ef_tests/tests/tests.rs b/ef_tests/tests/tests.rs
index ed9c64e7445..aeca688c370 100644
--- a/ef_tests/tests/tests.rs
+++ b/ef_tests/tests/tests.rs
@@ -1,4 +1,5 @@
-use ::ef_tests::{evm::execute_transaction, types::TestUnit};
+use ::ef_tests::types::TestUnit;
+use ethrex_core::evm::{execute_tx, SpecId};
fn execute_test(test: TestUnit) {
// TODO: Add support for multiple blocks and multiple transactions per block.
@@ -11,7 +12,22 @@ fn execute_test(test: TestUnit) {
.unwrap()
.first()
.unwrap();
- execute_transaction(&test.genesis_block_header, transaction, test.pre);
+ let pre = test.pre.into_iter().map(|(k, v)| (k, v.into())).collect();
+ assert!(execute_tx(
+ &transaction.clone().into(),
+ &test
+ .blocks
+ .first()
+ .as_ref()
+ .unwrap()
+ .block_header
+ .clone()
+ .unwrap()
+ .into(),
+ &pre,
+ SpecId::CANCUN,
+ )
+ .is_success());
}
#[cfg(test)]
@@ -33,4 +49,16 @@ mod ef_tests {
execute_test(test)
}
}
+
+ #[test]
+ fn solidity_example_test() {
+ let s: String =
+ std::fs::read_to_string("./vectors/solidityExample.json").expect("Unable to read file");
+ let tests: HashMap =
+ serde_json::from_str(&s).expect("Unable to parse JSON");
+
+ for (_k, test) in tests {
+ execute_test(test)
+ }
+ }
}
diff --git a/ef_tests/vectors/solidityExample.json b/ef_tests/vectors/solidityExample.json
new file mode 100644
index 00000000000..1b940fb6005
--- /dev/null
+++ b/ef_tests/vectors/solidityExample.json
@@ -0,0 +1,158 @@
+{
+ "solidityExample_d0g0v0_Cancun" : {
+ "_info" : {
+ "comment" : "An example test for using solidity contracts in the test",
+ "filling-rpc-server" : "evm version 1.14.4-unstable-3d8028a6-20240513",
+ "filling-tool-version" : "retesteth-0.3.2-cancun+commit.cae6bc33.Linux.g++",
+ "generatedTestHash" : "b1ef657f83adfccd30474ca07cbb852fe5de9016ca3d92f8171bcc8203dc86ef",
+ "lllcversion" : "Version: 0.5.14-develop.2023.7.11+commit.c58ab2c6.mod.Linux.g++",
+ "solidity" : "Version: 0.8.21+commit.d9974bed.Linux.g++",
+ "source" : "src/GeneralStateTestsFiller/stExample/solidityExampleFiller.yml",
+ "sourceHash" : "183aba76ca652aca9dba9268833bc330bb2c52d0d83d0f00d5d1bd167f983b5d"
+ },
+ "blocks" : [
+ {
+ "blockHeader" : {
+ "baseFeePerGas" : "0x0a",
+ "blobGasUsed" : "0x00",
+ "bloom" : "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "coinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
+ "difficulty" : "0x00",
+ "excessBlobGas" : "0x00",
+ "extraData" : "0x00",
+ "gasLimit" : "0x05f5e100",
+ "gasUsed" : "0x029e1f",
+ "hash" : "0x004932044123a9c656fa761728cb4cbced856df14aaec79b17819f7ff92110b1",
+ "mixHash" : "0x0000000000000000000000000000000000000000000000000000000000020000",
+ "nonce" : "0x0000000000000000",
+ "number" : "0x01",
+ "parentBeaconBlockRoot" : "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "parentHash" : "0x7f968420a06afa4d25513f22f68e1ed9708c5cab62493403a59e9972d6304b99",
+ "receiptTrie" : "0x8560690af929551571bc1385f70c39c932a89e08b43355d540a38125ebd87490",
+ "stateRoot" : "0x3cd24ae5e4ae9cf56a73a3f4e764cc50b63e8b1aaff75b7cbd3a840c8a449cca",
+ "timestamp" : "0x03e8",
+ "transactionsTrie" : "0xb8466b5b437cf6d45ed3dfa2058e2b6709f1a86145215e8a59694dc559a4526f",
+ "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
+ "withdrawalsRoot" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
+ },
+ "rlp" : "0xf902eaf9023ba07f968420a06afa4d25513f22f68e1ed9708c5cab62493403a59e9972d6304b99a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa03cd24ae5e4ae9cf56a73a3f4e764cc50b63e8b1aaff75b7cbd3a840c8a449ccaa0b8466b5b437cf6d45ed3dfa2058e2b6709f1a86145215e8a59694dc559a4526fa08560690af929551571bc1385f70c39c932a89e08b43355d540a38125ebd87490b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080018405f5e10083029e1f8203e800a000000000000000000000000000000000000000000000000000000000000200008800000000000000000aa056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000f8a8f8a6800a8404c4b40094095e7baea6a6c7c4c2dfeb977efac326af552d8780b844b66176a7000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000451ca0557b882a6f4c398e1dc30731cc0490f9743d2df3a13b9e465a87198920d34877a071659dc2b9ff195215e075315734b937a7773e470d47d33cdca183b1bc4093a7c0c0",
+ "transactions" : [
+ {
+ "data" : "0xb66176a700000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000045",
+ "gasLimit" : "0x04c4b400",
+ "gasPrice" : "0x0a",
+ "nonce" : "0x00",
+ "r" : "0x557b882a6f4c398e1dc30731cc0490f9743d2df3a13b9e465a87198920d34877",
+ "s" : "0x71659dc2b9ff195215e075315734b937a7773e470d47d33cdca183b1bc4093a7",
+ "sender" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
+ "to" : "0x095e7baea6a6c7c4c2dfeb977efac326af552d87",
+ "v" : "0x1c",
+ "value" : "0x00"
+ }
+ ],
+ "uncleHeaders" : [
+ ],
+ "withdrawals" : [
+ ]
+ }
+ ],
+ "genesisBlockHeader" : {
+ "baseFeePerGas" : "0x0b",
+ "blobGasUsed" : "0x00",
+ "bloom" : "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "coinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
+ "difficulty" : "0x00",
+ "excessBlobGas" : "0x060000",
+ "extraData" : "0x00",
+ "gasLimit" : "0x05f5e100",
+ "gasUsed" : "0x00",
+ "hash" : "0x7f968420a06afa4d25513f22f68e1ed9708c5cab62493403a59e9972d6304b99",
+ "mixHash" : "0x0000000000000000000000000000000000000000000000000000000000020000",
+ "nonce" : "0x0000000000000000",
+ "number" : "0x00",
+ "parentBeaconBlockRoot" : "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "stateRoot" : "0x8dd822dbb200af06a3faa988d8fead75e3af89e24c1d74ba377c7e4ec7cebf56",
+ "timestamp" : "0x00",
+ "transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
+ "withdrawalsRoot" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
+ },
+ "genesisRLP" : "0xf9023ff90239a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa08dd822dbb200af06a3faa988d8fead75e3af89e24c1d74ba377c7e4ec7cebf56a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080808405f5e100808000a000000000000000000000000000000000000000000000000000000000000200008800000000000000000ba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218083060000a00000000000000000000000000000000000000000000000000000000000000000c0c0c0",
+ "lastblockhash" : "0x004932044123a9c656fa761728cb4cbced856df14aaec79b17819f7ff92110b1",
+ "network" : "Cancun",
+ "postState" : {
+ "0x000f3df6d732807ef1319fb7b8bb8522d0beac02" : {
+ "balance" : "0x00",
+ "code" : "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500",
+ "nonce" : "0x01",
+ "storage" : {
+ "0x03e8" : "0x03e8"
+ }
+ },
+ "0x095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
+ "balance" : "0x0ba1a9ce0ba1a9ce",
+ "code" : "0x608060405234801561001057600080fd5b506004361061002b5760003560e01c8063b66176a714610030575b600080fd5b61004a6004803603810190610045919061018d565b61004c565b005b60405161005890610145565b604051809103906000f080158015610074573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b66176a783836040518363ffffffff1660e01b815260040161010f9291906101dc565b600060405180830381600087803b15801561012957600080fd5b505af115801561013d573d6000803e3d6000fd5b505050505050565b6101238061020683390190565b600080fd5b6000819050919050565b61016a81610157565b811461017557600080fd5b50565b60008135905061018781610161565b92915050565b600080604083850312156101a4576101a3610152565b5b60006101b285828601610178565b92505060206101c385828601610178565b9150509250929050565b6101d681610157565b82525050565b60006040820190506101f160008301856101cd565b6101fe60208301846101cd565b939250505056fe608060405267ff00ff00ff00ff0060005534801561001c57600080fd5b5060f88061002b6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063b66176a714602d575b600080fd5b60436004803603810190603f91906089565b6045565b005b806000819055508082555050565b600080fd5b6000819050919050565b6069816058565b8114607357600080fd5b50565b6000813590506083816062565b92915050565b60008060408385031215609d57609c6053565b5b600060a9858286016076565b925050602060b8858286016076565b915050925092905056fea2646970667358221220bf97a493dcca1030df869e90defab95c4fe213b1465aef60276095472e3250cf64736f6c63430008150033a26469706673582212206eab0a7969fe2c3def96c16981b10296eeffbe3ba077c61e666c576b0573f10f64736f6c63430008150033",
+ "nonce" : "0x01",
+ "storage" : {
+ "0x00" : "0xd2571607e241ecf590ed94b12d87c94babe36db6"
+ }
+ },
+ "0x195e7baea6a6c7c4c2dfeb977efac326af552d87" : {
+ "balance" : "0x0ba1a9ce0ba1a9ce",
+ "code" : "0x6080604052348015600e575f80fd5b50600436106026575f3560e01c8063b66176a714602a575b5f80fd5b60406004803603810190603c91906081565b6042565b005b805f819055508082555050565b5f80fd5b5f819050919050565b6063816053565b8114606c575f80fd5b50565b5f81359050607b81605c565b92915050565b5f80604083850312156094576093604f565b5b5f609f85828601606f565b925050602060ae85828601606f565b915050925092905056fea2646970667358221220019739184511327e8fcb2252099d4070e2bac9220fc47dcf6a69c5397c26b63e64736f6c63430008150033",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ },
+ "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
+ "balance" : "0x0ba1a9ce0b877c98",
+ "code" : "0x",
+ "nonce" : "0x01",
+ "storage" : {
+ }
+ },
+ "0xd2571607e241ecf590ed94b12d87c94babe36db6" : {
+ "balance" : "0x00",
+ "code" : "0x6080604052348015600f57600080fd5b506004361060285760003560e01c8063b66176a714602d575b600080fd5b60436004803603810190603f91906089565b6045565b005b806000819055508082555050565b600080fd5b6000819050919050565b6069816058565b8114607357600080fd5b50565b6000813590506083816062565b92915050565b60008060408385031215609d57609c6053565b5b600060a9858286016076565b925050602060b8858286016076565b915050925092905056fea2646970667358221220bf97a493dcca1030df869e90defab95c4fe213b1465aef60276095472e3250cf64736f6c63430008150033",
+ "nonce" : "0x01",
+ "storage" : {
+ "0x00" : "0x45",
+ "0x05" : "0x45"
+ }
+ }
+ },
+ "pre" : {
+ "0x000f3df6d732807ef1319fb7b8bb8522d0beac02" : {
+ "balance" : "0x00",
+ "code" : "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500",
+ "nonce" : "0x01",
+ "storage" : {
+ }
+ },
+ "0x095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
+ "balance" : "0x0ba1a9ce0ba1a9ce",
+ "code" : "0x608060405234801561001057600080fd5b506004361061002b5760003560e01c8063b66176a714610030575b600080fd5b61004a6004803603810190610045919061018d565b61004c565b005b60405161005890610145565b604051809103906000f080158015610074573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b66176a783836040518363ffffffff1660e01b815260040161010f9291906101dc565b600060405180830381600087803b15801561012957600080fd5b505af115801561013d573d6000803e3d6000fd5b505050505050565b6101238061020683390190565b600080fd5b6000819050919050565b61016a81610157565b811461017557600080fd5b50565b60008135905061018781610161565b92915050565b600080604083850312156101a4576101a3610152565b5b60006101b285828601610178565b92505060206101c385828601610178565b9150509250929050565b6101d681610157565b82525050565b60006040820190506101f160008301856101cd565b6101fe60208301846101cd565b939250505056fe608060405267ff00ff00ff00ff0060005534801561001c57600080fd5b5060f88061002b6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063b66176a714602d575b600080fd5b60436004803603810190603f91906089565b6045565b005b806000819055508082555050565b600080fd5b6000819050919050565b6069816058565b8114607357600080fd5b50565b6000813590506083816062565b92915050565b60008060408385031215609d57609c6053565b5b600060a9858286016076565b925050602060b8858286016076565b915050925092905056fea2646970667358221220bf97a493dcca1030df869e90defab95c4fe213b1465aef60276095472e3250cf64736f6c63430008150033a26469706673582212206eab0a7969fe2c3def96c16981b10296eeffbe3ba077c61e666c576b0573f10f64736f6c63430008150033",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ },
+ "0x195e7baea6a6c7c4c2dfeb977efac326af552d87" : {
+ "balance" : "0x0ba1a9ce0ba1a9ce",
+ "code" : "0x6080604052348015600e575f80fd5b50600436106026575f3560e01c8063b66176a714602a575b5f80fd5b60406004803603810190603c91906081565b6042565b005b805f819055508082555050565b5f80fd5b5f819050919050565b6063816053565b8114606c575f80fd5b50565b5f81359050607b81605c565b92915050565b5f80604083850312156094576093604f565b5b5f609f85828601606f565b925050602060ae85828601606f565b915050925092905056fea2646970667358221220019739184511327e8fcb2252099d4070e2bac9220fc47dcf6a69c5397c26b63e64736f6c63430008150033",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ },
+ "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
+ "balance" : "0x0ba1a9ce0ba1a9ce",
+ "code" : "0x",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ }
+ },
+ "sealEngine" : "NoProof"
+ }
+}