From bf77296bf64582be1aca380aec9251eea9127e7c Mon Sep 17 00:00:00 2001 From: Zhang Zhuo Date: Mon, 4 Jul 2022 11:30:08 +0800 Subject: [PATCH 1/3] squash --- zkevm-circuits/src/evm_circuit.rs | 103 +++--- zkevm-circuits/src/evm_circuit/table.rs | 68 +++- .../evm_circuit/util/constraint_builder.rs | 76 ++--- zkevm-circuits/src/evm_circuit/witness.rs | 95 +++--- zkevm-circuits/src/rw_table.rs | 72 ++-- zkevm-circuits/src/state_circuit.rs | 307 ++++++++++-------- .../src/state_circuit/binary_number.rs | 2 +- .../src/state_circuit/constraint_builder.rs | 83 ++++- .../state_circuit/lexicographic_ordering.rs | 44 ++- zkevm-circuits/src/state_circuit/lookups.rs | 77 ++++- .../multiple_precision_integer.rs | 15 +- .../random_linear_combination.rs | 14 +- zkevm-circuits/src/state_circuit/test.rs | 68 ++-- zkevm-circuits/src/test_util.rs | 9 +- 14 files changed, 657 insertions(+), 376 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 70ac7a1f55..0c03a03694 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -150,6 +150,7 @@ pub mod test { rw_table::RwTable, util::Expr, }; + use bus_mapping::evm::OpcodeId; use eth_types::{Field, Word}; use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner}, @@ -241,30 +242,8 @@ pub mod test { layouter.assign_region( || "rw table", |mut region| { - let mut offset = 0; - 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; - } + rws.check_rw_counter_sanity(); + self.rw_table.assign(&mut region, randomness, rws)?; Ok(()) }, ) @@ -352,13 +331,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 { @@ -446,31 +474,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..d23b72cc3d 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,41 @@ 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.into_iter() + .map(|r| r.table_assignment(randomness)) + .collect() + } } #[derive(Clone, Copy, Debug)] @@ -527,39 +566,20 @@ 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 Rw { pub fn tx_access_list_value_pair(&self) -> (bool, bool) { match self { @@ -656,16 +676,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..2a6b13f218 100644 --- a/zkevm-circuits/src/rw_table.rs +++ b/zkevm-circuits/src/rw_table.rs @@ -1,12 +1,16 @@ #![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::{ + table::LookupTable, + util::RandomLinearCombination, + witness::{RwMap, RwRow}, +}; /// The rw table shared between evm circuit and state circuit #[derive(Clone, Copy)] @@ -14,26 +18,26 @@ pub struct RwTable { 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 { +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.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()), @@ -42,35 +46,55 @@ impl LookupTable for RwTable { } } impl RwTable { - pub fn construct(meta: &mut ConstraintSystem) -> Self { + pub fn construct(meta: &mut ConstraintSystem) -> Self { Self { 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( + + pub fn assign( + &self, + region: &mut Region<'_, F>, + randomness: F, + rw_map: &RwMap, + ) -> Result>, Error> { + let rows = rw_map.table_assignments(randomness); + for (offset, row) in rows.iter().enumerate() { + self.assign_row(region, offset, randomness, row)?; + } + Ok(rows) + } + 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..c7c4ee9a6e 100644 --- a/zkevm-circuits/src/state_circuit.rs +++ b/zkevm-circuits/src/state_circuit.rs @@ -8,21 +8,22 @@ 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, RwRow}, + }, + rw_table::RwTable, }; use binary_number::{Chip as BinaryNumberChip, Config as BinaryNumberConfig}; use constraint_builder::{ConstraintBuilder, Queries}; use eth_types::{Address, Field, ToLittleEndian}; use gadgets::is_zero::{IsZeroChip, IsZeroConfig, IsZeroInstruction}; use halo2_proofs::{ - circuit::{Layouter, SimpleFloorPlanner}, - plonk::{ - Advice, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, Instance, VirtualCells, - }, + circuit::{Layouter, Region, SimpleFloorPlanner}, + plonk::{Circuit, Column, ConstraintSystem, Error, Expression, Fixed, Instance, VirtualCells}, poly::Rotation, }; use lexicographic_ordering::{ @@ -41,49 +42,49 @@ const N_LIMBS_ID: usize = 2; /// Config for StateCircuit #[derive(Clone)] -pub struct StateConfig { - selector: Column, // Figure out why you get errors when this is Selector. +pub struct StateConfig { + // Figure out why you get errors when this is Selector. // https://github.com/privacy-scaling-explorations/zkevm-circuits/issues/407 - rw_counter: MpiConfig, - is_write: Column, - tag: BinaryNumberConfig, - id: MpiConfig, + selector: Column, + rw_table: RwTable, + tag_bits: BinaryNumberConfig, + rw_counter_mpi: MpiConfig, + id_mpi: MpiConfig, + address_mpi: MpiConfig, + storage_key_rlc: RlcConfig, is_id_unchanged: IsZeroConfig, - address: MpiConfig, - field_tag: Column, - 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, + 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 +92,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> { @@ -98,10 +107,76 @@ impl StateCircuit { .map(|exp| vec![self.randomness.pow(&[exp, 0, 0, 0]); N_ROWS]) .collect() } + #[allow(clippy::too_many_arguments)] + fn assign_row( + &self, + config: &StateConfig, + region: &mut Region, + tag_chip: &binary_number::Chip, + is_storage_key_unchanged: &IsZeroChip, + is_id_unchanged: &IsZeroChip, + lexicographic_ordering_chip: &LexicographicOrderingChip, + offset: usize, + row: RwRow, + prev_row: Option>, + ) -> Result<(), Error> { + region.assign_fixed(|| "selector", config.selector, offset, || Ok(F::one()))?; + + config + .rw_table + .assign_row(region, offset, self.randomness, &row)?; + + tag_chip.assign(region, offset, &row.tag)?; + config + .rw_counter_mpi + .assign(region, offset, row.rw_counter as u32)?; + config.id_mpi.assign(region, offset, row.id as u32)?; + config.address_mpi.assign(region, offset, row.address)?; + config + .storage_key_rlc + .assign(region, offset, self.randomness, row.storage_key)?; + + if offset != 0 { + lexicographic_ordering_chip.assign(region, offset, &row, &prev_row.unwrap())?; + + // assign storage key diff + let cur_storage_key = RandomLinearCombination::random_linear_combine( + row.storage_key.to_le_bytes(), + self.randomness, + ); + let prev_storage_key = RandomLinearCombination::random_linear_combine( + prev_row.unwrap_or_default().storage_key.to_le_bytes(), + self.randomness, + ); + is_storage_key_unchanged.assign( + region, + offset, + Some(cur_storage_key - prev_storage_key), + )?; + + // assign id diff + let id_change = + F::from(row.id as u64) - F::from(prev_row.unwrap_or_default().id as u64); + is_id_unchanged.assign(region, offset, Some(id_change))?; + let _storage_key_change = RandomLinearCombination::random_linear_combine( + row.storage_key.to_le_bytes(), + self.randomness, + ) - RandomLinearCombination::random_linear_combine( + prev_row.unwrap_or_default().storage_key.to_le_bytes(), + self.randomness, + ); + } + + Ok(()) + } } -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 { @@ -113,33 +188,39 @@ impl Circuit for StateCircuit { let lookups = LookupsChip::configure(meta); let power_of_randomness = [0; N_BYTES_WORD - 1].map(|_| meta.instance_column()); - let [is_write, field_tag, value, is_id_unchanged_column, is_storage_key_unchanged_column] = - [0; 5].map(|_| meta.advice_column()); - - let tag = BinaryNumberChip::configure(meta, selector); + let rw_table = RwTable::construct(meta); + let is_storage_key_unchanged_column = meta.advice_column(); + let is_id_unchanged_column = meta.advice_column(); + let id_mpi = MpiChip::configure(meta, rw_table.id, selector, lookups); + let address_mpi = MpiChip::configure(meta, rw_table.address, selector, lookups); + let storage_key_rlc = RlcChip::configure( + meta, + selector, + rw_table.storage_key, + lookups, + power_of_randomness, + ); + let rw_counter_mpi = MpiChip::configure(meta, rw_table.rw_counter, selector, lookups); - 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 tag_bits = BinaryNumberChip::configure(meta, selector); let lexicographic_ordering = LexicographicOrderingChip::configure( meta, - tag, - field_tag, - id.limbs, - address.limbs, - storage_key.bytes, - rw_counter.limbs, - lookups.u16, + tag_bits, + rw_table.field_tag, + id_mpi.limbs, + address_mpi.limbs, + storage_key_rlc.bytes, + rw_counter_mpi.limbs, + lookups, ); let is_id_unchanged = IsZeroChip::configure( meta, |meta| meta.query_fixed(lexicographic_ordering.selector, Rotation::cur()), |meta| { - meta.query_advice(id.value, Rotation::cur()) - - meta.query_advice(id.value, Rotation::prev()) + meta.query_advice(rw_table.id, Rotation::cur()) + - meta.query_advice(rw_table.id, Rotation::prev()) }, is_id_unchanged_column, ); @@ -147,24 +228,22 @@ impl Circuit for StateCircuit { meta, |meta| meta.query_fixed(lexicographic_ordering.selector, Rotation::cur()), |meta| { - meta.query_advice(storage_key.encoded, Rotation::cur()) - - meta.query_advice(storage_key.encoded, Rotation::prev()) + meta.query_advice(rw_table.storage_key, Rotation::cur()) + - meta.query_advice(rw_table.storage_key, Rotation::prev()) }, is_storage_key_unchanged_column, ); let config = Self::Config { selector, - rw_counter, - is_write, - tag, - id, - is_id_unchanged, - address, - field_tag, - storage_key, - value, + rw_table, lexicographic_ordering, + address_mpi, + tag_bits, + id_mpi, + rw_counter_mpi, + storage_key_rlc, + is_id_unchanged, is_storage_key_unchanged, lookups, power_of_randomness, @@ -196,78 +275,31 @@ impl Circuit for StateCircuit { let lexicographic_ordering_chip = LexicographicOrderingChip::construct(config.lexicographic_ordering.clone()); - let tag_chip = BinaryNumberChip::construct(config.tag); + let tag_chip = BinaryNumberChip::construct(config.tag_bits); layouter.assign_region( || "rw table", |mut region| { let padding_length = N_ROWS - self.rows.len(); - let padding = (1..=padding_length).map(|rw_counter| Rw::Start { rw_counter }); + let padding = (1..=padding_length) + .map(|rw_counter| (Rw::Start { rw_counter }).table_assignment(self.randomness)); let rows = padding.chain(self.rows.iter().cloned()); let prev_rows = once(None).chain(rows.clone().map(Some)); for (offset, (row, prev_row)) in rows.zip(prev_rows).enumerate() { - region.assign_fixed(|| "selector", config.selector, offset, || Ok(F::one()))?; - config - .rw_counter - .assign(&mut region, offset, row.rw_counter() as u32)?; - region.assign_advice( - || "is_write", - config.is_write, + log::trace!("state citcuit assign offset:{} row:{:#?}", offset, row); + self.assign_row( + &config, + &mut region, + &tag_chip, + &is_storage_key_unchanged, + &is_id_unchanged, + &lexicographic_ordering_chip, offset, - || Ok(if row.is_write() { F::one() } else { F::zero() }), + row, + prev_row, )?; - tag_chip.assign(&mut region, offset, &row.tag())?; - if let Some(id) = row.id() { - config.id.assign(&mut region, offset, id as u32)?; - } - if let Some(address) = row.address() { - config.address.assign(&mut region, offset, address)?; - } - if let Some(field_tag) = row.field_tag() { - region.assign_advice( - || "field_tag", - config.field_tag, - offset, - || Ok(F::from(field_tag as u64)), - )?; - } - if let Some(storage_key) = row.storage_key() { - config.storage_key.assign( - &mut region, - offset, - self.randomness, - storage_key, - )?; - } - region.assign_advice( - || "value", - config.value, - offset, - || Ok(row.value_assignment(self.randomness)), - )?; - - if let Some(prev_row) = prev_row { - lexicographic_ordering_chip.assign(&mut region, offset, &row, &prev_row)?; - - let id_change = F::from(row.id().unwrap_or_default() as u64) - - F::from(prev_row.id().unwrap_or_default() as u64); - is_id_unchanged.assign(&mut region, offset, Some(id_change))?; - - let storage_key_change = RandomLinearCombination::random_linear_combine( - row.storage_key().unwrap_or_default().to_le_bytes(), - self.randomness, - ) - RandomLinearCombination::random_linear_combine( - prev_row.storage_key().unwrap_or_default().to_le_bytes(), - self.randomness, - ); - is_storage_key_unchanged.assign( - &mut region, - offset, - Some(storage_key_change), - )?; - } } #[cfg(test)] @@ -285,24 +317,31 @@ 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()), + rw_counter: MpiQueries::new(meta, c.rw_counter_mpi), + is_write: meta.query_advice(c.rw_table.is_write, Rotation::cur()), lexicographic_ordering_selector: meta .query_fixed(c.lexicographic_ordering.selector, Rotation::cur()), - rw_counter: MpiQueries::new(meta, c.rw_counter), - is_write: meta.query_advice(c.is_write, Rotation::cur()), - tag: c.tag.value(Rotation::cur())(meta), + aux2: meta.query_advice(c.rw_table.aux2, Rotation::cur()), + tag: c.tag_bits.value(Rotation::cur())(meta), tag_bits: c - .tag + .tag_bits .bits .map(|bit| meta.query_advice(bit, Rotation::cur())), - id: MpiQueries::new(meta, c.id), + //prev_tag: meta.query_advice(c.rw_table.tag, Rotation::prev()), + id: MpiQueries::new(meta, c.id_mpi), 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()), + address: MpiQueries::new(meta, c.address_mpi), + field_tag: meta.query_advice(c.rw_table.field_tag, Rotation::cur()), + storage_key: RlcQueries::new(meta, c.storage_key_rlc), + value: meta.query_advice(c.rw_table.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 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..7c036abcba 100644 --- a/zkevm-circuits/src/state_circuit/constraint_builder.rs +++ b/zkevm-circuits/src/state_circuit/constraint_builder.rs @@ -7,7 +7,7 @@ use super::{ use crate::evm_circuit::{ param::N_BYTES_WORD, table::{AccountFieldTag, RwTableTag}, - util::{not, or}, + util::{not, or, select}, }; use crate::util::Expr; use eth_types::Field; @@ -21,6 +21,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,6 +29,8 @@ 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, @@ -102,6 +105,52 @@ 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(), + ), + ); } fn build_start_constraints(&mut self, q: &Queries) { @@ -150,6 +199,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) { @@ -165,6 +218,8 @@ impl ConstraintBuilder { // cb.require_zero("first access is a write", q.is_write()); // // cb.require_zero("first access rw_counter is 0", // q.rw_counter.value.clone()); }) + + // TODO: value_prev == committed_value when keys changed } fn build_tx_access_list_account_constraints(&mut self, q: &Queries) { self.require_zero("field_tag is 0 for TxAccessListAccount", q.field_tag()); @@ -172,6 +227,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 +239,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 +253,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 +306,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)) } @@ -302,8 +373,18 @@ impl Queries { fn tag_matches(&self, tag: RwTableTag) -> Expression { BinaryNumberConfig::::value_equals_expr(tag, self.tag_bits.clone()) } + fn multi_tag_match(&self, target_tags: Vec) -> Expression { + let mut numerator = 1u64.expr(); + for unmatched_tag in RwTableTag::iter() { + if !target_tags.contains(&unmatched_tag) { + numerator = numerator * (self.tag.expr() - unmatched_tag.expr()); + } + } + numerator + } 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..2cd858c17d 100644 --- a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs +++ b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs @@ -1,9 +1,9 @@ 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::RwRow}, util::Expr, }; use eth_types::{Field, ToBigEndian}; @@ -72,7 +72,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 +98,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 +106,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 +186,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()); @@ -218,8 +213,8 @@ impl Chip { &self, region: &mut Region<'_, F>, offset: usize, - cur: &Rw, - prev: &Rw, + cur: &RwRow, + prev: &RwRow, ) -> Result<(), Error> { region.assign_fixed( || "upper_limb_difference", @@ -327,19 +322,19 @@ impl Queries { } } -fn rw_to_be_limbs(row: &Rw) -> Vec { - let mut id = row.id().unwrap_or_default() as u32; +fn rw_to_be_limbs(row: &RwRow) -> Vec { + let mut id = row.id as u32; assert_eq!(id.to_be_bytes().len(), 4); // The max value of `id` is 2^23 - 1, so the 9 most significant bits should be // 0. We use these 9 bits to hold value of `tag` and `field_tag`. assert!(id < (1 << 23)); - id += (((row.tag() as u32) << 5) + (row.field_tag().unwrap_or_default() as u32)) << 23; + id += (((row.tag as u32) << 5) + (row.field_tag as u32)) << 23; let mut be_bytes = vec![]; be_bytes.extend_from_slice(&id.to_be_bytes()); - be_bytes.extend_from_slice(&(row.address().unwrap_or_default().0)); - be_bytes.extend_from_slice(&(row.storage_key().unwrap_or_default().to_be_bytes())); - be_bytes.extend_from_slice(&((row.rw_counter() as u32).to_be_bytes())); + be_bytes.extend_from_slice(&(row.address.0)); + be_bytes.extend_from_slice(&(row.storage_key.to_be_bytes())); + be_bytes.extend_from_slice(&((row.rw_counter as u32).to_be_bytes())); be_bytes .iter() @@ -358,6 +353,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..bc831035e5 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,22 +129,20 @@ where } } - pub fn configure( + pub fn configure( meta: &mut ConstraintSystem, + value: Column, 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| { let selector = meta.query_fixed(selector, Rotation::cur()); let value = meta.query_advice(value, Rotation::cur()); diff --git a/zkevm-circuits/src/state_circuit/random_linear_combination.rs b/zkevm-circuits/src/state_circuit/random_linear_combination.rs index cf7fedaaa8..1edf36fa2e 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, + encoded: 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..f0d4d6a689 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,24 +44,27 @@ 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, - Self::AddressLimb0 => config.address.limbs[0], - Self::AddressLimb1 => config.address.limbs[1], - Self::StorageKey => config.storage_key.encoded, - Self::StorageKeyByte0 => config.storage_key.bytes[0], - Self::StorageKeyByte1 => config.storage_key.bytes[1], + Self::IsWrite => config.rw_table.is_write, + Self::Address => config.rw_table.address, + Self::AddressLimb0 => config.address_mpi.limbs[0], + Self::AddressLimb1 => config.address_mpi.limbs[1], + Self::StorageKey => config.rw_table.storage_key, + Self::StorageKeyByte0 => config.storage_key_rlc.bytes[0], + Self::StorageKeyByte1 => config.storage_key_rlc.bytes[1], Self::StorageKeyChangeInverse => config.is_storage_key_unchanged.value_inv, - Self::Value => config.value, - Self::RwCounter => config.rw_counter.value, - Self::RwCounterLimb0 => config.rw_counter.limbs[0], - Self::RwCounterLimb1 => config.rw_counter.limbs[1], - Self::TagBit0 => config.tag.bits[0], - Self::TagBit1 => config.tag.bits[1], - Self::TagBit2 => config.tag.bits[2], - Self::TagBit3 => config.tag.bits[3], + Self::Value => config.rw_table.value, + Self::RwCounter => config.rw_table.rw_counter, + Self::RwCounterLimb0 => config.rw_counter_mpi.limbs[0], + Self::RwCounterLimb1 => config.rw_counter_mpi.limbs[1], + Self::TagBit0 => config.tag_bits.bits[0], + Self::TagBit1 => config.tag_bits.bits[1], + Self::TagBit2 => config.tag_bits.bits[2], + Self::TagBit3 => config.tag_bits.bits[3], } } } @@ -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,11 @@ fn invalid_tags() { fn prover(rows: Vec, overrides: HashMap<(AdviceColumn, isize), Fr>) -> MockProver { let randomness = Fr::rand(); + let rows = rows + .iter() + .map(|r| r.table_assignment(randomness)) + .collect(); + let circuit = StateCircuit:: { randomness, rows, @@ -800,7 +808,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> { 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())? } From f67288abfb272229f26b4200eed2578a058a0573 Mon Sep 17 00:00:00 2001 From: Zhang Zhuo Date: Thu, 2 Jun 2022 14:47:46 +0800 Subject: [PATCH 2/3] evm circuit and state circuit share a same RLC rw table col --- zkevm-circuits/src/evm_circuit.rs | 11 ++- zkevm-circuits/src/evm_circuit/witness.rs | 25 +++++ zkevm-circuits/src/rw_table.rs | 95 +++++++++++++++---- zkevm-circuits/src/state_circuit.rs | 8 +- .../src/state_circuit/constraint_builder.rs | 21 ++++ zkevm-circuits/src/state_circuit/test.rs | 12 +++ 6 files changed, 143 insertions(+), 29 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 0c03a03694..81f587a0bb 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -147,7 +147,7 @@ pub mod test { witness::{Block, BlockContext, Bytecode, RwMap, Transaction}, EvmCircuit, }, - rw_table::RwTable, + rw_table::RwTableRlc, util::Expr, }; use bus_mapping::evm::OpcodeId; @@ -188,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, @@ -243,7 +243,10 @@ pub mod test { || "rw table", |mut region| { rws.check_rw_counter_sanity(); - self.rw_table.assign(&mut region, randomness, rws)?; + // TODO: fix this after cs.challenge() is implemented in halo2 + let randomness_phase_next = randomness; + self.rw_table + .assign(&mut region, randomness, rws, randomness_phase_next)?; Ok(()) }, ) @@ -399,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()); diff --git a/zkevm-circuits/src/evm_circuit/witness.rs b/zkevm-circuits/src/evm_circuit/witness.rs index d23b72cc3d..7f4f03b17c 100644 --- a/zkevm-circuits/src/evm_circuit/witness.rs +++ b/zkevm-circuits/src/evm_circuit/witness.rs @@ -445,6 +445,7 @@ impl RwMap { debug_assert_eq!(idx, rw_counter - 1); } } + pub fn table_assignments(&self, randomness: F) -> Vec> where F: Field, @@ -580,6 +581,30 @@ pub struct RwRow { pub aux1: F, pub aux2: F, } +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)), + (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 { diff --git a/zkevm-circuits/src/rw_table.rs b/zkevm-circuits/src/rw_table.rs index 2a6b13f218..86056eb0c0 100644 --- a/zkevm-circuits/src/rw_table.rs +++ b/zkevm-circuits/src/rw_table.rs @@ -6,15 +6,54 @@ use halo2_proofs::{ poly::Rotation, }; +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.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, @@ -28,26 +67,10 @@ pub struct RwTable { 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.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()), - ] - } -} impl RwTable { 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(), @@ -62,17 +85,47 @@ impl RwTable { } } + 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, - rw_map: &RwMap, - ) -> Result>, Error> { - let rows = rw_map.table_assignments(randomness); + rows: &[RwRow], + ) -> Result<(), Error> { for (offset, row) in rows.iter().enumerate() { self.assign_row(region, offset, randomness, row)?; } - Ok(rows) + 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, diff --git a/zkevm-circuits/src/state_circuit.rs b/zkevm-circuits/src/state_circuit.rs index c7c4ee9a6e..798204da81 100644 --- a/zkevm-circuits/src/state_circuit.rs +++ b/zkevm-circuits/src/state_circuit.rs @@ -123,11 +123,7 @@ impl region.assign_fixed(|| "selector", config.selector, offset, || Ok(F::one()))?; config - .rw_table - .assign_row(region, offset, self.randomness, &row)?; - tag_chip.assign(region, offset, &row.tag)?; - config .rw_counter_mpi .assign(region, offset, row.rw_counter as u32)?; config.id_mpi.assign(region, offset, row.id as u32)?; @@ -280,6 +276,9 @@ where layouter.assign_region( || "rw table", |mut region| { + config + .rw_table + .assign(&mut region, self.randomness, &self.rows)?; let padding_length = N_ROWS - self.rows.len(); let padding = (1..=padding_length) .map(|rw_counter| (Rw::Start { rw_counter }).table_assignment(self.randomness)); @@ -352,5 +351,6 @@ fn queries( .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/constraint_builder.rs b/zkevm-circuits/src/state_circuit/constraint_builder.rs index 7c036abcba..0e3251acdb 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::util::rlc; use crate::evm_circuit::{ param::N_BYTES_WORD, table::{AccountFieldTag, RwTableTag}, @@ -35,6 +36,7 @@ pub struct Queries { 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); @@ -151,6 +153,25 @@ impl ConstraintBuilder { 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) { diff --git a/zkevm-circuits/src/state_circuit/test.rs b/zkevm-circuits/src/state_circuit/test.rs index f0d4d6a689..37f188b5b6 100644 --- a/zkevm-circuits/src/state_circuit/test.rs +++ b/zkevm-circuits/src/state_circuit/test.rs @@ -833,6 +833,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, .. } => { From 3f1b8e4f2042c07b0bb6fe6dc651a95de308f791 Mon Sep 17 00:00:00 2001 From: Zhang Zhuo Date: Mon, 4 Jul 2022 12:11:30 +0800 Subject: [PATCH 3/3] merge friendly --- zkevm-circuits/src/evm_circuit/witness.rs | 9 +- zkevm-circuits/src/rw_table.rs | 4 +- zkevm-circuits/src/state_circuit.rs | 246 ++++++++---------- .../src/state_circuit/constraint_builder.rs | 57 ++-- .../state_circuit/lexicographic_ordering.rs | 22 +- .../multiple_precision_integer.rs | 3 +- .../random_linear_combination.rs | 2 +- zkevm-circuits/src/state_circuit/test.rs | 34 ++- 8 files changed, 175 insertions(+), 202 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit/witness.rs b/zkevm-circuits/src/evm_circuit/witness.rs index 7f4f03b17c..a548525ccb 100644 --- a/zkevm-circuits/src/evm_circuit/witness.rs +++ b/zkevm-circuits/src/evm_circuit/witness.rs @@ -446,7 +446,7 @@ impl RwMap { } } - pub fn table_assignments(&self, randomness: F) -> Vec> + pub fn table_assignments(&self, _randomness: F) -> Vec where F: Field, { @@ -462,10 +462,7 @@ impl RwMap { row.rw_counter(), ) }); - - rows.into_iter() - .map(|r| r.table_assignment(randomness)) - .collect() + rows } } @@ -586,7 +583,7 @@ impl RwRow { let values = [ F::from(self.rw_counter), F::from(self.is_write), - (F::from(self.tag)), + (F::from(self.tag as u64)), (F::from(self.id)), (self.address.to_scalar().unwrap()), (F::from(self.field_tag)), diff --git a/zkevm-circuits/src/rw_table.rs b/zkevm-circuits/src/rw_table.rs index 86056eb0c0..a4300fedb8 100644 --- a/zkevm-circuits/src/rw_table.rs +++ b/zkevm-circuits/src/rw_table.rs @@ -38,7 +38,9 @@ impl RwTableRlc { randomness_next_phase: F, ) -> Result<(), Error> { for (offset, row) in rw_map.table_assignments(randomness).iter().enumerate() { - let value = row.rlc(randomness, randomness_next_phase); + let value = row + .table_assignment(randomness) + .rlc(randomness, randomness_next_phase); region.assign_advice( || "assign rw row on rw table", self.rlc, diff --git a/zkevm-circuits/src/state_circuit.rs b/zkevm-circuits/src/state_circuit.rs index 798204da81..29a1c122e2 100644 --- a/zkevm-circuits/src/state_circuit.rs +++ b/zkevm-circuits/src/state_circuit.rs @@ -13,17 +13,18 @@ use crate::{ param::N_BYTES_WORD, table::RwTableTag, util::RandomLinearCombination, - witness::{Rw, RwMap, RwRow}, + witness::{Rw, RwMap}, }, - rw_table::RwTable, }; use binary_number::{Chip as BinaryNumberChip, Config as BinaryNumberConfig}; use constraint_builder::{ConstraintBuilder, Queries}; use eth_types::{Address, Field, ToLittleEndian}; use gadgets::is_zero::{IsZeroChip, IsZeroConfig, IsZeroInstruction}; use halo2_proofs::{ - circuit::{Layouter, Region, SimpleFloorPlanner}, - plonk::{Circuit, Column, ConstraintSystem, Error, Expression, Fixed, Instance, VirtualCells}, + circuit::{Layouter, SimpleFloorPlanner}, + plonk::{ + Advice, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, Instance, VirtualCells, + }, poly::Rotation, }; use lexicographic_ordering::{ @@ -43,17 +44,18 @@ const N_LIMBS_ID: usize = 2; /// Config for StateCircuit #[derive(Clone)] pub struct StateConfig { - // Figure out why you get errors when this is Selector. + selector: Column, // Figure out why you get errors when this is Selector. // https://github.com/privacy-scaling-explorations/zkevm-circuits/issues/407 - selector: Column, - rw_table: RwTable, - tag_bits: BinaryNumberConfig, - rw_counter_mpi: MpiConfig, - id_mpi: MpiConfig, - address_mpi: MpiConfig, - storage_key_rlc: RlcConfig, + rw_counter: MpiConfig, + is_write: Column, + tag: BinaryNumberConfig, + id: MpiConfig, is_id_unchanged: IsZeroConfig, + address: MpiConfig, + field_tag: Column, + storage_key: RlcConfig, is_storage_key_unchanged: IsZeroConfig, + value: Column, lookups: LookupsConfig, power_of_randomness: [Column; N_BYTES_WORD - 1], lexicographic_ordering: LexicographicOrderingConfig, @@ -74,7 +76,7 @@ pub type StateCircuitLight = StateCircuitBase { pub(crate) randomness: F, - pub(crate) rows: Vec>, + pub(crate) rows: Vec, #[cfg(test)] overrides: HashMap<(test::AdviceColumn, isize), F>, } @@ -107,64 +109,6 @@ impl .map(|exp| vec![self.randomness.pow(&[exp, 0, 0, 0]); N_ROWS]) .collect() } - #[allow(clippy::too_many_arguments)] - fn assign_row( - &self, - config: &StateConfig, - region: &mut Region, - tag_chip: &binary_number::Chip, - is_storage_key_unchanged: &IsZeroChip, - is_id_unchanged: &IsZeroChip, - lexicographic_ordering_chip: &LexicographicOrderingChip, - offset: usize, - row: RwRow, - prev_row: Option>, - ) -> Result<(), Error> { - region.assign_fixed(|| "selector", config.selector, offset, || Ok(F::one()))?; - - config - tag_chip.assign(region, offset, &row.tag)?; - .rw_counter_mpi - .assign(region, offset, row.rw_counter as u32)?; - config.id_mpi.assign(region, offset, row.id as u32)?; - config.address_mpi.assign(region, offset, row.address)?; - config - .storage_key_rlc - .assign(region, offset, self.randomness, row.storage_key)?; - - if offset != 0 { - lexicographic_ordering_chip.assign(region, offset, &row, &prev_row.unwrap())?; - - // assign storage key diff - let cur_storage_key = RandomLinearCombination::random_linear_combine( - row.storage_key.to_le_bytes(), - self.randomness, - ); - let prev_storage_key = RandomLinearCombination::random_linear_combine( - prev_row.unwrap_or_default().storage_key.to_le_bytes(), - self.randomness, - ); - is_storage_key_unchanged.assign( - region, - offset, - Some(cur_storage_key - prev_storage_key), - )?; - - // assign id diff - let id_change = - F::from(row.id as u64) - F::from(prev_row.unwrap_or_default().id as u64); - is_id_unchanged.assign(region, offset, Some(id_change))?; - let _storage_key_change = RandomLinearCombination::random_linear_combine( - row.storage_key.to_le_bytes(), - self.randomness, - ) - RandomLinearCombination::random_linear_combine( - prev_row.unwrap_or_default().storage_key.to_le_bytes(), - self.randomness, - ); - } - - Ok(()) - } } impl Circuit @@ -184,30 +128,24 @@ where let lookups = LookupsChip::configure(meta); let power_of_randomness = [0; N_BYTES_WORD - 1].map(|_| meta.instance_column()); - let rw_table = RwTable::construct(meta); - let is_storage_key_unchanged_column = meta.advice_column(); - let is_id_unchanged_column = meta.advice_column(); - let id_mpi = MpiChip::configure(meta, rw_table.id, selector, lookups); - let address_mpi = MpiChip::configure(meta, rw_table.address, selector, lookups); - let storage_key_rlc = RlcChip::configure( - meta, - selector, - rw_table.storage_key, - lookups, - power_of_randomness, - ); - let rw_counter_mpi = MpiChip::configure(meta, rw_table.rw_counter, selector, lookups); + let [is_write, field_tag, value, is_id_unchanged_column, is_storage_key_unchanged_column] = + [0; 5].map(|_| meta.advice_column()); + + let tag = BinaryNumberChip::configure(meta, selector); - let tag_bits = BinaryNumberChip::configure(meta, selector); + 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, - tag_bits, - rw_table.field_tag, - id_mpi.limbs, - address_mpi.limbs, - storage_key_rlc.bytes, - rw_counter_mpi.limbs, + tag, + field_tag, + id.limbs, + address.limbs, + storage_key.bytes, + rw_counter.limbs, lookups, ); @@ -215,8 +153,8 @@ where meta, |meta| meta.query_fixed(lexicographic_ordering.selector, Rotation::cur()), |meta| { - meta.query_advice(rw_table.id, Rotation::cur()) - - meta.query_advice(rw_table.id, Rotation::prev()) + meta.query_advice(id.value, Rotation::cur()) + - meta.query_advice(id.value, Rotation::prev()) }, is_id_unchanged_column, ); @@ -224,22 +162,24 @@ where meta, |meta| meta.query_fixed(lexicographic_ordering.selector, Rotation::cur()), |meta| { - meta.query_advice(rw_table.storage_key, Rotation::cur()) - - meta.query_advice(rw_table.storage_key, Rotation::prev()) + meta.query_advice(storage_key.encoded, Rotation::cur()) + - meta.query_advice(storage_key.encoded, Rotation::prev()) }, is_storage_key_unchanged_column, ); let config = Self::Config { selector, - rw_table, - lexicographic_ordering, - address_mpi, - tag_bits, - id_mpi, - rw_counter_mpi, - storage_key_rlc, + rw_counter, + is_write, + tag, + id, is_id_unchanged, + address, + field_tag, + storage_key, + value, + lexicographic_ordering, is_storage_key_unchanged, lookups, power_of_randomness, @@ -271,34 +211,79 @@ where let lexicographic_ordering_chip = LexicographicOrderingChip::construct(config.lexicographic_ordering.clone()); - let tag_chip = BinaryNumberChip::construct(config.tag_bits); + let tag_chip = BinaryNumberChip::construct(config.tag); layouter.assign_region( || "rw table", |mut region| { - config - .rw_table - .assign(&mut region, self.randomness, &self.rows)?; let padding_length = N_ROWS - self.rows.len(); - let padding = (1..=padding_length) - .map(|rw_counter| (Rw::Start { rw_counter }).table_assignment(self.randomness)); + let padding = (1..=padding_length).map(|rw_counter| Rw::Start { rw_counter }); let rows = padding.chain(self.rows.iter().cloned()); 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); - self.assign_row( - &config, - &mut region, - &tag_chip, - &is_storage_key_unchanged, - &is_id_unchanged, - &lexicographic_ordering_chip, + region.assign_fixed(|| "selector", config.selector, offset, || Ok(F::one()))?; + config + .rw_counter + .assign(&mut region, offset, row.rw_counter() as u32)?; + region.assign_advice( + || "is_write", + config.is_write, offset, - row, - prev_row, + || Ok(if row.is_write() { F::one() } else { F::zero() }), )?; + tag_chip.assign(&mut region, offset, &row.tag())?; + if let Some(id) = row.id() { + config.id.assign(&mut region, offset, id as u32)?; + } + if let Some(address) = row.address() { + config.address.assign(&mut region, offset, address)?; + } + if let Some(field_tag) = row.field_tag() { + region.assign_advice( + || "field_tag", + config.field_tag, + offset, + || Ok(F::from(field_tag as u64)), + )?; + } + if let Some(storage_key) = row.storage_key() { + config.storage_key.assign( + &mut region, + offset, + self.randomness, + storage_key, + )?; + } + region.assign_advice( + || "value", + config.value, + offset, + || Ok(row.value_assignment(self.randomness)), + )?; + + if let Some(prev_row) = prev_row { + lexicographic_ordering_chip.assign(&mut region, offset, &row, &prev_row)?; + + let id_change = F::from(row.id().unwrap_or_default() as u64) + - F::from(prev_row.id().unwrap_or_default() as u64); + is_id_unchanged.assign(&mut region, offset, Some(id_change))?; + + let storage_key_change = RandomLinearCombination::random_linear_combine( + row.storage_key().unwrap_or_default().to_le_bytes(), + self.randomness, + ) - RandomLinearCombination::random_linear_combine( + prev_row.storage_key().unwrap_or_default().to_le_bytes(), + self.randomness, + ); + is_storage_key_unchanged.assign( + &mut region, + offset, + Some(storage_key_change), + )?; + } } #[cfg(test)] @@ -322,25 +307,24 @@ fn queries( ) -> Queries { Queries { selector: meta.query_fixed(c.selector, Rotation::cur()), - rw_counter: MpiQueries::new(meta, c.rw_counter_mpi), - is_write: meta.query_advice(c.rw_table.is_write, Rotation::cur()), lexicographic_ordering_selector: meta .query_fixed(c.lexicographic_ordering.selector, Rotation::cur()), - aux2: meta.query_advice(c.rw_table.aux2, Rotation::cur()), - tag: c.tag_bits.value(Rotation::cur())(meta), + rw_counter: MpiQueries::new(meta, c.rw_counter), + is_write: meta.query_advice(c.is_write, Rotation::cur()), + tag: c.tag.value(Rotation::cur())(meta), tag_bits: c - .tag_bits + .tag .bits .map(|bit| meta.query_advice(bit, Rotation::cur())), - //prev_tag: meta.query_advice(c.rw_table.tag, Rotation::prev()), - id: MpiQueries::new(meta, c.id_mpi), + //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_mpi), - field_tag: meta.query_advice(c.rw_table.field_tag, Rotation::cur()), - storage_key: RlcQueries::new(meta, c.storage_key_rlc), - value: meta.query_advice(c.rw_table.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()), + 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 @@ -351,6 +335,6 @@ fn queries( .upper_limb_difference_is_zero .is_zero_expression .clone(), - rw_rlc: meta.query_advice(c.rw_table.rlc, Rotation::cur()), + //rw_rlc: meta.query_advice(c.rw_table.rlc, Rotation::cur()), } } diff --git a/zkevm-circuits/src/state_circuit/constraint_builder.rs b/zkevm-circuits/src/state_circuit/constraint_builder.rs index 0e3251acdb..5a0e202f0a 100644 --- a/zkevm-circuits/src/state_circuit/constraint_builder.rs +++ b/zkevm-circuits/src/state_circuit/constraint_builder.rs @@ -4,11 +4,11 @@ use super::{ random_linear_combination::Queries as RlcQueries, N_LIMBS_ACCOUNT_ADDRESS, N_LIMBS_ID, N_LIMBS_RW_COUNTER, }; -use crate::evm_circuit::util::rlc; + use crate::evm_circuit::{ param::N_BYTES_WORD, table::{AccountFieldTag, RwTableTag}, - util::{not, or, select}, + util::{not, or}, }; use crate::util::Expr; use eth_types::Field; @@ -22,7 +22,7 @@ pub struct Queries { pub rw_counter: MpiQueries, pub is_write: Expression, pub tag: Expression, - pub aux2: Expression, + //pub aux2: Expression, pub tag_bits: [Expression; 4], pub id: MpiQueries, pub is_id_unchanged: Expression, @@ -30,13 +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 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, + //pub rw_rlc: Expression, } type Constraint = (&'static str, Expression); @@ -108,6 +108,7 @@ impl ConstraintBuilder { // 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 @@ -172,6 +173,7 @@ impl ConstraintBuilder { &q.power_of_randomness, ) }) + */ } fn build_start_constraints(&mut self, q: &Queries) { @@ -220,10 +222,10 @@ impl ConstraintBuilder { q.address_change(), ) }); - self.require_zero( - "prev_value is 0 when keys changed", - q.first_access() * q.value_prev.clone(), - ); + //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) { @@ -239,8 +241,6 @@ impl ConstraintBuilder { // cb.require_zero("first access is a write", q.is_write()); // // cb.require_zero("first access rw_counter is 0", // q.rw_counter.value.clone()); }) - - // TODO: value_prev == committed_value when keys changed } fn build_tx_access_list_account_constraints(&mut self, q: &Queries) { self.require_zero("field_tag is 0 for TxAccessListAccount", q.field_tag()); @@ -248,10 +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(), - ); + //self.require_zero( + // "prev_value is 0 when keys changed", + // q.first_access() * q.value_prev.clone(), + //); // TODO: Missing constraints } @@ -260,10 +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(), - ); + //self.require_zero( + // "prev_value is 0 when keys changed", + // q.first_access() * q.value_prev.clone(), + //); // TODO: Missing constraints } @@ -274,10 +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(), - ); + //self.require_zero( + // "prev_value is 0 when keys changed", + // q.first_access() * q.value_prev.clone(), + //); // TODO: Missing constraints } @@ -394,15 +394,6 @@ impl Queries { fn tag_matches(&self, tag: RwTableTag) -> Expression { BinaryNumberConfig::::value_equals_expr(tag, self.tag_bits.clone()) } - fn multi_tag_match(&self, target_tags: Vec) -> Expression { - let mut numerator = 1u64.expr(); - for unmatched_tag in RwTableTag::iter() { - if !target_tags.contains(&unmatched_tag) { - numerator = numerator * (self.tag.expr() - unmatched_tag.expr()); - } - } - numerator - } fn first_access(&self) -> Expression { // upper diff changed OR storage key changed diff --git a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs index 2cd858c17d..1c86d87e58 100644 --- a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs +++ b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs @@ -3,7 +3,11 @@ use super::{ N_LIMBS_RW_COUNTER, }; use crate::{ - evm_circuit::{param::N_BYTES_WORD, table::RwTableTag, witness::RwRow}, + evm_circuit::{ + param::N_BYTES_WORD, + table::RwTableTag, + witness::{Rw}, + }, util::Expr, }; use eth_types::{Field, ToBigEndian}; @@ -213,8 +217,8 @@ impl Chip { &self, region: &mut Region<'_, F>, offset: usize, - cur: &RwRow, - prev: &RwRow, + cur: &Rw, + prev: &Rw, ) -> Result<(), Error> { region.assign_fixed( || "upper_limb_difference", @@ -322,19 +326,19 @@ impl Queries { } } -fn rw_to_be_limbs(row: &RwRow) -> Vec { - let mut id = row.id as u32; +fn rw_to_be_limbs(row: &Rw) -> Vec { + let mut id = row.id().unwrap_or_default() as u32; assert_eq!(id.to_be_bytes().len(), 4); // The max value of `id` is 2^23 - 1, so the 9 most significant bits should be // 0. We use these 9 bits to hold value of `tag` and `field_tag`. assert!(id < (1 << 23)); - id += (((row.tag as u32) << 5) + (row.field_tag as u32)) << 23; + id += (((row.tag() as u32) << 5) + (row.field_tag().unwrap_or_default() as u32)) << 23; let mut be_bytes = vec![]; be_bytes.extend_from_slice(&id.to_be_bytes()); - be_bytes.extend_from_slice(&(row.address.0)); - be_bytes.extend_from_slice(&(row.storage_key.to_be_bytes())); - be_bytes.extend_from_slice(&((row.rw_counter as u32).to_be_bytes())); + be_bytes.extend_from_slice(&(row.address().unwrap_or_default().0)); + be_bytes.extend_from_slice(&(row.storage_key().unwrap_or_default().to_be_bytes())); + be_bytes.extend_from_slice(&((row.rw_counter() as u32).to_be_bytes())); be_bytes .iter() diff --git a/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs b/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs index bc831035e5..dfbe80889c 100644 --- a/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs +++ b/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs @@ -131,10 +131,10 @@ where pub fn configure( meta: &mut ConstraintSystem, - value: Column, selector: Column, lookup: lookups::Config, ) -> Config { + let value = meta.advice_column(); let limbs = [0; N].map(|_| meta.advice_column()); for &limb in &limbs { @@ -142,7 +142,6 @@ where meta.query_advice(limb, Rotation::cur()) }); } - meta.create_gate("mpi value matches claimed limbs", |meta| { let selector = meta.query_fixed(selector, Rotation::cur()); let value = meta.query_advice(value, Rotation::cur()); diff --git a/zkevm-circuits/src/state_circuit/random_linear_combination.rs b/zkevm-circuits/src/state_circuit/random_linear_combination.rs index 1edf36fa2e..8749dda344 100644 --- a/zkevm-circuits/src/state_circuit/random_linear_combination.rs +++ b/zkevm-circuits/src/state_circuit/random_linear_combination.rs @@ -75,10 +75,10 @@ impl Chip { pub fn configure( meta: &mut ConstraintSystem, selector: Column, - encoded: 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 { diff --git a/zkevm-circuits/src/state_circuit/test.rs b/zkevm-circuits/src/state_circuit/test.rs index 37f188b5b6..c2dd398d83 100644 --- a/zkevm-circuits/src/state_circuit/test.rs +++ b/zkevm-circuits/src/state_circuit/test.rs @@ -49,22 +49,22 @@ impl AdviceColumn { config: &StateConfig, ) -> Column { match self { - Self::IsWrite => config.rw_table.is_write, - Self::Address => config.rw_table.address, - Self::AddressLimb0 => config.address_mpi.limbs[0], - Self::AddressLimb1 => config.address_mpi.limbs[1], - Self::StorageKey => config.rw_table.storage_key, - Self::StorageKeyByte0 => config.storage_key_rlc.bytes[0], - Self::StorageKeyByte1 => config.storage_key_rlc.bytes[1], + Self::IsWrite => config.is_write, + Self::Address => config.address.value, + Self::AddressLimb0 => config.address.limbs[0], + Self::AddressLimb1 => config.address.limbs[1], + Self::StorageKey => config.storage_key.encoded, + Self::StorageKeyByte0 => config.storage_key.bytes[0], + Self::StorageKeyByte1 => config.storage_key.bytes[1], Self::StorageKeyChangeInverse => config.is_storage_key_unchanged.value_inv, - Self::Value => config.rw_table.value, - Self::RwCounter => config.rw_table.rw_counter, - Self::RwCounterLimb0 => config.rw_counter_mpi.limbs[0], - Self::RwCounterLimb1 => config.rw_counter_mpi.limbs[1], - Self::TagBit0 => config.tag_bits.bits[0], - Self::TagBit1 => config.tag_bits.bits[1], - Self::TagBit2 => config.tag_bits.bits[2], - Self::TagBit3 => config.tag_bits.bits[3], + Self::Value => config.value, + Self::RwCounter => config.rw_counter.value, + Self::RwCounterLimb0 => config.rw_counter.limbs[0], + Self::RwCounterLimb1 => config.rw_counter.limbs[1], + Self::TagBit0 => config.tag.bits[0], + Self::TagBit1 => config.tag.bits[1], + Self::TagBit2 => config.tag.bits[2], + Self::TagBit3 => config.tag.bits[3], } } } @@ -796,10 +796,6 @@ fn invalid_tags() { fn prover(rows: Vec, overrides: HashMap<(AdviceColumn, isize), Fr>) -> MockProver { let randomness = Fr::rand(); - let rows = rows - .iter() - .map(|r| r.table_assignment(randomness)) - .collect(); let circuit = StateCircuit:: { randomness,