Skip to content

Commit

Permalink
Merge branch 'develop' into feat/continuations
Browse files Browse the repository at this point in the history
  • Loading branch information
Nashtare committed Jul 10, 2024
2 parents 2f1a3c5 + 43a984d commit 288828a
Show file tree
Hide file tree
Showing 16 changed files with 173 additions and 67 deletions.
2 changes: 1 addition & 1 deletion docs/arithmetization/framework.tex
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ \subsection{Range-checks}
\subsubsection{What to range-check?}
One can note that every element that ever appears on the stack has been pushed. Therefore, enforcing a range-check on pushed elements is enough to range-check all elements on the stack. Similarly, all elements in memory must have been written prior, and therefore it is enough to range-check memory writes. However, range-checking the PUSH and MSTORE opcodes is not sufficient.
\begin{enumerate}
\item Pushes and memory writes for ``MSTORE\_32BYTES'' are range-checked in ``BytePackingStark''.
\item Pushes and memory writes for ``MSTORE\_32BYTES'' are range-checked in ``BytePackingStark'', except PUSH operations happening in privileged mode. See \ref{push_general_view}.
\item Syscalls, exceptions and prover inputs are range-checked in ``ArithmeticStark''.
\item The inputs and outputs of binary and ternary arithmetic operations are range-checked in ``ArithmeticStark''.
\item The inputs' bits of logic operations are checked to be either 1 or 0 in ``LogicStark''. Since ``LogicStark'' only deals with bitwise operations, this is enough to have range-checked outputs as well.
Expand Down
2 changes: 2 additions & 0 deletions docs/arithmetization/tables/cpu.tex
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,6 @@ \subsubsection{CPU columns}
\item \texttt{Stack}: \texttt{stack\_inv}, \texttt{stack\_inv\_aux} and \texttt{stack\_inv\_aux\_2} are used by popping-only (resp. pushing-only) instructions to check if the stack is empty after (resp. was empty
before) the instruction. \texttt{stack\_len\_bounds\_ aux} is used to check that the stack doesn't overflow in user mode. We use the last four columns to prevent conflicts with the other general columns.
See \ref{stackhandling} for more details.
\label{push_general_view}
\item \texttt{Push}: \texttt{is\_not\_kernel} is used to skip range-checking the output of a PUSH operation when we are in privileged mode, as the kernel code is known and trusted.
\end{itemize}
Binary file modified docs/arithmetization/zkevm.pdf
Binary file not shown.
3 changes: 1 addition & 2 deletions evm_arithmetization/src/byte_packing/byte_packing_stark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,7 @@ impl<F: RichField + Extendable<D>, const D: usize> BytePackingStark<F, D> {
ops: Vec<BytePackingOp>,
min_rows: usize,
) -> Vec<[F; NUM_COLUMNS]> {
let base_len: usize = ops.iter().map(|op| usize::from(!op.bytes.is_empty())).sum();
let num_rows = core::cmp::max(base_len.max(BYTE_RANGE_MAX), min_rows).next_power_of_two();
let num_rows = core::cmp::max(ops.len().max(BYTE_RANGE_MAX), min_rows).next_power_of_two();
let mut rows = Vec::with_capacity(num_rows);

for op in ops {
Expand Down
85 changes: 73 additions & 12 deletions evm_arithmetization/src/cpu/columns/general.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ use core::borrow::{Borrow, BorrowMut};
use core::fmt::{Debug, Formatter};
use core::mem::{size_of, transmute};

use static_assertions::const_assert;

/// General purpose columns, which can have different meanings depending on what
/// CTL or other operation is occurring at this row.
#[repr(C)]
#[derive(Clone, Copy)]
pub(crate) union CpuGeneralColumnsView<T: Copy> {
exception: CpuExceptionView<T>,
logic: CpuLogicView<T>,
jumps: CpuJumpsView<T>,
shift: CpuShiftView<T>,
stack: CpuStackView<T>,
push: CpuPushView<T>,
context_pruning: CpuContextPruningView<T>,
}

Expand Down Expand Up @@ -77,6 +81,18 @@ impl<T: Copy> CpuGeneralColumnsView<T> {
unsafe { &mut self.stack }
}

/// View of the columns required for the push operation.
/// SAFETY: Each view is a valid interpretation of the underlying array.
pub(crate) fn push(&self) -> &CpuPushView<T> {
unsafe { &self.push }
}

/// Mutable view of the columns required for the push operation.
/// SAFETY: Each view is a valid interpretation of the underlying array.
pub(crate) fn push_mut(&mut self) -> &mut CpuPushView<T> {
unsafe { &mut self.push }
}

/// View of the column for context pruning.
/// SAFETY: Each view is a valid interpretation of the underlying array.
pub(crate) fn context_pruning(&self) -> &CpuContextPruningView<T> {
Expand Down Expand Up @@ -121,14 +137,21 @@ impl<T: Copy> BorrowMut<[T; NUM_SHARED_COLUMNS]> for CpuGeneralColumnsView<T> {
}

/// View of the first three `CpuGeneralColumns` containing exception code bits.
#[repr(C)]
#[derive(Copy, Clone)]
pub(crate) struct CpuExceptionView<T: Copy> {
/// Exception code as little-endian bits.
pub(crate) exc_code_bits: [T; 3],
/// Reserve the unused columns.
_padding_columns: [T; NUM_SHARED_COLUMNS - 3],
}

/// View of the `CpuGeneralColumns` storing pseudo-inverses used to prove logic
/// operations.
///
/// Because this is the largest field of the [`CpuGeneralColumnsView`] union,
/// we don't add any padding columns.
#[repr(C)]
#[derive(Copy, Clone)]
pub(crate) struct CpuLogicView<T: Copy> {
/// Pseudoinverse of `(input0 - input1)`. Used prove that they are unequal.
Expand All @@ -138,38 +161,40 @@ pub(crate) struct CpuLogicView<T: Copy> {

/// View of the first two `CpuGeneralColumns` storing a flag and a pseudoinverse
/// used to prove jumps.
#[repr(C)]
#[derive(Copy, Clone)]
pub(crate) struct CpuJumpsView<T: Copy> {
/// A flag indicating whether a jump should occur.
pub(crate) should_jump: T,
/// Pseudoinverse of `cond.iter().sum()`. Used to check `should_jump`.
pub(crate) cond_sum_pinv: T,
/// Reserve the unused columns.
_padding_columns: [T; NUM_SHARED_COLUMNS - 2],
}

/// View of the first `CpuGeneralColumns` storing a pseudoinverse used to prove
/// shift operations.
#[repr(C)]
#[derive(Copy, Clone)]
pub(crate) struct CpuShiftView<T: Copy> {
/// For a shift amount of displacement: [T], this is the inverse of
/// sum(displacement[1..]) or zero if the sum is zero.
pub(crate) high_limb_sum_inv: T,
}

/// View of the first `CpuGeneralColumns` storing a flag for context pruning.
#[derive(Copy, Clone)]
pub(crate) struct CpuContextPruningView<T: Copy> {
/// The flag is 1 if the OP flag `context_op` is set, the operation is
/// `SET_CONTEXT` and `new_ctx < old_ctx`, and 0 otherwise.
pub(crate) pruning_flag: T,
/// Reserve the unused columns.
_padding_columns: [T; NUM_SHARED_COLUMNS - 1],
}

/// View of the last four `CpuGeneralColumns` storing stack-related variables.
/// The first three are used for conditionally enabling and disabling channels
/// when reading the next `stack_top`, and the fourth one is used to check for
/// stack overflow.
#[repr(C)]
#[derive(Copy, Clone)]
pub(crate) struct CpuStackView<T: Copy> {
_unused: [T; 4],
/// Reserve the unused columns at the beginning. This allows `Self` to
/// coexist with any view that uses only the first four columns (i.e. all
/// except `CpuLogicView`).
_unused: [T; NUM_SHARED_COLUMNS - 4],
/// Pseudoinverse of `stack_len - num_pops`.
pub(crate) stack_inv: T,
/// stack_inv * stack_len.
Expand All @@ -181,6 +206,42 @@ pub(crate) struct CpuStackView<T: Copy> {
pub(crate) stack_len_bounds_aux: T,
}

/// Number of columns shared by all the views of `CpuGeneralColumnsView`.
/// `u8` is guaranteed to have a `size_of` of 1.
pub(crate) const NUM_SHARED_COLUMNS: usize = size_of::<CpuGeneralColumnsView<u8>>();
/// View of the first `CpuGeneralColumn` storing the negation of
/// `is_kernel_mode` flag, to filter out `PUSH` instructions from being
/// range-checked when happening in the KERNEL context.
#[repr(C)]
#[derive(Copy, Clone)]
pub(crate) struct CpuPushView<T: Copy> {
/// Product of `push_prover_input` with the negated `is_kernel_mode` flag.
pub(crate) is_not_kernel: T,
/// Reserve the unused columns.
_padding_columns: [T; NUM_SHARED_COLUMNS - 1],
}

/// View of the first `CpuGeneralColumn` storing a flag for context pruning.
#[derive(Copy, Clone)]
pub(crate) struct CpuContextPruningView<T: Copy> {
/// The flag is 1 if the OP flag `context_op` is set, the operation is
/// `SET_CONTEXT` and `new_ctx < old_ctx`, and 0 otherwise.
pub(crate) pruning_flag: T,
/// Reserve the unused columns.
_padding_columns: [T; NUM_SHARED_COLUMNS - 1],
}

/// The number of columns shared by all views of [`CpuGeneralColumnsView`].
/// This is defined in terms of the largest view in order to determine the
/// number of padding columns to add to each field without creating a cycle
/// for rustc.
/// NB: `u8` is guaranteed to have a `size_of` of 1.
pub(crate) const NUM_SHARED_COLUMNS: usize = size_of::<CpuLogicView<u8>>();
const_assert!(NUM_SHARED_COLUMNS == size_of::<CpuGeneralColumnsView<u8>>());

/// Assert that each field of the [`CpuGeneralColumnsView`] union contains the
/// correct number of columns.
const_assert!(size_of::<CpuExceptionView<u8>>() == NUM_SHARED_COLUMNS);
const_assert!(size_of::<CpuLogicView<u8>>() == NUM_SHARED_COLUMNS);
const_assert!(size_of::<CpuJumpsView<u8>>() == NUM_SHARED_COLUMNS);
const_assert!(size_of::<CpuShiftView<u8>>() == NUM_SHARED_COLUMNS);
const_assert!(size_of::<CpuStackView<u8>>() == NUM_SHARED_COLUMNS);
const_assert!(size_of::<CpuPushView<u8>>() == NUM_SHARED_COLUMNS);
const_assert!(size_of::<CpuContextPruningView<u8>>() == NUM_SHARED_COLUMNS);
17 changes: 17 additions & 0 deletions evm_arithmetization/src/cpu/control_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ pub(crate) fn eval_packed_generic<P: PackedField>(
);
yield_constr.constraint_transition(is_prover_input * (lv.is_kernel_mode - nv.is_kernel_mode));

// Check the helper value in the general columns. We do not need to enforce that
// it is set to 0 if the operation is a `PROVER_INPUT`, as the latter is a
// kernel-only instruction. This is enforced in the `decode` module.
yield_constr.constraint(
lv.op.push_prover_input * ((lv.is_kernel_mode + lv.general.push().is_not_kernel) - P::ONES),
);

// If a non-CPU cycle row is followed by a CPU cycle row, then:
// - the `program_counter` of the CPU cycle row is `main` (the entry point of
// our kernel),
Expand Down Expand Up @@ -140,6 +147,16 @@ pub(crate) fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
yield_constr.constraint_transition(builder, kernel_constr);
}

// Check the helper value in the general columns. We do not need to enforce that
// it is set to 0 if the operation is a `PROVER_INPUT`, as the latter is a
// kernel-only instruction. This is enforced in the `decode` module.
{
let temp = builder.add_extension(lv.is_kernel_mode, lv.general.push().is_not_kernel);
let constr =
builder.mul_sub_extension(lv.op.push_prover_input, temp, lv.op.push_prover_input);
yield_constr.constraint(builder, constr);
}

// If a non-CPU cycle row is followed by a CPU cycle row, then:
// - the `program_counter` of the CPU cycle row is `main` (the entry point of
// our kernel),
Expand Down
6 changes: 4 additions & 2 deletions evm_arithmetization/src/cpu/cpu_stark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,9 +297,11 @@ pub(crate) fn ctl_data_byte_packing_push<F: Field>() -> Vec<Column<F>> {

/// CTL filter for the `PUSH` operation.
pub(crate) fn ctl_filter_byte_packing_push<F: Field>() -> Filter<F> {
let bit_col = Column::single(COL_MAP.opcode_bits[5]);
Filter::new(
vec![(Column::single(COL_MAP.op.push_prover_input), bit_col)],
vec![(
Column::single(COL_MAP.general.push().is_not_kernel),
Column::single(COL_MAP.op.push_prover_input),
)],
vec![],
)
}
Expand Down
9 changes: 8 additions & 1 deletion evm_arithmetization/src/witness/operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use itertools::Itertools;
use keccak_hash::keccak;
use plonky2::field::types::Field;

use super::state::KERNEL_CONTEXT;
use super::transition::Transition;
use super::util::{
byte_packing_log, byte_unpacking_log, mem_read_with_log, mem_write_log,
Expand Down Expand Up @@ -389,7 +390,13 @@ pub(crate) fn generate_push<F: Field, T: Transition<F>>(
let val = U256::from_big_endian(&bytes);
push_with_write(state, &mut row, val)?;

byte_packing_log(state, base_address, bytes);
// This is necessary to filter out PUSH instructions from the BytePackingStark
// CTl when happening in the KERNEL context.
row.general.push_mut().is_not_kernel = F::ONE - row.is_kernel_mode;

if code_context != KERNEL_CONTEXT {
byte_packing_log(state, base_address, bytes);
}

state.push_cpu(row);

Expand Down
2 changes: 1 addition & 1 deletion evm_arithmetization/src/witness/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};

use crate::cpu::kernel::aggregator::KERNEL;

const KERNEL_CONTEXT: usize = 0;
pub(crate) const KERNEL_CONTEXT: usize = 0;

#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct RegistersState {
Expand Down
6 changes: 1 addition & 5 deletions evm_arithmetization/src/witness/traces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,7 @@ impl<T: Copy> Traces<T> {
Operation::RangeCheckOperation { .. } => 1,
})
.sum(),
byte_packing_len: self
.byte_packing_ops
.iter()
.map(|op| usize::from(!op.bytes.is_empty()))
.sum(),
byte_packing_len: self.byte_packing_ops.len(),
cpu_len: self.cpu.len(),
keccak_len: self.keccak_inputs.len() * keccak::keccak_stark::NUM_ROUNDS,
keccak_sponge_len: self
Expand Down
5 changes: 5 additions & 0 deletions evm_arithmetization/src/witness/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,11 @@ pub(crate) fn byte_packing_log<F: Field, T: Transition<F>>(
base_address: MemoryAddress,
bytes: Vec<u8>,
) {
if bytes.is_empty() {
// No-op
return;
}

let clock = state.get_clock();

let mut address = base_address;
Expand Down
9 changes: 7 additions & 2 deletions trace_decoder/src/decoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use crate::{
OtherBlockData, TrieRootHash, TxnIdx, EMPTY_ACCOUNT_BYTES_RLPED,
ZERO_STORAGE_SLOT_VAL_RLPED,
},
utils::{hash, optional_field, optional_field_hex, update_val_if_some},
utils::{eth_to_gwei, hash, optional_field, optional_field_hex, update_val_if_some},
};

/// Stores the result of parsing tries. Returns a [TraceParsingError] upon
Expand Down Expand Up @@ -576,8 +576,13 @@ impl ProcessedBlockTrace {
fn add_withdrawals_to_txns(
txn_ir: &mut [GenerationInputs],
final_trie_state: &mut PartialTrieState,
withdrawals: Vec<(Address, U256)>,
mut withdrawals: Vec<(Address, U256)>,
) -> TraceParsingResult<()> {
// Scale withdrawals amounts.
for (_addr, amt) in withdrawals.iter_mut() {
*amt = eth_to_gwei(*amt)
}

let withdrawals_with_hashed_addrs_iter = || {
withdrawals
.iter()
Expand Down
7 changes: 6 additions & 1 deletion trace_decoder/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use ethereum_types::H256;
use ethereum_types::{H256, U256};
use keccak_hash::keccak;
use log::trace;
use mpt_trie::{
Expand All @@ -8,6 +8,11 @@ use mpt_trie::{

use crate::types::HashedStorageAddr;

pub(crate) fn eth_to_gwei(eth: U256) -> U256 {
// 1 ether = 10^9 gwei.
eth * U256::from(10).pow(9.into())
}

pub(crate) fn hash(bytes: &[u8]) -> H256 {
H256::from(keccak(bytes).0)
}
Expand Down
3 changes: 2 additions & 1 deletion zero_bin/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ where
std::iter::successors(Some(target_block_number as i128 - 1 + odd_offset), |&it| {
Some(it - 1)
})
.take(PREVIOUS_HASHES_COUNT)
.take(PREVIOUS_HASHES_COUNT + 1)
.filter(|i| *i >= 0)
.collect::<Vec<_>>();
let concurrency = previous_block_numbers.len();
Expand Down Expand Up @@ -138,6 +138,7 @@ where
.into_iter()
.flatten()
.skip(odd_offset as usize)
.take(PREVIOUS_HASHES_COUNT)
.for_each(|(hash, block_num)| {
if let (Some(hash), Some(block_num)) = (hash, block_num) {
// Most recent previous block hash is expected at the end of the array
Expand Down
32 changes: 18 additions & 14 deletions zero_bin/tools/prove_rpc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,25 @@ export RUSTFLAGS='-C target-cpu=native -Zlinker-features=-lld'

if [[ $8 == "test_only" ]]; then
# Circuit sizes don't matter in test_only mode, so we keep them minimal.
export ARITHMETIC_CIRCUIT_SIZE="16..17"
export BYTE_PACKING_CIRCUIT_SIZE="9..10"
export CPU_CIRCUIT_SIZE="12..13"
export KECCAK_CIRCUIT_SIZE="14..15"
export KECCAK_SPONGE_CIRCUIT_SIZE="9..10"
export LOGIC_CIRCUIT_SIZE="12..13"
export MEMORY_CIRCUIT_SIZE="17..18"
export ARITHMETIC_CIRCUIT_SIZE="16..17"
export BYTE_PACKING_CIRCUIT_SIZE="9..10"
export CPU_CIRCUIT_SIZE="12..13"
export KECCAK_CIRCUIT_SIZE="4..5"
export KECCAK_SPONGE_CIRCUIT_SIZE="9..10"
export LOGIC_CIRCUIT_SIZE="12..13"
export MEMORY_CIRCUIT_SIZE="17..18"
export MEMORY_BEFORE_CIRCUIT_SIZE="7..8"
export MEMORY_AFTER_CIRCUIT_SIZE="7..8"
else
export ARITHMETIC_CIRCUIT_SIZE="16..23"
export BYTE_PACKING_CIRCUIT_SIZE="9..21"
export CPU_CIRCUIT_SIZE="12..25"
export KECCAK_CIRCUIT_SIZE="14..20"
export KECCAK_SPONGE_CIRCUIT_SIZE="9..15"
export LOGIC_CIRCUIT_SIZE="12..18"
export MEMORY_CIRCUIT_SIZE="17..28"
export ARITHMETIC_CIRCUIT_SIZE="16..23"
export BYTE_PACKING_CIRCUIT_SIZE="8..23"
export CPU_CIRCUIT_SIZE="8..25"
export KECCAK_CIRCUIT_SIZE="4..20"
export KECCAK_SPONGE_CIRCUIT_SIZE="8..15"
export LOGIC_CIRCUIT_SIZE="8..18"
export MEMORY_CIRCUIT_SIZE="17..28"
export MEMORY_BEFORE_CIRCUIT_SIZE="7..27"
export MEMORY_AFTER_CIRCUIT_SIZE="7..27"
fi

# Force the working directory to always be the `tools/` directory.
Expand Down
Loading

0 comments on commit 288828a

Please sign in to comment.