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" + } +}