diff --git a/bus-mapping/src/circuit_input_builder/call.rs b/bus-mapping/src/circuit_input_builder/call.rs index 6e92839445..5d82ca249f 100644 --- a/bus-mapping/src/circuit_input_builder/call.rs +++ b/bus-mapping/src/circuit_input_builder/call.rs @@ -126,6 +126,13 @@ pub struct CallContext { pub return_data: Vec, } +impl CallContext { + /// Memory size in words, rounded up + pub fn memory_word_size(&self) -> u64 { + u64::try_from(self.memory.len()).expect("failed to convert usize to u64") / 32 + } +} + /// A reversion group is the collection of calls and the operations which are /// [`Operation::reversible`](crate::operation::Operation::reversible) that /// happened in them, that will be reverted at once when the call that initiated diff --git a/bus-mapping/src/circuit_input_builder/input_state_ref.rs b/bus-mapping/src/circuit_input_builder/input_state_ref.rs index f25e1aa0e1..5091ea207b 100644 --- a/bus-mapping/src/circuit_input_builder/input_state_ref.rs +++ b/bus-mapping/src/circuit_input_builder/input_state_ref.rs @@ -379,6 +379,25 @@ impl<'a> CircuitInputStateRef<'a> { Ok(()) } + /// Add address to access list for the current transaction. + pub fn tx_access_list_write( + &mut self, + step: &mut ExecStep, + address: Address, + ) -> Result<(), Error> { + let is_warm = self.sdb.check_account_in_access_list(&address); + self.push_op_reversible( + step, + RW::WRITE, + TxAccessListAccountOp { + tx_id: self.tx_ctx.id(), + address, + is_warm: true, + is_warm_prev: is_warm, + }, + ) + } + /// Push a write type [`TxAccessListAccountOp`] into the /// [`OperationContainer`](crate::operation::OperationContainer) with the /// next [`RWCounter`](crate::operation::RWCounter), and then @@ -571,6 +590,18 @@ impl<'a> CircuitInputStateRef<'a> { )) } + pub(crate) fn reversion_info_read(&mut self, step: &mut ExecStep, call: &Call) { + for (field, value) in [ + ( + CallContextField::RwCounterEndOfReversion, + call.rw_counter_end_of_reversion.to_word(), + ), + (CallContextField::IsPersistent, call.is_persistent.to_word()), + ] { + self.call_context_read(step, call.call_id, field, value); + } + } + /// Check if address is a precompiled or not. pub fn is_precompiled(&self, address: &Address) -> bool { address.0[0..19] == [0u8; 19] && (1..=9).contains(&address.0[19]) diff --git a/bus-mapping/src/evm/opcodes.rs b/bus-mapping/src/evm/opcodes.rs index 0e3253d3c1..d4fa009783 100644 --- a/bus-mapping/src/evm/opcodes.rs +++ b/bus-mapping/src/evm/opcodes.rs @@ -70,7 +70,7 @@ use callop::CallOpcode; use callvalue::Callvalue; use codecopy::Codecopy; use codesize::Codesize; -use create::DummyCreate; +use create::Create; use dup::Dup; use error_invalid_jump::ErrorInvalidJump; use error_oog_call::OOGCall; @@ -234,19 +234,13 @@ fn fn_gen_associated_ops(opcode_id: &OpcodeId) -> FnGenAssociatedOps { OpcodeId::LOG4 => Log::gen_associated_ops, OpcodeId::CALL | OpcodeId::CALLCODE => CallOpcode::<7>::gen_associated_ops, OpcodeId::DELEGATECALL | OpcodeId::STATICCALL => CallOpcode::<6>::gen_associated_ops, + OpcodeId::CREATE => Create::::gen_associated_ops, + OpcodeId::CREATE2 => Create::::gen_associated_ops, OpcodeId::RETURN | OpcodeId::REVERT => ReturnRevert::gen_associated_ops, OpcodeId::SELFDESTRUCT => { warn!("Using dummy gen_selfdestruct_ops for opcode SELFDESTRUCT"); DummySelfDestruct::gen_associated_ops } - OpcodeId::CREATE => { - warn!("Using dummy gen_create_ops for opcode {:?}", opcode_id); - DummyCreate::::gen_associated_ops - } - OpcodeId::CREATE2 => { - warn!("Using dummy gen_create_ops for opcode {:?}", opcode_id); - DummyCreate::::gen_associated_ops - } _ => { warn!("Using dummy gen_associated_ops for opcode {:?}", opcode_id); Dummy::gen_associated_ops diff --git a/bus-mapping/src/evm/opcodes/create.rs b/bus-mapping/src/evm/opcodes/create.rs index c113b43b96..4cee1ef652 100644 --- a/bus-mapping/src/evm/opcodes/create.rs +++ b/bus-mapping/src/evm/opcodes/create.rs @@ -1,35 +1,43 @@ -use crate::circuit_input_builder::{CircuitInputStateRef, ExecStep}; -use crate::evm::Opcode; -use crate::operation::{AccountField, AccountOp, CallContextField, TxAccessListAccountOp, RW}; -use crate::Error; -use eth_types::{evm_types::gas_utils::memory_expansion_gas_cost, GethExecStep, ToWord, Word}; -use keccak256::EMPTY_HASH; +use crate::{ + circuit_input_builder::{ + CircuitInputStateRef, CopyDataType, CopyEvent, ExecStep, NumberOrHash, + }, + evm::Opcode, + operation::{AccountField, AccountOp, CallContextField, MemoryOp, RW}, + Error, +}; +use eth_types::{ + evm_types::gas_utils::memory_expansion_gas_cost, Bytecode, GethExecStep, ToBigEndian, ToWord, + Word, H160, H256, +}; +use ethers_core::utils::{get_create2_address, keccak256, rlp}; #[derive(Debug, Copy, Clone)] -pub struct DummyCreate; +pub struct Create; -impl Opcode for DummyCreate { +impl Opcode for Create { fn gen_associated_ops( state: &mut CircuitInputStateRef, geth_steps: &[GethExecStep], ) -> Result, Error> { - // TODO: replace dummy create here let geth_step = &geth_steps[0]; let offset = geth_step.stack.nth_last(1)?.as_usize(); let length = geth_step.stack.nth_last(2)?.as_usize(); - let curr_memory_word_size = (state.call_ctx()?.memory.len() as u64) / 32; + let curr_memory_word_size = state.call_ctx()?.memory_word_size(); if length != 0 { state .call_ctx_mut()? .memory .extend_at_least(offset + length); } - let next_memory_word_size = (state.call_ctx()?.memory.len() as u64) / 32; + let next_memory_word_size = state.call_ctx()?.memory_word_size(); let mut exec_step = state.new_step(geth_step)?; + let callee = state.parse_call(geth_step)?; + let n_pop = if IS_CREATE2 { 4 } else { 3 }; for i in 0..n_pop { state.stack_read( @@ -44,76 +52,76 @@ impl Opcode for DummyCreate { } else { state.create_address()? }; - - // TODO: rename this to initialization call? - let call = state.parse_call(geth_step)?; state.stack_write( &mut exec_step, geth_step.stack.nth_last_filled(n_pop - 1), - if call.is_success { + if callee.is_success { address.to_word() } else { Word::zero() }, )?; + let mut initialization_code = vec![]; + if length > 0 { + initialization_code = + handle_copy(state, &mut exec_step, state.call()?.call_id, offset, length)?; + } + let tx_id = state.tx_ctx.id(); - let current_call = state.call()?.clone(); - - // Quote from [EIP-2929](https://eips.ethereum.org/EIPS/eip-2929) - // > When a CREATE or CREATE2 opcode is called, - // > immediately (i.e. before checks are done to determine - // > whether or not the address is unclaimed) - // > add the address being created to accessed_addresses, - // > but gas costs of CREATE and CREATE2 are unchanged - let is_warm = state.sdb.check_account_in_access_list(&address); - state.push_op_reversible( + let caller = state.call()?.clone(); + + state.call_context_read( &mut exec_step, - RW::WRITE, - TxAccessListAccountOp { - tx_id: state.tx_ctx.id(), - address, - is_warm: true, - is_warm_prev: is_warm, - }, - )?; + caller.call_id, + CallContextField::TxId, + tx_id.to_word(), + ); + state.reversion_info_read(&mut exec_step, &caller); + state.tx_access_list_write(&mut exec_step, address)?; + + state.call_context_read( + &mut exec_step, + caller.call_id, + CallContextField::CalleeAddress, + caller.address.to_word(), + ); // Increase caller's nonce - let nonce_prev = state.sdb.get_nonce(&call.caller_address); + let caller_nonce = state.sdb.get_nonce(&caller.address); state.push_op_reversible( &mut exec_step, RW::WRITE, AccountOp { - address: call.caller_address, + address: caller.address, field: AccountField::Nonce, - value: (nonce_prev + 1).into(), - value_prev: nonce_prev.into(), + value: (caller_nonce + 1).into(), + value_prev: caller_nonce.into(), }, )?; - // Add callee into access list - let is_warm = state.sdb.check_account_in_access_list(&call.address); - state.push_op_reversible( - &mut exec_step, - RW::WRITE, - TxAccessListAccountOp { - tx_id, - address: call.address, - is_warm: true, - is_warm_prev: is_warm, - }, - )?; + // TODO: look into when this can be pushed. Could it be done in parse call? + state.push_call(callee.clone()); - state.push_call(call.clone()); + for (field, value) in [ + ( + CallContextField::RwCounterEndOfReversion, + callee.rw_counter_end_of_reversion.to_word(), + ), + ( + CallContextField::IsPersistent, + callee.is_persistent.to_word(), + ), + ] { + state.call_context_write(&mut exec_step, callee.call_id, field, value); + } - // Increase callee's nonce - let nonce_prev = state.sdb.get_nonce(&call.address); - debug_assert!(nonce_prev == 0); + debug_assert!(state.sdb.get_nonce(&callee.address) == 0); state.push_op_reversible( &mut exec_step, RW::WRITE, AccountOp { - address: call.address, + address: callee.address, field: AccountField::Nonce, value: 1.into(), value_prev: 0.into(), @@ -122,15 +130,16 @@ impl Opcode for DummyCreate { state.transfer( &mut exec_step, - call.caller_address, - call.address, - call.value, + callee.caller_address, + callee.address, + callee.value, )?; let memory_expansion_gas_cost = memory_expansion_gas_cost(curr_memory_word_size, next_memory_word_size); - // EIP-150: all but one 64th of the caller's gas is sent to the callee. + // Per EIP-150, all but one 64th of the caller's gas is sent to the + // initialization call. let caller_gas_left = (geth_step.gas.0 - geth_step.gas_cost.0 - memory_expansion_gas_cost) / 64; @@ -147,40 +156,121 @@ impl Opcode for DummyCreate { (CallContextField::MemorySize, next_memory_word_size.into()), ( CallContextField::ReversibleWriteCounter, - // +3 is because we do some transfers after pushing the call. can be just push the - // call later? - (exec_step.reversible_write_counter + 3).into(), + (exec_step.reversible_write_counter + 2).into(), ), ] { - state.call_context_write(&mut exec_step, current_call.call_id, field, value); + state.call_context_write(&mut exec_step, caller.call_id, field, value); } + state.call_context_read( + &mut exec_step, + caller.call_id, + CallContextField::Depth, + caller.depth.to_word(), + ); + + let code_hash = keccak256(&initialization_code); for (field, value) in [ - (CallContextField::CallerId, current_call.call_id.into()), - (CallContextField::IsSuccess, call.is_success.to_word()), - (CallContextField::IsPersistent, call.is_persistent.to_word()), + (CallContextField::CallerId, caller.call_id.into()), + (CallContextField::IsSuccess, callee.is_success.to_word()), + ( + CallContextField::IsPersistent, + callee.is_persistent.to_word(), + ), (CallContextField::TxId, state.tx_ctx.id().into()), ( CallContextField::CallerAddress, - current_call.address.to_word(), + callee.caller_address.to_word(), ), - (CallContextField::CalleeAddress, call.address.to_word()), + (CallContextField::CalleeAddress, callee.address.to_word()), ( CallContextField::RwCounterEndOfReversion, - call.rw_counter_end_of_reversion.to_word(), + callee.rw_counter_end_of_reversion.to_word(), ), - (CallContextField::IsPersistent, call.is_persistent.to_word()), + (CallContextField::Depth, callee.depth.to_word()), + (CallContextField::IsRoot, false.to_word()), + (CallContextField::IsStatic, false.to_word()), + (CallContextField::IsCreate, true.to_word()), + (CallContextField::CodeHash, Word::from(code_hash)), ] { - state.call_context_write(&mut exec_step, call.call_id, field, value); + state.call_context_write(&mut exec_step, callee.call_id, field, value); } - if call.code_hash.to_fixed_bytes() == *EMPTY_HASH { - // 1. Create with empty initcode. - state.handle_return(geth_step)?; - Ok(vec![exec_step]) + let keccak_input = if IS_CREATE2 { + let salt = geth_step.stack.nth_last(3)?; + assert_eq!( + address, + get_create2_address( + caller.address, + salt.to_be_bytes().to_vec(), + initialization_code.clone() + ) + ); + std::iter::once(0xffu8) + .chain(caller.address.to_fixed_bytes()) + .chain(salt.to_be_bytes()) + .chain(keccak256(&initialization_code)) + .collect::>() } else { - // 2. Create with non-empty initcode. - Ok(vec![exec_step]) + let mut stream = rlp::RlpStream::new(); + stream.begin_list(2); + stream.append(&caller.address); + stream.append(&Word::from(caller_nonce)); + stream.out().to_vec() + }; + + assert_eq!( + address, + H160(keccak256(&keccak_input)[12..].try_into().unwrap()) + ); + + state.block.sha3_inputs.push(keccak_input); + + if length == 0 { + state.handle_return(geth_step)?; } + + Ok(vec![exec_step]) + } +} + +fn handle_copy( + state: &mut CircuitInputStateRef, + step: &mut ExecStep, + callee_id: usize, + offset: usize, + length: usize, +) -> Result, Error> { + let initialization_bytes = state.call_ctx()?.memory.0[offset..offset + length].to_vec(); + let dst_id = NumberOrHash::Hash(H256(keccak256(&initialization_bytes))); + let bytes: Vec<_> = Bytecode::from(initialization_bytes.clone()) + .code + .iter() + .map(|element| (element.value, element.is_code)) + .collect(); + + let rw_counter_start = state.block_ctx.rwc; + for (i, (byte, _)) in bytes.iter().enumerate() { + // this could be a memory read, if this happens before we push the new call? + state.push_op( + step, + RW::READ, + MemoryOp::new(callee_id, (offset + i).into(), *byte), + ); } + + state.push_copy(CopyEvent { + rw_counter_start, + src_type: CopyDataType::Memory, + src_id: NumberOrHash::Number(callee_id), + src_addr: offset.try_into().unwrap(), + src_addr_end: (offset + length).try_into().unwrap(), + dst_type: CopyDataType::Bytecode, + dst_id, + dst_addr: 0, + log_id: None, + bytes, + }); + + Ok(initialization_bytes) } diff --git a/eth-types/src/evm_types.rs b/eth-types/src/evm_types.rs index 63733e3445..95ee1ac34a 100644 --- a/eth-types/src/evm_types.rs +++ b/eth-types/src/evm_types.rs @@ -98,7 +98,7 @@ impl GasCost { pub const SHA3: Self = Self(30); /// Constant cost for SELFDESTRUCT pub const SELFDESTRUCT: Self = Self(5000); - /// Constant cost for CREATE + /// Constant cost for CREATE and CREATE2 pub const CREATE: Self = Self(32000); /// Constant cost for copying every word pub const COPY: Self = Self(3); diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 1b14d2fbcb..909f00a6ce 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -46,6 +46,7 @@ mod chainid; mod codecopy; mod codesize; mod comparator; +mod create; mod dummy; mod dup; mod end_block; @@ -109,6 +110,7 @@ use chainid::ChainIdGadget; use codecopy::CodeCopyGadget; use codesize::CodesizeGadget; use comparator::ComparatorGadget; +use create::CreateGadget; use dummy::DummyGadget; use dup::DupGadget; use end_block::EndBlockGadget; @@ -238,8 +240,7 @@ pub(crate) struct ExecutionConfig { extcodecopy_gadget: DummyGadget, returndatasize_gadget: ReturnDataSizeGadget, returndatacopy_gadget: ReturnDataCopyGadget, - create_gadget: DummyGadget, - create2_gadget: DummyGadget, + create_gadget: CreateGadget, selfdestruct_gadget: DummyGadget, signed_comparator_gadget: SignedComparatorGadget, signextend_gadget: SignextendGadget, @@ -476,7 +477,6 @@ impl ExecutionConfig { returndatasize_gadget: configure_gadget!(), returndatacopy_gadget: configure_gadget!(), create_gadget: configure_gadget!(), - create2_gadget: configure_gadget!(), selfdestruct_gadget: configure_gadget!(), shl_shr_gadget: configure_gadget!(), signed_comparator_gadget: configure_gadget!(), @@ -1088,11 +1088,10 @@ impl ExecutionConfig { ExecutionState::BLOCKCTXU256 => assign_exec_step!(self.block_ctx_u256_gadget), ExecutionState::BLOCKHASH => assign_exec_step!(self.blockhash_gadget), ExecutionState::SELFBALANCE => assign_exec_step!(self.selfbalance_gadget), + ExecutionState::CREATE => assign_exec_step!(self.create_gadget), // dummy gadgets ExecutionState::SAR => assign_exec_step!(self.sar_gadget), ExecutionState::EXTCODECOPY => assign_exec_step!(self.extcodecopy_gadget), - ExecutionState::CREATE => assign_exec_step!(self.create_gadget), - ExecutionState::CREATE2 => assign_exec_step!(self.create2_gadget), ExecutionState::SELFDESTRUCT => assign_exec_step!(self.selfdestruct_gadget), // end of dummy gadgets ExecutionState::SHA3 => assign_exec_step!(self.sha3_gadget), @@ -1229,7 +1228,7 @@ impl ExecutionConfig { fn check_rw_lookup( assigned_stored_expressions: &[(String, F)], - step: &ExecStep, + _step: &ExecStep, block: &Block, ) { let mut assigned_rw_values = Vec::new(); @@ -1258,24 +1257,27 @@ impl ExecutionConfig { for (name, value) in assigned_rw_values.iter() { if !rlc_assignments.contains(value) { - log::error!("rw lookup error: name: {}, step: {:?}", *name, step); - } - } - for (idx, assigned_rw_value) in assigned_rw_values.iter().enumerate() { - let rw_idx = step.rw_indices[idx]; - let rw = block.rws[rw_idx]; - let table_assignments = rw.table_assignment_aux(block.randomness); - let rlc = table_assignments.rlc(block.randomness); - if rlc != assigned_rw_value.1 { - log::error!( - "incorrect rw witness. lookup input name: \"{}\"\n{:?}\nrw: {:?}, rw index: {:?}, {}th rw of step {:?}", - assigned_rw_value.0, - assigned_rw_value.1, - rw, - rw_idx, - idx, - step.execution_state); + log::error!("rw lookup error: name: {}", *name); + // log::error!("rw lookup error: name: {}, step: {:?}", *name, + // step); } } + // for (idx, assigned_rw_value) in assigned_rw_values.iter().enumerate() + // { let rw_idx = step.rw_indices[idx]; + // let rw = block.rws[rw_idx]; + // let table_assignments = + // rw.table_assignment_aux(block.randomness); let rlc = + // table_assignments.rlc(block.randomness); if rlc != + // assigned_rw_value.1 { log::error!( + // "incorrect rw witness. lookup input name: + // \"{}\"\n{:?}\nrw: {:?}, rw index: {:?}, {}th rw of step {:?}", + // assigned_rw_value.0, + // assigned_rw_value.1, + // rw, + // rw_idx, + // idx, + // step.execution_state); + // } + // } } } diff --git a/zkevm-circuits/src/evm_circuit/execution/create.rs b/zkevm-circuits/src/evm_circuit/execution/create.rs new file mode 100644 index 0000000000..05d86cf7b3 --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/execution/create.rs @@ -0,0 +1,864 @@ +use crate::{ + evm_circuit::{ + execution::ExecutionGadget, + param::{ + N_BYTES_ACCOUNT_ADDRESS, N_BYTES_GAS, N_BYTES_MEMORY_ADDRESS, N_BYTES_MEMORY_WORD_SIZE, + N_BYTES_U64, + }, + step::ExecutionState, + util::{ + and, + common_gadget::TransferGadget, + constraint_builder::{ + ConstraintBuilder, ReversionInfo, StepStateTransition, + Transition::{Delta, To}, + }, + from_bytes, + math_gadget::{ConstantDivisionGadget, IsZeroGadget}, + memory_gadget::{MemoryAddressGadget, MemoryExpansionGadget}, + not, rlc, select, sum, CachedRegion, Cell, RandomLinearCombination, Word, + }, + witness::{Block, Call, ExecStep, Transaction}, + }, + table::{AccountFieldTag, CallContextFieldTag}, + util::Expr, +}; +use bus_mapping::{circuit_input_builder::CopyDataType, evm::OpcodeId}; +use eth_types::{evm_types::GasCost, Field, ToBigEndian, ToLittleEndian, ToScalar, U256}; +use ethers_core::utils::{keccak256, rlp}; +use halo2_proofs::{ + circuit::Value, + plonk::{Error, Expression}, +}; +use keccak256::EMPTY_HASH_LE; +use std::iter::once; + +/// Gadget for CREATE and CREATE2 opcodes +#[derive(Clone, Debug)] +pub(crate) struct CreateGadget { + opcode: Cell, + is_create2: Cell, + + value: Word, + salt: Word, + + tx_id: Cell, + reversion_info: ReversionInfo, + was_warm: Cell, + + depth: Cell, + caller_address: RandomLinearCombination, + nonce: RlpU64Gadget, + + callee_reversion_info: ReversionInfo, + callee_is_success: Cell, + + transfer: TransferGadget, + + initialization_code: MemoryAddressGadget, + initialization_code_word_size: ConstantDivisionGadget, + memory_expansion: MemoryExpansionGadget, + + gas_left: ConstantDivisionGadget, + + code_hash: Cell, + + keccak_input: Cell, + keccak_input_length: Cell, + keccak_output: Word, +} + +impl ExecutionGadget for CreateGadget { + const NAME: &'static str = "CREATE"; + + const EXECUTION_STATE: ExecutionState = ExecutionState::CREATE; + + fn configure(cb: &mut ConstraintBuilder) -> Self { + // Use rw_counter of the step which triggers next call as its call_id. + let callee_call_id = cb.curr.state.rw_counter.clone(); + + let opcode = cb.query_cell(); + cb.opcode_lookup(opcode.expr(), 1.expr()); + + let is_create2 = cb.query_bool(); + cb.require_equal( + "Opcode is CREATE or CREATE2", + opcode.expr(), + select::expr( + is_create2.expr(), + OpcodeId::CREATE2.expr(), + OpcodeId::CREATE.expr(), + ), + ); + + let value = cb.query_word(); + cb.stack_pop(value.expr()); + + let initialization_code = MemoryAddressGadget::construct_2(cb); + cb.stack_pop(initialization_code.offset_rlc()); + cb.stack_pop(initialization_code.length_rlc()); + + let salt = cb.condition(is_create2.expr(), |cb| { + let salt = cb.query_word(); + cb.stack_pop(salt.expr()); + salt + }); + + let keccak_output = cb.query_word(); + let new_address_rlc = rlc::expr( + &keccak_output.cells[..20] + .iter() + .map(Expr::expr) + .collect::>(), + cb.power_of_randomness(), + ); + let callee_is_success = cb.query_bool(); + cb.stack_push(callee_is_success.expr() * new_address_rlc); + + let code_hash = cb.query_cell(); + cb.condition(initialization_code.has_length(), |cb| { + cb.copy_table_lookup( + cb.curr.state.call_id.expr(), + CopyDataType::Memory.expr(), + code_hash.expr(), + CopyDataType::Bytecode.expr(), + initialization_code.offset(), + initialization_code.address(), + 0.expr(), + initialization_code.length(), + 0.expr(), + initialization_code.length(), + ); + }); + cb.condition(not::expr(initialization_code.has_length()), |cb| { + cb.require_equal( + "", + code_hash.expr(), + Word::random_linear_combine_expr( + (*EMPTY_HASH_LE).map(|b| b.expr()), + cb.power_of_randomness(), + ), + ); + }); + + let tx_id = cb.call_context(None, CallContextFieldTag::TxId); + let new_address = from_bytes::expr(&keccak_output.cells[..20]); + let mut reversion_info = cb.reversion_info_read(None); + let was_warm = cb.query_bool(); + cb.account_access_list_write( + tx_id.expr(), + new_address.clone(), + 1.expr(), + was_warm.expr(), + Some(&mut reversion_info), + ); + + let caller_address = cb.query_rlc(); + cb.call_context_lookup( + 0.expr(), + None, + CallContextFieldTag::CalleeAddress, + from_bytes::expr(&caller_address.cells), + ); + + let nonce = RlpU64Gadget::construct(cb); + cb.account_write( + from_bytes::expr(&caller_address.cells), + AccountFieldTag::Nonce, + nonce.value() + 1.expr(), + nonce.value(), + Some(&mut reversion_info), + ); + + // TODO: deduplicate with the code in CallOpGadget + let mut callee_reversion_info = cb.reversion_info_write(Some(callee_call_id.expr())); + cb.require_equal( + "callee_is_persistent == is_persistent ⋅ is_success", + callee_reversion_info.is_persistent(), + reversion_info.is_persistent() * callee_is_success.expr(), + ); + cb.condition(callee_is_success.expr() * (1.expr() - reversion_info.is_persistent()), |cb| { + cb.require_equal( + "callee_rw_counter_end_of_reversion == rw_counter_end_of_reversion - (reversible_write_counter + 1)", + callee_reversion_info.rw_counter_end_of_reversion(), + reversion_info.rw_counter_of_reversion(), + ); + }); + + cb.account_write( + new_address.clone(), + AccountFieldTag::Nonce, + 1.expr(), + 0.expr(), + Some(&mut callee_reversion_info), + ); + + let transfer = TransferGadget::construct( + cb, + from_bytes::expr(&caller_address.cells), + new_address.clone(), + value.clone(), + &mut callee_reversion_info, + ); + + let memory_expansion = + MemoryExpansionGadget::construct(cb, [initialization_code.address()]); + + let initialization_code_word_size = + ConstantDivisionGadget::construct(cb, initialization_code.length() + 31.expr(), 32); + let keccak_gas_cost = GasCost::COPY_SHA3.expr() + * is_create2.expr() + * initialization_code_word_size.quotient(); + + let gas_cost = GasCost::CREATE.expr() + memory_expansion.gas_cost() + keccak_gas_cost; + let gas_remaining = cb.curr.state.gas_left.expr() - gas_cost.clone(); + let gas_left = ConstantDivisionGadget::construct(cb, gas_remaining.clone(), 64); + let callee_gas_left = gas_remaining - gas_left.quotient(); + for (field_tag, value) in [ + ( + CallContextFieldTag::ProgramCounter, + cb.curr.state.program_counter.expr() + 1.expr(), + ), + ( + CallContextFieldTag::StackPointer, + cb.curr.state.stack_pointer.expr() + 2.expr() + is_create2.expr(), + ), + (CallContextFieldTag::GasLeft, gas_left.quotient()), + ( + CallContextFieldTag::MemorySize, + memory_expansion.next_memory_word_size(), + ), + ( + CallContextFieldTag::ReversibleWriteCounter, + cb.curr.state.reversible_write_counter.expr() + 2.expr(), + ), + ] { + cb.call_context_lookup(true.expr(), None, field_tag, value); + } + + let depth = cb.call_context(None, CallContextFieldTag::Depth); + + for (field_tag, value) in [ + (CallContextFieldTag::CallerId, cb.curr.state.call_id.expr()), + (CallContextFieldTag::IsSuccess, callee_is_success.expr()), + ( + CallContextFieldTag::IsPersistent, + callee_reversion_info.is_persistent(), + ), + (CallContextFieldTag::TxId, tx_id.expr()), + ( + CallContextFieldTag::CallerAddress, + from_bytes::expr(&caller_address.cells), + ), + (CallContextFieldTag::CalleeAddress, new_address), + ( + CallContextFieldTag::RwCounterEndOfReversion, + callee_reversion_info.rw_counter_end_of_reversion(), + ), + (CallContextFieldTag::Depth, depth.expr() + 1.expr()), + (CallContextFieldTag::IsRoot, false.expr()), + (CallContextFieldTag::IsStatic, false.expr()), + (CallContextFieldTag::IsCreate, true.expr()), + (CallContextFieldTag::CodeHash, code_hash.expr()), + ] { + cb.call_context_lookup(true.expr(), Some(callee_call_id.expr()), field_tag, value); + } + + cb.condition(initialization_code.has_length(), |cb| { + cb.require_step_state_transition(StepStateTransition { + rw_counter: Delta(cb.rw_counter_offset()), + call_id: To(callee_call_id.expr()), + is_root: To(false.expr()), + is_create: To(true.expr()), + code_hash: To(code_hash.expr()), + gas_left: To(callee_gas_left), + reversible_write_counter: To(3.expr()), + ..StepStateTransition::new_context() + }) + }); + + cb.condition(not::expr(initialization_code.has_length()), |cb| { + cb.require_step_state_transition(StepStateTransition { + rw_counter: Delta(cb.rw_counter_offset()), + program_counter: Delta(1.expr()), + stack_pointer: Delta(2.expr() + is_create2.expr()), + gas_left: Delta(-gas_cost), + reversible_write_counter: Delta(5.expr()), + ..Default::default() + }) + }); + + let keccak_input = cb.query_cell(); + let keccak_input_length = cb.query_cell(); + cb.condition(is_create2.expr(), |cb| { + // For CREATE2, the keccak input is the concatenation of 0xff, address, salt, + // and code_hash. Each sequence of bytes occurs in a fixed position, so to + // compute the RLC of the input, we only need to compute some fixed powers of + // the randomness. + let randomness_raised_to_16 = cb.power_of_randomness()[15].clone(); + let randomness_raised_to_32 = randomness_raised_to_16.square(); + let randomness_raised_to_64 = randomness_raised_to_32.clone().square(); + let randomness_raised_to_84 = + randomness_raised_to_64.clone() * cb.power_of_randomness()[19].clone(); + cb.require_equal( + "for CREATE2, keccak input is 0xff ++ address ++ salt ++ code_hash", + keccak_input.expr(), + 0xff.expr() * randomness_raised_to_84 + + caller_address.expr() * randomness_raised_to_64 + + salt.expr() * randomness_raised_to_32 + + code_hash.expr(), + ); + cb.require_equal( + "for CREATE2, keccak input length is 85", + keccak_input_length.expr(), + (1 + 20 + 32 + 32).expr(), + ); + }); + + cb.condition(not::expr(is_create2.expr()), |cb| { + let randomness_raised_to_20 = cb.power_of_randomness()[19].clone(); + let randomness_raised_to_21 = cb.power_of_randomness()[20].clone(); + cb.require_equal( + "for CREATE, keccak input is rlp([address, nonce])", + keccak_input.expr(), + nonce.rlp_rlc(cb) + + nonce.randomness_raised_to_rlp_length(cb) + * (((0xc0.expr() + 21.expr() + nonce.rlp_length()) + * randomness_raised_to_21) + + (0x80 + 20).expr() * randomness_raised_to_20 + + caller_address.expr()), + ); + cb.require_equal( + "for CREATE, keccak input length is rlp([address, nonce]).len()", + keccak_input_length.expr(), + (1 + 1 + 20).expr() + nonce.rlp_length(), + ); + }); + + cb.keccak_table_lookup( + keccak_input.expr(), + keccak_input_length.expr(), + keccak_output.expr(), + ); + + Self { + opcode, + is_create2, + reversion_info, + tx_id, + was_warm, + value, + salt, + caller_address, + nonce, + depth, + callee_reversion_info, + transfer, + initialization_code, + memory_expansion, + gas_left, + callee_is_success, + code_hash, + keccak_output, + keccak_input, + keccak_input_length, + initialization_code_word_size, + } + } + + 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(); + let is_create2 = opcode == OpcodeId::CREATE2; + self.opcode + .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; + self.is_create2.assign( + region, + offset, + Value::known(is_create2.to_scalar().unwrap()), + )?; + + let [value, initialization_code_start, initialization_code_length] = [0, 1, 2] + .map(|i| step.rw_indices[i]) + .map(|idx| block.rws[idx].stack_value()); + let salt = if is_create2 { + block.rws[step.rw_indices[3]].stack_value() + } else { + U256::zero() + }; + + let values: Vec<_> = (4 + usize::from(is_create2) + ..4 + usize::from(is_create2) + initialization_code_length.as_usize()) + .map(|i| block.rws[step.rw_indices[i]].memory_value()) + .collect(); + let mut code_hash = keccak256(&values); + code_hash.reverse(); + let code_hash_rlc = + RandomLinearCombination::random_linear_combine(code_hash, block.randomness); + self.code_hash + .assign(region, offset, Value::known(code_hash_rlc))?; + + for (word, assignment) in [(&self.value, value), (&self.salt, salt)] { + word.assign(region, offset, Some(assignment.to_le_bytes()))?; + } + let initialization_code_address = self.initialization_code.assign( + region, + offset, + initialization_code_start, + initialization_code_length, + block.randomness, + )?; + + self.tx_id + .assign(region, offset, Value::known(tx.id.to_scalar().unwrap()))?; + self.depth.assign( + region, + offset, + Value::known(call.depth.to_scalar().unwrap()), + )?; + + self.reversion_info.assign( + region, + offset, + call.rw_counter_end_of_reversion, + call.is_persistent, + )?; + + let copy_rw_increase = initialization_code_length.as_usize(); + let tx_access_rw = + block.rws[step.rw_indices[7 + usize::from(is_create2) + copy_rw_increase]]; + self.was_warm.assign( + region, + offset, + Value::known( + tx_access_rw + .tx_access_list_value_pair() + .1 + .to_scalar() + .unwrap(), + ), + )?; + + let mut caller_address_bytes = call.callee_address.to_fixed_bytes(); + caller_address_bytes.reverse(); + self.caller_address + .assign(region, offset, Some(caller_address_bytes))?; + + let caller_nonce = block.rws + [step.rw_indices[9 + usize::from(is_create2) + copy_rw_increase]] + .account_value_pair() + .1 + .low_u64(); + self.nonce.assign(region, offset, caller_nonce)?; + + let [callee_rw_counter_end_of_reversion, callee_is_persistent] = [10, 11].map(|i| { + block.rws[step.rw_indices[i + usize::from(is_create2) + copy_rw_increase]] + .call_context_value() + }); + + self.callee_reversion_info.assign( + region, + offset, + callee_rw_counter_end_of_reversion + .low_u64() + .try_into() + .unwrap(), + callee_is_persistent.low_u64() != 0, + )?; + + let [caller_balance_pair, callee_balance_pair] = [13, 14].map(|i| { + block.rws[step.rw_indices[i + usize::from(is_create2) + copy_rw_increase]] + .account_value_pair() + }); + self.transfer.assign( + region, + offset, + caller_balance_pair, + callee_balance_pair, + value, + )?; + + let (_next_memory_word_size, memory_expansion_gas_cost) = self.memory_expansion.assign( + region, + offset, + step.memory_word_size(), + [initialization_code_address], + )?; + + let (initialization_code_word_size, _remainder) = + self.initialization_code_word_size.assign( + region, + offset, + (31u64 + initialization_code_length.as_u64()).into(), + )?; + + self.gas_left.assign( + region, + offset, + (step.gas_left + - GasCost::CREATE.as_u64() + - memory_expansion_gas_cost + - if is_create2 { + u64::try_from(initialization_code_word_size).unwrap() + * GasCost::COPY_SHA3.as_u64() + } else { + 0 + }) + .into(), + )?; + + self.callee_is_success.assign( + region, + offset, + Value::known( + block.rws[step.rw_indices[22 + usize::from(is_create2) + copy_rw_increase]] + .call_context_value() + .to_scalar() + .unwrap(), + ), + )?; + + let keccak_input: Vec = if is_create2 { + once(0xffu8) + .chain(call.callee_address.to_fixed_bytes()) + .chain(salt.to_be_bytes()) + .chain(keccak256(&values)) + .collect() + } else { + let mut stream = rlp::RlpStream::new(); + stream.begin_list(2); + stream.append(&call.callee_address); + stream.append(&U256::from(caller_nonce)); + stream.out().to_vec() + }; + let mut keccak_output = keccak256(&keccak_input); + keccak_output.reverse(); + + self.keccak_input.assign( + region, + offset, + Value::known(rlc::value(keccak_input.iter().rev(), block.randomness)), + )?; + self.keccak_input_length.assign( + region, + offset, + Value::known(keccak_input.len().to_scalar().unwrap()), + )?; + self.keccak_output + .assign(region, offset, Some(keccak_output))?; + + Ok(()) + } +} + +#[derive(Clone, Debug)] +struct RlpU64Gadget { + bytes: RandomLinearCombination, + is_most_significant_byte: [Cell; N_BYTES_U64], + most_significant_byte_is_zero: IsZeroGadget, + is_less_than_128: Cell, +} + +impl RlpU64Gadget { + fn construct(cb: &mut ConstraintBuilder) -> Self { + let bytes = cb.query_rlc(); + let is_most_significant_byte = [(); N_BYTES_U64].map(|()| cb.query_bool()); + let most_significant_byte = sum::expr( + bytes + .cells + .iter() + .zip(&is_most_significant_byte) + .map(|(byte, indicator)| byte.expr() * indicator.expr()), + ); + let most_significant_byte_is_zero = IsZeroGadget::construct(cb, most_significant_byte); + let is_less_than_128 = cb.query_bool(); + + cb.require_boolean( + "at most one of is_most_significant_byte is one", + sum::expr(&is_most_significant_byte), + ); + + let value = from_bytes::expr(&bytes.cells); + cb.condition(most_significant_byte_is_zero.expr(), |cb| { + cb.require_zero("if most significant byte is 0, value is 0", value.clone()); + }); + for (i, is_most_significant) in is_most_significant_byte.iter().enumerate() { + cb.condition(is_most_significant.expr(), |cb| { + cb.require_equal( + "most significant byte is non-zero", + most_significant_byte_is_zero.expr(), + 0.expr(), + ); + cb.require_equal( + "higher bytes are 0", + from_bytes::expr(&bytes.cells[..i + 1]), + value.clone(), + ); + }); + } + + cb.condition(is_less_than_128.expr(), |cb| { + cb.range_lookup(value, 128); + }); + + Self { + bytes, + is_most_significant_byte, + most_significant_byte_is_zero, + is_less_than_128, + } + } + + fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + value: u64, + ) -> Result<(), Error> { + let bytes = value.to_le_bytes(); + let most_significant_byte_index = bytes + .iter() + .rev() + .position(|byte| *byte != 0) + .map(|i| N_BYTES_U64 - i - 1); + self.most_significant_byte_is_zero.assign( + region, + offset, + most_significant_byte_index + .map(|i| u64::from(bytes[i]).into()) + .unwrap_or_default(), + )?; + self.bytes.assign(region, offset, Some(bytes))?; + for i in 0..N_BYTES_U64 { + self.is_most_significant_byte[i].assign( + region, + offset, + Value::known( + (Some(i) == most_significant_byte_index) + .to_scalar() + .unwrap(), + ), + )?; + } + self.is_less_than_128.assign( + region, + offset, + Value::known((value < 128).to_scalar().unwrap()), + )?; + Ok(()) + } + + fn value(&self) -> Expression { + from_bytes::expr(&self.bytes.cells) + } + + fn n_bytes_nonce(&self) -> Expression { + sum::expr( + self.is_most_significant_byte + .iter() + .enumerate() + .map(|(i, indicator)| (1 + i).expr() * indicator.expr()), + ) + } + + fn rlp_length(&self) -> Expression { + 1.expr() + not::expr(self.is_less_than_128.expr()) * self.n_bytes_nonce() + } + + fn rlp_rlc(&self, cb: &ConstraintBuilder) -> Expression { + select::expr( + and::expr(&[ + self.is_less_than_128.expr(), + not::expr(self.most_significant_byte_is_zero.expr()), + ]), + self.value(), + (0x80.expr() + self.n_bytes_nonce()) * self.randomness_raised_n_bytes_nonce(cb) + + self.bytes.expr(), + ) + } + + fn randomness_raised_to_rlp_length(&self, cb: &ConstraintBuilder) -> Expression { + let powers_of_randomness = cb.power_of_randomness(); + powers_of_randomness[0].clone() + * select::expr( + self.is_less_than_128.expr(), + 1.expr(), + self.randomness_raised_n_bytes_nonce(cb), + ) + } + + fn randomness_raised_n_bytes_nonce(&self, cb: &ConstraintBuilder) -> Expression { + let powers_of_randomness = cb.power_of_randomness(); + select::expr( + self.most_significant_byte_is_zero.expr(), + 1.expr(), + sum::expr( + self.is_most_significant_byte + .iter() + .zip(powers_of_randomness) + .map(|(indicator, power)| indicator.expr() * power.clone()), + ), + ) + } +} + +#[cfg(test)] +mod test { + use crate::test_util::run_test_circuits; + use eth_types::{ + address, bytecode, evm_types::OpcodeId, geth_types::Account, Address, Bytecode, Word, + }; + + use itertools::Itertools; + use lazy_static::lazy_static; + use mock::{eth, TestContext}; + + const CALLEE_ADDRESS: Address = Address::repeat_byte(0xff); + lazy_static! { + static ref CALLER_ADDRESS: Address = address!("0x00bbccddee000000000000000000000000002400"); + } + + // RETURN or REVERT with data of [0x60; 5] + fn initialization_bytecode(is_success: bool) -> Bytecode { + let memory_bytes = [0x60; 10]; + let memory_address = 0; + let memory_value = Word::from_big_endian(&memory_bytes); + let mut code = bytecode! { + PUSH10(memory_value) + PUSH1(memory_address) + MSTORE + PUSH2(5) + PUSH2(32u64 - u64::try_from(memory_bytes.len()).unwrap()) + }; + code.write_op(if is_success { + OpcodeId::RETURN + } else { + OpcodeId::REVERT + }); + code + } + + fn creater_bytecode( + initialization_bytecode: Bytecode, + is_create2: bool, + is_persistent: bool, + ) -> Bytecode { + let initialization_bytes = initialization_bytecode.code(); + let mut code = bytecode! { + PUSH32(Word::from_big_endian(&initialization_bytes)) + PUSH1(0) + MSTORE + }; + if is_create2 { + code.append(&bytecode! {PUSH1(45)}); // salt; + } + code.append(&bytecode! { + PUSH1(initialization_bytes.len()) // size + PUSH1(32 - initialization_bytes.len()) // length + PUSH2(23414) // value + }); + code.write_op(if is_create2 { + OpcodeId::CREATE2 + } else { + OpcodeId::CREATE + }); + if !is_persistent { + code.append(&bytecode! { + PUSH1(0) + PUSH1(0) + REVERT + }); + } + code + } + + fn test_context(caller: Account) -> 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(100000u64.into()); + }, + |block, _| block, + ) + .unwrap() + } + + #[test] + fn test_create() { + for ((is_success, is_create2), is_persistent) in [true, false] + .iter() + .cartesian_product(&[true, false]) + .cartesian_product(&[true, false]) + { + let initialization_code = initialization_bytecode(*is_success); + let root_code = creater_bytecode(initialization_code, *is_create2, *is_persistent); + let caller = Account { + address: *CALLER_ADDRESS, + code: root_code.into(), + nonce: Word::one(), + balance: eth(10), + ..Default::default() + }; + assert_eq!( + run_test_circuits(test_context(caller), None), + Ok(()), + "(is_success, is_create2, is_persistent) = {:?}", + (*is_success, *is_create2, *is_persistent), + ); + } + } + + #[test] + fn test_create_rlp_nonce() { + for nonce in [0, 1, 127, 128, 255, 256, 0x10000, u64::MAX - 1] { + let caller = Account { + address: *CALLER_ADDRESS, + code: creater_bytecode(initialization_bytecode(true), false, true).into(), + nonce: nonce.into(), + balance: eth(10), + ..Default::default() + }; + assert_eq!( + run_test_circuits(test_context(caller), None), + Ok(()), + "nonce = {:?}", + nonce, + ); + } + } + + #[test] + fn test_create_empty_initialization_code() { + for is_create2 in [true, false] { + let caller = Account { + address: *CALLER_ADDRESS, + code: creater_bytecode(vec![].into(), is_create2, true).into(), + nonce: 10.into(), + balance: eth(10), + ..Default::default() + }; + assert_eq!( + run_test_circuits(test_context(caller), None), + Ok(()), + "is_create2 = {:?}", + is_create2 + ); + } + } +} diff --git a/zkevm-circuits/src/evm_circuit/step.rs b/zkevm-circuits/src/evm_circuit/step.rs index e83cd007d9..084791056b 100644 --- a/zkevm-circuits/src/evm_circuit/step.rs +++ b/zkevm-circuits/src/evm_circuit/step.rs @@ -75,14 +75,13 @@ pub enum ExecutionState { MSIZE, GAS, JUMPDEST, - PUSH, // PUSH1, PUSH2, ..., PUSH32 - DUP, // DUP1, DUP2, ..., DUP16 - SWAP, // SWAP1, SWAP2, ..., SWAP16 - LOG, // LOG0, LOG1, ..., LOG4 - CREATE, + PUSH, // PUSH1, PUSH2, ..., PUSH32 + DUP, // DUP1, DUP2, ..., DUP16 + SWAP, // SWAP1, SWAP2, ..., SWAP16 + LOG, // LOG0, LOG1, ..., LOG4 + CREATE, // CREATE, CREATE2 CALL_OP, // CALL, CALLCODE, DELEGATECALL, STATICCALL RETURN_REVERT, // RETURN, REVERT - CREATE2, SELFDESTRUCT, // Error cases ErrorInvalidOpcode, @@ -299,7 +298,7 @@ impl ExecutionState { OpcodeId::LOG3, OpcodeId::LOG4, ], - Self::CREATE => vec![OpcodeId::CREATE], + Self::CREATE => vec![OpcodeId::CREATE, OpcodeId::CREATE2], Self::CALL_OP => vec![ OpcodeId::CALL, OpcodeId::CALLCODE, @@ -307,7 +306,6 @@ impl ExecutionState { OpcodeId::STATICCALL, ], Self::RETURN_REVERT => vec![OpcodeId::RETURN, OpcodeId::REVERT], - Self::CREATE2 => vec![OpcodeId::CREATE2], Self::SELFDESTRUCT => vec![OpcodeId::SELFDESTRUCT], _ => vec![], } diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index de47b5ea2b..fec601cb86 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -15,6 +15,7 @@ pub enum FixedTableTag { Range16, Range32, Range64, + Range128, Range256, Range512, Range1024, @@ -46,6 +47,9 @@ impl FixedTableTag { Self::Range64 => { Box::new((0..64).map(move |value| [tag, F::from(value), F::zero(), F::zero()])) } + Self::Range128 => { + Box::new((0..128).map(move |value| [tag, F::from(value), F::zero(), F::zero()])) + } Self::Range256 => { Box::new((0..256).map(move |value| [tag, F::from(value), F::zero(), F::zero()])) } diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index a6ea091cb5..41651660de 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -103,6 +103,29 @@ pub(crate) struct ReversionInfo { } impl ReversionInfo { + pub(crate) fn from_caller( + cb: &mut ConstraintBuilder, + caller: &mut ReversionInfo, + callee_is_success: Expression, + ) -> Self { + // not sure if this is correct?? + // let call_id = cb.curr.state.rw_counter.expr(); + let callee = cb.reversion_info_write(None); + cb.require_equal( + "callee_is_persistent == is_persistent ⋅ is_success", + callee.is_persistent(), + and::expr([caller.is_persistent(), callee_is_success.clone()]), + ); + cb.condition(callee_is_success * not::expr(caller.is_persistent()), |cb| { + cb.require_equal( + "callee_rw_counter_end_of_reversion == rw_counter_end_of_reversion - (reversible_write_counter + 1)", + callee.rw_counter_end_of_reversion(), + caller.rw_counter_of_reversion(), + ); + }); + callee + } + pub(crate) fn rw_counter_end_of_reversion(&self) -> Expression { self.rw_counter_end_of_reversion.expr() } @@ -502,6 +525,7 @@ impl<'a, F: Field> ConstraintBuilder<'a, F> { 16 => ("Range16", FixedTableTag::Range16), 32 => ("Range32", FixedTableTag::Range32), 64 => ("Range64", FixedTableTag::Range64), + 128 => ("Range64", FixedTableTag::Range128), 256 => ("Range256", FixedTableTag::Range256), 512 => ("Range512", FixedTableTag::Range512), 1024 => ("Range1024", FixedTableTag::Range1024), diff --git a/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs b/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs index e6a6d5a48d..05add3ebf5 100644 --- a/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs @@ -95,6 +95,12 @@ impl MemoryAddressGadget { } } + pub(crate) fn construct_2(cb: &mut ConstraintBuilder) -> Self { + let offset = cb.query_cell(); + let length = cb.query_rlc(); + Self::construct(cb, offset, length) + } + pub(crate) fn assign( &self, region: &mut CachedRegion<'_, '_, F>, @@ -148,6 +154,14 @@ impl MemoryAddressGadget { self.has_length() * from_bytes::expr(&self.memory_offset_bytes.cells) } + pub(crate) fn offset_rlc(&self) -> Expression { + self.memory_offset.expr() + } + + pub(crate) fn length_rlc(&self) -> Expression { + self.memory_length.expr() + } + pub(crate) fn length(&self) -> Expression { from_bytes::expr(&self.memory_length.cells) } diff --git a/zkevm-circuits/src/witness/step.rs b/zkevm-circuits/src/witness/step.rs index 479aedb589..a65b46416e 100644 --- a/zkevm-circuits/src/witness/step.rs +++ b/zkevm-circuits/src/witness/step.rs @@ -182,11 +182,10 @@ impl From<&circuit_input_builder::ExecStep> for ExecutionState { OpcodeId::RETURN | OpcodeId::REVERT => ExecutionState::RETURN_REVERT, OpcodeId::RETURNDATASIZE => ExecutionState::RETURNDATASIZE, OpcodeId::RETURNDATACOPY => ExecutionState::RETURNDATACOPY, + OpcodeId::CREATE | OpcodeId::CREATE2 => ExecutionState::CREATE, // dummy ops OpcodeId::SAR => dummy!(ExecutionState::SAR), OpcodeId::EXTCODECOPY => dummy!(ExecutionState::EXTCODECOPY), - OpcodeId::CREATE => dummy!(ExecutionState::CREATE), - OpcodeId::CREATE2 => dummy!(ExecutionState::CREATE2), OpcodeId::SELFDESTRUCT => dummy!(ExecutionState::SELFDESTRUCT), _ => unimplemented!("unimplemented opcode {:?}", op), }