Skip to content
This repository was archived by the owner on Jul 5, 2024. It is now read-only.

Extend memory gadgets to handle dynamic offset and length #279

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions bus-mapping/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ impl GasCost {
pub const TX: Self = Self(21000);
/// Constant cost for creation transaction
pub const CREATION_TX: Self = Self(53000);
/// Denominator of quadratic part of memory expansion gas cost
pub const MEMORY_EXPANSION_QUAD_DENOMINATOR: Self = Self(512);
/// Coefficient of linear part of memory expansion gas cost
pub const MEMORY_EXPANSION_LINEAR_COEFF: Self = Self(3);
}

impl GasCost {
Expand Down
9 changes: 5 additions & 4 deletions zkevm-circuits/src/evm_circuit/execution/begin_tx.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
evm_circuit::{
execution::ExecutionGadget,
param::{MAX_GAS_SIZE_IN_BYTES, STACK_CAPACITY},
param::{N_BYTES_GAS, STACK_CAPACITY},
step::ExecutionState,
table::{AccountFieldTag, CallContextFieldTag, TxContextFieldTag},
util::{
Expand Down Expand Up @@ -38,7 +38,7 @@ pub(crate) struct BeginTxGadget<F> {
tx_call_data_gas_cost: Cell<F>,
rw_counter_end_of_reversion: Cell<F>,
is_persistent: Cell<F>,
sufficient_gas_left: RangeCheckGadget<F, MAX_GAS_SIZE_IN_BYTES>,
sufficient_gas_left: RangeCheckGadget<F, N_BYTES_GAS>,
transfer_with_gas_fee: TransferWithGasFeeGadget<F>,
code_hash: Cell<F>,
}
Expand Down Expand Up @@ -328,6 +328,7 @@ mod test {
eth_types::{self, Address, ToLittleEndian, ToWord, Word},
evm::{GasCost, OpcodeId},
};
use std::convert::TryInto;

fn test_ok(tx: eth_types::Transaction, result: bool) {
let rw_counter_end_of_reversion = if result { 0 } else { 20 };
Expand Down Expand Up @@ -355,8 +356,8 @@ mod test {
randomness,
txs: vec![Transaction {
id: 1,
nonce: tx.nonce.low_u64(),
gas: tx.gas.low_u64(),
nonce: tx.nonce.try_into().unwrap(),
gas: tx.gas.try_into().unwrap(),
gas_price: tx.gas_price.unwrap_or_else(Word::zero),
caller_address: tx.from,
callee_address: tx.to.unwrap_or_else(Address::zero),
Expand Down
51 changes: 26 additions & 25 deletions zkevm-circuits/src/evm_circuit/execution/error_oog_pure_memory.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::{
evm_circuit::{
execution::ExecutionGadget,
param::MAX_MEMORY_SIZE_IN_BYTES,
param::{N_BYTES_GAS, N_BYTES_MEMORY_WORD_SIZE},
step::ExecutionState,
util::{
constraint_builder::ConstraintBuilder,
math_gadget::{IsEqualGadget, IsZeroGadget, LtGadget},
math_gadget::{IsEqualGadget, IsZeroGadget, RangeCheckGadget},
memory_gadget::{address_high, address_low, MemoryExpansionGadget},
Cell, Word,
},
Expand All @@ -21,9 +21,18 @@ pub(crate) struct ErrorOOGPureMemoryGadget<F> {
opcode: Cell<F>,
address: Word<F>,
address_in_range: IsZeroGadget<F>,
// Allow memory size to expand to 5 bytes, because memory address could be
// at most 2^40 - 1, after constant division by 32, the memory word size
// then could be at most 2^35 - 1.
// So generic N_BYTES_MEMORY_WORD_SIZE for MemoryExpansionGadget needs to
// be larger by 1 than normal usage (to be 5), to be able to contain
// number up to 2^35 - 1.
memory_expansion:
MemoryExpansionGadget<F, { MAX_MEMORY_SIZE_IN_BYTES * 2 - 1 }>,
insufficient_gas: LtGadget<F, { MAX_MEMORY_SIZE_IN_BYTES * 2 - 1 }>,
MemoryExpansionGadget<F, 1, { N_BYTES_MEMORY_WORD_SIZE + 1 }>,
// Even memory size at most could be 2^35 - 1, the qudratic part of memory
// expansion gas cost could be at most 2^61 - 2^27, due to the constant
// division by 512, which still fits in 8 bytes.
insufficient_gas: RangeCheckGadget<F, N_BYTES_GAS>,
is_mstore8: IsEqualGadget<F>,
}

Expand Down Expand Up @@ -52,30 +61,24 @@ impl<F: FieldExt> ExecutionGadget<F> for ErrorOOGPureMemoryGadget<F> {
let memory_expansion = MemoryExpansionGadget::construct(
cb,
cb.curr.state.memory_word_size.expr(),
address_low::expr(&address)
[address_low::expr(&address)
+ 1.expr()
+ (is_not_mstore8 * 31.expr()),
+ (is_not_mstore8 * 31.expr())],
);

// Check if the memory address is too large
let address_in_range =
IsZeroGadget::construct(cb, address_high::expr(&address));
// Check if the amount of gas available is less than the amount of gas
// required
let insufficient_gas = LtGadget::construct(
cb,
cb.curr.state.gas_left.expr(),
OpcodeId::MLOAD.constant_gas_cost().expr()
+ memory_expansion.gas_cost(),
);

// Make sure we are out of gas
// Out of gas when either the address is too big and/or the amount of
// gas available is lower than what is required.
cb.require_zero(
"Either the address is too big or insufficient gas",
address_in_range.expr() * (1.expr() - insufficient_gas.expr()),
);
let insufficient_gas = cb.condition(address_in_range.expr(), |cb| {
RangeCheckGadget::construct(
cb,
OpcodeId::MLOAD.constant_gas_cost().expr()
+ memory_expansion.gas_cost()
- cb.curr.state.gas_left.expr(),
)
});

// Pop the address from the stack
// We still have to do this to verify the correctness of `address`
Expand Down Expand Up @@ -130,18 +133,16 @@ impl<F: FieldExt> ExecutionGadget<F> for ErrorOOGPureMemoryGadget<F> {
region,
offset,
step.memory_word_size(),
address_low::value::<F>(address.to_le_bytes())
+ 1
+ if is_mstore8 == F::one() { 0 } else { 31 },
[address_low::value(address.to_le_bytes())
+ if is_mstore8 == F::one() { 1 } else { 32 }],
)?;

// Gas insufficient check
// Get `gas_available` variable here once it's available
self.insufficient_gas.assign(
region,
offset,
F::from(step.gas_left),
F::from(step.gas_cost),
F::from(step.gas_cost - step.gas_left),
)?;

Ok(())
Expand Down
11 changes: 4 additions & 7 deletions zkevm-circuits/src/evm_circuit/execution/jump.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
evm_circuit::{
execution::ExecutionGadget,
param::NUM_BYTES_PROGRAM_COUNTER,
param::N_BYTES_PROGRAM_COUNTER,
step::ExecutionState,
util::{
common_gadget::SameContextGadget,
Expand All @@ -22,7 +22,7 @@ use std::convert::TryInto;
#[derive(Clone, Debug)]
pub(crate) struct JumpGadget<F> {
same_context: SameContextGadget<F>,
destination: RandomLinearCombination<F, NUM_BYTES_PROGRAM_COUNTER>,
destination: RandomLinearCombination<F, N_BYTES_PROGRAM_COUNTER>,
}

impl<F: FieldExt> ExecutionGadget<F> for JumpGadget<F> {
Expand All @@ -31,10 +31,7 @@ impl<F: FieldExt> ExecutionGadget<F> for JumpGadget<F> {
const EXECUTION_STATE: ExecutionState = ExecutionState::JUMP;

fn configure(cb: &mut ConstraintBuilder<F>) -> Self {
let destination = RandomLinearCombination::new(
cb.query_bytes(),
cb.power_of_randomness(),
);
let destination = cb.query_rlc();

// Pop the value from the stack
cb.stack_pop(destination.expr());
Expand Down Expand Up @@ -83,7 +80,7 @@ impl<F: FieldExt> ExecutionGadget<F> for JumpGadget<F> {
region,
offset,
Some(
destination.to_le_bytes()[..NUM_BYTES_PROGRAM_COUNTER]
destination.to_le_bytes()[..N_BYTES_PROGRAM_COUNTER]
.try_into()
.unwrap(),
),
Expand Down
11 changes: 4 additions & 7 deletions zkevm-circuits/src/evm_circuit/execution/jumpi.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
evm_circuit::{
execution::ExecutionGadget,
param::NUM_BYTES_PROGRAM_COUNTER,
param::N_BYTES_PROGRAM_COUNTER,
step::ExecutionState,
util::{
common_gadget::SameContextGadget,
Expand All @@ -24,7 +24,7 @@ use std::convert::TryInto;
#[derive(Clone, Debug)]
pub(crate) struct JumpiGadget<F> {
same_context: SameContextGadget<F>,
destination: RandomLinearCombination<F, NUM_BYTES_PROGRAM_COUNTER>,
destination: RandomLinearCombination<F, N_BYTES_PROGRAM_COUNTER>,
condition: Cell<F>,
is_condition_zero: IsZeroGadget<F>,
}
Expand All @@ -35,10 +35,7 @@ impl<F: FieldExt> ExecutionGadget<F> for JumpiGadget<F> {
const EXECUTION_STATE: ExecutionState = ExecutionState::JUMPI;

fn configure(cb: &mut ConstraintBuilder<F>) -> Self {
let destination = RandomLinearCombination::new(
cb.query_bytes(),
cb.power_of_randomness(),
);
let destination = cb.query_rlc();
let condition = cb.query_cell();

// Pop the value from the stack
Expand Down Expand Up @@ -111,7 +108,7 @@ impl<F: FieldExt> ExecutionGadget<F> for JumpiGadget<F> {
region,
offset,
Some(
destination.to_le_bytes()[..NUM_BYTES_PROGRAM_COUNTER]
destination.to_le_bytes()[..N_BYTES_PROGRAM_COUNTER]
.try_into()
.unwrap(),
),
Expand Down
15 changes: 7 additions & 8 deletions zkevm-circuits/src/evm_circuit/execution/memory.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
evm_circuit::{
execution::ExecutionGadget,
param::{MAX_GAS_SIZE_IN_BYTES, NUM_ADDRESS_BYTES_USED},
param::{N_BYTES_MEMORY_ADDRESS, N_BYTES_MEMORY_WORD_SIZE},
step::ExecutionState,
util::{
common_gadget::SameContextGadget,
Expand All @@ -27,7 +27,7 @@ pub(crate) struct MemoryGadget<F> {
same_context: SameContextGadget<F>,
address: MemoryAddress<F>,
value: Word<F>,
memory_expansion: MemoryExpansionGadget<F, MAX_GAS_SIZE_IN_BYTES>,
memory_expansion: MemoryExpansionGadget<F, 1, N_BYTES_MEMORY_WORD_SIZE>,
is_mload: IsEqualGadget<F>,
is_mstore8: IsEqualGadget<F>,
}
Expand All @@ -41,8 +41,7 @@ impl<F: FieldExt> ExecutionGadget<F> for MemoryGadget<F> {
let opcode = cb.query_cell();

// In successful case the address must be in 5 bytes
let address =
MemoryAddress::new(cb.query_bytes(), cb.power_of_randomness());
let address = cb.query_rlc();
let value = cb.query_word();

// Check if this is an MLOAD
Expand All @@ -64,9 +63,9 @@ impl<F: FieldExt> ExecutionGadget<F> for MemoryGadget<F> {
let memory_expansion = MemoryExpansionGadget::construct(
cb,
cb.curr.state.memory_word_size.expr(),
from_bytes::expr(&address.cells)
[from_bytes::expr(&address.cells)
+ 1.expr()
+ (is_not_mstore8.clone() * 31.expr()),
+ (is_not_mstore8.clone() * 31.expr())],
);

/* Stack operations */
Expand Down Expand Up @@ -166,7 +165,7 @@ impl<F: FieldExt> ExecutionGadget<F> for MemoryGadget<F> {
region,
offset,
Some(
address.to_le_bytes()[..NUM_ADDRESS_BYTES_USED]
address.to_le_bytes()[..N_BYTES_MEMORY_ADDRESS]
.try_into()
.unwrap(),
),
Expand Down Expand Up @@ -194,7 +193,7 @@ impl<F: FieldExt> ExecutionGadget<F> for MemoryGadget<F> {
region,
offset,
step.memory_word_size(),
address.as_u64() + 1 + if is_mstore8 == F::one() { 0 } else { 31 },
[address.as_u64() + if is_mstore8 == F::one() { 1 } else { 32 }],
)?;

Ok(())
Expand Down
14 changes: 6 additions & 8 deletions zkevm-circuits/src/evm_circuit/execution/msize.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
evm_circuit::{
execution::ExecutionGadget,
param::NUM_BYTES_WORD,
param::N_BYTES_WORD,
step::ExecutionState,
util::{
common_gadget::SameContextGadget,
Expand All @@ -14,7 +14,6 @@ use crate::{
},
util::Expr,
};
use array_init::array_init;
use halo2::{arithmetic::FieldExt, circuit::Region, plonk::Error};

#[derive(Clone, Debug)]
Expand All @@ -29,17 +28,16 @@ impl<F: FieldExt> ExecutionGadget<F> for MsizeGadget<F> {
const EXECUTION_STATE: ExecutionState = ExecutionState::MSIZE;

fn configure(cb: &mut ConstraintBuilder<F>) -> Self {
let value = cb.query_rlc();

// memory_size is limited to 64 bits so we only consider 8 bytes
let bytes = array_init(|_| cb.query_cell());
cb.require_equal(
"Constrain memory_size equal to stack value",
from_bytes::expr(&bytes),
cb.curr.state.memory_word_size.expr() * NUM_BYTES_WORD.expr(),
from_bytes::expr(&value.cells),
cb.curr.state.memory_word_size.expr() * N_BYTES_WORD.expr(),
);

// Push the value on the stack
let value =
RandomLinearCombination::new(bytes, cb.power_of_randomness());
cb.stack_push(value.expr());

// State transition
Expand Down Expand Up @@ -76,7 +74,7 @@ impl<F: FieldExt> ExecutionGadget<F> for MsizeGadget<F> {
self.value.assign(
region,
offset,
Some(step.memory_size.to_le_bytes()),
Some((step.memory_size as u64).to_le_bytes()),
)?;

Ok(())
Expand Down
12 changes: 5 additions & 7 deletions zkevm-circuits/src/evm_circuit/execution/pc.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
evm_circuit::{
execution::ExecutionGadget,
param::NUM_BYTES_PROGRAM_COUNTER,
param::N_BYTES_PROGRAM_COUNTER,
step::ExecutionState,
util::{
common_gadget::SameContextGadget,
Expand All @@ -14,13 +14,12 @@ use crate::{
},
util::Expr,
};
use array_init::array_init;
use halo2::{arithmetic::FieldExt, circuit::Region, plonk::Error};

#[derive(Clone, Debug)]
pub(crate) struct PcGadget<F> {
same_context: SameContextGadget<F>,
value: RandomLinearCombination<F, NUM_BYTES_PROGRAM_COUNTER>,
value: RandomLinearCombination<F, N_BYTES_PROGRAM_COUNTER>,
}

impl<F: FieldExt> ExecutionGadget<F> for PcGadget<F> {
Expand All @@ -29,17 +28,16 @@ impl<F: FieldExt> ExecutionGadget<F> for PcGadget<F> {
const EXECUTION_STATE: ExecutionState = ExecutionState::PC;

fn configure(cb: &mut ConstraintBuilder<F>) -> Self {
let value = cb.query_rlc();

// program_counter is limited to 64 bits so we only consider 8 bytes
let bytes = array_init(|_| cb.query_cell());
cb.require_equal(
"Constrain program_counter equal to stack value",
from_bytes::expr(&bytes),
from_bytes::expr(&value.cells),
cb.curr.state.program_counter.expr(),
);

// Push the value on the stack
let value =
RandomLinearCombination::new(bytes, cb.power_of_randomness());
cb.stack_push(value.expr());

// State transition
Expand Down
Loading