Skip to content

Commit

Permalink
feat: support EIP-3860(Limit and meter initcode) for Shanghai
Browse files Browse the repository at this point in the history
  • Loading branch information
chokobole committed Apr 16, 2024
1 parent c73e7af commit 77cfe5d
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 15 deletions.
9 changes: 4 additions & 5 deletions circuit-benchmarks/src/bytecode_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions eth-types/src/evm_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
/// <https://github.com/ethereum/go-ethereum/blob/e6b6a8b738069ad0579f6798ee59fde93ed13b43/core/vm/gas_table.go#L38>
Expand Down
54 changes: 44 additions & 10 deletions zkevm-circuits/src/evm_circuit/execution/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand All @@ -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<F> {
Expand All @@ -56,6 +64,9 @@ pub(crate) struct CreateGadget<F> {

initialization_code: MemoryAddressGadget<F>,
initialization_code_word_size: ConstantDivisionGadget<F, N_BYTES_MEMORY_ADDRESS>,
// 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<F, { N_BYTES_MEMORY_ADDRESS + 1 }>,
memory_expansion: MemoryExpansionGadget<F, 1, N_BYTES_MEMORY_WORD_SIZE>,

gas_left: ConstantDivisionGadget<F, N_BYTES_GAS>,
Expand Down Expand Up @@ -98,6 +109,20 @@ impl<F: Field> ExecutionGadget<F> for CreateGadget<F> {
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());
Expand Down Expand Up @@ -207,9 +232,11 @@ impl<F: Field> ExecutionGadget<F> for CreateGadget<F> {

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();
Expand Down Expand Up @@ -375,6 +402,7 @@ impl<F: Field> ExecutionGadget<F> for CreateGadget<F> {
keccak_input,
keccak_input_length,
initialization_code_word_size,
initialization_code_word_size_not_overflow,
}
}

Expand Down Expand Up @@ -429,6 +457,12 @@ impl<F: Field> ExecutionGadget<F> for CreateGadget<F> {
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()))?;
Expand Down Expand Up @@ -525,14 +559,14 @@ impl<F: Field> ExecutionGadget<F> for CreateGadget<F> {
(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(
Expand Down

0 comments on commit 77cfe5d

Please sign in to comment.