diff --git a/bus-mapping/src/circuit_input_builder.rs b/bus-mapping/src/circuit_input_builder.rs index 6ef3fc03fad..b76dca32265 100644 --- a/bus-mapping/src/circuit_input_builder.rs +++ b/bus-mapping/src/circuit_input_builder.rs @@ -269,49 +269,73 @@ impl<'a, C: CircuitsParams> CircuitInputBuilder { Ok(()) } - fn set_begin_chunk(&mut self) { + // TODO re-design `gen_chunk_associated_steps` to separate RW + fn gen_chunk_associated_steps(&mut self, step: &mut ExecStep, rw: RW) { + let STEP_STATE_LEN = 10; let mut dummy_tx = Transaction::default(); let mut dummy_tx_ctx = TransactionContext::default(); - let tags = [ - StepStateField::CodeHash, - StepStateField::CallID, - StepStateField::IsRoot, - StepStateField::IsCreate, - StepStateField::ProgramCounter, - StepStateField::StackPointer, - StepStateField::GasLeft, - StepStateField::MemoryWordSize, - StepStateField::ReversibleWriteCounter, - StepStateField::LogID, - ]; - let rw_counters = (0..tags.len()) + + let rw_counters = (0..STEP_STATE_LEN) .map(|_| self.block_ctx.rwc.inc_pre()) .collect::>(); // just bump rwc in chunk_ctx as block_ctx rwc to assure same delta apply - let rw_counters_inner_chunk = (0..tags.len()) + let rw_counters_inner_chunk = (0..STEP_STATE_LEN) .map(|_| self.chunk_ctx.rwc.inc_pre()) .collect::>(); - let mut begin_chunk = self.block.block_steps.begin_chunk.clone(); + + let tags = { + let state = self.state_ref(&mut dummy_tx, &mut dummy_tx_ctx); + let last_call = state + .block + .txs + .last() + .map(|tx| tx.calls[0].clone()) + .unwrap_or_else(Call::default); + [ + (StepStateField::CodeHash, last_call.code_hash.to_word()), + (StepStateField::CallID, Word::from(last_call.call_id)), + (StepStateField::IsRoot, Word::from(last_call.is_root as u64)), + ( + StepStateField::IsCreate, + Word::from(last_call.is_create() as u64), + ), + (StepStateField::ProgramCounter, Word::from(step.pc)), + ( + StepStateField::StackPointer, + Word::from(step.stack_pointer()), + ), + (StepStateField::GasLeft, Word::from(step.gas_left)), + ( + StepStateField::MemoryWordSize, + Word::from(step.memory_word_size()), + ), + ( + StepStateField::ReversibleWriteCounter, + Word::from(step.reversible_write_counter), + ), + (StepStateField::LogID, Word::from(step.log_id)), + ] + }; + + debug_assert_eq!(STEP_STATE_LEN, tags.len()); let state = self.state_ref(&mut dummy_tx, &mut dummy_tx_ctx); tags.iter() .zip_eq(rw_counters) .zip_eq(rw_counters_inner_chunk) - .for_each(|((tag, rw_counter), inner_rw_counter)| { + .for_each(|(((tag, value), rw_counter), inner_rw_counter)| { push_op( &mut state.block.container, - &mut begin_chunk, + step, rw_counter, inner_rw_counter, - RW::READ, + rw, StepStateOp { field: tag.clone(), - value: Word::zero(), + value: *value, }, ); }); - - self.block.block_steps.begin_chunk = begin_chunk; } } @@ -325,18 +349,24 @@ impl CircuitInputBuilder { ) -> Result<&CircuitInputBuilder, Error> { // accumulates gas across all txs in the block self.begin_handle_block(eth_block, geth_traces)?; - self.set_end_block(self.circuits_params.max_rws); + self.set_end_chunk_or_block(self.circuits_params.max_rws); Ok(self) } - fn set_end_block(&mut self, max_rws: usize) { + fn set_end_chunk_or_block(&mut self, max_rws: usize) { + if !self.chunk_ctx.is_last_chunk() { + self.set_end_chunk(max_rws); + return; + } + + // set end block let mut end_block_not_last = self.block.block_steps.end_block_not_last.clone(); let mut end_block_last = self.block.block_steps.end_block_last.clone(); end_block_not_last.rwc = self.block_ctx.rwc; end_block_last.rwc = self.block_ctx.rwc; end_block_not_last.rwc_inner_chunk = self.chunk_ctx.rwc; end_block_last.rwc_inner_chunk = self.chunk_ctx.rwc; - let is_first_chunk = self.chunk_ctx.chunk_index == 0; + let is_first_chunk = self.chunk_ctx.is_first_chunk(); let mut dummy_tx = Transaction::default(); let mut dummy_tx_ctx = TransactionContext::default(); @@ -406,6 +436,71 @@ impl CircuitInputBuilder { // set final rwc value to chunkctx self.chunk_ctx.end_rwc = end_rwc; } + + fn set_end_chunk(&mut self, max_rws: usize) { + let mut end_chunk = self.block.block_steps.end_chunk.clone().unwrap(); + end_chunk.rwc = self.block_ctx.rwc; + end_chunk.rwc_inner_chunk = self.chunk_ctx.rwc; + let is_first_chunk = self.chunk_ctx.is_first_chunk(); + + let mut dummy_tx = Transaction::default(); + let mut dummy_tx_ctx = TransactionContext::default(); + self.gen_chunk_associated_steps(&mut end_chunk, RW::WRITE); + let state = self.state_ref(&mut dummy_tx, &mut dummy_tx_ctx); + + // rwc index start from 1 + let end_rwc = state.chunk_ctx.rwc.0; + let total_inner_rws = end_rwc - 1; + + // We need at least 1 extra row at offset 0 for chunk continuous + #[allow(clippy::int_plus_one)] + { + assert!( + total_inner_rws + 1 <= max_rws, + "total_inner_rws + 1 <= max_rws, total_inner_rws={}, max_rws={}", + total_inner_rws, + max_rws + ); + } + + if is_first_chunk { + push_op( + &mut state.block.container, + &mut end_chunk, + RWCounter(1), + RWCounter(1), + RW::READ, + StartOp {}, + ); + } + // TODO fix below to adapt multiple chunk + if max_rws - total_inner_rws > 1 { + let (padding_start, padding_end) = (total_inner_rws + 1, max_rws - 1); + push_op( + &mut state.block.container, + &mut end_chunk, + RWCounter(padding_start), + RWCounter(padding_start), + RW::READ, + PaddingOp {}, + ); + if padding_end != padding_start { + push_op( + &mut state.block.container, + &mut end_chunk, + RWCounter(padding_end), + RWCounter(padding_end), + RW::READ, + PaddingOp {}, + ); + } + } + + self.block.block_steps.end_chunk = Some(end_chunk); + + // set final rwc value to chunkctx + self.chunk_ctx.end_rwc = end_rwc; + } } fn push_op( @@ -428,7 +523,9 @@ impl CircuitInputBuilder { geth_traces: &[eth_types::GethExecTrace], ) -> Result<(), Error> { if self.chunk_ctx.chunk_index > 0 { - self.set_begin_chunk(); + let mut begin_chunk = self.block.block_steps.begin_chunk.clone(); + self.gen_chunk_associated_steps(&mut begin_chunk, RW::READ); + self.block.block_steps.begin_chunk = begin_chunk; } // accumulates gas across all txs in the block @@ -492,7 +589,10 @@ impl CircuitInputBuilder { let max_rws = total_rws_before_end_block + { 1 // +1 for reserving RW::Start at row 1 (offset 0) - + if total_rws_before_end_block > 0 { 1 /*end_block -> CallContextFieldTag::TxId lookup*/ } else { 0 } + + if self.chunk_ctx.is_last_chunk() && total_rws_before_end_block > 0 { 1 /*end_block -> CallContextFieldTag::TxId lookup*/ } else { 0 } + + if !self.chunk_ctx.is_last_chunk() { + 10 /* stepstate lookup */ + } else {0} }; // Computing the number of rows for the EVM circuit requires the size of ExecStep, // which is determined in the code of zkevm-circuits and cannot be imported here. @@ -524,7 +624,7 @@ impl CircuitInputBuilder { chunk_ctx: self.chunk_ctx, }; - cib.set_end_block(c_params.max_rws); + cib.set_end_chunk_or_block(c_params.max_rws); Ok(cib) } } diff --git a/bus-mapping/src/circuit_input_builder/chunk.rs b/bus-mapping/src/circuit_input_builder/chunk.rs index 857dafe367c..1c7963ba9ed 100644 --- a/bus-mapping/src/circuit_input_builder/chunk.rs +++ b/bus-mapping/src/circuit_input_builder/chunk.rs @@ -44,4 +44,14 @@ impl ChunkContext { end_rwc: 0, // end_rwc should be set in later phase } } + + /// is first chunk + pub fn is_first_chunk(&self) -> bool { + self.chunk_index == 0 + } + + /// is last chunk + pub fn is_last_chunk(&self) -> bool { + self.total_chunks - self.chunk_index - 1 == 0 + } } diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 1293f83696d..80058df7fd1 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -172,7 +172,7 @@ impl SubCircuitConfig for EvmCircuitConfig { let chunk_index = meta.query_advice(chunk_index, Rotation::cur()); let total_chunks = meta.query_advice(total_chunks, Rotation::cur()); - chunk_index + 1.expr() - total_chunks + total_chunks - chunk_index - 1.expr() }, chunk_diff, ); @@ -337,7 +337,7 @@ impl EvmCircuitConfig { &mut region, offset, Value::known(F::from( - (chunk_context.chunk_index + 1 - chunk_context.total_chunks) as u64, + (chunk_context.total_chunks - chunk_context.chunk_index - 1) as u64, )), )?; } @@ -463,7 +463,7 @@ impl SubCircuit for EvmCircuit { let (rw_rows_padding, _) = RwMap::table_assignments_padding( &block.rws.table_assignments(true), block.circuits_params.max_rws, - block.chunk_context.chunk_index == 0, + block.chunk_context.is_first_chunk(), ); let ( alpha_cell, @@ -485,7 +485,7 @@ impl SubCircuit for EvmCircuit { &block.rws.table_assignments(true), // align with state circuit to padding to same max_rws block.circuits_params.max_rws, - block.chunk_context.chunk_index == 0, + block.chunk_context.is_first_chunk(), )?; let permutation_cells = config.rw_permutation_config.assign( &mut region, diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index dfe6d26dd72..57437ef5acd 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -1059,7 +1059,7 @@ impl ExecutionConfig { .map(|tx| tx.calls()[0].clone()) .unwrap_or_else(Call::default); - let is_first_chunk = block.chunk_context.chunk_index == 0; + let is_first_chunk = block.chunk_context.is_first_chunk(); let is_last_chunk = block.chunk_context.chunk_index == block.chunk_context.total_chunks - 1; diff --git a/zkevm-circuits/src/evm_circuit/execution/end_block.rs b/zkevm-circuits/src/evm_circuit/execution/end_block.rs index de273a96d5d..0802c839368 100644 --- a/zkevm-circuits/src/evm_circuit/execution/end_block.rs +++ b/zkevm-circuits/src/evm_circuit/execution/end_block.rs @@ -3,32 +3,28 @@ use crate::{ execution::ExecutionGadget, step::ExecutionState, util::{ + common_gadget::RwTablePaddingGadget, constraint_builder::{ ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::Same, }, - math_gadget::{IsEqualGadget, IsZeroGadget, LtGadget}, + math_gadget::{IsEqualGadget, IsZeroGadget}, not, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, }, - table::{chunkctx_table::ChunkCtxFieldTag, CallContextFieldTag, TxContextFieldTag}, + table::{CallContextFieldTag, TxContextFieldTag}, util::{word::Word, Expr}, }; use eth_types::Field; -use gadgets::util::select; use halo2_proofs::{circuit::Value, plonk::Error}; -/// current SRS size < 2^30 so use 4 bytes (2^32) in LtGadet should be enough -const MAX_RW_BYTES: usize = u32::BITS as usize / 8; - #[derive(Clone, Debug)] pub(crate) struct EndBlockGadget { total_txs: Cell, total_txs_is_max_txs: IsEqualGadget, is_empty_rwc: IsZeroGadget, - max_rws: Cell, max_txs: Cell, - is_end_padding_exist: LtGadget, + rw_table_padding_gadget: RwTablePaddingGadget, } impl ExecutionGadget for EndBlockGadget { @@ -38,18 +34,11 @@ impl ExecutionGadget for EndBlockGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let max_txs = cb.query_copy_cell(); - let max_rws = cb.query_copy_cell(); let total_txs = cb.query_cell(); - let chunk_index = cb.query_cell(); let total_txs_is_max_txs = IsEqualGadget::construct(cb, total_txs.expr(), max_txs.expr()); - // Note that inner_rw_counter starts at 1 let is_empty_rwc = IsZeroGadget::construct(cb, cb.curr.state.rw_counter.clone().expr() - 1.expr()); - // lookup to get chunk index - cb.chunk_context_lookup(ChunkCtxFieldTag::CurrentChunkIndex, chunk_index.expr()); - let is_first_chunk = IsZeroGadget::construct(cb, chunk_index.expr()); - // 1. Constraint total_rws and total_txs witness values depending on the empty // block case. cb.condition(is_empty_rwc.expr(), |cb| { @@ -85,32 +74,16 @@ impl ExecutionGadget for EndBlockGadget { // meaningful txs in the tx_table is total_tx. }); - // TODO fix below checking logic let total_inner_rws_before_padding = cb.curr.state.inner_rw_counter.clone().expr() - 1.expr() // start from 1 - + select::expr( // CallContext lookup to check total_txs - is_empty_rwc.expr(), - 0.expr(), - 1.expr(), - ); + + cb.rw_counter_offset(); // 3. Verify rw_counter counts to the same number of meaningful rows in // rw_table to ensure there is no malicious insertion. // Verify that there are at most total_rws meaningful entries in the rw_table // - startop only exist in first chunk - cb.condition(is_first_chunk.expr(), |cb| { - cb.rw_table_start_lookup(1.expr()); - }); - // TODO Fix below for multiple chunk logic - let is_end_padding_exist = LtGadget::<_, MAX_RW_BYTES>::construct( - cb, - 1.expr(), - max_rws.expr() - total_inner_rws_before_padding.expr(), - ); - cb.condition(is_end_padding_exist.expr(), |cb| { - cb.rw_table_padding_lookup(total_inner_rws_before_padding.expr() + 1.expr()); - cb.rw_table_padding_lookup(max_rws.expr() - 1.expr()); - }); + let rw_table_padding_gadget = + RwTablePaddingGadget::construct(cb, total_inner_rws_before_padding); // Since every lookup done in the EVM circuit must succeed and uses // a unique rw_counter, we know that at least there are // total_rws meaningful entries in the rw_table. @@ -132,11 +105,10 @@ impl ExecutionGadget for EndBlockGadget { Self { max_txs, - max_rws, total_txs, total_txs_is_max_txs, is_empty_rwc, - is_end_padding_exist, + rw_table_padding_gadget, } } @@ -152,16 +124,15 @@ impl ExecutionGadget for EndBlockGadget { let total_rwc = u64::from(step.rwc) - 1; self.is_empty_rwc .assign(region, offset, F::from(total_rwc))?; - let max_rws = F::from(block.circuits_params.max_rws as u64); - let max_rws_assigned = self.max_rws.assign(region, offset, Value::known(max_rws))?; - self.is_end_padding_exist.assign( + let inner_rws_before_padding = + step.rwc_inner_chunk.0 as u64 - 1 + if total_rwc > 0 { 1 } else { 0 }; + self.rw_table_padding_gadget.assign_exec_step( region, offset, - F::ZERO, - max_rws.sub(F::from( - step.rwc_inner_chunk.0 as u64 - 1 + 1 + if total_rwc > 0 { 1 } else { 0 }, - )), + block, + inner_rws_before_padding, + step, )?; let total_txs = F::from(block.txs.len() as u64); @@ -175,7 +146,6 @@ impl ExecutionGadget for EndBlockGadget { // last row (at a fixed offset), where we need to access the max_rws and max_txs // constant. if step.rw_indices_len() != 0 { - region.constrain_constant(max_rws_assigned, max_rws)?; region.constrain_constant(max_txs_assigned, max_txs)?; } Ok(()) diff --git a/zkevm-circuits/src/evm_circuit/execution/end_chunk.rs b/zkevm-circuits/src/evm_circuit/execution/end_chunk.rs index 9669e27b065..0fbbb237173 100644 --- a/zkevm-circuits/src/evm_circuit/execution/end_chunk.rs +++ b/zkevm-circuits/src/evm_circuit/execution/end_chunk.rs @@ -4,6 +4,7 @@ use crate::{ evm_circuit::{ step::ExecutionState, util::{ + common_gadget::RwTablePaddingGadget, constraint_builder::{EVMConstraintBuilder, StepStateTransition}, CachedRegion, }, @@ -19,6 +20,7 @@ use super::ExecutionGadget; #[derive(Clone, Debug)] pub(crate) struct EndChunkGadget { _marker: PhantomData, + rw_table_padding_gadget: RwTablePaddingGadget, } impl ExecutionGadget for EndChunkGadget { @@ -29,30 +31,40 @@ impl ExecutionGadget for EndChunkGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { // State transition cb.not_step_last(|cb| { - // Propagate all the way down. + // Propagate all the way down. cb.require_step_state_transition(StepStateTransition::same()); }); - cb.step_last(|cb| { - cb.step_state_lookup(1.expr()); - }); + // step state write to rw_table + cb.step_state_lookup(1.expr()); - // TODO constraint Rw::Padding are append consecutively to avoid malicious insert + let rw_table_padding_gadget = RwTablePaddingGadget::construct( + cb, + cb.curr.state.inner_rw_counter.clone().expr() - 1.expr() + cb.rw_counter_offset(), /* start from 1 */ + ); Self { + rw_table_padding_gadget, _marker: PhantomData {}, } } fn assign_exec_step( &self, - _region: &mut CachedRegion<'_, '_, F>, - _offset: usize, - _block: &Block, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + block: &Block, _: &Transaction, _: &Call, - _step: &ExecStep, + step: &ExecStep, ) -> Result<(), Error> { + self.rw_table_padding_gadget.assign_exec_step( + region, + offset, + block, + (step.rwc_inner_chunk.0 - 1 + step.bus_mapping_instance.len()) as u64, + step, + )?; Ok(()) } } @@ -63,18 +75,27 @@ mod test { use eth_types::bytecode; use mock::TestContext; - fn test_ok(bytecode: bytecode::Bytecode) { - CircuitTestBuilder::new_from_test_ctx( - TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), - ) - .run() - } + // fn test_ok(bytecode: bytecode::Bytecode) { + // CircuitTestBuilder::new_from_test_ctx( + // TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), + // ) + // .run() + // } #[test] + #[ignore] // still under development and testing fn end_chunk_test() { let bytecode = bytecode! { STOP }; - test_ok(bytecode); + CircuitTestBuilder::new_from_test_ctx( + TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), + ) + .block_modifier(Box::new(move |block| { + block.circuits_params.max_evm_rows = 0; // auto padding + block.chunk_context.chunk_index = 3; + block.chunk_context.total_chunks = 10; + })) + .run(); } } diff --git a/zkevm-circuits/src/evm_circuit/param.rs b/zkevm-circuits/src/evm_circuit/param.rs index 5924bc32748..0ebe0a9a538 100644 --- a/zkevm-circuits/src/evm_circuit/param.rs +++ b/zkevm-circuits/src/evm_circuit/param.rs @@ -8,7 +8,7 @@ use halo2_proofs::{ use std::collections::HashMap; // Step dimension -pub(crate) const STEP_WIDTH: usize = 128; +pub(crate) const STEP_WIDTH: usize = 131; /// Step height pub const MAX_STEP_HEIGHT: usize = 19; /// The height of the state of a step, used by gates that connect two @@ -64,7 +64,7 @@ pub const FIXED_TABLE_LOOKUPS: usize = 8; pub const TX_TABLE_LOOKUPS: usize = 4; /// Rw Table lookups done in EVMCircuit -pub const RW_TABLE_LOOKUPS: usize = 8; +pub const RW_TABLE_LOOKUPS: usize = 13; /// Bytecode Table lookups done in EVMCircuit pub const BYTECODE_TABLE_LOOKUPS: usize = 4; diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs index 9ea5047ef60..4426ca8c10f 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs @@ -20,7 +20,7 @@ use crate::{ not, or, Cell, }, }, - table::{AccountFieldTag, CallContextFieldTag}, + table::{chunkctx_table::ChunkCtxFieldTag, AccountFieldTag, CallContextFieldTag}, util::{ word::{Word, Word32, Word32Cell, WordCell, WordExpr}, Expr, @@ -1245,3 +1245,105 @@ impl WordByteRangeGadget { self.not_overflow.expr() } } + +/// current SRS size < 2^30 so use 4 bytes (2^32) in LtGadet should be enough +const MAX_RW_BYTES: usize = u32::BITS as usize / 8; + +/// Check consecutive Rw::Padding in rw_table to assure rw_table no malicious insertion +#[derive(Clone, Debug)] +pub(crate) struct RwTablePaddingGadget { + is_empty_rwc: IsZeroGadget, + max_rws: Cell, + chunk_index: Cell, + is_end_padding_exist: LtGadget, + is_first_chunk: IsZeroGadget, +} + +impl RwTablePaddingGadget { + pub(crate) fn construct( + cb: &mut EVMConstraintBuilder, + inner_rws_before_padding: Expression, + ) -> Self { + let max_rws = cb.query_copy_cell(); + let chunk_index = cb.query_cell(); + let is_empty_rwc = + IsZeroGadget::construct(cb, cb.curr.state.rw_counter.clone().expr() - 1.expr()); + + cb.chunk_context_lookup(ChunkCtxFieldTag::CurrentChunkIndex, chunk_index.expr()); + let is_first_chunk = IsZeroGadget::construct(cb, chunk_index.expr()); + + // Verify rw_counter counts to the same number of meaningful rows in + // rw_table to ensure there is no malicious insertion. + // Verify that there are at most total_rws meaningful entries in the rw_table + // - startop only exist in first chunk + // - end paddings are consecutively + cb.condition(is_first_chunk.expr(), |cb| { + cb.rw_table_start_lookup(1.expr()); + }); + + let is_end_padding_exist = LtGadget::<_, MAX_RW_BYTES>::construct( + cb, + 1.expr(), + max_rws.expr() - inner_rws_before_padding.expr(), + ); + cb.condition(is_end_padding_exist.expr(), |cb| { + cb.rw_table_padding_lookup(inner_rws_before_padding.expr() + 1.expr()); + cb.rw_table_padding_lookup(max_rws.expr() - 1.expr()); + }); + // Since every lookup done in the EVM circuit must succeed and uses + // a unique rw_counter, we know that at least there are + // total_rws meaningful entries in the rw_table. + // We conclude that the number of meaningful entries in the rw_table + // is total_rws. + + Self { + is_empty_rwc, + max_rws, + chunk_index, + is_first_chunk, + is_end_padding_exist, + } + } + + pub(crate) fn assign_exec_step( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + block: &Block, + inner_rws_before_padding: u64, + step: &ExecStep, + ) -> Result<(), Error> { + let total_rwc = u64::from(step.rwc) - 1; + self.is_empty_rwc + .assign(region, offset, F::from(total_rwc))?; + let max_rws = F::from(block.circuits_params.max_rws as u64); + let max_rws_assigned = self.max_rws.assign(region, offset, Value::known(max_rws))?; + + self.chunk_index.assign( + region, + offset, + Value::known(F::from(block.chunk_context.chunk_index as u64)), + )?; + + self.is_first_chunk.assign( + region, + offset, + F::from(block.chunk_context.chunk_index as u64), + )?; + + self.is_end_padding_exist.assign( + region, + offset, + F::ONE, + max_rws.sub(F::from(inner_rws_before_padding)), + )?; + + // When rw_indices is not empty, means current step is non-padding step, we're at the + // last row (at a fixed offset), where we need to access the max_rws + // constant. + if step.rw_indices_len() != 0 { + region.constrain_constant(max_rws_assigned, max_rws)?; + } + Ok(()) + } +} diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 5cd9d8a6e7e..0b8a82e29d6 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -293,7 +293,6 @@ enum ConstraintLocation { Step, StepFirst, NotStepLast, - StepLast, } /// Collection of constraints grouped by which selectors will enable them @@ -1593,11 +1592,6 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { self.constraint_at_location(ConstraintLocation::NotStepLast, constraint) } - /// TODO: Doc - pub(crate) fn step_last(&mut self, constraint: impl FnOnce(&mut Self) -> R) -> R { - self.constraint_at_location(ConstraintLocation::StepLast, constraint) - } - /// TODO: Doc fn push_constraint(&mut self, name: &'static str, constraint: Expression) { match self.constraints_location { @@ -1606,7 +1600,6 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { ConstraintLocation::NotStepLast => { self.constraints.not_step_last.push((name, constraint)) } - ConstraintLocation::StepLast => self.constraints.step_last.push((name, constraint)), } } diff --git a/zkevm-circuits/src/util/cell_placement_strategy.rs b/zkevm-circuits/src/util/cell_placement_strategy.rs index 88e0ff05113..6be8b94a267 100644 --- a/zkevm-circuits/src/util/cell_placement_strategy.rs +++ b/zkevm-circuits/src/util/cell_placement_strategy.rs @@ -123,6 +123,7 @@ impl CellPlacementStrategy for CMFixedWidthStrategy { cell_type: CellType, ) -> CellPlacement { let (mut column_idx, mut row) = self.get_next(&cell_type); + if self.perm_substitution && cell_type == CellType::StoragePhase1 { let (_, row_perm) = self.get_next(&CellType::StoragePermutation); if row_perm < row { diff --git a/zkevm-circuits/src/witness/block.rs b/zkevm-circuits/src/witness/block.rs index cbe0c015200..70d340a82ab 100644 --- a/zkevm-circuits/src/witness/block.rs +++ b/zkevm-circuits/src/witness/block.rs @@ -307,12 +307,12 @@ pub fn block_convert( let (rws_rows, _) = RwMap::table_assignments_padding( &block.rws.table_assignments(false), block.circuits_params.max_rws, - block.chunk_context.chunk_index == 0, + block.chunk_context.is_first_chunk(), ); let (chronological_rws_rows, _) = RwMap::table_assignments_padding( &block.rws.table_assignments(true), block.circuits_params.max_rws, - block.chunk_context.chunk_index == 0, + block.chunk_context.is_first_chunk(), ); block.permu_rwtable_next_continuous_fingerprint = unwrap_value( get_permutation_fingerprints(