From 77cfe5d0d847f131bef7c5254f382193ce43883c Mon Sep 17 00:00:00 2001 From: Ryan Kim Date: Mon, 15 Apr 2024 21:51:18 +0900 Subject: [PATCH] feat: support EIP-3860(Limit and meter initcode) for Shanghai See https://github.com/scroll-tech/zkevm-circuits/pull/507. --- circuit-benchmarks/src/bytecode_circuit.rs | 9 ++-- eth-types/src/evm_types.rs | 6 +++ .../src/evm_circuit/execution/create.rs | 54 +++++++++++++++---- 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/circuit-benchmarks/src/bytecode_circuit.rs b/circuit-benchmarks/src/bytecode_circuit.rs index 457925fab..b01653034 100644 --- a/circuit-benchmarks/src/bytecode_circuit.rs +++ b/circuit-benchmarks/src/bytecode_circuit.rs @@ -4,7 +4,7 @@ mod tests { use ark_std::{end_timer, start_timer}; use bus_mapping::evm::OpcodeId; - use eth_types::Field; + use eth_types::{evm_types::MAX_CODE_SIZE, Field}; use halo2_proofs::{ halo2curves::bn256::{Bn256, Fr, G1Affine}, plonk::{create_proof, keygen_pk, keygen_vk, verify_proof}, @@ -42,13 +42,12 @@ mod tests { // Unique string used by bench results module for parsing the result const BENCHMARK_ID: &str = "Bytecode Circuit"; - // Contract code size exceeds 24576 bytes may not be deployable on Mainnet. - const MAX_BYTECODE_LEN: usize = 24576; - let num_rows = 1 << degree; const NUM_BLINDING_ROWS: usize = 7 - 1; let max_bytecode_row_num = num_rows - NUM_BLINDING_ROWS; - let bytecode_len = std::cmp::min(MAX_BYTECODE_LEN, max_bytecode_row_num); + + // Contract code size exceeds 24576 bytes may not be deployable on Mainnet. + let bytecode_len = std::cmp::min(MAX_CODE_SIZE as usize, max_bytecode_row_num); let bytecodes_num: usize = max_bytecode_row_num / bytecode_len; // Create the circuit diff --git a/eth-types/src/evm_types.rs b/eth-types/src/evm_types.rs index 1cdc029dc..8045d1023 100644 --- a/eth-types/src/evm_types.rs +++ b/eth-types/src/evm_types.rs @@ -61,6 +61,12 @@ impl fmt::Debug for Gas { } } +/// Gas per word of the init code when creating a contract. +pub const INIT_CODE_WORD_GAS: u64 = 2; +/// Maximum bytecode size to permit for a contract. +pub const MAX_CODE_SIZE: u64 = 24576; +/// Maximum initcode size to permit in a creation transaction and create instructions. +pub const MAX_INIT_CODE_SIZE: u64 = 2 * MAX_CODE_SIZE; /// This constant ((2^32 - 1) * 32) is the highest number that can be used without overflowing the /// square operation of gas calculation. /// diff --git a/zkevm-circuits/src/evm_circuit/execution/create.rs b/zkevm-circuits/src/evm_circuit/execution/create.rs index 0fb38c944..6e06c2a66 100644 --- a/zkevm-circuits/src/evm_circuit/execution/create.rs +++ b/zkevm-circuits/src/evm_circuit/execution/create.rs @@ -13,7 +13,7 @@ use crate::{ Transition::{Delta, To}, }, from_bytes, - math_gadget::{ConstantDivisionGadget, IsZeroGadget}, + math_gadget::{ConstantDivisionGadget, IsZeroGadget, LtGadget}, memory_gadget::{MemoryAddressGadget, MemoryExpansionGadget}, not, rlc, select, sum, CachedRegion, Cell, RandomLinearCombination, Word, }, @@ -32,6 +32,14 @@ use halo2_proofs::{ use std::iter::once; +mod constants { + use eth_types::evm_types::GasCost; + pub use eth_types::evm_types::{ + INIT_CODE_WORD_GAS as CREATE_GAS_PER_CODE_WORD, MAX_INIT_CODE_SIZE, + }; + pub const CREATE2_GAS_PER_CODE_WORD: u64 = CREATE_GAS_PER_CODE_WORD + GasCost::COPY_SHA3.0; +} + /// Gadget for CREATE and CREATE2 opcodes #[derive(Clone, Debug)] pub(crate) struct CreateGadget { @@ -56,6 +64,9 @@ pub(crate) struct CreateGadget { initialization_code: MemoryAddressGadget, initialization_code_word_size: ConstantDivisionGadget, + // Init code size must be less than 49152 if Shanghai, otherwise should be + // less than 2^40 (maximum value for N_BYTES_MEMORY_ADDRESS bytes). + initialization_code_word_size_not_overflow: LtGadget, memory_expansion: MemoryExpansionGadget, gas_left: ConstantDivisionGadget, @@ -98,6 +109,20 @@ impl ExecutionGadget for CreateGadget { cb.stack_pop(initialization_code.offset_rlc()); cb.stack_pop(initialization_code.length_rlc()); + let initialization_code_word_size_not_overflow = LtGadget::construct( + cb, + initialization_code.length(), + constants::MAX_INIT_CODE_SIZE.expr() + 1.expr(), + ); + + // Init code size overflow is checked before ErrDepth, ErrInsufficientBalance, + // ErrNonceUintOverflow and ErrContractAddressCollision. + cb.require_equal( + "Init code size must be not overflow", + initialization_code_word_size_not_overflow.expr(), + 1.expr(), + ); + let salt = cb.condition(is_create2.expr(), |cb| { let salt = cb.query_word_rlc(); cb.stack_pop(salt.expr()); @@ -207,9 +232,11 @@ impl ExecutionGadget for CreateGadget { 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 keccak_gas_cost = select::expr( + is_create2.expr(), + constants::CREATE2_GAS_PER_CODE_WORD.expr(), + constants::CREATE_GAS_PER_CODE_WORD.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(); @@ -375,6 +402,7 @@ impl ExecutionGadget for CreateGadget { keccak_input, keccak_input_length, initialization_code_word_size, + initialization_code_word_size_not_overflow, } } @@ -429,6 +457,12 @@ impl ExecutionGadget for CreateGadget { initialization_code_start, initialization_code_length, )?; + self.initialization_code_word_size_not_overflow.assign( + region, + offset, + F::from(initialization_code_length.as_u64()), + F::from(constants::MAX_INIT_CODE_SIZE + 1), + )?; self.tx_id .assign(region, offset, Value::known(tx.id.to_scalar().unwrap()))?; @@ -525,14 +559,14 @@ impl ExecutionGadget for CreateGadget { (31u64 + initialization_code_length.as_u64()).into(), )?; - let gas_left = 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() + let keccak_gas_cost = u64::try_from(initialization_code_word_size).unwrap() + * if is_create2 { + constants::CREATE2_GAS_PER_CODE_WORD } else { - 0 + constants::CREATE_GAS_PER_CODE_WORD }; + let gas_left = + step.gas_left - GasCost::CREATE.as_u64() - memory_expansion_gas_cost - keccak_gas_cost; self.gas_left.assign(region, offset, gas_left.into())?; self.callee_is_success.assign(