diff --git a/bus-mapping/Cargo.toml b/bus-mapping/Cargo.toml index 004f872cff2..b08591add18 100644 --- a/bus-mapping/Cargo.toml +++ b/bus-mapping/Cargo.toml @@ -13,9 +13,9 @@ lazy_static = "1.4" serde_json = "1.0.66" hex = "0.4" geth-utils = { path = "../geth-utils" } -web3 = {version = "0.17", default-features = false} uint = "0.9.1" -ethers-providers = "0.5.5" +ethers-providers = "0.6.0" +ethers-core = "0.6.0" regex = "1.5.4" [dev-dependencies] diff --git a/bus-mapping/src/circuit_input_builder.rs b/bus-mapping/src/circuit_input_builder.rs index 6788fc01de4..4f06026a362 100644 --- a/bus-mapping/src/circuit_input_builder.rs +++ b/bus-mapping/src/circuit_input_builder.rs @@ -1,14 +1,18 @@ //! This module contains the CircuitInputBuilder, which is an object that takes //! types from geth / web3 and outputs the circuit inputs. -use crate::eth_types::{self, Address, GethExecStep, GethExecTrace, Word}; -use crate::evm::GlobalCounter; -use crate::evm::OpcodeId; +use crate::eth_types::{ + self, Address, GethExecStep, GethExecTrace, ToBigEndian, Word, H256, +}; +use crate::evm::{Gas, GasCost, GlobalCounter, OpcodeId, ProgramCounter}; use crate::exec_trace::OperationRef; use crate::geth_errors::*; use crate::operation::container::OperationContainer; use crate::operation::{Op, Operation}; +use crate::state_db::StateDB; use crate::{BlockConstants, Error}; use core::fmt::Debug; +use ethers_core::utils::{get_contract_address, get_create2_address}; +use std::collections::HashMap; /// Out of Gas errors by opcode #[derive(Debug, PartialEq)] @@ -86,22 +90,54 @@ pub enum ExecError { pub struct ExecStep { /// The opcode ID pub op: OpcodeId, - /// The global counter when this step was executed + /// Program Counter + pub pc: ProgramCounter, + /// Stack size + pub stack_size: usize, + /// Memory size + pub memory_size: usize, + /// Gas left + pub gas_left: Gas, + /// Gas cost of the step. If the error is OutOfGas caused by a "gas uint64 + /// overflow", this value will **not** be the actual Gas cost of the + /// step. + pub gas_cost: GasCost, + /// Call index within the [`Transaction`] + pub call_index: usize, + /// The global counter when this step was executed. pub gc: GlobalCounter, + /// State Write Counter. Counter of state write operations in the call + /// that haven't been reverted yet up to this step. + pub swc: usize, /// The list of references to Operations in the container pub bus_mapping_instance: Vec, /// Error generated by this step pub error: Option, + /// The step has been reverted + pub reverted: bool, } impl ExecStep { - /// Create a new Self from a `geth_step`. - pub fn new(geth_step: &GethExecStep, gc: GlobalCounter) -> Self { + /// Create a new Self from a [`GethExecStep`]. + pub fn new( + step: &GethExecStep, + call_index: usize, + gc: GlobalCounter, + swc: usize, // State Write Counter + ) -> Self { ExecStep { - op: geth_step.op, + op: step.op, + pc: step.pc, + stack_size: step.stack.0.len(), + memory_size: step.memory.0.len(), + gas_left: step.gas, + gas_cost: step.gas_cost, + call_index, gc, + swc, bus_mapping_instance: Vec::new(), error: None, + reverted: false, } } } @@ -136,6 +172,7 @@ pub struct Block { /// Container of operations done in this block. pub container: OperationContainer, txs: Vec, + code: HashMap>, } impl Block { @@ -148,6 +185,7 @@ impl Block { constants, container: OperationContainer::new(), txs: Vec::new(), + code: HashMap::new(), } } @@ -164,9 +202,7 @@ impl Block { /// Type of a *CALL* Function. #[derive(Debug, PartialEq)] -pub enum CallType { - /// Implicit call of a transaction - Root, +pub enum CallKind { /// CALL Call, /// CALLCODE @@ -181,101 +217,164 @@ pub enum CallType { Create2, } -impl CallType { +impl CallKind { fn is_create(&self) -> bool { matches!(self, Self::Create | Self::Create2) } } -impl TryFrom for CallType { +impl TryFrom for CallKind { type Error = Error; fn try_from(op: OpcodeId) -> Result { Ok(match op { - OpcodeId::CALL => CallType::Call, - OpcodeId::CALLCODE => CallType::CallCode, - OpcodeId::DELEGATECALL => CallType::DelegateCall, - OpcodeId::STATICCALL => CallType::StaticCall, - OpcodeId::CREATE => CallType::Create, - OpcodeId::CREATE2 => CallType::Create2, + OpcodeId::CALL => CallKind::Call, + OpcodeId::CALLCODE => CallKind::CallCode, + OpcodeId::DELEGATECALL => CallKind::DelegateCall, + OpcodeId::STATICCALL => CallKind::StaticCall, + OpcodeId::CREATE => CallKind::Create, + OpcodeId::CREATE2 => CallKind::Create2, _ => return Err(Error::OpcodeIdNotCallType), }) } } -/// Context of a Call during a [`Transaction`] which can mutate in an -/// [`ExecStep`]. +/// Circuit Input related to an Ethereum Call #[derive(Debug)] -pub struct CallContext { +pub struct Call { /// Type of call - call: CallType, + kind: CallKind, /// This call is being executed without write access (STATIC) is_static: bool, - /// This call is root call with tx.to == null, or op == CREATE or op == - /// CREATE2 - is_create: bool, + /// This call generated implicity by a Transaction. + is_root: bool, /// Address where this call is being executed pub address: Address, + /// Code Hash + code_hash: H256, +} + +impl Call { + /// This call is root call with tx.to == null, or op == CREATE or op == + /// CREATE2 + pub fn is_create(&self) -> bool { + self.kind.is_create() + } +} + +/// Context of a [`Call`]. +#[derive(Debug)] +pub struct CallContext { + /// State Write Counter tracks the count of state write operations in the + /// call. When a subcall in this call succeeds, the `swc` increases by the + /// number of successful state writes in the subcall. + pub swc: usize, } #[derive(Debug)] /// Context of a [`Transaction`] which can mutate in an [`ExecStep`]. pub struct TransactionContext { - call_stack: Vec, + /// Call Stack by indices with CallContext. + /// The call_stack will always have a fixed element at index 0 which + /// corresponds to the call implicitly created by the transaction. + call_stack: Vec<(usize, CallContext)>, } impl TransactionContext { /// Create a new Self. - pub fn new(eth_tx: ð_types::Transaction) -> Self { - let mut call_stack = Vec::new(); - if let Some(address) = eth_tx.to { - call_stack.push(CallContext { - call: CallType::Root, - is_static: false, - is_create: false, - address, - }); - } else { - call_stack.push(CallContext { - call: CallType::Root, - is_static: false, - is_create: true, - address: Address::zero(), - }); + pub fn new(_eth_tx: ð_types::Transaction) -> Self { + Self { + call_stack: vec![(0, CallContext { swc: 0 })], } - Self { call_stack } } - /// Return a reference to the current call context (the last call context in - /// the call stack). - pub fn call_ctx(&self) -> &CallContext { - self.call_stack.last().expect("call_stack is empty") + /// Return the index and context of the current call (the last call in the + /// call stack). + fn call_index(&self) -> usize { + let (index, _) = self.call_stack.last().expect("call_stack is empty"); + *index } - /// Push a new call context into the call stack. - pub fn push_call_ctx(&mut self, call: CallType, address: Address) { - let is_static = - call == CallType::StaticCall || self.call_ctx().is_static; - let is_create = call.is_create(); - self.call_stack.push(CallContext { - call, - is_static, - is_create, - address, - }); + fn call_ctx(&self) -> &CallContext { + let (_, call_ctx) = + self.call_stack.last().expect("call_stack is empty"); + call_ctx + } + + fn call_ctx_mut(&mut self) -> &mut CallContext { + let (_, ref mut call_ctx) = + self.call_stack.last_mut().expect("call_stack is empty"); + call_ctx + } + + /// Push a new call index and context into the call stack. + fn push_call_index_ctx(&mut self, index: usize, call_ctx: CallContext) { + self.call_stack.push((index, call_ctx)); + } + + /// Pop the last entry in the call stack. + fn pop_call_index_ctx(&mut self) -> Option<(usize, CallContext)> { + self.call_stack.pop() } } #[derive(Debug)] /// Result of the parsing of an Ethereum Transaction. pub struct Transaction { + /// Nonce + pub nonce: u64, + /// Gas + pub gas: u64, + /// From / Caller Address + pub from: Address, // caller_address + /// To / Callee Address + pub to: Address, // callee_address + /// Value + pub value: Word, + /// Input / Call Data + pub input: Vec, // call_data + calls: Vec, steps: Vec, } impl Transaction { /// Create a new Self. - pub fn new(_eth_tx: ð_types::Transaction) -> Self { - Self { steps: Vec::new() } + pub fn new(eth_tx: ð_types::Transaction) -> Self { + let mut calls = Vec::new(); + let code_hash = H256::zero(); + if let Some(address) = eth_tx.to { + calls.push(Call { + kind: CallKind::Call, + is_static: false, + is_root: true, + address, + code_hash, + }); + } else { + calls.push(Call { + kind: CallKind::Create, + is_static: false, + is_root: true, + address: Address::zero(), + code_hash, + }); + } + Self { + nonce: eth_tx.nonce.as_u64(), + gas: eth_tx.gas.as_u64(), + from: eth_tx.from, + to: eth_tx.to.unwrap_or_default(), + value: eth_tx.value, + input: eth_tx.input.to_vec(), + + calls, + steps: Vec::new(), + } + } + + /// Wether this [`Transaction`] is a create one + pub fn is_create(&self) -> bool { + self.calls[0].is_create() } /// Return the list of execution steps of this transaction. @@ -287,11 +386,32 @@ impl Transaction { pub fn steps_mut(&mut self) -> &mut Vec { &mut self.steps } + + fn push_call( + &mut self, + parent_index: usize, + kind: CallKind, + address: Address, + ) -> usize { + let is_static = + kind == CallKind::StaticCall || self.calls[parent_index].is_static; + let code_hash = H256::zero(); + self.calls.push(Call { + kind, + is_static, + is_root: false, + address, + code_hash, + }); + self.calls.len() - 1 + } } /// Reference to the internal state of the CircuitInputBuilder in a particular /// [`ExecStep`]. pub struct CircuitInputStateRef<'a> { + /// StateDB + pub sdb: &'a mut StateDB, /// Block pub block: &'a mut Block, /// Block Context @@ -304,6 +424,18 @@ pub struct CircuitInputStateRef<'a> { pub step: &'a mut ExecStep, } +/// Helper function to push a call into a Transaction and TransactionContext +fn push_call( + tx: &mut Transaction, + tx_ctx: &mut TransactionContext, + kind: CallKind, + address: Address, +) { + let parent_index = tx_ctx.call_index(); + let index = tx.push_call(parent_index, kind, address); + tx_ctx.push_call_index_ctx(index, CallContext { swc: 0 }); +} + impl<'a> CircuitInputStateRef<'a> { /// Push an [`Operation`] into the [`OperationContainer`] with the next /// [`GlobalCounter`] and then adds a reference to the stored operation @@ -316,6 +448,27 @@ impl<'a> CircuitInputStateRef<'a> { .insert(Operation::new(self.block_ctx.gc.inc_pre(), op)); self.step.bus_mapping_instance.push(op_ref); } + + /// Reference to the current Call + pub fn call(&self) -> &Call { + &self.tx.calls[self.tx_ctx.call_index()] + } + + /// Reference to the current CallContext + pub fn call_ctx(&self) -> &CallContext { + self.tx_ctx.call_ctx() + } + + /// Mutable reference to the current Call + pub fn call_mut(&mut self) -> &mut Call { + &mut self.tx.calls[self.tx_ctx.call_index()] + } + + /// Push a new [`Call`] into the [`Transaction`], and add its index and + /// [`CallContext`] in the `call_stack` of the [`TransactionContext`] + pub fn push_call(&mut self, kind: CallKind, address: Address) { + push_call(self.tx, self.tx_ctx, kind, address) + } } #[derive(Debug)] @@ -338,6 +491,8 @@ impl<'a> CircuitInputStateRef<'a> { /// the State Proof witnesses are already generated on a structured manner and /// ready to be added into the State circuit. pub struct CircuitInputBuilder { + /// StateDB key-value DB + pub sdb: StateDB, /// Block pub block: Block, /// Block Context @@ -352,6 +507,7 @@ impl<'a> CircuitInputBuilder { constants: BlockConstants, ) -> Self { Self { + sdb: StateDB::new(), block: Block::new(ð_block, constants), block_ctx: BlockContext::new(), } @@ -367,6 +523,7 @@ impl<'a> CircuitInputBuilder { step: &'a mut ExecStep, ) -> CircuitInputStateRef { CircuitInputStateRef { + sdb: &mut self.sdb, block: &mut self.block, block_ctx: &mut self.block_ctx, tx, @@ -387,13 +544,44 @@ impl<'a> CircuitInputBuilder { let mut tx = Transaction::new(eth_tx); let mut tx_ctx = TransactionContext::new(eth_tx); for (index, geth_step) in geth_trace.struct_logs.iter().enumerate() { - let mut step = ExecStep::new(geth_step, self.block_ctx.gc); + if index != 0 { + let geth_prev_step = &geth_trace.struct_logs[index - 1]; + // Handle *CALL* + if geth_step.depth == geth_prev_step.depth + 1 { + // TODO: Set the proper address according to the call kind. + let address = Address::zero(); + let kind = CallKind::try_from(geth_step.op)?; + push_call(&mut tx, &mut tx_ctx, kind, address); + } + } + + let mut step = ExecStep::new( + geth_step, + tx_ctx.call_index(), + self.block_ctx.gc, + tx_ctx.call_ctx().swc, + ); let mut state_ref = self.state_ref(&mut tx, &mut tx_ctx, &mut step); geth_step.op.gen_associated_ops( &mut state_ref, &geth_trace.struct_logs[index..], )?; tx.steps.push(step); + + if let Some(geth_next_step) = geth_trace.struct_logs.get(index + 1) + { + // Handle *CALL* return + if geth_step.depth == geth_next_step.depth - 1 { + let (_, call_ctx) = + tx_ctx.pop_call_index_ctx().ok_or_else(|| { + Error::InvalidGethExecStep( + "*CALL* return with empty call stack", + Box::new(geth_step.clone()), + ) + })?; + tx_ctx.call_ctx_mut().swc += call_ctx.swc; + } + } } self.block.txs.push(tx); Ok(()) @@ -442,14 +630,48 @@ fn get_step_reported_error(op: &OpcodeId, error: &str) -> ExecError { } } +/// Retreive the init_code from memory for {CREATE, CREATE2} +pub fn get_create_init_code(step: &GethExecStep) -> Result<&[u8], Error> { + let offset = step.stack.nth_last(1)?; + let length = step.stack.nth_last(2)?; + Ok(&step.memory.0[offset.low_u64() as usize + ..(offset.low_u64() + length.low_u64()) as usize]) +} + impl<'a> CircuitInputStateRef<'a> { + fn create_address(&self) -> Result { + let sender = self.call().address; + let (found, account) = self.sdb.get_account(&sender); + if !found { + return Err(Error::AccountNotFound(sender)); + } + Ok(get_contract_address(sender, account.nonce)) + } + + fn create2_address(&self, step: &GethExecStep) -> Result { + let salt = step.stack.nth_last(3)?; + let init_code = get_create_init_code(step)?; + Ok(get_create2_address( + self.call().address, + salt.to_be_bytes().to_vec(), + init_code.to_vec(), + )) + } + fn get_step_err( &self, step: &GethExecStep, next_step: Option<&GethExecStep>, - ) -> Option { + ) -> Result, Error> { if let Some(error) = &step.error { - return Some(get_step_reported_error(&step.op, error)); + return Ok(Some(get_step_reported_error(&step.op, error))); + } + + // When last step is RETURN or STOP there's no error. + if matches!(next_step, None) + && matches!(step.op, OpcodeId::RETURN | OpcodeId::STOP) + { + return Ok(None); } let next_depth = next_step.map(|s| s.depth).unwrap_or(0); @@ -459,58 +681,69 @@ impl<'a> CircuitInputStateRef<'a> { // Return from a call with a failure if step.depth != next_depth && next_result == Word::zero() { - if step.op != OpcodeId::RETURN { + if !matches!(step.op, OpcodeId::RETURN) { // Without calling RETURN - return Some(match step.op { + return Ok(Some(match step.op { OpcodeId::REVERT => ExecError::ExecutionReverted, OpcodeId::JUMP | OpcodeId::JUMPI => ExecError::InvalidJump, OpcodeId::RETURNDATACOPY => { ExecError::ReturnDataOutOfBounds } _ => { - panic!( - "Cannot figure out call failure error in {:?}", - step - ) + return Err(Error::UnexpectedExecStepError( + "call failure without return", + Box::new(step.clone()), + )); } - }); + })); } else { // Calling RETURN - let call_ctx = self.tx_ctx.call_ctx(); + let call = self.call(); // Return from a {CREATE, CREATE2} with a failure, via RETURN - if call_ctx.call != CallType::Root && call_ctx.is_create { - let offset = step.stack.nth_last(0).unwrap(); - let length = step.stack.nth_last(1).unwrap(); - if length > Word::from(0x6000) { - return Some(ExecError::MaxCodeSizeExceeded); + if !call.is_root && call.is_create() { + let offset = step.stack.nth_last(0)?; + let length = step.stack.nth_last(1)?; + if length > Word::from(0x6000u64) { + return Ok(Some(ExecError::MaxCodeSizeExceeded)); } else if length > Word::zero() && !step.memory.0.is_empty() && step.memory.0.get(offset.low_u64() as usize) == Some(&0xef) { - return Some(ExecError::InvalidCode); - } else if Word::from(200) * length > Word::from(step.gas.0) + return Ok(Some(ExecError::InvalidCode)); + } else if Word::from(200u64) * length + > Word::from(step.gas.0) { - return Some(ExecError::CodeStoreOutOfGas); + return Ok(Some(ExecError::CodeStoreOutOfGas)); } else { - panic!("Cannot figure out RETURN error in {:?}", step); + return Err(Error::UnexpectedExecStepError( + "failure in RETURN from {CREATE, CREATE2}", + Box::new(step.clone()), + )); } } else { - panic!("Cannot figure out RETURN error in {:?}", step); + return Err(Error::UnexpectedExecStepError( + "failure in RETURN", + Box::new(step.clone()), + )); } } } - // Return from a call via RETURN and having a success result is OK. + // Return from a call via RETURN or STOP and having a success result is + // OK. - // Return from a call without calling RETURN and having success is - // unexpected. + // Return from a call without calling RETURN or STOP and having success + // is unexpected. if step.depth != next_depth && next_result != Word::zero() - && step.op != OpcodeId::RETURN + && !matches!(step.op, OpcodeId::RETURN | OpcodeId::STOP) { - panic!("Cannot figure out call failure error in {:?}", step); + return Err(Error::UnexpectedExecStepError( + "success result without {RETURN, STOP}", + Box::new(step.clone()), + )); } // The *CALL* code was not executed @@ -527,65 +760,48 @@ impl<'a> CircuitInputStateRef<'a> { && next_pc != 0 { if step.depth == 1025 { - return Some(ExecError::Depth); - } - // TODO: address_collision - // https://github.com/appliedzkp/zkevm-circuits/issues/180 - if matches!(step.op, OpcodeId::CREATE | OpcodeId::CREATE2) { - // TODO: Calculate address - // https://github.com/appliedzkp/zkevm-circuits/issues/187 - let _address = if matches!(step.op, OpcodeId::CREATE) { - // CREATE - let _value = step.stack.nth_last(0).unwrap(); - let _offset = step.stack.nth_last(1).unwrap(); - let _length = step.stack.nth_last(2).unwrap(); - // TODO: - // let caller_address = self.tx_ctx.caller_addr(); - // let caller = self.get_account(caller_address) - // let nonce = caller.nonce; - // https://github.com/appliedzkp/zkevm-circuits/issues/186 - Address::zero() - } else { - // CREATE2 - let _value = step.stack.nth_last(0).unwrap(); - let _offset = step.stack.nth_last(1).unwrap(); - let _length = step.stack.nth_last(2).unwrap(); - let _salt = step.stack.nth_last(3).unwrap(); - Address::zero() - }; - // TODO: - // if Some(_) = self.get_account(_address) { - // return Some(ExecError::ContractAddressCollision); - // } - // https://github.com/appliedzkp/zkevm-circuits/issues/186 + return Ok(Some(ExecError::Depth)); } - // TODO: insufficient_balance - // https://github.com/appliedzkp/zkevm-circuits/issues/180 - let _value = match step.op { + // Insufficient_balance + let value = match step.op { OpcodeId::CALL | OpcodeId::CALLCODE => { - step.stack.nth_last(2).unwrap() + step.stack.nth_last(2)? } OpcodeId::CREATE | OpcodeId::CREATE2 => { - step.stack.nth_last(0).unwrap() + step.stack.nth_last(0)? } _ => Word::zero(), }; - // TODO - // let caller_address = self.tx_ctx.caller_addr(); - // let caller = self.get_account(caller_address) - // if caller.balance < value { - // return Some(ExecError::InsufficientBalance); - // } - // https://github.com/appliedzkp/zkevm-circuits/issues/186 - - panic!( - "Cannot figure out *CALL* code not executed error in {:?}", - step - ); + let sender = self.call().address; + let (found, account) = self.sdb.get_account(&sender); + if !found { + return Err(Error::AccountNotFound(sender)); + } + if account.balance < value { + return Ok(Some(ExecError::InsufficientBalance)); + } + + // Address collision + if matches!(step.op, OpcodeId::CREATE | OpcodeId::CREATE2) { + let address = match step.op { + OpcodeId::CREATE => self.create_address()?, + OpcodeId::CREATE2 => self.create2_address(step)?, + _ => unreachable!(), + }; + let (found, _) = self.sdb.get_account(&address); + if found { + return Ok(Some(ExecError::ContractAddressCollision)); + } + } + + return Err(Error::UnexpectedExecStepError( + "*CALL* code not executed", + Box::new(step.clone()), + )); } - None + Ok(None) } } @@ -597,7 +813,9 @@ mod tracer_tests { bytecode::Bytecode, eth_types::{ToWord, Word}, evm::{stack::Stack, Gas, OpcodeId}, - mock, word, + mock, + state_db::Account, + word, }; use lazy_static::lazy_static; @@ -621,7 +839,7 @@ mod tracer_tests { ), tx: Transaction::new(&block.eth_tx), tx_ctx: TransactionContext::new(&block.eth_tx), - step: ExecStep::new(geth_step, GlobalCounter(0)), + step: ExecStep::new(geth_step, 0, GlobalCounter(0), 0), } } @@ -712,7 +930,7 @@ mod tracer_tests { let mut builder = CircuitInputBuilderTx::new(&block, step); assert_eq!( - builder.state_ref().get_step_err(step, next_step), + builder.state_ref().get_step_err(step, next_step).unwrap(), Some(ExecError::Depth) ); } @@ -764,12 +982,21 @@ mod tracer_tests { assert_eq!(next_step.unwrap().op, OpcodeId::PUSH2); assert_eq!(next_step.unwrap().stack, Stack(vec![Word::from(0)])); // success = 0 - // TODO: Uncomment once get_step_err correctly detects - // InsufficientBalance let mut builder = - // CircuitInputBuilderTx::new(&block, step); assert_eq!( - // builder.state_ref().get_step_err(step, next_step), - // Some(ExecError::InsufficientBalance) - // ); + let mut builder = CircuitInputBuilderTx::new(&block, step); + builder.builder.sdb.set_account( + &ADDR_A, + Account { + nonce: Word::zero(), + balance: Word::from(555u64), /* same value as in + * `mock::new_tracer_account` */ + storage: HashMap::new(), + codeHash: H256::zero(), + }, + ); + assert_eq!( + builder.state_ref().get_step_err(step, next_step).unwrap(), + Some(ExecError::InsufficientBalance) + ); } fn check_err_address_collision( @@ -830,13 +1057,13 @@ mod tracer_tests { code_b.write_op(OpcodeId::MSTORE); } let code_b_end = bytecode! { - PUSH1(0x00) // salt + PUSH3(0x123456) // salt PUSH1(len) // length PUSH1(0x00) // offset PUSH1(0x00) // value CREATE2 - PUSH1(0x00) // salt + PUSH3(0x123456) // salt PUSH1(len) // length PUSH1(0x00) // offset PUSH1(0x00) // value @@ -861,12 +1088,47 @@ mod tracer_tests { let next_step = block.geth_trace.struct_logs.get(index + 1); assert!(check_err_address_collision(step, next_step)); - // TODO: Uncomment once get_step_err correctly detects - // ContractAddressCollision let mut builder = - // CircuitInputBuilderTx::new(&block, step); assert_eq!( - // builder.state_ref().get_step_err(step, next_step), - // Some(ExecError::ContractAddressCollision) - // ); + let create2_address: Address = { + // get first RETURN + let (index, _) = block + .geth_trace + .struct_logs + .iter() + .enumerate() + .find(|(_, s)| s.op == OpcodeId::RETURN) + .unwrap(); + let next_step = block.geth_trace.struct_logs.get(index + 1); + let addr_word = next_step.unwrap().stack.last().unwrap(); + Address::from_slice(&addr_word.to_be_bytes()[12..]) + }; + + let mut builder = CircuitInputBuilderTx::new(&block, step); + // Set up call context at CREATE2 + builder.state_ref().push_call(CallKind::Create, *ADDR_B); + // Set up account and contract that exist during the second CREATE2 + builder.builder.sdb.set_account( + &ADDR_B, + Account { + nonce: Word::zero(), + balance: Word::from(555u64), /* same value as in + * `mock::new_tracer_account` */ + storage: HashMap::new(), + codeHash: H256::zero(), + }, + ); + builder.builder.sdb.set_account( + &create2_address, + Account { + nonce: Word::zero(), + balance: Word::zero(), + storage: HashMap::new(), + codeHash: H256::zero(), + }, + ); + assert_eq!( + builder.state_ref().get_step_err(step, next_step).unwrap(), + Some(ExecError::ContractAddressCollision) + ); } fn check_err_code_store_out_of_gas( @@ -948,9 +1210,10 @@ mod tracer_tests { assert!(check_err_code_store_out_of_gas(step, next_step)); let mut builder = CircuitInputBuilderTx::new(&block, step); - builder.tx_ctx.push_call_ctx(CallType::Create, *ADDR_B); + // Set up call context at CREATE + builder.state_ref().push_call(CallKind::Create, *ADDR_B); assert_eq!( - builder.state_ref().get_step_err(step, next_step), + builder.state_ref().get_step_err(step, next_step).unwrap(), Some(ExecError::CodeStoreOutOfGas) ); } @@ -1036,9 +1299,10 @@ mod tracer_tests { assert!(check_err_invalid_code(step, next_step)); let mut builder = CircuitInputBuilderTx::new(&block, step); - builder.tx_ctx.push_call_ctx(CallType::Create, *ADDR_B); + // Set up call context at RETURN + builder.state_ref().push_call(CallKind::Create, *ADDR_B); assert_eq!( - builder.state_ref().get_step_err(step, next_step), + builder.state_ref().get_step_err(step, next_step).unwrap(), Some(ExecError::InvalidCode) ); } @@ -1122,13 +1386,86 @@ mod tracer_tests { assert!(check_err_max_code_size_exceeded(step, next_step)); let mut builder = CircuitInputBuilderTx::new(&block, step); - builder.tx_ctx.push_call_ctx(CallType::Create, *ADDR_B); + // Set up call context at RETURN + builder.state_ref().push_call(CallKind::Create, *ADDR_B); assert_eq!( - builder.state_ref().get_step_err(step, next_step), + builder.state_ref().get_step_err(step, next_step).unwrap(), Some(ExecError::MaxCodeSizeExceeded) ); } + #[test] + fn tracer_create_stop() { + // code_creator doesn't output anything because it stops. + let code_creator = bytecode! { + PUSH32(word!("0xef00000000000000000000000000000000000000000000000000000000000000")) // value + PUSH1(0x00) // offset + MSTORE + PUSH1(0x01) // length + PUSH1(0x00) // offset + STOP + }; + + // code_a calls code_b which executes code_creator in CREATE + let code_a = bytecode! { + PUSH1(0x0) // retLength + PUSH1(0x0) // retOffset + PUSH1(0x0) // argsLength + PUSH1(0x0) // argsOffset + PUSH1(0x0) // value + PUSH32(*WORD_ADDR_B) // addr + PUSH32(0x1_0000) // gas + CALL + + PUSH2(0xaa) + }; + + let mut code_b = Bytecode::default(); + // pad code_creator to multiple of 32 bytes + let len = code_creator.code().len(); + let code_creator: Vec = code_creator + .code() + .iter() + .cloned() + .chain(0u8..((32 - len % 32) as u8)) + .collect(); + for (index, word) in code_creator.chunks(32).enumerate() { + code_b.push(32, Word::from_big_endian(word)); + code_b.push(32, Word::from(index * 32)); + code_b.write_op(OpcodeId::MSTORE); + } + let code_b_end = bytecode! { + PUSH1(len) // length + PUSH1(0x00) // offset + PUSH1(0x00) // value + CREATE + + PUSH3(0xbb) + }; + code_b.append(&code_b_end); + let block = + mock::BlockData::new_single_tx_trace_code_2(&code_a, &code_b) + .unwrap(); + + // get first STOP + let (index, step) = block + .geth_trace + .struct_logs + .iter() + .enumerate() + .find(|(_, s)| s.op == OpcodeId::STOP) + .unwrap(); + let next_step = block.geth_trace.struct_logs.get(index + 1); + + let mut builder = CircuitInputBuilderTx::new(&block, step); + // Set up call context at STOP + builder.state_ref().push_call(CallKind::Create, *ADDR_B); + assert_eq!( + builder.state_ref().get_step_err(step, next_step).unwrap(), + None + ); + } + // // Geth Errors not reported // @@ -1171,7 +1508,7 @@ mod tracer_tests { let mut builder = CircuitInputBuilderTx::new(&block, step); assert_eq!( - builder.state_ref().get_step_err(step, next_step), + builder.state_ref().get_step_err(step, next_step).unwrap(), Some(ExecError::InvalidJump) ); @@ -1198,7 +1535,7 @@ mod tracer_tests { let mut builder = CircuitInputBuilderTx::new(&block, step); assert_eq!( - builder.state_ref().get_step_err(step, next_step), + builder.state_ref().get_step_err(step, next_step).unwrap(), Some(ExecError::InvalidJump) ); } @@ -1233,7 +1570,7 @@ mod tracer_tests { let mut builder = CircuitInputBuilderTx::new(&block, step); assert_eq!( - builder.state_ref().get_step_err(step, next_step), + builder.state_ref().get_step_err(step, next_step).unwrap(), Some(ExecError::ExecutionReverted) ); @@ -1261,11 +1598,48 @@ mod tracer_tests { let mut builder = CircuitInputBuilderTx::new(&block, step); assert_eq!( - builder.state_ref().get_step_err(step, next_step), + builder.state_ref().get_step_err(step, next_step).unwrap(), Some(ExecError::ExecutionReverted) ); } + #[test] + fn tracer_stop() { + // Do a STOP + let code = bytecode! { + PUSH1(0x0) + PUSH2(0x0) + STOP + PUSH3(0x12) + STOP + }; + + // code_a calls code + let code_a = bytecode! { + PUSH1(0x0) // retLength + PUSH1(0x0) // retOffset + PUSH1(0x0) // argsLength + PUSH1(0x0) // argsOffset + PUSH1(0x0) // value + PUSH32(*WORD_ADDR_B) // addr + PUSH32(0x1_0000) // gas + CALL + + PUSH2(0xaa) + }; + let index = 10; // STOP + let block = mock::BlockData::new_single_tx_trace_code_2(&code_a, &code) + .unwrap(); + let step = &block.geth_trace.struct_logs[index]; + let next_step = block.geth_trace.struct_logs.get(index + 1); + + let mut builder = CircuitInputBuilderTx::new(&block, step); + assert_eq!( + builder.state_ref().get_step_err(step, next_step).unwrap(), + None + ); + } + fn check_err_return_data_out_of_bounds( step: &GethExecStep, next_step: Option<&GethExecStep>, @@ -1324,7 +1698,7 @@ mod tracer_tests { let mut builder = CircuitInputBuilderTx::new(&block, step); assert_eq!( - builder.state_ref().get_step_err(step, next_step), + builder.state_ref().get_step_err(step, next_step).unwrap(), Some(ExecError::ReturnDataOutOfBounds) ); } @@ -1353,7 +1727,7 @@ mod tracer_tests { let mut builder = CircuitInputBuilderTx::new(&block, step); assert_eq!( - builder.state_ref().get_step_err(step, next_step), + builder.state_ref().get_step_err(step, next_step).unwrap(), Some(ExecError::OutOfGas(OogError::PureMemory)) ); } @@ -1380,7 +1754,7 @@ mod tracer_tests { let mut builder = CircuitInputBuilderTx::new(&block, step); assert_eq!( - builder.state_ref().get_step_err(step, next_step), + builder.state_ref().get_step_err(step, next_step).unwrap(), Some(ExecError::InvalidOpcode) ); } @@ -1418,7 +1792,7 @@ mod tracer_tests { let mut builder = CircuitInputBuilderTx::new(&block, step); assert_eq!( - builder.state_ref().get_step_err(step, next_step), + builder.state_ref().get_step_err(step, next_step).unwrap(), Some(ExecError::WriteProtection) ); } @@ -1458,7 +1832,7 @@ mod tracer_tests { let mut builder = CircuitInputBuilderTx::new(&block, step); assert_eq!( - builder.state_ref().get_step_err(step, next_step), + builder.state_ref().get_step_err(step, next_step).unwrap(), Some(ExecError::StackOverflow) ); } @@ -1481,8 +1855,190 @@ mod tracer_tests { let mut builder = CircuitInputBuilderTx::new(&block, step); assert_eq!( - builder.state_ref().get_step_err(step, next_step), + builder.state_ref().get_step_err(step, next_step).unwrap(), Some(ExecError::StackUnderflow) ); } + + // + // Circuit Input Builder tests + // + + #[test] + fn create2_address() { + // code_creator outputs 0x6050. + let code_creator = bytecode! { + PUSH32(word!("0x6050000000000000000000000000000000000000000000000000000000000000")) // value + PUSH1(0x00) // offset + MSTORE + PUSH1(0x02) // length + PUSH1(0x00) // offset + RETURN + }; + + // code_a calls code_b which executes code_creator in CREATE + let code_a = bytecode! { + PUSH1(0x0) // retLength + PUSH1(0x0) // retOffset + PUSH1(0x0) // argsLength + PUSH1(0x0) // argsOffset + PUSH1(0x0) // value + PUSH32(*WORD_ADDR_B) // addr + PUSH32(0x1_0000) // gas + CALL + + PUSH2(0xaa) + }; + + let mut code_b = Bytecode::default(); + // pad code_creator to multiple of 32 bytes + let len = code_creator.code().len(); + let code_creator: Vec = code_creator + .code() + .iter() + .cloned() + .chain(0u8..((32 - len % 32) as u8)) + .collect(); + for (index, word) in code_creator.chunks(32).enumerate() { + code_b.push(32, Word::from_big_endian(word)); + code_b.push(32, Word::from(index * 32)); + code_b.write_op(OpcodeId::MSTORE); + } + let code_b_end = bytecode! { + PUSH3(0x123456) // salt + PUSH1(len) // length + PUSH1(0x00) // offset + PUSH1(0x00) // value + CREATE2 + + PUSH3(0xbb) + }; + code_b.append(&code_b_end); + let block = + mock::BlockData::new_single_tx_trace_code_2(&code_a, &code_b) + .unwrap(); + + // get RETURN + let (index_return, _) = block + .geth_trace + .struct_logs + .iter() + .enumerate() + .find(|(_, s)| s.op == OpcodeId::RETURN) + .unwrap(); + let next_step_return = + block.geth_trace.struct_logs.get(index_return + 1); + let addr_expect = next_step_return.unwrap().stack.last().unwrap(); + + // get CREATE2 + let step_create2 = block + .geth_trace + .struct_logs + .iter() + .find(|s| s.op == OpcodeId::CREATE2) + .unwrap(); + let mut builder = CircuitInputBuilderTx::new(&block, step_create2); + // Set up call context at CREATE2 + builder.state_ref().push_call(CallKind::Create, *ADDR_B); + let addr = builder.state_ref().create2_address(step_create2).unwrap(); + + assert_eq!(addr.to_word(), addr_expect); + } + + #[test] + fn create_address() { + // code_creator outputs 0x6050. + let code_creator = bytecode! { + PUSH32(word!("0x6050000000000000000000000000000000000000000000000000000000000000")) // value + PUSH1(0x00) // offset + MSTORE + PUSH1(0x02) // length + PUSH1(0x00) // offset + RETURN + }; + + // code_a calls code_b which executes code_creator in CREATE + let code_a = bytecode! { + PUSH1(0x0) // retLength + PUSH1(0x0) // retOffset + PUSH1(0x0) // argsLength + PUSH1(0x0) // argsOffset + PUSH1(0x0) // value + PUSH32(*WORD_ADDR_B) // addr + PUSH32(0x1_0000) // gas + CALL + + PUSH2(0xaa) + }; + + let mut code_b = Bytecode::default(); + // pad code_creator to multiple of 32 bytes + let len = code_creator.code().len(); + let code_creator: Vec = code_creator + .code() + .iter() + .cloned() + .chain(0u8..((32 - len % 32) as u8)) + .collect(); + for (index, word) in code_creator.chunks(32).enumerate() { + code_b.push(32, Word::from_big_endian(word)); + code_b.push(32, Word::from(index * 32)); + code_b.write_op(OpcodeId::MSTORE); + } + // We do CREATE 2 times to use a nonce != 0 in the second one. + let code_b_end = bytecode! { + PUSH1(len) // length + PUSH1(0x00) // offset + PUSH1(0x00) // value + CREATE + + PUSH1(len) // length + PUSH1(0x00) // offset + PUSH1(0x00) // value + CREATE + + PUSH3(0xbb) + }; + code_b.append(&code_b_end); + let block = + mock::BlockData::new_single_tx_trace_code_2(&code_a, &code_b) + .unwrap(); + + // get last RETURN + let (index_return, _) = block + .geth_trace + .struct_logs + .iter() + .enumerate() + .rev() + .find(|(_, s)| s.op == OpcodeId::RETURN) + .unwrap(); + let next_step_return = + block.geth_trace.struct_logs.get(index_return + 1); + let addr_expect = next_step_return.unwrap().stack.last().unwrap(); + + // get last CREATE + let step_create = block + .geth_trace + .struct_logs + .iter() + .rev() + .find(|s| s.op == OpcodeId::CREATE) + .unwrap(); + let mut builder = CircuitInputBuilderTx::new(&block, step_create); + // Set up call context at CREATE + builder.state_ref().push_call(CallKind::Create, *ADDR_B); + builder.builder.sdb.set_account( + &ADDR_B, + Account { + nonce: Word::from(1), + balance: Word::zero(), + storage: HashMap::new(), + codeHash: H256::zero(), + }, + ); + let addr = builder.state_ref().create_address().unwrap(); + + assert_eq!(addr.to_word(), addr_expect); + } } diff --git a/bus-mapping/src/error.rs b/bus-mapping/src/error.rs index 4dd3778f1f7..a01117cba09 100644 --- a/bus-mapping/src/error.rs +++ b/bus-mapping/src/error.rs @@ -1,5 +1,6 @@ //! Error module for the bus-mapping crate +use crate::eth_types::{Address, GethExecStep, Word}; use core::fmt::{Display, Formatter, Result as FmtResult}; use ethers_providers::ProviderError; use std::error::Error as StdError; @@ -32,6 +33,14 @@ pub enum Error { JSONRpcError(ProviderError), /// OpcodeId is not a call type. OpcodeIdNotCallType, + /// Account not found in the StateDB + AccountNotFound(Address), + /// Storage key not found in the StateDB + StorageKeyNotFound(Address, Word), + /// Unable to figure out error at a [`GethExecStep`] + UnexpectedExecStepError(&'static str, Box), + /// Invalid [`GethExecStep`] due to an invalid/unexpected value in it. + InvalidGethExecStep(&'static str, Box), } impl From for Error { diff --git a/bus-mapping/src/eth_types.rs b/bus-mapping/src/eth_types.rs index e46035e13cd..1a2ada7725b 100644 --- a/bus-mapping/src/eth_types.rs +++ b/bus-mapping/src/eth_types.rs @@ -2,15 +2,15 @@ use crate::evm::{memory::Memory, stack::Stack, storage::Storage}; use crate::evm::{Gas, GasCost, OpcodeId, ProgramCounter}; +use ethers_core::types; +pub use ethers_core::types::{ + transaction::response::Transaction, Address, Block, Bytes, H160, H256, + U256, U64, +}; use pasta_curves::arithmetic::FieldExt; use serde::{de, Deserialize, Serialize}; use std::collections::HashMap; use std::str::FromStr; -use web3::types; -pub use web3::types::{ - AccessList, Address, Block, Bytes, Index, Transaction, H2048, H256, H64, - U256, U64, -}; /// Trait used to define types that can be converted to a 256 bit scalar value. pub trait ToScalar { diff --git a/bus-mapping/src/evm/opcodes/dup.rs b/bus-mapping/src/evm/opcodes/dup.rs index 49ba9163532..a4a23f116df 100644 --- a/bus-mapping/src/evm/opcodes/dup.rs +++ b/bus-mapping/src/evm/opcodes/dup.rs @@ -82,7 +82,9 @@ mod dup_tests { { let mut step = ExecStep::new( &block.geth_trace.struct_logs[i], + 0, test_builder.block_ctx.gc, + 0, ); let mut state_ref = test_builder.state_ref(&mut tx, &mut tx_ctx, &mut step); diff --git a/bus-mapping/src/evm/opcodes/mload.rs b/bus-mapping/src/evm/opcodes/mload.rs index 48d4b90f79b..17e6c42dae4 100644 --- a/bus-mapping/src/evm/opcodes/mload.rs +++ b/bus-mapping/src/evm/opcodes/mload.rs @@ -100,7 +100,9 @@ mod mload_tests { // Generate step corresponding to MLOAD let mut step = ExecStep::new( &block.geth_trace.struct_logs[0], + 0, test_builder.block_ctx.gc, + 0, ); let mut state_ref = test_builder.state_ref(&mut tx, &mut tx_ctx, &mut step); diff --git a/bus-mapping/src/evm/opcodes/mstore.rs b/bus-mapping/src/evm/opcodes/mstore.rs index 12c4159d221..5ac6010a650 100644 --- a/bus-mapping/src/evm/opcodes/mstore.rs +++ b/bus-mapping/src/evm/opcodes/mstore.rs @@ -90,7 +90,9 @@ mod mstore_tests { // Generate step corresponding to MSTORE let mut step = ExecStep::new( &block.geth_trace.struct_logs[0], + 0, test_builder.block_ctx.gc, + 0, ); let mut state_ref = test_builder.state_ref(&mut tx, &mut tx_ctx, &mut step); diff --git a/bus-mapping/src/evm/opcodes/pc.rs b/bus-mapping/src/evm/opcodes/pc.rs index af88e6c108d..021cf758b2b 100644 --- a/bus-mapping/src/evm/opcodes/pc.rs +++ b/bus-mapping/src/evm/opcodes/pc.rs @@ -73,7 +73,9 @@ mod pc_tests { // Generate step corresponding to MLOAD let mut step = ExecStep::new( &block.geth_trace.struct_logs[0], + 0, test_builder.block_ctx.gc, + 0, ); let mut state_ref = test_builder.state_ref(&mut tx, &mut tx_ctx, &mut step); diff --git a/bus-mapping/src/evm/opcodes/push.rs b/bus-mapping/src/evm/opcodes/push.rs index dd69d594f5c..780d9105b06 100644 --- a/bus-mapping/src/evm/opcodes/push.rs +++ b/bus-mapping/src/evm/opcodes/push.rs @@ -84,7 +84,9 @@ mod push_tests { { let mut step = ExecStep::new( &block.geth_trace.struct_logs[i], + 0, test_builder.block_ctx.gc, + 0, ); let mut state_ref = test_builder.state_ref(&mut tx, &mut tx_ctx, &mut step); diff --git a/bus-mapping/src/evm/opcodes/sload.rs b/bus-mapping/src/evm/opcodes/sload.rs index d24047568a4..f591aed736f 100644 --- a/bus-mapping/src/evm/opcodes/sload.rs +++ b/bus-mapping/src/evm/opcodes/sload.rs @@ -30,7 +30,7 @@ impl Opcode for Sload { let storage_value_read = step.storage.get_or_err(&stack_value_read)?; state.push_op(StorageOp::new( RW::READ, - state.tx_ctx.call_ctx().address, + state.call().address, stack_value_read, storage_value_read, storage_value_read, @@ -96,7 +96,9 @@ mod sload_tests { // Generate step corresponding to SLOAD let mut step = ExecStep::new( &block.geth_trace.struct_logs[0], + 0, test_builder.block_ctx.gc, + 0, ); let mut state_ref = test_builder.state_ref(&mut tx, &mut tx_ctx, &mut step); diff --git a/bus-mapping/src/evm/opcodes/stackonlyop.rs b/bus-mapping/src/evm/opcodes/stackonlyop.rs index e6a8040af33..fd1784a7b9c 100644 --- a/bus-mapping/src/evm/opcodes/stackonlyop.rs +++ b/bus-mapping/src/evm/opcodes/stackonlyop.rs @@ -85,7 +85,9 @@ mod stackonlyop_tests { // Generate step corresponding to NOT let mut step = ExecStep::new( &block.geth_trace.struct_logs[0], + 0, test_builder.block_ctx.gc, + 0, ); let mut state_ref = test_builder.state_ref(&mut tx, &mut tx_ctx, &mut step); @@ -149,7 +151,9 @@ mod stackonlyop_tests { // Generate step corresponding to ADD let mut step = ExecStep::new( &block.geth_trace.struct_logs[0], + 0, test_builder.block_ctx.gc, + 0, ); let mut state_ref = test_builder.state_ref(&mut tx, &mut tx_ctx, &mut step); @@ -227,7 +231,9 @@ mod stackonlyop_tests { // Generate step corresponding to ADDMOD let mut step = ExecStep::new( &block.geth_trace.struct_logs[0], + 0, test_builder.block_ctx.gc, + 0, ); let mut state_ref = test_builder.state_ref(&mut tx, &mut tx_ctx, &mut step); diff --git a/bus-mapping/src/evm/opcodes/swap.rs b/bus-mapping/src/evm/opcodes/swap.rs index e2212faed59..31578534d00 100644 --- a/bus-mapping/src/evm/opcodes/swap.rs +++ b/bus-mapping/src/evm/opcodes/swap.rs @@ -101,7 +101,9 @@ mod swap_tests { for (i, (a, b)) in [(6, 5), (5, 3), (3, 1)].iter().enumerate() { let mut step = ExecStep::new( &block.geth_trace.struct_logs[i], + 0, test_builder.block_ctx.gc, + 0, ); let mut state_ref = test_builder.state_ref(&mut tx, &mut tx_ctx, &mut step); diff --git a/bus-mapping/src/external_tracer.rs b/bus-mapping/src/external_tracer.rs index 17521d054e0..6060fbc0c9e 100644 --- a/bus-mapping/src/external_tracer.rs +++ b/bus-mapping/src/external_tracer.rs @@ -20,7 +20,7 @@ impl Transaction { /// Create Self from a web3 transaction pub fn from_eth_tx(tx: ð_types::Transaction) -> Self { Self { - origin: tx.from.unwrap(), + origin: tx.from, gas_limit: tx.gas, target: tx.to.unwrap(), } diff --git a/bus-mapping/src/lib.rs b/bus-mapping/src/lib.rs index fafd0a5e397..afdefa8d75a 100644 --- a/bus-mapping/src/lib.rs +++ b/bus-mapping/src/lib.rs @@ -226,6 +226,6 @@ pub mod eth_types; pub(crate) mod geth_errors; pub mod mock; pub mod rpc; -pub(crate) mod statedb; +pub(crate) mod state_db; pub use error::Error; pub use exec_trace::BlockConstants; diff --git a/bus-mapping/src/mock.rs b/bus-mapping/src/mock.rs index c753de0e050..8389a377d7e 100644 --- a/bus-mapping/src/mock.rs +++ b/bus-mapping/src/mock.rs @@ -1,7 +1,7 @@ //! Mock types and functions to generate mock data useful for tests use crate::address; use crate::bytecode::Bytecode; -use crate::eth_types::{self, Address, Bytes, Hash, Index, Word, H64, U64}; +use crate::eth_types::{self, Address, Bytes, Hash, Word, U64}; use crate::evm::Gas; use crate::external_tracer; use crate::BlockConstants; @@ -10,18 +10,18 @@ use crate::Error; /// Generate a new mock block with preloaded data, useful for tests. pub fn new_block() -> eth_types::Block<()> { eth_types::Block { - hash: Some(Hash::from([0u8; 32])), - parent_hash: Hash::from([0u8; 32]), - uncles_hash: Hash::from([0u8; 32]), - author: Address::from([0u8; 20]), - state_root: Hash::from([0u8; 32]), - transactions_root: Hash::from([0u8; 32]), - receipts_root: Hash::from([0u8; 32]), + hash: Some(Hash::zero()), + parent_hash: Hash::zero(), + uncles_hash: Hash::zero(), + author: Address::zero(), + state_root: Hash::zero(), + transactions_root: Hash::zero(), + receipts_root: Hash::zero(), number: Some(U64([123456u64])), gas_used: Word::from(15_000_000u64), gas_limit: Word::from(15_000_000u64), base_fee_per_gas: Some(Word::from(97u64)), - extra_data: Bytes(Vec::new()), + extra_data: Bytes::default(), logs_bloom: None, timestamp: Word::from(1633398551u64), difficulty: Word::from(0x200000u64), @@ -31,30 +31,32 @@ pub fn new_block() -> eth_types::Block<()> { transactions: Vec::new(), size: None, mix_hash: None, - nonce: Some(H64([0u8; 8])), + nonce: Some(U64::zero()), } } /// Generate a new mock transaction with preloaded data, useful for tests. pub fn new_tx(block: ð_types::Block) -> eth_types::Transaction { eth_types::Transaction { - hash: Hash::from([0u8; 32]), - nonce: Word::from([0u8; 32]), + hash: Hash::zero(), + nonce: Word::zero(), block_hash: block.hash, block_number: block.number, - transaction_index: Some(Index::from(0u64)), - from: Some(address!("0x00000000000000000000000000000000c014ba5e")), + transaction_index: Some(U64::zero()), + from: address!("0x00000000000000000000000000000000c014ba5e"), to: Some(Address::zero()), - value: Word::from([0u8; 32]), - gas_price: Word::from([0u8; 32]), + value: Word::zero(), + gas_price: Some(Word::zero()), gas: Word::from(1_000_000u64), - input: Bytes(Vec::new()), - v: Some(U64([0u64])), - r: Some(Word::from([0u8; 32])), - s: Some(Word::from([0u8; 32])), - raw: Some(Bytes(Vec::new())), - transaction_type: Some(U64([0u64])), + input: Bytes::default(), + v: U64::zero(), + r: Word::zero(), + s: Word::zero(), + transaction_type: Some(U64::zero()), access_list: None, + max_priority_fee_per_gas: Some(Word::zero()), + max_fee_per_gas: Some(Word::zero()), + chain_id: Some(Word::zero()), } } diff --git a/bus-mapping/src/operation.rs b/bus-mapping/src/operation.rs index cc6214b7eec..492991b148f 100644 --- a/bus-mapping/src/operation.rs +++ b/bus-mapping/src/operation.rs @@ -265,6 +265,8 @@ pub enum OpEnum { #[derive(Debug, Clone)] pub struct Operation { gc: GlobalCounter, + /// True when this Operation is a revert of its mirror + revert: bool, op: T, } @@ -294,7 +296,11 @@ impl Ord for Operation { impl Operation { /// Create a new Operation from an `op` with a `gc` pub fn new(gc: GlobalCounter, op: T) -> Self { - Self { gc, op } + Self { + gc, + revert: false, + op, + } } /// Return this `Operation` `gc` diff --git a/bus-mapping/src/statedb.rs b/bus-mapping/src/state_db.rs similarity index 97% rename from bus-mapping/src/statedb.rs rename to bus-mapping/src/state_db.rs index 270212f48e5..96143f7750d 100644 --- a/bus-mapping/src/statedb.rs +++ b/bus-mapping/src/state_db.rs @@ -4,7 +4,7 @@ use std::collections::HashMap; /// Account of the Ethereum State Trie, which contains an in-memory key-value /// database that represents the Account Storage Trie. #[derive(Debug, PartialEq)] -pub(crate) struct Account { +pub struct Account { pub nonce: Word, pub balance: Word, pub storage: HashMap, @@ -24,12 +24,18 @@ impl Account { /// In-memory key-value database that represents the Ethereum State Trie. #[derive(Debug)] -pub(crate) struct StateDB { +pub struct StateDB { state: HashMap, acc_zero: Account, value_zero: Word, } +impl Default for StateDB { + fn default() -> Self { + Self::new() + } +} + impl StateDB { /// Create an empty Self pub fn new() -> Self { diff --git a/geth-utils/README.md b/geth-utils/README.md index 82deb759f2f..71b754408be 100644 --- a/geth-utils/README.md +++ b/geth-utils/README.md @@ -14,3 +14,16 @@ For [`./example/mstore_mload.go`](./example/mstore_mload.go) as an example, it d ```bash go run ./example/mstore_mload.go > ./mstore_mload.json ``` + +### Debuging + +The execution traces returned by geth omit some information like execution +errors in some situations. Moreover you may want to inspect some intermediate +values of the EVM execution for debugging purposes. + +Print debugging can be easily achieved by replacing the dependency of `go-ethereum` by a local copy of the repository. Just clone `go-ethereum` into a folder next to the `zkevm-circuits` repository, and uncomment the following line in `go.mod`: +``` +replace github.com/ethereum/go-ethereum => ../../go-ethereum +``` + +Now you can add print logs in your `go-ethereum` copy as necessary. diff --git a/geth-utils/go.mod b/geth-utils/go.mod index a522054ab66..0c9d73de9ab 100644 --- a/geth-utils/go.mod +++ b/geth-utils/go.mod @@ -6,3 +6,6 @@ require ( github.com/ethereum/go-ethereum v1.10.12 github.com/holiman/uint256 v1.2.0 ) + +// Uncomment for debugging +// replace github.com/ethereum/go-ethereum => ../../go-ethereum