diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 70ac7a1f55..81f587a0bb 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -147,9 +147,10 @@ pub mod test { witness::{Block, BlockContext, Bytecode, RwMap, Transaction}, EvmCircuit, }, - rw_table::RwTable, + rw_table::RwTableRlc, util::Expr, }; + use bus_mapping::evm::OpcodeId; use eth_types::{Field, Word}; use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner}, @@ -187,7 +188,7 @@ pub mod test { #[derive(Clone)] pub struct TestCircuitConfig { tx_table: [Column; 4], - rw_table: RwTable, + rw_table: RwTableRlc, bytecode_table: [Column; 5], block_table: [Column; 3], evm_circuit: EvmCircuit, @@ -241,30 +242,11 @@ pub mod test { layouter.assign_region( || "rw table", |mut region| { - let mut offset = 0; + rws.check_rw_counter_sanity(); + // TODO: fix this after cs.challenge() is implemented in halo2 + let randomness_phase_next = randomness; self.rw_table - .assign(&mut region, offset, &Default::default())?; - offset += 1; - - let mut rows = rws - .0 - .values() - .flat_map(|rws| rws.iter()) - .collect::>(); - - rows.sort_by_key(|a| a.rw_counter()); - let mut expected_rw_counter = 1; - for rw in rows { - assert!(rw.rw_counter() == expected_rw_counter); - expected_rw_counter += 1; - - self.rw_table.assign( - &mut region, - offset, - &rw.table_assignment(randomness), - )?; - offset += 1; - } + .assign(&mut region, randomness, rws, randomness_phase_next)?; Ok(()) }, ) @@ -352,13 +334,62 @@ pub mod test { fixed_table_tags: Vec, } - impl TestCircuit { + impl TestCircuit { pub fn new(block: Block, fixed_table_tags: Vec) -> Self { + let mut fixed_table_tags = fixed_table_tags; + if fixed_table_tags.is_empty() { + // create fixed_table_tags by trace + let need_bitwise_lookup = block.txs.iter().any(|tx| { + tx.steps.iter().any(|step| { + matches!( + step.opcode, + Some(OpcodeId::AND) | Some(OpcodeId::OR) | Some(OpcodeId::XOR) + ) + }) + }); + fixed_table_tags = FixedTableTag::iter() + .filter(|t| { + !matches!( + t, + FixedTableTag::BitwiseAnd + | FixedTableTag::BitwiseOr + | FixedTableTag::BitwiseXor + ) || need_bitwise_lookup + }) + .collect(); + } Self { block, fixed_table_tags, } } + + pub fn estimate_k(&self) -> u32 { + let log2_ceil = |n| u32::BITS - (n as u32).leading_zeros() - (n & (n - 1) == 0) as u32; + + let k = log2_ceil( + 64 + self + .fixed_table_tags + .iter() + .map(|tag| tag.build::().count()) + .sum::(), + ); + + let num_rows_required_for_steps = TestCircuit::get_num_rows_required(&self.block); + let k = k.max(log2_ceil(64 + num_rows_required_for_steps)); + + let k = k.max(log2_ceil( + 64 + self + .block + .bytecodes + .iter() + .map(|bytecode| bytecode.bytes.len()) + .sum::(), + )); + + log::debug!("evm circuit uses k = {}", k); + k + } } impl Circuit for TestCircuit { @@ -371,7 +402,7 @@ pub mod test { fn configure(meta: &mut ConstraintSystem) -> Self::Config { let tx_table = [(); 4].map(|_| meta.advice_column()); - let rw_table = RwTable::construct(meta); + let rw_table = RwTableRlc::construct(meta); let bytecode_table = [(); 5].map(|_| meta.advice_column()); let block_table = [(); 3].map(|_| meta.advice_column()); @@ -446,31 +477,12 @@ pub mod test { block: Block, fixed_table_tags: Vec, ) -> Result<(), Vec> { - let log2_ceil = |n| u32::BITS - (n as u32).leading_zeros() - (n & (n - 1) == 0) as u32; - - let num_rows_required_for_steps = TestCircuit::get_num_rows_required(&block); - - let k = log2_ceil( - 64 + fixed_table_tags - .iter() - .map(|tag| tag.build::().count()) - .sum::(), - ); - let k = k.max(log2_ceil( - 64 + block - .bytecodes - .iter() - .map(|bytecode| bytecode.bytes.len()) - .sum::(), - )); - let k = k.max(log2_ceil(64 + num_rows_required_for_steps)); - log::debug!("evm circuit uses k = {}", k); - + let (active_gate_rows, active_lookup_rows) = TestCircuit::get_active_rows(&block); + let circuit = TestCircuit::::new(block.clone(), fixed_table_tags); + let k = circuit.estimate_k(); let power_of_randomness = (1..32) .map(|exp| vec![block.randomness.pow(&[exp, 0, 0, 0]); (1 << k) - 64]) .collect(); - let (active_gate_rows, active_lookup_rows) = TestCircuit::get_active_rows(&block); - let circuit = TestCircuit::::new(block, fixed_table_tags); let prover = MockProver::::run(k, &circuit, power_of_randomness).unwrap(); prover.verify_at_rows(active_gate_rows.into_iter(), active_lookup_rows.into_iter()) } diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index cc94351f49..82bd81c875 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -1,4 +1,5 @@ use crate::{evm_circuit::step::ExecutionState, impl_expr}; +use eth_types::Field; use halo2_proofs::{ arithmetic::FieldExt, plonk::{Advice, Column, Expression, Fixed, VirtualCells}, @@ -158,6 +159,12 @@ pub enum RwTableTag { TxReceipt, } +impl Default for RwTableTag { + fn default() -> Self { + RwTableTag::Start + } +} + impl RwTableTag { pub fn is_reversible(self) -> bool { return matches!( @@ -251,6 +258,43 @@ pub(crate) enum Table { Byte, } +#[derive(Clone, Debug)] +pub struct RwValues { + pub id: Expression, + pub address: Expression, + pub field_tag: Expression, + pub storage_key: Expression, + pub value: Expression, + pub value_prev: Expression, + pub aux1: Expression, + pub aux2: Expression, +} + +impl RwValues { + #[allow(clippy::too_many_arguments)] + pub fn new( + id: Expression, + address: Expression, + field_tag: Expression, + storage_key: Expression, + value: Expression, + value_prev: Expression, + aux1: Expression, + aux2: Expression, + ) -> Self { + Self { + id, + address, + field_tag, + storage_key, + value, + value_prev, + aux1, + aux2, + } + } +} + #[derive(Clone, Debug)] pub(crate) enum Lookup { /// Lookup to fixed table, which contains serveral pre-built tables such as @@ -285,7 +329,7 @@ pub(crate) enum Lookup { /// all tags. tag: Expression, /// Values corresponding to the tag. - values: [Expression; 8], + values: RwValues, }, /// Lookup to bytecode table, which contains all used creation code and /// contract code. @@ -322,7 +366,7 @@ pub(crate) enum Lookup { Conditional(Expression, Box>), } -impl Lookup { +impl Lookup { pub(crate) fn conditional(self, condition: Expression) -> Self { Self::Conditional(condition, self.into()) } @@ -353,11 +397,21 @@ impl Lookup { is_write, tag, values, - } => [ - vec![counter.clone(), is_write.clone(), tag.clone()], - values.to_vec(), - ] - .concat(), + } => { + vec![ + counter.clone(), + is_write.clone(), + tag.clone(), + values.id.clone(), + values.address.clone(), + values.field_tag.clone(), + values.storage_key.clone(), + values.value.clone(), + values.value_prev.clone(), + values.aux1.clone(), + values.aux2.clone(), + ] + } Self::Bytecode { hash, tag, diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 32943e5cd3..cd29ca9dcd 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -4,12 +4,13 @@ use crate::{ step::{ExecutionState, Step}, table::{ AccountFieldTag, BytecodeFieldTag, CallContextFieldTag, FixedTableTag, Lookup, - RwTableTag, Table, TxContextFieldTag, TxLogFieldTag, TxReceiptFieldTag, + RwTableTag, RwValues, Table, TxContextFieldTag, TxLogFieldTag, TxReceiptFieldTag, }, util::{Cell, RandomLinearCombination, Word}, }, util::Expr, }; +use eth_types::Field; use halo2_proofs::{ arithmetic::FieldExt, plonk::{ @@ -256,7 +257,7 @@ pub(crate) struct ConstraintBuilder<'a, F> { stored_expressions: Vec>, } -impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { +impl<'a, F: Field> ConstraintBuilder<'a, F> { pub(crate) fn new( curr: Step, next: Step, @@ -623,7 +624,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { counter: Expression, is_write: Expression, tag: RwTableTag, - values: [Expression; 8], + values: RwValues, ) { self.add_lookup( name, @@ -643,7 +644,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { name: &'static str, is_write: Expression, tag: RwTableTag, - values: [Expression; 8], + values: RwValues, ) { self.rw_lookup_with_counter( name, @@ -671,7 +672,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { &mut self, name: &'static str, tag: RwTableTag, - mut values: [Expression; 8], + values: RwValues, reversion_info: Option<&mut ReversionInfo>, ) { debug_assert!( @@ -684,15 +685,16 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { if let Some(reversion_info) = reversion_info { // Revert if is_persistent is 0 self.condition(1.expr() - reversion_info.is_persistent(), |cb| { - // Swap value and value_prev - values.swap(4, 5); - cb.rw_lookup_with_counter( name, reversion_info.rw_counter_of_reversion(), true.expr(), tag, - values, + RwValues { + value_prev: values.value, + value: values.value_prev, + ..values + }, ) }); } @@ -711,7 +713,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { self.reversible_write( "TxAccessListAccount write", RwTableTag::TxAccessListAccount, - [ + RwValues::new( tx_id, account_address, 0.expr(), @@ -720,7 +722,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { value_prev, 0.expr(), 0.expr(), - ], + ), reversion_info, ); } @@ -737,7 +739,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { self.reversible_write( "TxAccessListAccountStorage write", RwTableTag::TxAccessListAccountStorage, - [ + RwValues::new( tx_id, account_address, 0.expr(), @@ -746,7 +748,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { value_prev, 0.expr(), 0.expr(), - ], + ), reversion_info, ); } @@ -758,7 +760,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { "TxRefund read", false.expr(), RwTableTag::TxRefund, - [ + RwValues::new( tx_id, 0.expr(), 0.expr(), @@ -767,7 +769,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { value, 0.expr(), 0.expr(), - ], + ), ); } @@ -781,7 +783,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { self.reversible_write( "TxRefund write", RwTableTag::TxRefund, - [ + RwValues::new( tx_id, 0.expr(), 0.expr(), @@ -790,7 +792,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { value_prev, 0.expr(), 0.expr(), - ], + ), reversion_info, ); } @@ -807,7 +809,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { "Account read", false.expr(), RwTableTag::Account, - [ + RwValues::new( 0.expr(), account_address, field_tag.expr(), @@ -816,7 +818,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { value, 0.expr(), 0.expr(), - ], + ), ); } @@ -831,7 +833,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { self.reversible_write( "Account write with reversion", RwTableTag::Account, - [ + RwValues::new( 0.expr(), account_address, field_tag.expr(), @@ -840,7 +842,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { value_prev, 0.expr(), 0.expr(), - ], + ), reversion_info, ); } @@ -859,7 +861,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { "account_storage_read", false.expr(), RwTableTag::AccountStorage, - [ + RwValues::new( tx_id, account_address, 0.expr(), @@ -868,7 +870,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { value, 0.expr(), committed_value, - ], + ), ); } @@ -886,7 +888,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { self.reversible_write( "AccountStorage write", RwTableTag::AccountStorage, - [ + RwValues::new( tx_id, account_address, 0.expr(), @@ -895,7 +897,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { value_prev, 0.expr(), committed_value, - ], + ), reversion_info, ); } @@ -923,7 +925,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { "CallContext lookup", is_write, RwTableTag::CallContext, - [ + RwValues::new( call_id.unwrap_or_else(|| self.curr.state.call_id.expr()), 0.expr(), field_tag.expr(), @@ -932,7 +934,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { 0.expr(), 0.expr(), 0.expr(), - ], + ), ); } @@ -975,7 +977,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { "Stack lookup", is_write, RwTableTag::Stack, - [ + RwValues::new( self.curr.state.call_id.expr(), self.curr.state.stack_pointer.expr() + stack_pointer_offset, 0.expr(), @@ -984,7 +986,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { 0.expr(), 0.expr(), 0.expr(), - ], + ), ); } @@ -1001,7 +1003,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { "Memory lookup", is_write, RwTableTag::Memory, - [ + RwValues::new( call_id.unwrap_or_else(|| self.curr.state.call_id.expr()), memory_address, 0.expr(), @@ -1010,7 +1012,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { 0.expr(), 0.expr(), 0.expr(), - ], + ), ); } @@ -1026,7 +1028,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { rw_counter, is_write, RwTableTag::Memory, - [ + RwValues::new( self.curr.state.call_id.expr(), memory_address, 0.expr(), @@ -1035,7 +1037,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { 0.expr(), 0.expr(), 0.expr(), - ], + ), ); } @@ -1051,7 +1053,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { "log data lookup", 1.expr(), RwTableTag::TxLog, - [ + RwValues::new( tx_id, index + (1u64 << 8).expr() * log_id, tag.expr(), @@ -1060,7 +1062,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { 0.expr(), 0.expr(), 0.expr(), - ], + ), ); } @@ -1086,7 +1088,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { "tx receipt lookup", 0.expr(), RwTableTag::TxReceipt, - [ + RwValues::new( tx_id, 0.expr(), tag.expr(), @@ -1095,7 +1097,7 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { 0.expr(), 0.expr(), 0.expr(), - ], + ), ); } diff --git a/zkevm-circuits/src/evm_circuit/witness.rs b/zkevm-circuits/src/evm_circuit/witness.rs index 4d8b28ae5e..a548525ccb 100644 --- a/zkevm-circuits/src/evm_circuit/witness.rs +++ b/zkevm-circuits/src/evm_circuit/witness.rs @@ -22,7 +22,11 @@ use halo2_proofs::arithmetic::{BaseExt, FieldExt}; use halo2_proofs::pairing::bn256::Fr; use itertools::Itertools; use sha3::{Digest, Keccak256}; -use std::{collections::HashMap, convert::TryInto, iter}; +use std::{ + collections::HashMap, + convert::TryInto, + iter::{self}, +}; #[derive(Debug, Default, Clone)] pub struct Block { @@ -427,6 +431,39 @@ impl RwMap { }); sorted } + + // check rw_counter is continous and starting from 1 + pub fn check_rw_counter_sanity(&self) { + for (idx, rw_counter) in self + .0 + .values() + .flatten() + .map(|r| r.rw_counter()) + .sorted() + .enumerate() + { + debug_assert_eq!(idx, rw_counter - 1); + } + } + + pub fn table_assignments(&self, _randomness: F) -> Vec + where + F: Field, + { + let mut rows: Vec = self.0.values().flatten().cloned().collect(); + + rows.sort_by_key(|row| { + ( + row.tag() as u64, + row.field_tag().unwrap_or_default(), + row.id().unwrap_or_default(), + row.address().unwrap_or_default(), + row.storage_key().unwrap_or_default(), + row.rw_counter(), + ) + }); + rows + } } #[derive(Clone, Copy, Debug)] @@ -527,39 +564,44 @@ pub enum Rw { value: u64, }, } -#[derive(Default, Clone, Copy)] -pub struct RwRow { - pub rw_counter: F, - pub is_write: F, - pub tag: F, - pub key1: F, - pub key2: F, - pub key3: F, - pub key4: F, +#[derive(Default, Clone, Copy, Debug)] +pub struct RwRow { + pub rw_counter: u64, + pub is_write: bool, + pub tag: RwTableTag, + pub id: u64, + pub address: Address, + pub field_tag: u64, + pub storage_key: U256, pub value: F, pub value_prev: F, pub aux1: F, pub aux2: F, } - -impl From<[F; 11]> for RwRow { - fn from(row: [F; 11]) -> Self { - Self { - rw_counter: row[0], - is_write: row[1], - tag: row[2], - key1: row[3], - key2: row[4], - key3: row[5], - key4: row[6], - value: row[7], - value_prev: row[8], - aux1: row[9], - aux2: row[10], - } +impl RwRow { + pub fn rlc(&self, randomness: F, randomness_next_phase: F) -> F { + let values = [ + F::from(self.rw_counter), + F::from(self.is_write), + (F::from(self.tag as u64)), + (F::from(self.id)), + (self.address.to_scalar().unwrap()), + (F::from(self.field_tag)), + (RandomLinearCombination::random_linear_combine( + self.storage_key.to_le_bytes(), + randomness, + )), + (self.value), + (self.value_prev), + (self.aux1), + (self.aux2), + ]; + values + .iter() + .rev() + .fold(F::zero(), |acc, value| acc * randomness_next_phase + value) } } - impl Rw { pub fn tx_access_list_value_pair(&self) -> (bool, bool) { match self { @@ -656,16 +698,13 @@ impl Rw { pub fn table_assignment(&self, randomness: F) -> RwRow { RwRow { - rw_counter: F::from(self.rw_counter() as u64), - is_write: F::from(self.is_write() as u64), - tag: F::from(self.tag() as u64), - key1: F::from(self.id().unwrap_or_default() as u64), - key2: self.address().unwrap_or_default().to_scalar().unwrap(), - key3: F::from(self.field_tag().unwrap_or_default() as u64), - key4: RandomLinearCombination::random_linear_combine( - self.storage_key().unwrap_or_default().to_le_bytes(), - randomness, - ), + rw_counter: self.rw_counter() as u64, + is_write: self.is_write(), + tag: self.tag(), + id: self.id().unwrap_or_default() as u64, + address: self.address().unwrap_or_default(), + field_tag: self.field_tag().unwrap_or_default() as u64, + storage_key: self.storage_key().unwrap_or_default(), value: self.value_assignment(randomness), value_prev: self.value_prev_assignment(randomness).unwrap_or_default(), aux1: F::zero(), // only used for AccountStorage::tx_id, which moved to key1. diff --git a/zkevm-circuits/src/rw_table.rs b/zkevm-circuits/src/rw_table.rs index c08c05dce3..a4300fedb8 100644 --- a/zkevm-circuits/src/rw_table.rs +++ b/zkevm-circuits/src/rw_table.rs @@ -1,76 +1,155 @@ #![allow(missing_docs)] +use eth_types::{Field, ToLittleEndian, ToScalar}; use halo2_proofs::{ - arithmetic::FieldExt, circuit::Region, plonk::{Advice, Column, ConstraintSystem, Error, Expression, VirtualCells}, poly::Rotation, }; -use crate::evm_circuit::{table::LookupTable, witness::RwRow}; +use crate::evm_circuit::util::rlc; +use crate::evm_circuit::{ + table::LookupTable, + util::RandomLinearCombination, + witness::{RwMap, RwRow}, +}; + +#[derive(Clone, Copy)] +pub struct RwTableRlc { + // TODO: change to instance col? + pub rlc: Column, +} + +impl LookupTable for RwTableRlc { + fn table_exprs(&self, meta: &mut VirtualCells) -> Vec> { + vec![meta.query_advice(self.rlc, Rotation::cur())] + } +} +impl RwTableRlc { + pub fn construct(meta: &mut ConstraintSystem) -> Self { + Self { + rlc: meta.advice_column(), + } + } + pub fn assign( + &self, + region: &mut Region<'_, F>, + randomness: F, + rw_map: &RwMap, + randomness_next_phase: F, + ) -> Result<(), Error> { + for (offset, row) in rw_map.table_assignments(randomness).iter().enumerate() { + let value = row + .table_assignment(randomness) + .rlc(randomness, randomness_next_phase); + region.assign_advice( + || "assign rw row on rw table", + self.rlc, + offset, + || Ok(value), + )?; + } + Ok(()) + } +} /// The rw table shared between evm circuit and state circuit #[derive(Clone, Copy)] pub struct RwTable { + pub rlc: Column, pub rw_counter: Column, pub is_write: Column, pub tag: Column, - pub key1: Column, - pub key2: Column, - pub key3: Column, - pub key4: Column, + pub id: Column, + pub address: Column, + pub field_tag: Column, + pub storage_key: Column, pub value: Column, pub value_prev: Column, pub aux1: Column, pub aux2: Column, } -impl LookupTable for RwTable { - fn table_exprs(&self, meta: &mut VirtualCells) -> Vec> { - vec![ - meta.query_advice(self.rw_counter, Rotation::cur()), - meta.query_advice(self.is_write, Rotation::cur()), - meta.query_advice(self.tag, Rotation::cur()), - meta.query_advice(self.key1, Rotation::cur()), - meta.query_advice(self.key2, Rotation::cur()), - meta.query_advice(self.key3, Rotation::cur()), - meta.query_advice(self.key4, Rotation::cur()), - meta.query_advice(self.value, Rotation::cur()), - meta.query_advice(self.value_prev, Rotation::cur()), - meta.query_advice(self.aux1, Rotation::cur()), - meta.query_advice(self.aux2, Rotation::cur()), - ] - } -} impl RwTable { - pub fn construct(meta: &mut ConstraintSystem) -> Self { + pub fn construct(meta: &mut ConstraintSystem) -> Self { Self { + rlc: meta.advice_column(), rw_counter: meta.advice_column(), is_write: meta.advice_column(), tag: meta.advice_column(), - key1: meta.advice_column(), - key2: meta.advice_column(), - key3: meta.advice_column(), - key4: meta.advice_column(), + id: meta.advice_column(), + address: meta.advice_column(), + field_tag: meta.advice_column(), + storage_key: meta.advice_column(), value: meta.advice_column(), value_prev: meta.advice_column(), aux1: meta.advice_column(), aux2: meta.advice_column(), } } - pub fn assign( + + fn rlc_expr( + &self, + meta: &mut VirtualCells, + power_of_randomness: &[Expression], + ) -> Expression { + rlc::expr( + &[ + meta.query_advice(self.rw_counter, Rotation::cur()), + meta.query_advice(self.is_write, Rotation::cur()), + meta.query_advice(self.tag, Rotation::cur()), + meta.query_advice(self.id, Rotation::cur()), + meta.query_advice(self.address, Rotation::cur()), + meta.query_advice(self.field_tag, Rotation::cur()), + meta.query_advice(self.storage_key, Rotation::cur()), + meta.query_advice(self.value, Rotation::cur()), + meta.query_advice(self.value_prev, Rotation::cur()), + meta.query_advice(self.aux1, Rotation::cur()), + meta.query_advice(self.aux2, Rotation::cur()), + ], + power_of_randomness, + ) + } + pub fn assign( + &self, + region: &mut Region<'_, F>, + randomness: F, + rows: &[RwRow], + ) -> Result<(), Error> { + for (offset, row) in rows.iter().enumerate() { + self.assign_row(region, offset, randomness, row)?; + } + for (offset, row) in rows.iter().enumerate() { + let value = row.rlc(randomness, randomness); + region.assign_advice( + || "assign rw row on rw table", + self.rlc, + offset, + || Ok(value), + )?; + } + Ok(()) + } + pub fn assign_row( &self, region: &mut Region<'_, F>, offset: usize, + randomness: F, row: &RwRow, ) -> Result<(), Error> { for (column, value) in [ - (self.rw_counter, row.rw_counter), - (self.is_write, row.is_write), - (self.tag, row.tag), - (self.key1, row.key1), - (self.key2, row.key2), - (self.key3, row.key3), - (self.key4, row.key4), + (self.rw_counter, F::from(row.rw_counter)), + (self.is_write, F::from(row.is_write)), + (self.tag, F::from(row.tag as u64)), + (self.id, F::from(row.id)), + (self.address, (row.address.to_scalar().unwrap())), + (self.field_tag, F::from(row.field_tag)), + ( + self.storage_key, + RandomLinearCombination::random_linear_combine( + row.storage_key.to_le_bytes(), + randomness, + ), + ), (self.value, row.value), (self.value_prev, row.value_prev), (self.aux1, row.aux1), diff --git a/zkevm-circuits/src/state_circuit.rs b/zkevm-circuits/src/state_circuit.rs index b09b86cce5..29a1c122e2 100644 --- a/zkevm-circuits/src/state_circuit.rs +++ b/zkevm-circuits/src/state_circuit.rs @@ -8,11 +8,13 @@ mod random_linear_combination; #[cfg(test)] mod test; -use crate::evm_circuit::{ - param::N_BYTES_WORD, - table::RwTableTag, - util::RandomLinearCombination, - witness::{Rw, RwMap}, +use crate::{ + evm_circuit::{ + param::N_BYTES_WORD, + table::RwTableTag, + util::RandomLinearCombination, + witness::{Rw, RwMap}, + }, }; use binary_number::{Chip as BinaryNumberChip, Config as BinaryNumberConfig}; use constraint_builder::{ConstraintBuilder, Queries}; @@ -41,7 +43,7 @@ const N_LIMBS_ID: usize = 2; /// Config for StateCircuit #[derive(Clone)] -pub struct StateConfig { +pub struct StateConfig { selector: Column, // Figure out why you get errors when this is Selector. // https://github.com/privacy-scaling-explorations/zkevm-circuits/issues/407 rw_counter: MpiConfig, @@ -54,36 +56,37 @@ pub struct StateConfig { storage_key: RlcConfig, is_storage_key_unchanged: IsZeroConfig, value: Column, - lookups: LookupsConfig, + lookups: LookupsConfig, power_of_randomness: [Column; N_BYTES_WORD - 1], lexicographic_ordering: LexicographicOrderingConfig, } type Lookup = (&'static str, Expression, Expression); +/// State Circuit for proving RwTable is valid +pub type StateCircuit = StateCircuitBase; +/// StateCircuit with lexicographic ordering u16 lookup disabled to allow +/// smaller `k`. It is almost impossible to trigger u16 lookup verification +/// error. So StateCircuitLight can be used in opcode gadgets test. +/// Normal opcodes constaints error can still be captured but cost much less +/// time. +pub type StateCircuitLight = StateCircuitBase; + /// State Circuit for proving RwTable is valid #[derive(Default)] -pub struct StateCircuit { +pub struct StateCircuitBase { pub(crate) randomness: F, pub(crate) rows: Vec, #[cfg(test)] overrides: HashMap<(test::AdviceColumn, isize), F>, } -impl StateCircuit { +impl + StateCircuitBase +{ /// make a new state circuit from an RwMap pub fn new(randomness: F, rw_map: RwMap) -> Self { - let mut rows: Vec<_> = rw_map.0.into_values().flatten().collect(); - rows.sort_by_key(|row| { - ( - row.tag() as u64, - row.field_tag().unwrap_or_default(), - row.id().unwrap_or_default(), - row.address().unwrap_or_default(), - row.storage_key().unwrap_or_default(), - row.rw_counter(), - ) - }); + let rows = rw_map.table_assignments(randomness); Self { randomness, rows, @@ -91,6 +94,14 @@ impl StateCircuit { overrides: HashMap::new(), } } + /// estimate k needed to prover + pub fn estimate_k(&self) -> u32 { + let log2_ceil = |n| u32::BITS - (n as u32).leading_zeros() - (n & (n - 1) == 0) as u32; + let k = if QUICK_CHECK { 12 } else { 18 }; + let k = k.max(log2_ceil(64 + self.rows.len())); + log::debug!("state circuit uses k = {}", k); + k + } /// powers of randomness for instance columns pub fn instance(&self) -> Vec> { @@ -100,8 +111,12 @@ impl StateCircuit { } } -impl Circuit for StateCircuit { - type Config = StateConfig; +impl Circuit + for StateCircuitBase +where + F: Field, +{ + type Config = StateConfig; type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { @@ -118,10 +133,10 @@ impl Circuit for StateCircuit { let tag = BinaryNumberChip::configure(meta, selector); - let id = MpiChip::configure(meta, selector, lookups.u16); - let address = MpiChip::configure(meta, selector, lookups.u16); - let storage_key = RlcChip::configure(meta, selector, lookups.u8, power_of_randomness); - let rw_counter = MpiChip::configure(meta, selector, lookups.u16); + let id = MpiChip::configure(meta, selector, lookups); + let address = MpiChip::configure(meta, selector, lookups); + let storage_key = RlcChip::configure(meta, selector, lookups, power_of_randomness); + let rw_counter = MpiChip::configure(meta, selector, lookups); let lexicographic_ordering = LexicographicOrderingChip::configure( meta, @@ -131,7 +146,7 @@ impl Circuit for StateCircuit { address.limbs, storage_key.bytes, rw_counter.limbs, - lookups.u16, + lookups, ); let is_id_unchanged = IsZeroChip::configure( @@ -208,6 +223,7 @@ impl Circuit for StateCircuit { let prev_rows = once(None).chain(rows.clone().map(Some)); for (offset, (row, prev_row)) in rows.zip(prev_rows).enumerate() { + log::trace!("state citcuit assign offset:{} row:{:#?}", offset, row); region.assign_fixed(|| "selector", config.selector, offset, || Ok(F::one()))?; config .rw_counter @@ -285,7 +301,10 @@ impl Circuit for StateCircuit { } } -fn queries(meta: &mut VirtualCells<'_, F>, c: &StateConfig) -> Queries { +fn queries( + meta: &mut VirtualCells<'_, F>, + c: &StateConfig, +) -> Queries { Queries { selector: meta.query_fixed(c.selector, Rotation::cur()), lexicographic_ordering_selector: meta @@ -297,12 +316,15 @@ fn queries(meta: &mut VirtualCells<'_, F>, c: &StateConfig) -> Quer .tag .bits .map(|bit| meta.query_advice(bit, Rotation::cur())), + //aux2: meta.query_advice(c.rw_table.aux2, Rotation::cur()), id: MpiQueries::new(meta, c.id), is_id_unchanged: c.is_id_unchanged.is_zero_expression.clone(), address: MpiQueries::new(meta, c.address), field_tag: meta.query_advice(c.field_tag, Rotation::cur()), storage_key: RlcQueries::new(meta, c.storage_key), value: meta.query_advice(c.value, Rotation::cur()), + //value_at_prev_rotation: meta.query_advice(c.rw_table.value, Rotation::prev()), + //value_prev: meta.query_advice(c.rw_table.value_prev, Rotation::cur()), lookups: LookupsQueries::new(meta, c.lookups), power_of_randomness: c .power_of_randomness @@ -313,5 +335,6 @@ fn queries(meta: &mut VirtualCells<'_, F>, c: &StateConfig) -> Quer .upper_limb_difference_is_zero .is_zero_expression .clone(), + //rw_rlc: meta.query_advice(c.rw_table.rlc, Rotation::cur()), } } diff --git a/zkevm-circuits/src/state_circuit/binary_number.rs b/zkevm-circuits/src/state_circuit/binary_number.rs index 5dc02bb817..36c8123c16 100644 --- a/zkevm-circuits/src/state_circuit/binary_number.rs +++ b/zkevm-circuits/src/state_circuit/binary_number.rs @@ -102,7 +102,7 @@ where // T. // - creating expressions (via the Config) that evaluate to 1 when the bits // match a specific value and 0 otherwise. -pub struct Chip { +pub struct Chip { config: Config, _marker: PhantomData, } diff --git a/zkevm-circuits/src/state_circuit/constraint_builder.rs b/zkevm-circuits/src/state_circuit/constraint_builder.rs index 0945576354..5a0e202f0a 100644 --- a/zkevm-circuits/src/state_circuit/constraint_builder.rs +++ b/zkevm-circuits/src/state_circuit/constraint_builder.rs @@ -4,6 +4,7 @@ use super::{ random_linear_combination::Queries as RlcQueries, N_LIMBS_ACCOUNT_ADDRESS, N_LIMBS_ID, N_LIMBS_RW_COUNTER, }; + use crate::evm_circuit::{ param::N_BYTES_WORD, table::{AccountFieldTag, RwTableTag}, @@ -21,6 +22,7 @@ pub struct Queries { pub rw_counter: MpiQueries, pub is_write: Expression, pub tag: Expression, + //pub aux2: Expression, pub tag_bits: [Expression; 4], pub id: MpiQueries, pub is_id_unchanged: Expression, @@ -28,10 +30,13 @@ pub struct Queries { pub field_tag: Expression, pub storage_key: RlcQueries, pub value: Expression, + //pub value_at_prev_rotation: Expression, + //pub value_prev: Expression, pub lookups: LookupsQueries, pub power_of_randomness: [Expression; N_BYTES_WORD - 1], pub is_storage_key_unchanged: Expression, pub lexicographic_ordering_upper_limb_difference_is_zero: Expression, + //pub rw_rlc: Expression, } type Constraint = (&'static str, Expression); @@ -102,6 +107,73 @@ impl ConstraintBuilder { fn build_general_constraints(&mut self, q: &Queries) { // tag value in RwTableTag range is enforced in BinaryNumberChip self.require_boolean("is_write is boolean", q.is_write()); + + /* + // Only reversible rws have `value_prev`. + // There is no need to constain MemoryRw and StackRw since the 'read + // consistency' part of the constaints are enough for them to behave + // correctly. + // For these 6 Rws whose `value_prev` need to be + // constrained: + // (1) `AccountStorage` and `Account`: they are related to storage + // and they should be connected to MPT cricuit later to check the + // `value_prev`. + // (2)`TxAccessListAccount` and + // `TxAccessListAccountStorage`: Default values of them should be `false` + // indicating "not accessed yet". + // (3) `AccountDestructed`: Since we probably + // will not support this feature, it is skipped now. + // (4) `TxRefund`: Default values should be '0'. BTW it may be moved out of rw table in the future. See https://github.com/privacy-scaling-explorations/zkevm-circuits/issues/395 + // for more details. + self.require_equal( + "prev value", + q.value_prev.clone(), + (q.tag_matches(RwTableTag::TxAccessListAccount) + + q.tag_matches(RwTableTag::TxAccessListAccountStorage) + + q.tag_matches(RwTableTag::AccountDestructed) + + q.tag_matches(RwTableTag::TxRefund)) + * select::expr( + q.first_access(), + 0u64.expr(), + q.value_at_prev_rotation.clone(), + ) + + q.tag_matches(RwTableTag::Account) + * select::expr( + q.first_access(), + // FIXME: this is a dummy placeholder to pass constraints + // It should be aux2/committed_value. + // We should fix this after the committed_value field of Rw::Account in + // both bus-mapping and evm-circuits are implemented. + q.value_prev.clone(), + q.value_at_prev_rotation.clone(), + ) + + q.tag_matches(RwTableTag::AccountStorage) + * select::expr( + q.first_access(), + q.aux2.clone(), // committed value + q.value_at_prev_rotation.clone(), + ), + ); + + self.require_equal("rw table rlc", q.rw_rlc.clone(), { + rlc::expr( + &[ + q.rw_counter.value.clone(), + q.is_write.clone(), + q.tag.clone(), + q.id.value.clone(), + q.address.value.clone(), + q.field_tag.clone(), + q.storage_key.encoded.clone(), + q.value.clone(), + q.value_prev.clone(), + 0u64.expr(), //q.aux1, + q.aux2.clone(), + ], + &q.power_of_randomness, + ) + }) + */ } fn build_start_constraints(&mut self, q: &Queries) { @@ -150,6 +222,10 @@ impl ConstraintBuilder { q.address_change(), ) }); + //self.require_zero( + // "prev_value is 0 when keys changed", + // q.first_access() * q.value_prev.clone(), + //); } fn build_account_storage_constraints(&mut self, q: &Queries) { @@ -172,6 +248,10 @@ impl ConstraintBuilder { "storage_key is 0 for TxAccessListAccount", q.storage_key.encoded.clone(), ); + //self.require_zero( + // "prev_value is 0 when keys changed", + // q.first_access() * q.value_prev.clone(), + //); // TODO: Missing constraints } @@ -180,6 +260,10 @@ impl ConstraintBuilder { "field_tag is 0 for TxAccessListAccountStorage", q.field_tag(), ); + //self.require_zero( + // "prev_value is 0 when keys changed", + // q.first_access() * q.value_prev.clone(), + //); // TODO: Missing constraints } @@ -190,6 +274,10 @@ impl ConstraintBuilder { "storage_key is 0 for TxRefund", q.storage_key.encoded.clone(), ); + //self.require_zero( + // "prev_value is 0 when keys changed", + // q.first_access() * q.value_prev.clone(), + //); // TODO: Missing constraints } @@ -239,6 +327,10 @@ impl ConstraintBuilder { self.constraints.push((name, self.condition.clone() * e)); } + fn require_equal(&mut self, name: &'static str, a: Expression, b: Expression) { + self.require_zero(name, a - b); + } + fn require_boolean(&mut self, name: &'static str, e: Expression) { self.require_zero(name, e.clone() * (1.expr() - e)) } @@ -304,6 +396,7 @@ impl Queries { } fn first_access(&self) -> Expression { + // upper diff changed OR storage key changed or::expr(&[ not::expr( self.lexicographic_ordering_upper_limb_difference_is_zero diff --git a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs index d16092ce98..1c86d87e58 100644 --- a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs +++ b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs @@ -1,9 +1,13 @@ use super::{ - binary_number::Config as BinaryNumberConfig, N_LIMBS_ACCOUNT_ADDRESS, N_LIMBS_ID, + binary_number::Config as BinaryNumberConfig, lookups, N_LIMBS_ACCOUNT_ADDRESS, N_LIMBS_ID, N_LIMBS_RW_COUNTER, }; use crate::{ - evm_circuit::{param::N_BYTES_WORD, table::RwTableTag, witness::Rw}, + evm_circuit::{ + param::N_BYTES_WORD, + table::RwTableTag, + witness::{Rw}, + }, util::Expr, }; use eth_types::{Field, ToBigEndian}; @@ -72,7 +76,7 @@ use std::ops::Mul; // = 480 bits #[derive(Clone)] -pub struct Config { +pub struct Config { pub(crate) selector: Column, upper_limb_difference: Column, pub(crate) upper_limb_difference_is_zero: IsZeroConfig, @@ -98,7 +102,7 @@ impl Chip { #[allow(clippy::too_many_arguments)] // TODO: fix this to not have too many arguments? - pub fn configure( + pub fn configure( meta: &mut ConstraintSystem, tag: BinaryNumberConfig, field_tag: Column, @@ -106,7 +110,7 @@ impl Chip { address_limbs: [Column; N_LIMBS_ACCOUNT_ADDRESS], storage_key_bytes: [Column; N_BYTES_WORD], rw_counter_limbs: [Column; N_LIMBS_RW_COUNTER], - u16_range: Column, + lookup: lookups::Config, ) -> Config { let selector = meta.fixed_column(); let [upper_limb_difference, upper_limb_difference_inverse, lower_limb_difference, lower_limb_difference_inverse] = @@ -186,24 +190,19 @@ impl Chip { ] }); assert!(meta.degree() <= 16); - meta.lookup_any("upper_limb_difference fits into u16", |meta| { - let upper_limb_difference = meta.query_advice(upper_limb_difference, Rotation::cur()); - vec![( - upper_limb_difference, - meta.query_fixed(u16_range, Rotation::cur()), - )] + lookup.range_check_u16(meta, "upper_limb_difference fits into u16", |meta| { + meta.query_advice(upper_limb_difference, Rotation::cur()) }); - meta.lookup_any( + lookup.range_check_u16( + meta, "upper_limb_difference is zero or lower_limb_difference fits into u16", |meta| { let lower_limb_difference = meta.query_advice(lower_limb_difference, Rotation::cur()); - vec![( - upper_limb_difference_is_zero * lower_limb_difference, - meta.query_fixed(u16_range, Rotation::cur()), - )] + upper_limb_difference_is_zero * lower_limb_difference }, ); + assert!(meta.degree() <= 16); meta.create_gate("lower_limb_difference is not zero", |meta| { let selector = meta.query_fixed(selector, Rotation::cur()); @@ -358,6 +357,7 @@ fn upper_limb_difference_possible_values( partial_sum = partial_sum * (1u64 << 16).expr() + cur_limb.clone() - prev_limb.clone(); result.push(partial_sum.clone()) } + assert_eq!(result.len(), 15); result } diff --git a/zkevm-circuits/src/state_circuit/lookups.rs b/zkevm-circuits/src/state_circuit/lookups.rs index d90ee9e670..59291dc906 100644 --- a/zkevm-circuits/src/state_circuit/lookups.rs +++ b/zkevm-circuits/src/state_circuit/lookups.rs @@ -9,15 +9,58 @@ use std::marker::PhantomData; use strum::IntoEnumIterator; #[derive(Clone, Copy)] -pub struct Config { +pub struct Config { // Can these be TableColumn's? // https://github.com/zcash/halo2/blob/642efc1536d3ea2566b04814bd60a00c4745ae22/halo2_proofs/src/plonk/circuit.rs#L266 - pub u8: Column, - pub u10: Column, - pub u16: Column, + u8: Column, + u10: Column, + u16: Column, pub call_context_field_tag: Column, } +impl Config { + pub fn range_check_u8( + &self, + meta: &mut ConstraintSystem, + msg: &'static str, + exp_fn: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, + ) { + meta.lookup_any(msg, |meta| { + let exp = exp_fn(meta); + vec![(exp, meta.query_fixed(self.u8, Rotation::cur()))] + }); + } + pub fn range_check_u10( + &self, + meta: &mut ConstraintSystem, + msg: &'static str, + exp_fn: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, + ) { + meta.lookup_any(msg, |meta| { + let exp = exp_fn(meta); + vec![(exp, meta.query_fixed(self.u10, Rotation::cur()))] + }); + } + pub fn range_check_u16( + &self, + meta: &mut ConstraintSystem, + msg: &'static str, + exp_fn: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, + ) { + if !QUICK_CHECK { + meta.lookup_any(msg, |meta| { + let exp = exp_fn(meta); + vec![(exp, meta.query_fixed(self.u16, Rotation::cur()))] + }); + } else { + log::debug!( + "{} u16 range check is skipped because `QUICK_CHECK` is enabled", + msg + ); + } + } +} + #[derive(Clone)] pub struct Queries { pub u8: Expression, @@ -27,7 +70,10 @@ pub struct Queries { } impl Queries { - pub fn new(meta: &mut VirtualCells<'_, F>, c: Config) -> Self { + pub fn new( + meta: &mut VirtualCells<'_, F>, + c: Config, + ) -> Self { Self { u8: meta.query_fixed(c.u8, Rotation::cur()), u10: meta.query_fixed(c.u10, Rotation::cur()), @@ -37,20 +83,20 @@ impl Queries { } } -pub struct Chip { - config: Config, +pub struct Chip { + config: Config, _marker: PhantomData, } -impl Chip { - pub fn construct(config: Config) -> Self { +impl Chip { + pub fn construct(config: Config) -> Self { Self { config, _marker: PhantomData, } } - pub fn configure(meta: &mut ConstraintSystem) -> Config { + pub fn configure(meta: &mut ConstraintSystem) -> Config { Config { u8: meta.fixed_column(), u10: meta.fixed_column(), @@ -60,11 +106,11 @@ impl Chip { } pub fn load(&self, layouter: &mut impl Layouter) -> Result<(), Error> { - for (column, exponent) in [ - (self.config.u8, 8), - (self.config.u10, 10), - (self.config.u16, 16), - ] { + let mut columns = vec![(self.config.u8, 8), (self.config.u10, 10)]; + if !QUICK_CHECK { + columns.push((self.config.u16, 16)); + } + for (column, exponent) in columns { layouter.assign_region( || format!("assign u{} fixed column", exponent), |mut region| { @@ -80,6 +126,7 @@ impl Chip { }, )?; } + layouter.assign_region( || "assign call_context_field_tags fixed column", |mut region| { diff --git a/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs b/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs index a31b16c726..dfbe80889c 100644 --- a/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs +++ b/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs @@ -1,3 +1,4 @@ +use super::lookups; use super::N_LIMBS_ACCOUNT_ADDRESS; use super::N_LIMBS_RW_COUNTER; use crate::util::Expr; @@ -128,20 +129,17 @@ where } } - pub fn configure( + pub fn configure( meta: &mut ConstraintSystem, selector: Column, - u16_range: Column, + lookup: lookups::Config, ) -> Config { let value = meta.advice_column(); let limbs = [0; N].map(|_| meta.advice_column()); for &limb in &limbs { - meta.lookup_any("mpi limb fits into u16", |meta| { - vec![( - meta.query_advice(limb, Rotation::cur()), - meta.query_fixed(u16_range, Rotation::cur()), - )] + lookup.range_check_u16(meta, "mpi limb fits into u16", |meta| { + meta.query_advice(limb, Rotation::cur()) }); } meta.create_gate("mpi value matches claimed limbs", |meta| { diff --git a/zkevm-circuits/src/state_circuit/random_linear_combination.rs b/zkevm-circuits/src/state_circuit/random_linear_combination.rs index cf7fedaaa8..8749dda344 100644 --- a/zkevm-circuits/src/state_circuit/random_linear_combination.rs +++ b/zkevm-circuits/src/state_circuit/random_linear_combination.rs @@ -7,6 +7,8 @@ use halo2_proofs::{ }; use std::marker::PhantomData; +use super::lookups; + #[derive(Clone, Debug, Copy)] pub struct Config { pub encoded: Column, @@ -70,20 +72,18 @@ impl Chip { } } - pub fn configure( + pub fn configure( meta: &mut ConstraintSystem, selector: Column, - u8_lookup: Column, + lookup: lookups::Config, power_of_randomness: [Column; 31], ) -> Config { let encoded = meta.advice_column(); let bytes = [0; N].map(|_| meta.advice_column()); for &byte in &bytes { - meta.lookup_any("rlc bytes fit into u8", |meta| { - let byte = meta.query_advice(byte, Rotation::cur()); - let u8_lookup = meta.query_fixed(u8_lookup, Rotation::cur()); - vec![(byte, u8_lookup)] + lookup.range_check_u8(meta, "rlc bytes fit into u8", |meta| { + meta.query_advice(byte, Rotation::cur()) }); } diff --git a/zkevm-circuits/src/state_circuit/test.rs b/zkevm-circuits/src/state_circuit/test.rs index fd6c52ed85..c2dd398d83 100644 --- a/zkevm-circuits/src/state_circuit/test.rs +++ b/zkevm-circuits/src/state_circuit/test.rs @@ -23,8 +23,7 @@ use std::collections::{BTreeSet, HashMap}; use strum::IntoEnumIterator; const N_ROWS: usize = 1 << 16; - -#[derive(Hash, Eq, PartialEq)] +#[derive(Hash, Eq, PartialEq, Clone)] pub enum AdviceColumn { IsWrite, Address, @@ -45,7 +44,10 @@ pub enum AdviceColumn { } impl AdviceColumn { - pub fn value(&self, config: &StateConfig) -> Column { + pub fn value( + &self, + config: &StateConfig, + ) -> Column { match self { Self::IsWrite => config.is_write, Self::Address => config.address.value, @@ -83,7 +85,8 @@ fn test_state_circuit_ok( let circuit = StateCircuit::::new(randomness, rw_map); let power_of_randomness = circuit.instance(); - let prover = MockProver::::run(19, &circuit, power_of_randomness).unwrap(); + let prover = + MockProver::::run(circuit.estimate_k(), &circuit, power_of_randomness).unwrap(); let verify_result = prover.verify(); assert_eq!(verify_result, Ok(())); } @@ -158,7 +161,7 @@ fn state_circuit_simple_2() { ); let storage_op_0 = Operation::new( - RWCounter::from(0), + RWCounter::from(1), RW::WRITE, StorageOp::new( U256::from(100).to_address(), @@ -319,7 +322,7 @@ fn storage_key_rlc() { account_address: Address::default(), storage_key: U256::from(256), value: U256::zero(), - value_prev: U256::zero(), + value_prev: U256::from(5), tx_id: 4, committed_value: U256::from(5), }]; @@ -372,7 +375,7 @@ fn storage_key_mismatch() { account_address: Address::default(), storage_key: U256::from(6), value: U256::zero(), - value_prev: U256::zero(), + value_prev: U256::from(5), tx_id: 4, committed_value: U256::from(5), }]; @@ -397,7 +400,7 @@ fn storage_key_byte_out_of_range() { account_address: Address::default(), storage_key: U256::from(256), value: U256::zero(), - value_prev: U256::zero(), + value_prev: U256::from(5), tx_id: 4, committed_value: U256::from(5), }]; @@ -439,7 +442,7 @@ fn nonlexicographic_order_tag() { is_write: true, call_id: 1, memory_address: 10, - byte: 12, + byte: 0, }; let second = Rw::CallContext { rw_counter: 2, @@ -520,7 +523,7 @@ fn nonlexicographic_order_address() { account_address: address!("0x2000000000000000000000000000000000000000"), field_tag: AccountFieldTag::CodeHash, value: U256::one(), - value_prev: U256::one(), + value_prev: U256::zero(), }; assert_eq!(verify(vec![first, second]), Ok(())); @@ -538,7 +541,7 @@ fn nonlexicographic_order_storage_key_upper() { account_address: Address::default(), storage_key: U256::zero(), value: U256::zero(), - value_prev: U256::zero(), + value_prev: U256::from(5), tx_id: 4, committed_value: U256::from(5), }; @@ -548,7 +551,7 @@ fn nonlexicographic_order_storage_key_upper() { account_address: Address::default(), storage_key: U256::MAX - U256::one(), value: U256::zero(), - value_prev: U256::zero(), + value_prev: U256::from(5), tx_id: 4, committed_value: U256::from(5), }; @@ -568,7 +571,7 @@ fn nonlexicographic_order_storage_key_lower() { account_address: Address::default(), storage_key: U256::zero(), value: U256::zero(), - value_prev: U256::zero(), + value_prev: U256::from(5), tx_id: 4, committed_value: U256::from(5), }; @@ -578,7 +581,7 @@ fn nonlexicographic_order_storage_key_lower() { account_address: Address::default(), storage_key: U256::one(), value: U256::zero(), - value_prev: U256::zero(), + value_prev: U256::from(5), tx_id: 4, committed_value: U256::from(5), }; @@ -793,6 +796,7 @@ fn invalid_tags() { fn prover(rows: Vec, overrides: HashMap<(AdviceColumn, isize), Fr>) -> MockProver { let randomness = Fr::rand(); + let circuit = StateCircuit:: { randomness, rows, @@ -800,7 +804,7 @@ fn prover(rows: Vec, overrides: HashMap<(AdviceColumn, isize), Fr>) -> MockP }; let power_of_randomness = circuit.instance(); - MockProver::::run(17, &circuit, power_of_randomness).unwrap() + MockProver::::run(circuit.estimate_k(), &circuit, power_of_randomness).unwrap() } fn verify(rows: Vec) -> Result<(), Vec> { @@ -825,6 +829,18 @@ fn verify_with_overrides( fn assert_error_matches(result: Result<(), Vec>, name: &str) { let errors = result.err().expect("result is not an error"); + let errors: Vec<_> = errors + .iter() + .filter(|e| { + if let VerifyFailure::ConstraintNotSatisfied { constraint, .. } = e { + let constraint = format!("{}", constraint); + if constraint.contains("rw table rlc") { + return false; + } + } + true + }) + .collect(); assert_eq!(errors.len(), 1, "{:?}", errors); match &errors[0] { VerifyFailure::ConstraintNotSatisfied { constraint, .. } => { diff --git a/zkevm-circuits/src/test_util.rs b/zkevm-circuits/src/test_util.rs index 86182447b0..6eff33e4aa 100644 --- a/zkevm-circuits/src/test_util.rs +++ b/zkevm-circuits/src/test_util.rs @@ -55,7 +55,7 @@ impl Default for BytecodeTestConfig { enable_evm_circuit_test: true, enable_state_circuit_test: true, gas_limit: 1_000_000u64, - evm_circuit_lookup_tags: get_fixed_table(FixedTableConfig::Incomplete), + evm_circuit_lookup_tags: vec![], } } } @@ -93,7 +93,12 @@ pub fn test_circuits_using_witness_block( const N_ROWS: usize = 1 << 16; let state_circuit = StateCircuit::::new(block.randomness, block.rws); let power_of_randomness = state_circuit.instance(); - let prover = MockProver::::run(18, &state_circuit, power_of_randomness).unwrap(); + let prover = MockProver::::run( + state_circuit.estimate_k(), + &state_circuit, + power_of_randomness, + ) + .unwrap(); prover.verify_at_rows(0..state_circuit.rows.len(), 0..state_circuit.rows.len())? }