From 1c60be2e482684e0a5add5d876b0d09f90c9c4a2 Mon Sep 17 00:00:00 2001 From: Zhang Zhuo Date: Fri, 12 Aug 2022 10:30:56 +0800 Subject: [PATCH] add state circuit to super circuit (#670) * add state circuit to super circuit * fix * remove QUICK_CHECK * clean codes * address review comments Co-authored-by: Rohit Narurkar --- gadgets/src/binary_number.rs | 14 +- zkevm-circuits/src/copy_circuit.rs | 9 +- zkevm-circuits/src/evm_circuit.rs | 10 +- zkevm-circuits/src/evm_circuit/witness.rs | 98 +++-- zkevm-circuits/src/state_circuit.rs | 395 ++++++++++-------- .../src/state_circuit/constraint_builder.rs | 78 ++-- .../state_circuit/lexicographic_ordering.rs | 19 +- zkevm-circuits/src/state_circuit/lookups.rs | 44 +- .../multiple_precision_integer.rs | 40 +- .../random_linear_combination.rs | 38 +- zkevm-circuits/src/state_circuit/test.rs | 21 +- zkevm-circuits/src/super_circuit.rs | 26 +- zkevm-circuits/src/table.rs | 84 ++-- 13 files changed, 498 insertions(+), 378 deletions(-) diff --git a/gadgets/src/binary_number.rs b/gadgets/src/binary_number.rs index 111c67b968..232f54548d 100644 --- a/gadgets/src/binary_number.rs +++ b/gadgets/src/binary_number.rs @@ -101,7 +101,7 @@ where /// - creating expressions (via the Config) that evaluate to 1 when the bits /// match a specific value and 0 otherwise. #[derive(Clone, Debug)] -pub struct BinaryNumberChip { +pub struct BinaryNumberChip { config: BinaryNumberConfig, _marker: PhantomData, } @@ -122,6 +122,7 @@ where pub fn configure( meta: &mut ConstraintSystem, selector: Column, + value: Option>, ) -> BinaryNumberConfig { let bits = [0; N].map(|_| meta.advice_column()); bits.map(|bit| { @@ -137,6 +138,17 @@ where _marker: PhantomData, }; + if let Some(value) = value { + meta.create_gate("binary number value", |meta| { + let selector = meta.query_fixed(selector, Rotation::cur()); + vec![ + selector + * (config.value(Rotation::cur())(meta) + - meta.query_advice(value, Rotation::cur())), + ] + }); + } + // Disallow bit patterns (if any) that don't correspond to a variant of T. let valid_values: BTreeSet = T::iter().map(|t| from_bits(&t.as_bits())).collect(); let mut invalid_values = (0..1 << N).filter(|i| !valid_values.contains(i)).peekable(); diff --git a/zkevm-circuits/src/copy_circuit.rs b/zkevm-circuits/src/copy_circuit.rs index 7e6a3b1845..806542bd78 100644 --- a/zkevm-circuits/src/copy_circuit.rs +++ b/zkevm-circuits/src/copy_circuit.rs @@ -770,9 +770,12 @@ mod tests { config .tx_table .load(&mut layouter, &self.block.txs, self.randomness)?; - config - .rw_table - .load(&mut layouter, &self.block.rws, self.randomness)?; + config.rw_table.load( + &mut layouter, + &self.block.rws.table_assignments(), + self.block.state_circuit_pad_to, + self.randomness, + )?; config.bytecode_table.load( &mut layouter, self.block.bytecodes.values(), diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 4d259eaf06..9bb8479e5d 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -264,9 +264,13 @@ pub mod test { config .tx_table .load(&mut layouter, &self.block.txs, self.block.randomness)?; - config - .rw_table - .load(&mut layouter, &self.block.rws, self.block.randomness)?; + self.block.rws.check_rw_counter_sanity(); + config.rw_table.load( + &mut layouter, + &self.block.rws.table_assignments(), + self.block.state_circuit_pad_to, + self.block.randomness, + )?; config.bytecode_table.load( &mut layouter, self.block.bytecodes.values(), diff --git a/zkevm-circuits/src/evm_circuit/witness.rs b/zkevm-circuits/src/evm_circuit/witness.rs index 149a390ddc..2851c97d8b 100644 --- a/zkevm-circuits/src/evm_circuit/witness.rs +++ b/zkevm-circuits/src/evm_circuit/witness.rs @@ -40,6 +40,8 @@ pub struct Block { pub context: BlockContext, /// Copy events for the EVM circuit's copy table. pub copy_events: Vec, + /// Length to rw table rows in state circuit + pub state_circuit_pad_to: usize, } #[derive(Debug, Default, Clone)] @@ -377,7 +379,62 @@ impl std::ops::Index<(RwTableTag, usize)> for RwMap { &self.0.get(&tag).unwrap()[idx] } } - +impl RwMap { + /// Check rw_counter is continuous 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); + } + } + /// Calculates the number of Rw::Start rows needed. + /// `target_len` is allowed to be 0 as an "auto" mode, + /// then only 1 Rw::Start row will be prepadded. + pub(crate) fn padding_len(rows: &[Rw], target_len: usize) -> usize { + if target_len > rows.len() { + target_len - rows.len() + } else { + if target_len != 0 { + log::error!( + "RwMap::padding_len overflow, target_len: {}, rows.len(): {}", + target_len, + rows.len() + ); + } + 1 + } + } + /// Prepad Rw::Start rows to target length + pub fn table_assignments_prepad(rows: &[Rw], target_len: usize) -> (Vec, usize) { + let padding_length = Self::padding_len(rows, target_len); + let padding = (1..=padding_length).map(|rw_counter| Rw::Start { rw_counter }); + ( + padding.chain(rows.iter().cloned()).collect(), + padding_length, + ) + } + /// Build Rws for assignment + pub fn table_assignments(&self) -> Vec { + let mut rows: Vec = self.0.values().flatten().cloned().collect(); + rows.sort_by_key(|row| { + ( + row.tag() as u64, + row.id().unwrap_or_default(), + row.address().unwrap_or_default(), + row.field_tag().unwrap_or_default(), + row.storage_key().unwrap_or_default(), + row.rw_counter(), + ) + }); + rows + } +} #[derive(Clone, Copy, Debug)] pub enum Rw { Start { @@ -476,39 +533,21 @@ pub enum Rw { value: u64, }, } -#[derive(Default, Clone, Copy)] -pub struct RwRow { +#[derive(Default, Clone, Copy, Debug)] +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, + pub id: F, + pub address: F, + pub field_tag: F, + pub storage_key: F, 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 { @@ -608,10 +647,10 @@ impl Rw { 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( + id: F::from(self.id().unwrap_or_default() as u64), + address: self.address().unwrap_or_default().to_scalar().unwrap(), + field_tag: F::from(self.field_tag().unwrap_or_default() as u64), + storage_key: RandomLinearCombination::random_linear_combine( self.storage_key().unwrap_or_default().to_le_bytes(), randomness, ), @@ -1384,5 +1423,6 @@ pub fn block_convert( }) .collect(), copy_events: block.copy_events.clone(), + ..Default::default() } } diff --git a/zkevm-circuits/src/state_circuit.rs b/zkevm-circuits/src/state_circuit.rs index 9ba2628fef..0c0d2a40c4 100644 --- a/zkevm-circuits/src/state_circuit.rs +++ b/zkevm-circuits/src/state_circuit.rs @@ -12,19 +12,15 @@ use crate::{ param::N_BYTES_WORD, witness::{Rw, RwMap}, }, - table::RwTableTag, + table::{RwTable, RwTableTag}, + util::{power_of_randomness_from_instance, Expr}, }; use constraint_builder::{ConstraintBuilder, Queries}; use eth_types::{Address, Field}; -use gadgets::{ - binary_number::{BinaryNumberChip, BinaryNumberConfig}, - util::Expr, -}; +use gadgets::binary_number::{BinaryNumberChip, BinaryNumberConfig}; use halo2_proofs::{ - circuit::{Layouter, SimpleFloorPlanner}, - plonk::{ - Advice, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, Instance, VirtualCells, - }, + circuit::{Layouter, Region, SimpleFloorPlanner}, + plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, poly::Rotation, }; use lexicographic_ordering::Config as LexicographicOrderingConfig; @@ -35,24 +31,174 @@ use random_linear_combination::{Chip as RlcChip, Config as RlcConfig, Queries as use std::collections::HashMap; use std::iter::once; +use self::constraint_builder::RwTableQueries; + const N_LIMBS_RW_COUNTER: usize = 2; const N_LIMBS_ACCOUNT_ADDRESS: usize = 10; const N_LIMBS_ID: usize = 2; /// Config for StateCircuit -#[derive(Clone, Copy)] -pub struct StateConfig { +#[derive(Clone)] +pub struct StateCircuitConfig { selector: Column, // Figure out why you get errors when this is Selector. // https://github.com/privacy-scaling-explorations/zkevm-circuits/issues/407 + rw_table: RwTable, sort_keys: SortKeysConfig, - is_write: Column, - value: Column, initial_value: Column, /* Assigned value at the start of the block. For Rw::Account * and Rw::AccountStorage rows this is the committed value in * the MPT, for others, it is 0. */ lexicographic_ordering: LexicographicOrderingConfig, lookups: LookupsConfig, - power_of_randomness: [Column; N_BYTES_WORD - 1], + power_of_randomness: [Expression; N_BYTES_WORD - 1], +} + +impl StateCircuitConfig { + /// Configure StateCircuit + pub fn configure( + meta: &mut ConstraintSystem, + power_of_randomness: [Expression; 31], + rw_table: &RwTable, + ) -> Self { + let selector = meta.fixed_column(); + let lookups = LookupsChip::configure(meta); + + let rw_counter = MpiChip::configure(meta, selector, rw_table.rw_counter, lookups); + let tag = BinaryNumberChip::configure(meta, selector, Some(rw_table.tag)); + let id = MpiChip::configure(meta, selector, rw_table.id, lookups); + let address = MpiChip::configure(meta, selector, rw_table.address, lookups); + let storage_key = RlcChip::configure( + meta, + selector, + rw_table.storage_key, + lookups, + power_of_randomness.clone(), + ); + + let initial_value = meta.advice_column(); + + let sort_keys = SortKeysConfig { + tag, + id, + field_tag: rw_table.field_tag, + address, + storage_key, + rw_counter, + }; + + let lexicographic_ordering = LexicographicOrderingConfig::configure( + meta, + sort_keys, + lookups, + power_of_randomness.clone(), + ); + + let config = Self { + selector, + sort_keys, + initial_value, + lexicographic_ordering, + lookups, + power_of_randomness, + rw_table: *rw_table, + }; + + let mut constraint_builder = ConstraintBuilder::new(); + meta.create_gate("state circuit constraints", |meta| { + let queries = queries(meta, &config); + constraint_builder.build(&queries); + constraint_builder.gate(queries.selector) + }); + for (name, expressions) in constraint_builder.lookups() { + meta.lookup_any(name, |_| vec![expressions]); + } + + config + } + + /// load fixed tables + pub(crate) fn load(&self, layouter: &mut impl Layouter) -> Result<(), Error> { + LookupsChip::construct(self.lookups).load(layouter) + } + /// Make the assignments to the StateCircuit + pub fn assign( + &self, + layouter: &mut impl Layouter, + rows: &[Rw], + n_rows: usize, + randomness: F, + ) -> Result<(), Error> { + layouter.assign_region( + || "state circuit", + |mut region| self.assign_with_region(&mut region, rows, n_rows, randomness), + ) + } + + fn assign_with_region( + &self, + region: &mut Region<'_, F>, + rows: &[Rw], + n_rows: usize, + randomness: F, + ) -> Result<(), Error> { + let tag_chip = BinaryNumberChip::construct(self.sort_keys.tag); + + let (rows, padding_length) = RwMap::table_assignments_prepad(rows, n_rows); + let rows = rows.into_iter(); + let prev_rows = once(None).chain(rows.clone().map(Some)); + + let mut initial_value = F::zero(); + + for (offset, (row, prev_row)) in rows.zip(prev_rows).enumerate() { + if offset >= padding_length { + log::trace!("state circuit assign offset:{} row:{:#?}", offset, row); + } + + region.assign_fixed(|| "selector", self.selector, offset, || Ok(F::one()))?; + + tag_chip.assign(region, offset, &row.tag())?; + + self.sort_keys + .rw_counter + .assign(region, offset, row.rw_counter() as u32)?; + if let Some(id) = row.id() { + self.sort_keys.id.assign(region, offset, id as u32)?; + } + if let Some(address) = row.address() { + self.sort_keys.address.assign(region, offset, address)?; + } + if let Some(storage_key) = row.storage_key() { + self.sort_keys + .storage_key + .assign(region, offset, randomness, storage_key)?; + } + + if let Some(prev_row) = prev_row { + let is_first_access = self + .lexicographic_ordering + .assign(region, offset, &row, &prev_row)?; + + // TODO: Get initial_values from MPT updates instead. + if is_first_access { + // TODO: Set initial values for Rw::CallContext to be 0 instead of + // special casing it. + initial_value = if matches!(row.tag(), RwTableTag::CallContext) { + row.value_assignment(randomness) + } else { + row.value_prev_assignment(randomness).unwrap_or_default() + }; + } + } + + region.assign_advice( + || "initial_value", + self.initial_value, + offset, + || Ok(initial_value), + )?; + } + + Ok(()) + } } /// Keys for sorting the rows of the state circuit @@ -69,11 +215,11 @@ pub struct SortKeysConfig { type Lookup = (&'static str, Expression, Expression); /// State Circuit for proving RwTable is valid -#[derive(Default)] -pub struct StateCircuit { - pub(crate) randomness: F, +#[derive(Default, Clone)] +pub struct StateCircuit { pub(crate) rows: Vec, pub(crate) n_rows: usize, + pub(crate) randomness: F, #[cfg(test)] overrides: HashMap<(test::AdviceColumn, isize), F>, } @@ -81,17 +227,7 @@ pub struct StateCircuit { impl StateCircuit { /// make a new state circuit from an RwMap pub fn new(randomness: F, rw_map: RwMap, n_rows: usize) -> Self { - let mut rows: Vec<_> = rw_map.0.into_values().flatten().collect(); - rows.sort_by_key(|row| { - ( - row.tag() as u64, - row.id().unwrap_or_default(), - row.address().unwrap_or_default(), - row.field_tag().unwrap_or_default(), - row.storage_key().unwrap_or_default(), - row.rw_counter(), - ) - }); + let rows = rw_map.table_assignments(); Self { randomness, rows, @@ -109,8 +245,11 @@ impl StateCircuit { } } -impl Circuit for StateCircuit { - type Config = StateConfig; +impl Circuit for StateCircuit +where + F: Field, +{ + type Config = StateCircuitConfig; type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { @@ -118,57 +257,9 @@ impl Circuit for StateCircuit { } fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let selector = meta.fixed_column(); - let lookups = LookupsChip::configure(meta); - let power_of_randomness = [0; N_BYTES_WORD - 1].map(|_| meta.instance_column()); - - let [is_write, field_tag, value, initial_value] = [0; 4].map(|_| meta.advice_column()); - - 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 sort_keys = SortKeysConfig { - tag, - id, - field_tag, - address, - storage_key, - rw_counter, - }; - - let lexicographic_ordering = LexicographicOrderingConfig::configure( - meta, - sort_keys, - lookups.u16, - power_of_randomness, - ); - - let config = Self::Config { - selector, - sort_keys, - is_write, - value, - initial_value, - lexicographic_ordering, - lookups, - power_of_randomness, - }; - - let mut constraint_builder = ConstraintBuilder::new(); - meta.create_gate("state circuit constraints", |meta| { - let queries = queries(meta, &config); - constraint_builder.build(&queries); - constraint_builder.gate(queries.selector) - }); - for (name, expressions) in constraint_builder.lookups() { - meta.lookup_any(name, |_| vec![expressions]); - } - - config + let rw_table = RwTable::construct(meta); + let power_of_randomness: [Expression; 31] = power_of_randomness_from_instance(meta); + Self::Config::configure(meta, power_of_randomness, &rw_table) } fn synthesize( @@ -176,103 +267,32 @@ impl Circuit for StateCircuit { config: Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { - LookupsChip::construct(config.lookups).load(&mut layouter)?; - - let tag_chip = BinaryNumberChip::construct(config.sort_keys.tag); + config.load(&mut layouter)?; + // Assigning to same columns in different regions should be avoided. + // Here we use one single region to assign `overrides` to both rw table and + // other parts. layouter.assign_region( - || "rw table", + || "state circuit", |mut region| { - let padding_length = self.n_rows - self.rows.len(); - 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)); - - let mut initial_value = F::zero(); - - for (offset, (row, prev_row)) in rows.zip(prev_rows).enumerate() { - region.assign_fixed(|| "selector", config.selector, offset, || Ok(F::one()))?; - config.sort_keys.rw_counter.assign( - &mut region, - offset, - row.rw_counter() as u32, - )?; - region.assign_advice( - || "is_write", - config.is_write, - offset, - || 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.sort_keys.id.assign(&mut region, offset, id as u32)?; - } - if let Some(address) = row.address() { - config - .sort_keys - .address - .assign(&mut region, offset, address)?; - } - if let Some(field_tag) = row.field_tag() { - region.assign_advice( - || "field_tag", - config.sort_keys.field_tag, - offset, - || Ok(F::from(field_tag as u64)), - )?; - } - if let Some(storage_key) = row.storage_key() { - config.sort_keys.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 { - let is_first_access = config.lexicographic_ordering.assign( - &mut region, - offset, - &row, - &prev_row, - )?; - - // TODO: Get initial_values from MPT updates instead. - if is_first_access { - // TODO: Set initial values for Rw::CallContext to be 0 instead of - // special casing it. - initial_value = if matches!(row.tag(), RwTableTag::CallContext) { - row.value_assignment(self.randomness) - } else { - row.value_prev_assignment(self.randomness) - .unwrap_or_default() - }; - } - } - - region.assign_advice( - || "initial_value", - config.initial_value, - offset, - || Ok(initial_value), - )?; - } - + config.rw_table.load_with_region( + &mut region, + &self.rows, + self.n_rows, + self.randomness, + )?; + + config.assign_with_region(&mut region, &self.rows, self.n_rows, self.randomness)?; #[cfg(test)] - for ((column, row_offset), &f) in &self.overrides { - let advice_column = column.value(&config); - let offset = - usize::try_from(isize::try_from(padding_length).unwrap() + *row_offset) - .unwrap(); - region.assign_advice(|| "override", advice_column, offset, || Ok(f))?; + { + let padding_length = RwMap::padding_len(&self.rows, self.n_rows); + for ((column, row_offset), &f) in &self.overrides { + let advice_column = column.value(&config); + let offset = + usize::try_from(isize::try_from(padding_length).unwrap() + *row_offset) + .unwrap(); + region.assign_advice(|| "override", advice_column, offset, || Ok(f))?; + } } Ok(()) @@ -281,18 +301,32 @@ impl Circuit for StateCircuit { } } -fn queries(meta: &mut VirtualCells<'_, F>, c: &StateConfig) -> Queries { +fn queries(meta: &mut VirtualCells<'_, F>, c: &StateCircuitConfig) -> Queries { let first_different_limb = c.lexicographic_ordering.first_different_limb; let final_bits_sum = meta.query_advice(first_different_limb.bits[3], Rotation::cur()) + meta.query_advice(first_different_limb.bits[4], Rotation::cur()); Queries { selector: meta.query_fixed(c.selector, Rotation::cur()), + rw_table: RwTableQueries { + rw_counter: meta.query_advice(c.rw_table.rw_counter, Rotation::cur()), + prev_rw_counter: meta.query_advice(c.rw_table.rw_counter, Rotation::prev()), + is_write: meta.query_advice(c.rw_table.is_write, Rotation::cur()), + tag: meta.query_advice(c.rw_table.tag, Rotation::cur()), + id: meta.query_advice(c.rw_table.id, Rotation::cur()), + prev_id: meta.query_advice(c.rw_table.id, Rotation::prev()), + address: meta.query_advice(c.rw_table.address, Rotation::cur()), + prev_address: meta.query_advice(c.rw_table.address, Rotation::prev()), + field_tag: meta.query_advice(c.rw_table.field_tag, Rotation::cur()), + storage_key: meta.query_advice(c.rw_table.storage_key, Rotation::cur()), + value: meta.query_advice(c.rw_table.value, Rotation::cur()), + // TODO: we should constain value.prev() <-> value_prev.cur() later + // see https://github.com/privacy-scaling-explorations/zkevm-specs/issues/202 for more details + value_prev: meta.query_advice(c.rw_table.value, Rotation::prev()), + }, lexicographic_ordering_selector: meta .query_fixed(c.lexicographic_ordering.selector, Rotation::cur()), rw_counter: MpiQueries::new(meta, c.sort_keys.rw_counter), - is_write: meta.query_advice(c.is_write, Rotation::cur()), - tag: c.sort_keys.tag.value(Rotation::cur())(meta), tag_bits: c .sort_keys .tag @@ -309,16 +343,11 @@ fn queries(meta: &mut VirtualCells<'_, F>, c: &StateConfig) -> Queries + meta.query_advice(first_different_limb.bits[2], Rotation::cur())) + final_bits_sum.clone() * (1.expr() - final_bits_sum), address: MpiQueries::new(meta, c.sort_keys.address), - field_tag: meta.query_advice(c.sort_keys.field_tag, Rotation::cur()), storage_key: RlcQueries::new(meta, c.sort_keys.storage_key), - value: meta.query_advice(c.value, Rotation::cur()), - value_prev: meta.query_advice(c.value, Rotation::prev()), initial_value: meta.query_advice(c.initial_value, Rotation::cur()), initial_value_prev: meta.query_advice(c.initial_value, Rotation::prev()), lookups: LookupsQueries::new(meta, c.lookups), - power_of_randomness: c - .power_of_randomness - .map(|c| meta.query_instance(c, Rotation::cur())), + power_of_randomness: c.power_of_randomness.clone(), // this isn't binary! only 0 if most significant 4 bits are all 1. first_access: 4.expr() - meta.query_advice(first_different_limb.bits[0], Rotation::cur()) diff --git a/zkevm-circuits/src/state_circuit/constraint_builder.rs b/zkevm-circuits/src/state_circuit/constraint_builder.rs index f5a84b7611..c05606b365 100644 --- a/zkevm-circuits/src/state_circuit/constraint_builder.rs +++ b/zkevm-circuits/src/state_circuit/constraint_builder.rs @@ -13,21 +13,34 @@ use gadgets::binary_number::BinaryNumberConfig; use halo2_proofs::plonk::Expression; use strum::IntoEnumIterator; +#[derive(Clone)] +pub struct RwTableQueries { + pub rw_counter: Expression, + pub prev_rw_counter: Expression, + pub is_write: Expression, + pub tag: Expression, + pub id: Expression, + pub prev_id: Expression, + pub address: Expression, + pub prev_address: Expression, + pub field_tag: Expression, + pub storage_key: Expression, + pub value: Expression, + pub value_prev: Expression, + // TODO: aux1 and aux2 +} + #[derive(Clone)] pub struct Queries { pub selector: Expression, + pub rw_table: RwTableQueries, pub lexicographic_ordering_selector: Expression, pub rw_counter: MpiQueries, - pub is_write: Expression, - pub tag: Expression, pub tag_bits: [Expression; 4], pub id: MpiQueries, pub is_tag_and_id_unchanged: Expression, pub address: MpiQueries, - pub field_tag: Expression, pub storage_key: RlcQueries, - pub value: Expression, - pub value_prev: Expression, pub initial_value: Expression, pub initial_value_prev: Expression, pub lookups: LookupsQueries, @@ -113,7 +126,7 @@ impl ConstraintBuilder { self.condition(q.first_access(), |cb| { cb.require_zero( "first access reads don't change value", - q.is_read() * (q.value.clone() - q.initial_value()), + q.is_read() * (q.rw_table.value.clone() - q.initial_value()), ); }); @@ -121,7 +134,7 @@ impl ConstraintBuilder { self.condition(q.not_first_access.clone(), |cb| { cb.require_zero( "non-first access reads don't change value", - q.is_read() * (q.value.clone() - q.value_prev.clone()), + q.is_read() * (q.rw_table.value.clone() - q.rw_table.value_prev.clone()), ); cb.require_zero( "initial value doesn't change in an access group", @@ -132,9 +145,9 @@ impl ConstraintBuilder { fn build_start_constraints(&mut self, q: &Queries) { self.require_zero("field_tag is 0 for Start", q.field_tag()); - self.require_zero("address is 0 for Start", q.address.value.clone()); + self.require_zero("address is 0 for Start", q.rw_table.address.clone()); self.require_zero("id is 0 for Start", q.id()); - self.require_zero("storage_key is 0 for Start", q.storage_key.encoded.clone()); + self.require_zero("storage_key is 0 for Start", q.rw_table.storage_key.clone()); self.require_zero( "rw_counter increases by 1 for every non-first row", q.lexicographic_ordering_selector.clone() * (q.rw_counter_change() - 1.expr()), @@ -145,7 +158,10 @@ impl ConstraintBuilder { fn build_memory_constraints(&mut self, q: &Queries) { self.require_zero("field_tag is 0 for Memory", q.field_tag()); - self.require_zero("storage_key is 0 for Memory", q.storage_key.encoded.clone()); + self.require_zero( + "storage_key is 0 for Memory", + q.rw_table.storage_key.clone(), + ); // could do this more efficiently by just asserting address = limb0 + 2^16 * // limb1? for limb in &q.address.limbs[2..] { @@ -153,21 +169,21 @@ impl ConstraintBuilder { } self.add_lookup( "memory value is a byte", - (q.value.clone(), q.lookups.u8.clone()), + (q.rw_table.value.clone(), q.lookups.u8.clone()), ); self.require_zero("initial Memory value is 0", q.initial_value()); } fn build_stack_constraints(&mut self, q: &Queries) { self.require_zero("field_tag is 0 for Stack", q.field_tag()); - self.require_zero("storage_key is 0 for Stack", q.storage_key.encoded.clone()); + self.require_zero("storage_key is 0 for Stack", q.rw_table.storage_key.clone()); self.require_zero( "first access to new stack address is a write", q.first_access() * (1.expr() - q.is_write()), ); self.add_lookup( "stack address fits into 10 bits", - (q.address.value.clone(), q.lookups.u10.clone()), + (q.rw_table.address.clone(), q.lookups.u10.clone()), ); self.condition(q.is_tag_and_id_unchanged.clone(), |cb| { cb.require_boolean( @@ -190,7 +206,7 @@ impl ConstraintBuilder { self.require_zero("field_tag is 0 for TxAccessListAccount", q.field_tag()); self.require_zero( "storage_key is 0 for TxAccessListAccount", - q.storage_key.encoded.clone(), + q.rw_table.storage_key.clone(), ); self.require_boolean("TxAccessListAccount value is boolean", q.value()); self.require_zero( @@ -212,11 +228,11 @@ impl ConstraintBuilder { } fn build_tx_refund_constraints(&mut self, q: &Queries) { - self.require_zero("address is 0 for TxRefund", q.address.value.clone()); + self.require_zero("address is 0 for TxRefund", q.rw_table.address.clone()); self.require_zero("field_tag is 0 for TxRefund", q.field_tag()); self.require_zero( "storage_key is 0 for TxRefund", - q.storage_key.encoded.clone(), + q.rw_table.storage_key.clone(), ); self.require_zero("initial TxRefund value is 0", q.initial_value()); } @@ -225,7 +241,7 @@ impl ConstraintBuilder { self.require_zero("id is 0 for Account", q.id()); self.require_zero( "storage_key is 0 for Account", - q.storage_key.encoded.clone(), + q.rw_table.storage_key.clone(), ); self.require_in_set( "field_tag in AccountFieldTag range", @@ -242,16 +258,16 @@ impl ConstraintBuilder { self.require_zero("field_tag is 0 for AccountDestructed", q.field_tag()); self.require_zero( "storage_key is 0 for AccountDestructed", - q.storage_key.encoded.clone(), + q.rw_table.storage_key.clone(), ); // TODO: Missing constraints } fn build_call_context_constraints(&mut self, q: &Queries) { - self.require_zero("address is 0 for CallContext", q.address.value.clone()); + self.require_zero("address is 0 for CallContext", q.rw_table.address.clone()); self.require_zero( "storage_key is 0 for CallContext", - q.storage_key.encoded.clone(), + q.rw_table.storage_key.clone(), ); self.add_lookup( "field_tag in CallContextFieldTag range", @@ -263,7 +279,7 @@ impl ConstraintBuilder { fn build_tx_log_constraints(&mut self, q: &Queries) { self.require_equal( "is_write is always true for TxLog", - q.is_write.clone(), + q.rw_table.is_write.clone(), 1.expr(), ); self.require_zero("initial TxLog value is 0", q.initial_value()); @@ -380,35 +396,35 @@ impl Queries { } fn is_write(&self) -> Expression { - self.is_write.clone() + self.rw_table.is_write.clone() } fn is_read(&self) -> Expression { - not::expr(&self.is_write) + not::expr(self.is_write()) } fn tag(&self) -> Expression { - self.tag.clone() + self.rw_table.tag.clone() } fn id(&self) -> Expression { - self.id.value.clone() + self.rw_table.id.clone() } fn id_change(&self) -> Expression { - self.id() - self.id.value_prev.clone() + self.id() - self.rw_table.prev_id.clone() } fn field_tag(&self) -> Expression { - self.field_tag.clone() + self.rw_table.field_tag.clone() } fn value(&self) -> Expression { - self.value.clone() + self.rw_table.value.clone() } fn value_prev(&self) -> Expression { - self.value_prev.clone() + self.rw_table.value_prev.clone() } fn initial_value(&self) -> Expression { @@ -428,11 +444,11 @@ impl Queries { } fn address_change(&self) -> Expression { - self.address.value.clone() - self.address.value_prev.clone() + self.rw_table.address.clone() - self.rw_table.prev_address.clone() } fn rw_counter_change(&self) -> Expression { - self.rw_counter.value.clone() - self.rw_counter.value_prev.clone() + self.rw_table.rw_counter.clone() - self.rw_table.prev_rw_counter.clone() } fn tx_log_index(&self) -> Expression { diff --git a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs index 13967385c0..3d1afc291a 100644 --- a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs +++ b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs @@ -1,4 +1,4 @@ -use super::{SortKeysConfig, N_LIMBS_ACCOUNT_ADDRESS, N_LIMBS_ID, N_LIMBS_RW_COUNTER}; +use super::{lookups, SortKeysConfig, N_LIMBS_ACCOUNT_ADDRESS, N_LIMBS_ID, N_LIMBS_RW_COUNTER}; use crate::{ evm_circuit::{param::N_BYTES_WORD, witness::Rw}, impl_expr, @@ -8,7 +8,7 @@ use eth_types::{Field, ToBigEndian}; use gadgets::binary_number::{AsBits, BinaryNumberChip, BinaryNumberConfig}; use halo2_proofs::{ circuit::Region, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, Instance, VirtualCells}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, poly::Rotation, }; use itertools::Itertools; @@ -110,11 +110,11 @@ impl Config { pub fn configure( meta: &mut ConstraintSystem, keys: SortKeysConfig, - u16_range: Column, - power_of_randomness: [Column; 31], + lookup: lookups::Config, + powers_of_randomness: [Expression; N_BYTES_WORD - 1], ) -> Self { let selector = meta.fixed_column(); - let first_different_limb = BinaryNumberChip::configure(meta, selector); + let first_different_limb = BinaryNumberChip::configure(meta, selector, None); let limb_difference = meta.advice_column(); let limb_difference_inverse = meta.advice_column(); @@ -125,11 +125,8 @@ impl Config { limb_difference_inverse, }; - meta.lookup_any("limb_difference fits into u16", |meta| { - vec![( - meta.query_advice(limb_difference, Rotation::cur()), - meta.query_fixed(u16_range, Rotation::cur()), - )] + lookup.range_check_u16(meta, "limb_difference fits into u16", |meta| { + meta.query_advice(limb_difference, Rotation::cur()) }); meta.create_gate("limb_difference is not zero", |meta| { @@ -146,8 +143,6 @@ impl Config { let selector = meta.query_fixed(selector, Rotation::cur()); let cur = Queries::new(meta, keys, Rotation::cur()); let prev = Queries::new(meta, keys, Rotation::prev()); - let powers_of_randomness = - power_of_randomness.map(|i| meta.query_instance(i, Rotation::cur())); let mut constraints = vec![]; for (i, rlc_expression) in diff --git a/zkevm-circuits/src/state_circuit/lookups.rs b/zkevm-circuits/src/state_circuit/lookups.rs index 70eeaca760..a3bc95ad92 100644 --- a/zkevm-circuits/src/state_circuit/lookups.rs +++ b/zkevm-circuits/src/state_circuit/lookups.rs @@ -8,16 +8,52 @@ use halo2_proofs::{ use std::marker::PhantomData; use strum::IntoEnumIterator; -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] 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, + ) { + meta.lookup_any(msg, |meta| { + let exp = exp_fn(meta); + vec![(exp, meta.query_fixed(self.u16, Rotation::cur()))] + }); + } +} + #[derive(Clone)] pub struct Queries { pub u8: Expression, diff --git a/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs b/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs index c7e6ec0b1d..2b1d8baf9a 100644 --- a/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs +++ b/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs @@ -1,8 +1,9 @@ +use super::lookups; use super::{N_LIMBS_ACCOUNT_ADDRESS, N_LIMBS_RW_COUNTER}; use crate::util::Expr; -use eth_types::{Address, Field, ToScalar}; +use eth_types::{Address, Field}; use halo2_proofs::{ - circuit::{AssignedCell, Layouter, Region}, + circuit::{Layouter, Region}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, poly::Rotation, }; @@ -34,7 +35,6 @@ pub struct Config where T: ToLimbs, { - pub value: Column, // TODO: we can save a column here by not storing the lsb, and then checking that // value - value_from_limbs(limbs.prepend(0)) fits into a limb. // Does this apply for RLC's too? @@ -44,8 +44,6 @@ where #[derive(Clone)] pub struct Queries { - pub value: Expression, - pub value_prev: Expression, // move this up, as it's not always needed. pub limbs: [Expression; N], pub limbs_prev: [Expression; N], } @@ -53,8 +51,6 @@ pub struct Queries { impl Queries { pub fn new>(meta: &mut VirtualCells<'_, F>, c: Config) -> Self { Self { - value: meta.query_advice(c.value, Rotation::cur()), - value_prev: meta.query_advice(c.value, Rotation::prev()), limbs: c.limbs.map(|limb| meta.query_advice(limb, Rotation::cur())), limbs_prev: c .limbs @@ -69,7 +65,7 @@ impl Config { region: &mut Region<'_, F>, offset: usize, value: Address, - ) -> Result, Error> { + ) -> Result<(), Error> { for (i, &limb) in value.to_limbs().iter().enumerate() { region.assign_advice( || format!("limb[{}] in address mpi", i), @@ -78,12 +74,7 @@ impl Config { || Ok(F::from(limb as u64)), )?; } - region.assign_advice( - || "value in u32 mpi", - self.value, - offset, - || Ok(value.to_scalar().unwrap()), // do this better - ) + Ok(()) } } @@ -93,7 +84,7 @@ impl Config { region: &mut Region<'_, F>, offset: usize, value: u32, - ) -> Result, Error> { + ) -> Result<(), Error> { for (i, &limb) in value.to_limbs().iter().enumerate() { region.assign_advice( || format!("limb[{}] in u32 mpi", i), @@ -102,12 +93,7 @@ impl Config { || Ok(F::from(limb as u64)), )?; } - region.assign_advice( - || "value in u32 mpi", - self.value, - offset, - || Ok(F::from(value as u64)), - ) + Ok(()) } } @@ -133,17 +119,14 @@ where pub fn configure( meta: &mut ConstraintSystem, selector: Column, - u16_range: Column, + value: 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| { @@ -154,7 +137,6 @@ where }); Config { - value, limbs, _marker: PhantomData, } diff --git a/zkevm-circuits/src/state_circuit/random_linear_combination.rs b/zkevm-circuits/src/state_circuit/random_linear_combination.rs index cf7fedaaa8..c6104590f1 100644 --- a/zkevm-circuits/src/state_circuit/random_linear_combination.rs +++ b/zkevm-circuits/src/state_circuit/random_linear_combination.rs @@ -1,31 +1,28 @@ use crate::evm_circuit::util::rlc; use eth_types::{Field, ToLittleEndian, U256}; use halo2_proofs::{ - circuit::{AssignedCell, Layouter, Region}, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, Instance, VirtualCells}, + circuit::{Layouter, Region}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, poly::Rotation, }; use std::marker::PhantomData; +use super::lookups; + #[derive(Clone, Debug, Copy)] pub struct Config { - pub encoded: Column, // bytes are little endian pub bytes: [Column; N], } #[derive(Clone)] pub struct Queries { - pub encoded: Expression, - pub encoded_prev: Expression, pub bytes: [Expression; N], } impl Queries { pub fn new(meta: &mut VirtualCells<'_, F>, c: Config) -> Self { Self { - encoded: meta.query_advice(c.encoded, Rotation::cur()), - encoded_prev: meta.query_advice(c.encoded, Rotation::prev()), bytes: c.bytes.map(|byte| meta.query_advice(byte, Rotation::cur())), } } @@ -36,9 +33,9 @@ impl Config { &self, region: &mut Region<'_, F>, offset: usize, - randomness: F, + _randomness: F, // kept for future use value: U256, - ) -> Result, Error> { + ) -> Result<(), Error> { let bytes = value.to_le_bytes(); for (i, &byte) in bytes.iter().enumerate() { region.assign_advice( @@ -48,12 +45,7 @@ impl Config { || Ok(F::from(byte as u64)), )?; } - region.assign_advice( - || "encoded value in rlc", - self.encoded, - offset, - || Ok(rlc::value(&bytes, randomness)), - ) + Ok(()) } } @@ -73,17 +65,15 @@ impl Chip { pub fn configure( meta: &mut ConstraintSystem, selector: Column, - u8_lookup: Column, - power_of_randomness: [Column; 31], + encoded: Column, + lookup: lookups::Config, + power_of_randomness: [Expression; 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()) }); } @@ -91,12 +81,10 @@ impl Chip { let selector = meta.query_fixed(selector, Rotation::cur()); let encoded = meta.query_advice(encoded, Rotation::cur()); let bytes = bytes.map(|c| meta.query_advice(c, Rotation::cur())); - let power_of_randomness = - power_of_randomness.map(|c| meta.query_instance(c, Rotation::cur())); vec![selector * (encoded - rlc::expr(&bytes, &power_of_randomness))] }); - Config { encoded, bytes } + Config { bytes } } pub fn load(&self, _layouter: &mut impl Layouter) -> Result<(), Error> { diff --git a/zkevm-circuits/src/state_circuit/test.rs b/zkevm-circuits/src/state_circuit/test.rs index 9a6267dd75..9caa98de4f 100644 --- a/zkevm-circuits/src/state_circuit/test.rs +++ b/zkevm-circuits/src/state_circuit/test.rs @@ -1,4 +1,4 @@ -use super::{StateCircuit, StateConfig}; +use super::{StateCircuit, StateCircuitConfig}; use crate::{ evm_circuit::witness::{Rw, RwMap}, table::{AccountFieldTag, CallContextFieldTag, RwTableTag, TxLogFieldTag, TxReceiptFieldTag}, @@ -9,7 +9,7 @@ use bus_mapping::operation::{ use eth_types::{ address, evm_types::{MemoryAddress, StackAddress}, - Address, ToAddress, Word, U256, + Address, Field, ToAddress, Word, U256, }; use gadgets::binary_number::AsBits; use halo2_proofs::poly::commitment::Params; @@ -24,7 +24,7 @@ use strum::IntoEnumIterator; const N_ROWS: usize = 1 << 16; -#[derive(Hash, Eq, PartialEq)] +#[derive(Hash, Eq, PartialEq, Clone, Debug)] pub enum AdviceColumn { IsWrite, Address, @@ -37,6 +37,7 @@ pub enum AdviceColumn { RwCounter, RwCounterLimb0, RwCounterLimb1, + Tag, TagBit0, TagBit1, TagBit2, @@ -50,19 +51,20 @@ pub enum AdviceColumn { } impl AdviceColumn { - pub fn value(&self, config: &StateConfig) -> Column { + pub fn value(&self, config: &StateCircuitConfig) -> Column { match self { - Self::IsWrite => config.is_write, - Self::Address => config.sort_keys.address.value, + Self::IsWrite => config.rw_table.is_write, + Self::Address => config.rw_table.address, Self::AddressLimb0 => config.sort_keys.address.limbs[0], Self::AddressLimb1 => config.sort_keys.address.limbs[1], - Self::StorageKey => config.sort_keys.storage_key.encoded, + Self::StorageKey => config.rw_table.storage_key, Self::StorageKeyByte0 => config.sort_keys.storage_key.bytes[0], Self::StorageKeyByte1 => config.sort_keys.storage_key.bytes[1], - Self::Value => config.value, - Self::RwCounter => config.sort_keys.rw_counter.value, + Self::Value => config.rw_table.value, + Self::RwCounter => config.rw_table.rw_counter, Self::RwCounterLimb0 => config.sort_keys.rw_counter.limbs[0], Self::RwCounterLimb1 => config.sort_keys.rw_counter.limbs[1], + Self::Tag => config.rw_table.tag, Self::TagBit0 => config.sort_keys.tag.bits[0], Self::TagBit1 => config.sort_keys.tag.bits[1], Self::TagBit2 => config.sort_keys.tag.bits[2], @@ -868,6 +870,7 @@ fn invalid_tags() { ((AdviceColumn::TagBit1, first_row_offset), bits[1]), ((AdviceColumn::TagBit2, first_row_offset), bits[2]), ((AdviceColumn::TagBit3, first_row_offset), bits[3]), + ((AdviceColumn::Tag, first_row_offset), Fr::from(i as u64)), ]); let result = prover(vec![], overrides).verify_at_rows(0..1, 0..1); diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index b7457f7029..c9e1013163 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -49,6 +49,7 @@ //! - [ ] MPT Circuit use crate::copy_circuit::CopyCircuit; +use crate::state_circuit::StateCircuitConfig; use crate::tx_circuit::{self, TxCircuit, TxCircuitConfig}; use crate::bytecode_circuit::bytecode_unroller::{ @@ -74,6 +75,7 @@ pub struct SuperCircuitConfig, + state_circuit: StateCircuitConfig, tx_circuit: TxCircuitConfig, bytecode_circuit: BytecodeConfig, copy_circuit: CopyCircuit, @@ -133,6 +135,11 @@ impl Circuit ©_table, &keccak_table, ); + let state_circuit = StateCircuitConfig::configure( + meta, + power_of_randomness[..31].to_vec().try_into().unwrap(), + &rw_table, + ); Self::Config { tx_table: tx_table.clone(), @@ -142,6 +149,7 @@ impl Circuit keccak_table: keccak_table.clone(), copy_table, evm_circuit, + state_circuit, copy_circuit: CopyCircuit::configure( meta, &tx_table, @@ -176,9 +184,13 @@ impl Circuit .evm_circuit .load_fixed_table(&mut layouter, self.fixed_table_tags.clone())?; config.evm_circuit.load_byte_table(&mut layouter)?; - config - .rw_table - .load(&mut layouter, &self.block.rws, self.block.randomness)?; + config.rw_table.load( + &mut layouter, + &self.block.rws.table_assignments(), + self.block.state_circuit_pad_to, + self.block.randomness, + )?; + config.state_circuit.load(&mut layouter)?; config .block_table .load(&mut layouter, &self.block.context, self.block.randomness)?; @@ -188,6 +200,12 @@ impl Circuit config .evm_circuit .assign_block(&mut layouter, &self.block)?; + config.state_circuit.assign( + &mut layouter, + &self.block.rws.table_assignments(), + self.block.state_circuit_pad_to, + self.block.randomness, + )?; // --- Tx Circuit --- self.tx_circuit.assign(&config.tx_circuit, &mut layouter)?; // --- Bytecode Circuit --- @@ -317,7 +335,7 @@ mod super_circuit_tests { let k = k.max(log2_ceil(64 + bytecodes_len)); let k = k.max(log2_ceil(64 + num_rows_required_for_steps)); let k = k + 1; - log::debug!("evm circuit uses k = {}", k); + log::debug!("super circuit uses k = {}", k); let mut instance: Vec> = (1..POW_RAND_SIZE + 1) .map(|exp| vec![block.randomness.pow(&[exp as u64, 0, 0, 0]); (1 << k) - 64]) diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index fa03e59077..1b5ac6458f 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -1,7 +1,7 @@ //! Table definitions used cross-circuits use crate::copy_circuit::number_or_hash_to_field; -use crate::evm_circuit::witness::RwRow; +use crate::evm_circuit::witness::{Rw, RwRow}; use crate::evm_circuit::{ util::{rlc, RandomLinearCombination}, witness::{Block, BlockContext, Bytecode, RwMap, Transaction}, @@ -305,7 +305,7 @@ impl_expr!(CallContextFieldTag); /// The RwTable shared between EVM Circuit and State Circuit, which contains /// traces of the EVM state operations. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub struct RwTable { /// Read Write Counter pub rw_counter: Column, @@ -314,20 +314,20 @@ pub struct RwTable { /// Tag pub tag: Column, /// Key1 (Id) - pub key1: Column, + pub id: Column, /// Key2 (Address) - pub key2: Column, + pub address: Column, /// Key3 (FieldTag) - pub key3: Column, + pub field_tag: Column, /// Key3 (StorageKey) - pub key4: Column, + pub storage_key: Column, /// Value pub value: Column, /// Value Previous pub value_prev: Column, - /// Aux1 (Committed Value) + /// Aux1 pub aux1: Column, - /// Aux2 + /// Aux2 (Committed Value) pub aux2: Column, } @@ -337,10 +337,10 @@ impl DynamicTableColumns for RwTable { self.rw_counter, self.is_write, self.tag, - self.key1, - self.key2, - self.key3, - self.key4, + self.id, + self.address, + self.field_tag, + self.storage_key, self.value, self.value_prev, self.aux1, @@ -355,10 +355,10 @@ impl RwTable { 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(), @@ -366,7 +366,7 @@ impl RwTable { } } /// Assign a `RwRow` at offset into the `RwTable` - pub fn assign( + pub fn assign( &self, region: &mut Region<'_, F>, offset: usize, @@ -376,10 +376,10 @@ impl RwTable { (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.id, row.id), + (self.address, row.address), + (self.field_tag, row.field_tag), + (self.storage_key, row.storage_key), (self.value, row.value), (self.value_prev, row.value_prev), (self.aux1, row.aux1), @@ -395,35 +395,29 @@ impl RwTable { pub fn load( &self, layouter: &mut impl Layouter, - rws: &RwMap, + rws: &[Rw], + n_rows: usize, randomness: F, ) -> Result<(), Error> { layouter.assign_region( || "rw table", - |mut region| { - let mut offset = 0; - self.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.assign(&mut region, offset, &rw.table_assignment(randomness))?; - offset += 1; - } - Ok(()) - }, + |mut region| self.load_with_region(&mut region, rws, n_rows, randomness), ) } + + pub(crate) fn load_with_region( + &self, + region: &mut Region<'_, F>, + rws: &[Rw], + n_rows: usize, + randomness: F, + ) -> Result<(), Error> { + let (rows, _) = RwMap::table_assignments_prepad(rws, n_rows); + for (offset, row) in rows.iter().enumerate() { + self.assign(region, offset, &row.table_assignment(randomness))?; + } + Ok(()) + } } /// Tag to identify the field in a Bytecode Table row @@ -748,7 +742,7 @@ impl CopyTable { Self { is_first: meta.advice_column(), id: meta.advice_column(), - tag: BinaryNumberChip::configure(meta, q_enable), + tag: BinaryNumberChip::configure(meta, q_enable, None), addr: meta.advice_column(), src_addr_end: meta.advice_column(), bytes_left: meta.advice_column(),