diff --git a/bus-mapping/src/evm/opcodes.rs b/bus-mapping/src/evm/opcodes.rs index c163d4016f..d30910f74d 100644 --- a/bus-mapping/src/evm/opcodes.rs +++ b/bus-mapping/src/evm/opcodes.rs @@ -44,6 +44,7 @@ mod stackonlyop; mod stop; mod swap; +mod error_code_store; mod error_invalid_jump; mod error_oog_call; mod error_oog_exp; @@ -71,6 +72,7 @@ use codecopy::Codecopy; use codesize::Codesize; use create::Create; use dup::Dup; +use error_code_store::ErrorCodeStore; use error_invalid_jump::InvalidJump; use error_oog_call::OOGCall; use error_oog_exp::OOGExp; @@ -80,6 +82,7 @@ use error_oog_sload_sstore::OOGSloadSstore; use error_return_data_outofbound::ErrorReturnDataOutOfBound; use error_simple::ErrorSimple; use error_write_protection::ErrorWriteProtection; + use exp::Exponentiation; use extcodecopy::Extcodecopy; use extcodehash::Extcodehash; @@ -297,6 +300,8 @@ fn fn_gen_error_state_associated_ops(error: &ExecError) -> Option Some(ErrorWriteProtection::gen_associated_ops), ExecError::ReturnDataOutOfBounds => Some(ErrorReturnDataOutOfBound::gen_associated_ops), + ExecError::CodeStoreOutOfGas => Some(ErrorCodeStore::gen_associated_ops), + ExecError::MaxCodeSizeExceeded => Some(ErrorCodeStore::gen_associated_ops), // call, callcode, create & create2 can encounter DepthError error, ExecError::Depth(DepthError::Call) => Some(CallOpcode::<7>::gen_associated_ops), ExecError::Depth(DepthError::Create) => Some(Create::::gen_associated_ops), diff --git a/bus-mapping/src/evm/opcodes/error_code_store.rs b/bus-mapping/src/evm/opcodes/error_code_store.rs new file mode 100644 index 0000000000..2e186f60cd --- /dev/null +++ b/bus-mapping/src/evm/opcodes/error_code_store.rs @@ -0,0 +1,42 @@ +use crate::{ + circuit_input_builder::{CircuitInputStateRef, ExecStep}, + error::ExecError, + evm::Opcode, + Error, +}; +use eth_types::GethExecStep; + +#[derive(Debug, Copy, Clone)] +pub struct ErrorCodeStore; + +impl Opcode for ErrorCodeStore { + fn gen_associated_ops( + state: &mut CircuitInputStateRef, + geth_steps: &[GethExecStep], + ) -> Result, Error> { + let geth_step = &geth_steps[0]; + let mut exec_step = state.new_step(geth_step)?; + let next_step = geth_steps.get(1); + + exec_step.error = state.get_step_err(geth_step, next_step)?; + + assert!( + exec_step.error == Some(ExecError::CodeStoreOutOfGas) + || exec_step.error == Some(ExecError::MaxCodeSizeExceeded) + ); + + let offset = geth_step.stack.nth_last(0)?; + let length = geth_step.stack.nth_last(1)?; + state.stack_read(&mut exec_step, geth_step.stack.nth_last_filled(0), offset)?; + state.stack_read(&mut exec_step, geth_step.stack.nth_last_filled(1), length)?; + + // in internal call context + let call = state.call()?; + + // create context check + assert!(call.is_create()); + + state.handle_return(&mut exec_step, geth_steps, true)?; + Ok(vec![exec_step]) + } +} diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 9c9dc8194d..900f4276fb 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -64,6 +64,7 @@ mod dummy; mod dup; mod end_block; mod end_tx; +mod error_code_store; mod error_invalid_jump; mod error_invalid_opcode; mod error_oog_call; @@ -76,6 +77,7 @@ mod error_oog_static_memory; mod error_return_data_oo_bound; mod error_stack; mod error_write_protection; + mod exp; mod extcodecopy; mod extcodehash; @@ -137,6 +139,7 @@ use dummy::DummyGadget; use dup::DupGadget; use end_block::EndBlockGadget; use end_tx::EndTxGadget; +use error_code_store::ErrorCodeStoreGadget; use error_invalid_jump::ErrorInvalidJumpGadget; use error_invalid_opcode::ErrorInvalidOpcodeGadget; use error_oog_call::ErrorOOGCallGadget; @@ -148,6 +151,7 @@ use error_oog_sload_sstore::ErrorOOGSloadSstoreGadget; use error_return_data_oo_bound::ErrorReturnDataOutOfBoundGadget; use error_stack::ErrorStackGadget; use error_write_protection::ErrorWriteProtectionGadget; + use exp::ExponentiationGadget; use extcodecopy::ExtcodecopyGadget; use extcodehash::ExtcodehashGadget; @@ -305,7 +309,9 @@ pub struct ExecutionConfig { error_oog_create2: Box>, error_oog_self_destruct: Box>, - error_oog_code_store: Box>, + error_code_store: Box>, + error_insufficient_balance: + Box>, error_invalid_jump: Box>, error_invalid_opcode: Box>, error_depth: Box>, @@ -567,7 +573,8 @@ impl ExecutionConfig { error_oog_exp: configure_gadget!(), error_oog_create2: configure_gadget!(), error_oog_self_destruct: configure_gadget!(), - error_oog_code_store: configure_gadget!(), + error_code_store: configure_gadget!(), + error_insufficient_balance: configure_gadget!(), error_invalid_jump: configure_gadget!(), error_invalid_opcode: configure_gadget!(), error_write_protection: configure_gadget!(), @@ -1276,8 +1283,8 @@ impl ExecutionConfig { assign_exec_step!(self.error_oog_self_destruct) } - ExecutionState::ErrorOutOfGasCodeStore => { - assign_exec_step!(self.error_oog_code_store) + ExecutionState::ErrorCodeStore => { + assign_exec_step!(self.error_code_store) } ExecutionState::ErrorStack => { assign_exec_step!(self.error_stack) diff --git a/zkevm-circuits/src/evm_circuit/execution/error_code_store.rs b/zkevm-circuits/src/evm_circuit/execution/error_code_store.rs new file mode 100644 index 0000000000..9684f84055 --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/execution/error_code_store.rs @@ -0,0 +1,320 @@ +use crate::{ + evm_circuit::{ + execution::ExecutionGadget, + param::{N_BYTES_GAS, N_BYTES_U64}, + step::ExecutionState, + util::{ + common_gadget::CommonErrorGadget, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + math_gadget::LtGadget, + memory_gadget::MemoryAddressGadget, + CachedRegion, Cell, + }, + witness::{Block, Call, ExecStep, Transaction}, + }, + util::Expr, +}; + +use eth_types::{evm_types::GasCost, Field}; + +use halo2_proofs::{circuit::Value, plonk::Error}; + +const MAXCODESIZE: u64 = 0x6000u64; + +/// Gadget for code store oog and max code size exceed +#[derive(Clone, Debug)] +pub(crate) struct ErrorCodeStoreGadget { + opcode: Cell, + memory_address: MemoryAddressGadget, + // check for CodeStoreOutOfGas error + code_store_gas_insufficient: LtGadget, + // check for MaxCodeSizeExceeded error + max_code_size_exceed: LtGadget, + common_error_gadget: CommonErrorGadget, +} + +impl ExecutionGadget for ErrorCodeStoreGadget { + const NAME: &'static str = "ErrorCodeStore"; + + const EXECUTION_STATE: ExecutionState = ExecutionState::ErrorCodeStore; + + fn configure(cb: &mut EVMConstraintBuilder) -> Self { + let opcode = cb.query_cell(); + + let offset = cb.query_cell_phase2(); + let length = cb.query_word_rlc(); + + cb.stack_pop(offset.expr()); + cb.stack_pop(length.expr()); + let memory_address = MemoryAddressGadget::construct(cb, offset, length); + + cb.require_true("is_create is true", cb.curr.state.is_create.expr()); + + // constrain code store gas > gas left, that is GasCost::CODE_DEPOSIT_BYTE_COST + // * length > gas left + let code_store_gas_insufficient = LtGadget::construct( + cb, + cb.curr.state.gas_left.expr(), + GasCost::CODE_DEPOSIT_BYTE_COST.expr() * memory_address.length(), + ); + + // constrain code size > MAXCODESIZE + let max_code_size_exceed = + LtGadget::construct(cb, MAXCODESIZE.expr(), memory_address.length()); + + // check must be one of CodeStoreOutOfGas or MaxCodeSizeExceeded + cb.require_in_set( + "CodeStoreOutOfGas or MaxCodeSizeExceeded", + code_store_gas_insufficient.expr() + max_code_size_exceed.expr(), + vec![1.expr(), 2.expr()], + ); + + let common_error_gadget = CommonErrorGadget::construct_with_lastcallee_return_data( + cb, + opcode.expr(), + 4.expr(), + memory_address.offset(), + memory_address.length(), + ); + + Self { + opcode, + memory_address, + code_store_gas_insufficient, + max_code_size_exceed, + common_error_gadget, + } + } + + fn assign_exec_step( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + block: &Block, + _tx: &Transaction, + call: &Call, + step: &ExecStep, + ) -> Result<(), Error> { + let opcode = step.opcode().unwrap(); + self.opcode + .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; + + let [memory_offset, length] = [0, 1].map(|i| block.get_rws(step, i).stack_value()); + self.memory_address + .assign(region, offset, memory_offset, length)?; + + self.code_store_gas_insufficient.assign( + region, + offset, + F::from(step.gas_left), + F::from(GasCost::CODE_DEPOSIT_BYTE_COST * length.as_u64()), + )?; + + self.max_code_size_exceed.assign( + region, + offset, + F::from(MAXCODESIZE), + F::from(length.as_u64()), + )?; + + self.common_error_gadget + .assign(region, offset, block, call, step, 4)?; + Ok(()) + } +} + +#[cfg(test)] +mod test { + use bus_mapping::circuit_input_builder::FixedCParams; + use eth_types::{ + address, + bytecode, + evm_types::OpcodeId, + geth_types::Account, + Address, + Bytecode, + Word, + // word, + }; + + use lazy_static::lazy_static; + use mock::{eth, TestContext, MOCK_ACCOUNTS}; + + use crate::test_util::CircuitTestBuilder; + + const CALLEE_ADDRESS: Address = Address::repeat_byte(0xff); + lazy_static! { + static ref CALLER_ADDRESS: Address = address!("0x00bbccddee000000000000000000000000002400"); + } + + const MAXCODESIZE: u64 = 0x6000u64; + + fn run_test_circuits(ctx: TestContext<2, 1>) { + CircuitTestBuilder::new_from_test_ctx(ctx) + .params(FixedCParams { + max_rws: 4500, + ..Default::default() + }) + .run(); + } + + fn initialization_bytecode(is_oog: bool) -> Bytecode { + let memory_bytes = [0x60; 10]; + let memory_value = Word::from_big_endian(&memory_bytes); + let code_len = if is_oog { 0 } else { MAXCODESIZE + 1 }; + + let mut code = bytecode! { + PUSH10(memory_value) + PUSH32(code_len) + MSTORE + PUSH2(if is_oog { 5 } else { code_len}) // length to copy + PUSH2(32u64 - u64::try_from(memory_bytes.len()).unwrap()) // offset + //PUSH2(0x00) // offset + + }; + code.write_op(OpcodeId::RETURN); + + code + } + + fn creator_bytecode(initialization_bytecode: Bytecode, is_create2: bool) -> Bytecode { + let initialization_bytes = initialization_bytecode.code(); + let mut code = Bytecode::default(); + + // construct maxcodesize + 1 memory bytes + let code_creator: Vec = initialization_bytes + .to_vec() + .iter() + .cloned() + .chain(0u8..((32 - initialization_bytes.len() % 32) as u8)) + .collect(); + for (index, word) in code_creator.chunks(32).enumerate() { + code.push(32, Word::from_big_endian(word)); + code.push(32, Word::from(index * 32)); + code.write_op(OpcodeId::MSTORE); + } + + if is_create2 { + code.append(&bytecode! {PUSH1(45)}); // salt; + } + code.append(&bytecode! { + PUSH32(initialization_bytes.len()) // size + PUSH2(0x00) // offset + PUSH2(23414) // value + }); + code.write_op(if is_create2 { + OpcodeId::CREATE2 + } else { + OpcodeId::CREATE + }); + code.append(&bytecode! { + PUSH1(0) + PUSH1(0) + RETURN + }); + + code + } + + fn test_context(caller: Account, is_oog: bool) -> TestContext<2, 1> { + TestContext::new( + None, + |accs| { + accs[0] + .address(address!("0x000000000000000000000000000000000000cafe")) + .balance(eth(10)); + accs[1].account(&caller); + }, + |mut txs, accs| { + txs[0] + .from(accs[0].address) + .to(accs[1].address) + .gas(if is_oog { + 53800u64.into() + } else { + 103800u64.into() + }); + }, + |block, _| block, + ) + .unwrap() + } + + #[test] + fn test_create_codestore_oog() { + for is_create2 in [false, true] { + let initialization_code = initialization_bytecode(true); + let root_code = creator_bytecode(initialization_code, is_create2); + let caller = Account { + address: *CALLER_ADDRESS, + code: root_code.into(), + nonce: 1.into(), + balance: eth(10), + ..Default::default() + }; + run_test_circuits(test_context(caller, true)); + } + } + + #[test] + fn test_create_max_code_size_exceed() { + for is_create2 in [false, true] { + let initialization_code = initialization_bytecode(false); + let root_code = creator_bytecode(initialization_code, is_create2); + let caller = Account { + address: *CALLER_ADDRESS, + code: root_code.into(), + nonce: 1.into(), + balance: eth(10), + ..Default::default() + }; + run_test_circuits(test_context(caller, false)); + } + } + + #[test] + fn tx_deploy_code_store_oog() { + let code = initialization_bytecode(true); + + let ctx = TestContext::<1, 1>::new( + None, + |accs| { + accs[0].address(MOCK_ACCOUNTS[0]).balance(eth(20)); + }, + |mut txs, _accs| { + txs[0] + .from(MOCK_ACCOUNTS[0]) + .gas(53446u64.into()) + .value(eth(2)) + .input(code.into()); + }, + |block, _tx| block.number(0xcafeu64), + ) + .unwrap(); + + CircuitTestBuilder::new_from_test_ctx(ctx).run(); + } + + #[test] + fn tx_deploy_max_code_size_exceed() { + let code = initialization_bytecode(false); + + let ctx = TestContext::<1, 1>::new( + None, + |accs| { + accs[0].address(MOCK_ACCOUNTS[0]).balance(eth(20)); + }, + |mut txs, _accs| { + txs[0] + .from(MOCK_ACCOUNTS[0]) + .gas(58000u64.into()) + .value(eth(2)) + .input(code.into()); + }, + |block, _tx| block.number(0xcafeu64), + ) + .unwrap(); + + CircuitTestBuilder::new_from_test_ctx(ctx).run(); + } +} diff --git a/zkevm-circuits/src/evm_circuit/step.rs b/zkevm-circuits/src/evm_circuit/step.rs index 0fb0f1f74d..5fdf5bd419 100644 --- a/zkevm-circuits/src/evm_circuit/step.rs +++ b/zkevm-circuits/src/evm_circuit/step.rs @@ -95,7 +95,7 @@ pub enum ExecutionState { ErrorInsufficientBalance, ErrorContractAddressCollision, ErrorInvalidCreationCode, - ErrorMaxCodeSizeExceeded, + ErrorCodeStore, ErrorInvalidJump, ErrorReturnDataOutOfBound, ErrorOutOfGasConstant, @@ -103,7 +103,6 @@ pub enum ExecutionState { ErrorOutOfGasDynamicMemoryExpansion, ErrorOutOfGasMemoryCopy, ErrorOutOfGasAccountAccess, - ErrorOutOfGasCodeStore, ErrorOutOfGasLOG, ErrorOutOfGasEXP, ErrorOutOfGasSHA3, @@ -151,8 +150,8 @@ impl From<&ExecError> for ExecutionState { ExecError::InvalidCreationCode => ExecutionState::ErrorInvalidCreationCode, ExecError::InvalidJump => ExecutionState::ErrorInvalidJump, ExecError::ReturnDataOutOfBounds => ExecutionState::ErrorReturnDataOutOfBound, - ExecError::CodeStoreOutOfGas => ExecutionState::ErrorOutOfGasCodeStore, - ExecError::MaxCodeSizeExceeded => ExecutionState::ErrorMaxCodeSizeExceeded, + ExecError::CodeStoreOutOfGas => ExecutionState::ErrorCodeStore, + ExecError::MaxCodeSizeExceeded => ExecutionState::ErrorCodeStore, ExecError::OutOfGas(oog_error) => match oog_error { OogError::Constant => ExecutionState::ErrorOutOfGasConstant, OogError::StaticMemoryExpansion => { @@ -163,7 +162,7 @@ impl From<&ExecError> for ExecutionState { } OogError::MemoryCopy => ExecutionState::ErrorOutOfGasMemoryCopy, OogError::AccountAccess => ExecutionState::ErrorOutOfGasAccountAccess, - OogError::CodeStore => ExecutionState::ErrorOutOfGasCodeStore, + OogError::CodeStore => ExecutionState::ErrorCodeStore, OogError::Log => ExecutionState::ErrorOutOfGasLOG, OogError::Exp => ExecutionState::ErrorOutOfGasEXP, OogError::Sha3 => ExecutionState::ErrorOutOfGasSHA3, @@ -304,7 +303,7 @@ impl ExecutionState { | Self::ErrorStack | Self::ErrorWriteProtection | Self::ErrorInvalidCreationCode - | Self::ErrorMaxCodeSizeExceeded + | Self::ErrorCodeStore | Self::ErrorInvalidJump | Self::ErrorReturnDataOutOfBound | Self::ErrorOutOfGasConstant @@ -312,7 +311,6 @@ impl ExecutionState { | Self::ErrorOutOfGasDynamicMemoryExpansion | Self::ErrorOutOfGasMemoryCopy | Self::ErrorOutOfGasAccountAccess - | Self::ErrorOutOfGasCodeStore | Self::ErrorOutOfGasLOG | Self::ErrorOutOfGasEXP | Self::ErrorOutOfGasSHA3 diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs index 7313f14f3e..6b549b02db 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs @@ -969,6 +969,22 @@ impl CommonErrorGadget { cb: &mut EVMConstraintBuilder, opcode: Expression, rw_counter_delta: Expression, + ) -> Self { + Self::construct_with_lastcallee_return_data( + cb, + opcode, + rw_counter_delta, + 0.expr(), + 0.expr(), + ) + } + + pub(crate) fn construct_with_lastcallee_return_data( + cb: &mut EVMConstraintBuilder, + opcode: Expression, + rw_counter_delta: Expression, + return_data_offset: Expression, + return_data_length: Expression, ) -> Self { cb.opcode_lookup(opcode.expr(), 1.expr()); @@ -1009,8 +1025,8 @@ impl CommonErrorGadget { cb, 0.expr(), 0.expr(), - 0.expr(), - 0.expr(), + return_data_offset, + return_data_length, 0.expr(), 0.expr(), ) diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 93ea3d51ca..237b2f7745 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -148,6 +148,10 @@ pub(crate) trait ConstrainBuilderCommon { self.add_constraint(name, constraint); } + fn require_true(&mut self, name: &'static str, constraint: Expression) { + self.require_equal(name, constraint, 1.expr()); + } + fn require_equal(&mut self, name: &'static str, lhs: Expression, rhs: Expression) { self.add_constraint(name, lhs - rhs); } diff --git a/zkevm-circuits/src/witness/step.rs b/zkevm-circuits/src/witness/step.rs new file mode 100644 index 0000000000..4ec40cdcb8 --- /dev/null +++ b/zkevm-circuits/src/witness/step.rs @@ -0,0 +1,243 @@ +use bus_mapping::{ + circuit_input_builder, + error::{ExecError, OogError}, + evm::OpcodeId, + operation, +}; +use eth_types::evm_unimplemented; + +use crate::{ + evm_circuit::{ + param::{N_BYTES_WORD, STACK_CAPACITY}, + step::ExecutionState, + }, + table::RwTableTag, +}; + +/// Step executed in a transaction +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct ExecStep { + /// The index in the Transaction calls + pub call_index: usize, + /// The indices in the RW trace incurred in this step + pub rw_indices: Vec<(RwTableTag, usize)>, + /// Number of rw operations performed via a copy event in this step. + pub copy_rw_counter_delta: u64, + /// The execution state for the step + pub execution_state: ExecutionState, + /// The Read/Write counter before the step + pub rw_counter: usize, + /// The program counter + pub program_counter: u64, + /// The stack pointer + pub stack_pointer: usize, + /// The amount of gas left + pub gas_left: u64, + /// The gas cost in this step + pub gas_cost: u64, + /// The memory size in bytes + pub memory_size: u64, + /// The counter for reversible writes at the beginning of the step + pub reversible_write_counter: usize, + /// The number of reversible writes from this step + pub reversible_write_counter_delta: usize, + /// The counter for log index within tx + pub log_id: usize, + /// The opcode corresponds to the step + pub opcode: Option, +} + +impl ExecStep { + /// The memory size in word **before** this step + pub fn memory_word_size(&self) -> u64 { + // EVM always pads the memory size to word size + // https://github.com/ethereum/go-ethereum/blob/master/core/vm/interpreter.go#L212-L216 + // Thus, the memory size must be a multiple of 32 bytes. + assert_eq!(self.memory_size % N_BYTES_WORD as u64, 0); + self.memory_size / N_BYTES_WORD as u64 + } +} + +impl From<&ExecError> for ExecutionState { + fn from(error: &ExecError) -> Self { + match error { + ExecError::InvalidOpcode => ExecutionState::ErrorInvalidOpcode, + ExecError::StackOverflow | ExecError::StackUnderflow => ExecutionState::ErrorStack, + ExecError::WriteProtection => ExecutionState::ErrorWriteProtection, + ExecError::Depth => ExecutionState::ErrorDepth, + ExecError::InsufficientBalance => ExecutionState::ErrorInsufficientBalance, + ExecError::ContractAddressCollision => ExecutionState::ErrorContractAddressCollision, + ExecError::InvalidCreationCode => ExecutionState::ErrorInvalidCreationCode, + ExecError::InvalidJump => ExecutionState::ErrorInvalidJump, + ExecError::ReturnDataOutOfBounds => ExecutionState::ErrorReturnDataOutOfBound, + ExecError::CodeStoreOutOfGas | ExecError::MaxCodeSizeExceeded => { + ExecutionState::ErrorCodeStore + } + ExecError::OutOfGas(oog_error) => match oog_error { + OogError::Constant => ExecutionState::ErrorOutOfGasConstant, + OogError::StaticMemoryExpansion => { + ExecutionState::ErrorOutOfGasStaticMemoryExpansion + } + OogError::DynamicMemoryExpansion => { + ExecutionState::ErrorOutOfGasDynamicMemoryExpansion + } + OogError::MemoryCopy => ExecutionState::ErrorOutOfGasMemoryCopy, + OogError::AccountAccess => ExecutionState::ErrorOutOfGasAccountAccess, + OogError::CodeStore => ExecutionState::ErrorCodeStore, + OogError::Log => ExecutionState::ErrorOutOfGasLOG, + OogError::Exp => ExecutionState::ErrorOutOfGasEXP, + OogError::Sha3 => ExecutionState::ErrorOutOfGasSHA3, + OogError::Call => ExecutionState::ErrorOutOfGasCall, + OogError::SloadSstore => ExecutionState::ErrorOutOfGasSloadSstore, + OogError::Create2 => ExecutionState::ErrorOutOfGasCREATE2, + OogError::SelfDestruct => ExecutionState::ErrorOutOfGasSELFDESTRUCT, + }, + } + } +} + +impl From<&circuit_input_builder::ExecStep> for ExecutionState { + fn from(step: &circuit_input_builder::ExecStep) -> Self { + if let Some(error) = step.error.as_ref() { + return error.into(); + } + match step.exec_state { + circuit_input_builder::ExecState::Op(op) => { + if op.is_dup() { + return ExecutionState::DUP; + } + if op.is_push() { + return ExecutionState::PUSH; + } + if op.is_swap() { + return ExecutionState::SWAP; + } + if op.is_log() { + return ExecutionState::LOG; + } + + macro_rules! dummy { + ($name:expr) => {{ + evm_unimplemented!("{:?} is implemented with DummyGadget", $name); + $name + }}; + } + + match op { + OpcodeId::ADD | OpcodeId::SUB => ExecutionState::ADD_SUB, + OpcodeId::ADDMOD => ExecutionState::ADDMOD, + OpcodeId::ADDRESS => ExecutionState::ADDRESS, + OpcodeId::BALANCE => ExecutionState::BALANCE, + OpcodeId::MUL | OpcodeId::DIV | OpcodeId::MOD => ExecutionState::MUL_DIV_MOD, + OpcodeId::MULMOD => ExecutionState::MULMOD, + OpcodeId::SDIV | OpcodeId::SMOD => ExecutionState::SDIV_SMOD, + OpcodeId::EQ | OpcodeId::LT | OpcodeId::GT => ExecutionState::CMP, + OpcodeId::SLT | OpcodeId::SGT => ExecutionState::SCMP, + OpcodeId::SIGNEXTEND => ExecutionState::SIGNEXTEND, + OpcodeId::STOP => ExecutionState::STOP, + OpcodeId::AND => ExecutionState::BITWISE, + OpcodeId::XOR => ExecutionState::BITWISE, + OpcodeId::OR => ExecutionState::BITWISE, + OpcodeId::NOT => ExecutionState::NOT, + OpcodeId::EXP => ExecutionState::EXP, + OpcodeId::POP => ExecutionState::POP, + OpcodeId::PUSH32 => ExecutionState::PUSH, + OpcodeId::BYTE => ExecutionState::BYTE, + OpcodeId::MLOAD => ExecutionState::MEMORY, + OpcodeId::MSTORE => ExecutionState::MEMORY, + OpcodeId::MSTORE8 => ExecutionState::MEMORY, + OpcodeId::JUMPDEST => ExecutionState::JUMPDEST, + OpcodeId::JUMP => ExecutionState::JUMP, + OpcodeId::JUMPI => ExecutionState::JUMPI, + OpcodeId::GASPRICE => ExecutionState::GASPRICE, + OpcodeId::PC => ExecutionState::PC, + OpcodeId::MSIZE => ExecutionState::MSIZE, + OpcodeId::CALLER => ExecutionState::CALLER, + OpcodeId::CALLVALUE => ExecutionState::CALLVALUE, + OpcodeId::EXTCODEHASH => ExecutionState::EXTCODEHASH, + OpcodeId::EXTCODESIZE => ExecutionState::EXTCODESIZE, + OpcodeId::BLOCKHASH => ExecutionState::BLOCKHASH, + OpcodeId::TIMESTAMP | OpcodeId::NUMBER | OpcodeId::GASLIMIT => { + ExecutionState::BLOCKCTXU64 + } + OpcodeId::COINBASE => ExecutionState::BLOCKCTXU160, + OpcodeId::DIFFICULTY | OpcodeId::BASEFEE => ExecutionState::BLOCKCTXU256, + OpcodeId::GAS => ExecutionState::GAS, + OpcodeId::SAR => ExecutionState::SAR, + OpcodeId::SELFBALANCE => ExecutionState::SELFBALANCE, + OpcodeId::SHA3 => ExecutionState::SHA3, + OpcodeId::SHL | OpcodeId::SHR => ExecutionState::SHL_SHR, + OpcodeId::SLOAD => ExecutionState::SLOAD, + OpcodeId::SSTORE => ExecutionState::SSTORE, + OpcodeId::CALLDATASIZE => ExecutionState::CALLDATASIZE, + OpcodeId::CALLDATACOPY => ExecutionState::CALLDATACOPY, + OpcodeId::CHAINID => ExecutionState::CHAINID, + OpcodeId::ISZERO => ExecutionState::ISZERO, + OpcodeId::CALL + | OpcodeId::CALLCODE + | OpcodeId::DELEGATECALL + | OpcodeId::STATICCALL => ExecutionState::CALL_OP, + OpcodeId::ORIGIN => ExecutionState::ORIGIN, + OpcodeId::CODECOPY => ExecutionState::CODECOPY, + OpcodeId::CALLDATALOAD => ExecutionState::CALLDATALOAD, + OpcodeId::CODESIZE => ExecutionState::CODESIZE, + OpcodeId::RETURN | OpcodeId::REVERT => ExecutionState::RETURN_REVERT, + OpcodeId::RETURNDATASIZE => ExecutionState::RETURNDATASIZE, + OpcodeId::RETURNDATACOPY => ExecutionState::RETURNDATACOPY, + // dummy ops + OpcodeId::EXTCODECOPY => dummy!(ExecutionState::EXTCODECOPY), + OpcodeId::CREATE => dummy!(ExecutionState::CREATE), + OpcodeId::CREATE2 => dummy!(ExecutionState::CREATE2), + OpcodeId::SELFDESTRUCT => dummy!(ExecutionState::SELFDESTRUCT), + _ => unimplemented!("unimplemented opcode {:?}", op), + } + } + circuit_input_builder::ExecState::BeginTx => ExecutionState::BeginTx, + circuit_input_builder::ExecState::EndTx => ExecutionState::EndTx, + circuit_input_builder::ExecState::EndBlock => ExecutionState::EndBlock, + } + } +} + +pub(super) fn step_convert(step: &circuit_input_builder::ExecStep) -> ExecStep { + ExecStep { + call_index: step.call_index, + rw_indices: step + .bus_mapping_instance + .iter() + .map(|x| { + let tag = match x.target() { + operation::Target::Memory => RwTableTag::Memory, + operation::Target::Stack => RwTableTag::Stack, + operation::Target::Storage => RwTableTag::AccountStorage, + operation::Target::TxAccessListAccount => RwTableTag::TxAccessListAccount, + operation::Target::TxAccessListAccountStorage => { + RwTableTag::TxAccessListAccountStorage + } + operation::Target::TxRefund => RwTableTag::TxRefund, + operation::Target::Account => RwTableTag::Account, + operation::Target::CallContext => RwTableTag::CallContext, + operation::Target::TxReceipt => RwTableTag::TxReceipt, + operation::Target::TxLog => RwTableTag::TxLog, + operation::Target::Start => RwTableTag::Start, + }; + (tag, x.as_usize()) + }) + .collect(), + copy_rw_counter_delta: step.copy_rw_counter_delta, + execution_state: ExecutionState::from(step), + rw_counter: usize::from(step.rwc), + program_counter: usize::from(step.pc) as u64, + stack_pointer: STACK_CAPACITY - step.stack_size, + gas_left: step.gas_left.0, + gas_cost: step.gas_cost.as_u64(), + opcode: match step.exec_state { + circuit_input_builder::ExecState::Op(op) => Some(op), + _ => None, + }, + memory_size: step.memory_size as u64, + reversible_write_counter: step.reversible_write_counter, + reversible_write_counter_delta: step.reversible_write_counter_delta, + log_id: step.log_id, + } +}