diff --git a/zkevm-circuits/src/copy_circuit/dev.rs b/zkevm-circuits/src/copy_circuit/dev.rs index 9671989777..0c00f862d4 100644 --- a/zkevm-circuits/src/copy_circuit/dev.rs +++ b/zkevm-circuits/src/copy_circuit/dev.rs @@ -63,7 +63,6 @@ impl Circuit for CopyCircuit { &mut layouter, &self.external_data.rws.table_assignments(), self.external_data.max_rws, - challenge_values.evm_word(), )?; config.0.bytecode_table.load( diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index e1f19121ed..dba99e85e2 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -416,7 +416,6 @@ impl Circuit for EvmCircuit { &mut layouter, &block.rws.table_assignments(), block.circuits_params.max_rws, - challenges.evm_word(), )?; config .bytecode_table diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 630200b5ec..0a23973b59 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -1377,10 +1377,7 @@ impl ExecutionConfig { .rw_indices .iter() .map(|rw_idx| block.rws[*rw_idx]) - .map(|rw| { - rw.table_assignment_aux(evm_randomness) - .rlc(lookup_randomness) - }) + .map(|rw| rw.table_assignment().unwrap().rlc(lookup_randomness)) .fold(BTreeSet::::new(), |mut set, value| { set.insert(value); set @@ -1429,8 +1426,8 @@ impl ExecutionConfig { }; let rw_idx = step.rw_indices[idx]; let rw = block.rws[rw_idx]; - let table_assignments = rw.table_assignment_aux(evm_randomness); - let rlc = table_assignments.rlc(lookup_randomness); + let table_assignments = rw.table_assignment(); + let rlc = table_assignments.unwrap().rlc(lookup_randomness); if rlc != assigned_rw_value.1 { log::error!( "incorrect rw witness. lookup input name: \"{}\"\nassigned={:?}\nrlc ={:?}\nrw index: {:?}, {}th rw of step {:?}, rw: {:?}", diff --git a/zkevm-circuits/src/state_circuit.rs b/zkevm-circuits/src/state_circuit.rs index 0270ca098d..1a24c9e974 100644 --- a/zkevm-circuits/src/state_circuit.rs +++ b/zkevm-circuits/src/state_circuit.rs @@ -4,7 +4,6 @@ mod lexicographic_ordering; mod lookups; mod multiple_precision_integer; mod param; -mod random_linear_combination; #[cfg(any(feature = "test", test, feature = "test-circuits"))] mod dev; @@ -18,13 +17,12 @@ use self::{ lexicographic_ordering::LimbIndex, }; use crate::{ - evm_circuit::{param::N_BYTES_WORD, util::rlc}, table::{AccountFieldTag, LookupTable, MPTProofType, MptTable, RwTable, RwTableTag}, - util::{Challenges, Expr, SubCircuit, SubCircuitConfig}, + util::{word, Challenges, Expr, SubCircuit, SubCircuitConfig}, witness::{self, MptUpdates, Rw, RwMap}, }; use constraint_builder::{ConstraintBuilder, Queries}; -use eth_types::{Address, Field, ToLittleEndian}; +use eth_types::{Address, Field, Word}; use gadgets::{ batched_is_zero::{BatchedIsZeroChip, BatchedIsZeroConfig}, binary_number::{BinaryNumberChip, BinaryNumberConfig}, @@ -32,7 +30,8 @@ use gadgets::{ use halo2_proofs::{ circuit::{Layouter, Region, Value}, plonk::{ - Advice, Column, ConstraintSystem, Error, Expression, Fixed, SecondPhase, VirtualCells, + Advice, Column, ConstraintSystem, Error, Expression, FirstPhase, Fixed, SecondPhase, + VirtualCells, }, poly::Rotation, }; @@ -40,7 +39,6 @@ use lexicographic_ordering::Config as LexicographicOrderingConfig; use lookups::{Chip as LookupsChip, Config as LookupsConfig, Queries as LookupsQueries}; use multiple_precision_integer::{Chip as MpiChip, Config as MpiConfig, Queries as MpiQueries}; use param::*; -use random_linear_combination::{Chip as RlcChip, Config as RlcConfig, Queries as RlcQueries}; use std::marker::PhantomData; #[cfg(any(feature = "test", test, feature = "test-circuits"))] @@ -57,20 +55,20 @@ pub struct StateCircuitConfig { // 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. - initial_value: Column, + initial_value: word::Word>, // For Rw::AccountStorage, identify non-existing if both committed value and // new value are zero. Will do lookup for MPTProofType::NonExistingStorageProof if // non-existing, otherwise do lookup for MPTProofType::StorageMod. is_non_exist: BatchedIsZeroConfig, // Intermediary witness used to reduce mpt lookup expression degree mpt_proof_type: Column, - state_root: Column, + state_root: word::Word>, lexicographic_ordering: LexicographicOrderingConfig, not_first_access: Column, lookups: LookupsConfig, - power_of_randomness: [Expression; N_BYTES_WORD - 1], // External tables mpt_table: MptTable, + _marker: PhantomData, } /// Circuit configuration arguments @@ -97,35 +95,36 @@ impl SubCircuitConfig for StateCircuitConfig { ) -> Self { let selector = meta.fixed_column(); let lookups = LookupsChip::configure(meta); - let power_of_randomness: [Expression; 31] = challenges.evm_word_powers_of_randomness(); - let rw_counter = MpiChip::configure(meta, selector, rw_table.rw_counter, lookups); + 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 id = MpiChip::configure(meta, selector, [rw_table.id], lookups); + + let address = MpiChip::configure(meta, selector, [rw_table.address], lookups); - let storage_key = RlcChip::configure( + let storage_key = MpiChip::configure( meta, selector, - rw_table.storage_key, + [rw_table.storage_key.lo(), rw_table.storage_key.hi()], lookups, - challenges.evm_word(), ); + let initial_value = word::Word::new([meta.advice_column(), meta.advice_column()]); - let initial_value = meta.advice_column_in(SecondPhase); let is_non_exist = BatchedIsZeroChip::configure( meta, - (SecondPhase, SecondPhase), + (FirstPhase, FirstPhase), |meta| meta.query_fixed(selector, Rotation::cur()), |meta| { [ - meta.query_advice(initial_value, Rotation::cur()), - meta.query_advice(rw_table.value, Rotation::cur()), + meta.query_advice(initial_value.lo(), Rotation::cur()), + meta.query_advice(initial_value.hi(), Rotation::cur()), + meta.query_advice(rw_table.value.lo(), Rotation::cur()), + meta.query_advice(rw_table.value.hi(), Rotation::cur()), ] }, ); let mpt_proof_type = meta.advice_column_in(SecondPhase); - let state_root = meta.advice_column_in(SecondPhase); + let state_root = word::Word::new([meta.advice_column(), meta.advice_column()]); let sort_keys = SortKeysConfig { tag, @@ -136,12 +135,9 @@ impl SubCircuitConfig for StateCircuitConfig { rw_counter, }; - let lexicographic_ordering = LexicographicOrderingConfig::configure( - meta, - sort_keys, - lookups, - power_of_randomness.clone(), - ); + let power_of_randomness: [Expression; 31] = challenges.keccak_powers_of_randomness(); + let lexicographic_ordering = + LexicographicOrderingConfig::configure(meta, sort_keys, lookups, power_of_randomness); // annotate columns rw_table.annotate_columns(meta); @@ -157,9 +153,9 @@ impl SubCircuitConfig for StateCircuitConfig { lexicographic_ordering, not_first_access: meta.advice_column(), lookups, - power_of_randomness, rw_table, mpt_table, + _marker: PhantomData::default(), }; let mut constraint_builder = ConstraintBuilder::new(); @@ -188,14 +184,11 @@ impl StateCircuitConfig { layouter: &mut impl Layouter, rows: &[Rw], n_rows: usize, // 0 means dynamically calculated from `rows`. - challenges: &Challenges>, ) -> Result<(), Error> { let updates = MptUpdates::mock_from(rows); layouter.assign_region( || "state circuit", - |mut region| { - self.assign_with_region(&mut region, rows, &updates, n_rows, challenges.evm_word()) - }, + |mut region| self.assign_with_region(&mut region, rows, &updates, n_rows), ) } @@ -205,15 +198,13 @@ impl StateCircuitConfig { rows: &[Rw], updates: &MptUpdates, n_rows: usize, // 0 means dynamically calculated from `rows`. - randomness: Value, ) -> Result<(), Error> { let tag_chip = BinaryNumberChip::construct(self.sort_keys.tag); let (rows, padding_length) = RwMap::table_assignments_prepad(rows, n_rows); let rows_len = rows.len(); - let mut state_root = - randomness.map(|randomness| rlc::value(&updates.old_root().to_le_bytes(), randomness)); + let mut state_root = updates.old_root(); // annotate columns self.annotate_circuit_in_region(region); @@ -247,7 +238,7 @@ impl StateCircuitConfig { if let Some(storage_key) = row.storage_key() { self.sort_keys .storage_key - .assign(region, offset, randomness, storage_key)?; + .assign(region, offset, storage_key)?; } if offset > 0 { @@ -267,107 +258,106 @@ impl StateCircuitConfig { if is_first_access { // If previous row was a last access, we need to update the state root. - state_root = randomness - .zip(state_root) - .map(|(randomness, mut state_root)| { - if let Some(update) = updates.get(prev_row) { - let (new_root, old_root) = update.root_assignments(randomness); - assert_eq!(state_root, old_root); - state_root = new_root; - } - if matches!(row.tag(), RwTableTag::CallContext) && !row.is_write() { - assert_eq!(row.value_assignment(randomness), F::ZERO, "{:?}", row); - } - state_root - }); + if let Some(update) = updates.get(prev_row) { + let (new_root, old_root) = update.root_assignments(); + assert_eq!(state_root, old_root); + state_root = new_root; + } + if matches!(row.tag(), RwTableTag::CallContext) && !row.is_write() { + assert_eq!(row.value_assignment(), 0.into(), "{:?}", row); + } } } // The initial value can be determined from the mpt updates or is 0. - let initial_value = randomness.map(|randomness| { + let initial_value = word::Word::::from( updates .get(row) - .map(|u| u.value_assignments(randomness).1) - .unwrap_or_default() - }); - region.assign_advice( + .map(|u| u.value_assignments().1) + .unwrap_or_default(), + ); + + initial_value.into_value().assign_advice( + region, || "initial_value", self.initial_value, offset, - || initial_value, )?; // Identify non-existing if both committed value and new value are zero. - let committed_value_value = randomness.map(|randomness| { + let (committed_value, value) = { let (_, committed_value) = updates .get(row) - .map(|u| u.value_assignments(randomness)) + .map(|u| u.value_assignments()) .unwrap_or_default(); - let value = row.value_assignment(randomness); - [committed_value, value] - }); + let value = row.value_assignment(); + ( + word::Word::::from(committed_value), + word::Word::::from(value), + ) + }; + BatchedIsZeroChip::construct(self.is_non_exist.clone()).assign( region, offset, - committed_value_value, + Value::known([ + committed_value.lo(), + committed_value.hi(), + value.lo(), + value.hi(), + ]), )?; - let mpt_proof_type = committed_value_value.map(|pair| { - F::from(match row { - Rw::AccountStorage { .. } => { - if pair[0].is_zero_vartime() && pair[1].is_zero_vartime() { - MPTProofType::NonExistingStorageProof as u64 - } else { - MPTProofType::StorageMod as u64 - } + + let mpt_proof_type = match row { + Rw::AccountStorage { .. } => { + if committed_value.is_zero_vartime() && value.is_zero_vartime() { + MPTProofType::NonExistingStorageProof as u64 + } else { + MPTProofType::StorageMod as u64 } - Rw::Account { field_tag, .. } => { - if pair[0].is_zero_vartime() - && pair[1].is_zero_vartime() - && matches!(field_tag, AccountFieldTag::CodeHash) - { - MPTProofType::NonExistingAccountProof as u64 - } else { - *field_tag as u64 - } + } + Rw::Account { field_tag, .. } => { + if committed_value.is_zero_vartime() + && value.is_zero_vartime() + && matches!(field_tag, AccountFieldTag::CodeHash) + { + MPTProofType::NonExistingAccountProof as u64 + } else { + *field_tag as u64 } - _ => 0, - }) - }); + } + _ => 0, + }; + region.assign_advice( || "mpt_proof_type", self.mpt_proof_type, offset, - || mpt_proof_type, + || Value::known(F::from(mpt_proof_type)), )?; // TODO: Switch from Rw::Start -> Rw::Padding to simplify this logic. // State root assignment is at previous row (offset - 1) because the state root // changes on the last access row. if offset != 0 { - region.assign_advice( - || "state_root", - self.state_root, - offset - 1, - || state_root, - )?; + word::Word::::from(state_root) + .into_value() + .assign_advice(region, || "state root", self.state_root, offset - 1)?; } if offset == rows_len - 1 { // The last row is always a last access, so we need to handle the case where the // state root changes because of an mpt lookup on the last row. if let Some(update) = updates.get(row) { - state_root = randomness.zip(state_root).map(|(randomness, state_root)| { - let (new_root, old_root) = update.root_assignments(randomness); + state_root = { + let (new_root, old_root) = update.root_assignments(); assert_eq!(state_root, old_root); new_root - }); + }; } - region.assign_advice( - || "last row state_root", - self.state_root, - offset, - || state_root, - )?; + word::Word::::from(state_root) + .into_value() + .assign_advice(region, || "last row state_root", self.state_root, offset)?; } } @@ -384,9 +374,11 @@ impl StateCircuitConfig { self.sort_keys.annotate_columns_in_region(region, "STATE"); region.name_column(|| "STATE_selector", self.selector); region.name_column(|| "STATE_not_first_access", self.not_first_access); - region.name_column(|| "STATE_phase2_initial_value", self.initial_value); - region.name_column(|| "STATE_phase2_mpt_proof_type", self.mpt_proof_type); - region.name_column(|| "STATE_phase2_state_root", self.state_root); + region.name_column(|| "STATE_initial_value lo", self.initial_value.lo()); + region.name_column(|| "STATE_initial_value hi", self.initial_value.hi()); + region.name_column(|| "STATE_mpt_proof_type", self.mpt_proof_type); + region.name_column(|| "STATE_state_root lo", self.state_root.lo()); + region.name_column(|| "STATE_state_root hi", self.state_root.hi()); } } @@ -397,7 +389,7 @@ pub struct SortKeysConfig { id: MpiConfig, address: MpiConfig, field_tag: Column, - storage_key: RlcConfig, + storage_key: MpiConfig, rw_counter: MpiConfig, } @@ -468,33 +460,22 @@ impl SubCircuit for StateCircuit { fn synthesize_sub( &self, config: &Self::Config, - challenges: &Challenges>, + _challenges: &Challenges>, layouter: &mut impl Layouter, ) -> Result<(), Error> { config.load_aux_tables(layouter)?; - let randomness = challenges.evm_word(); - // 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( || "state circuit", |mut region| { - config.rw_table.load_with_region( - &mut region, - &self.rows, - self.n_rows, - randomness, - )?; + config + .rw_table + .load_with_region(&mut region, &self.rows, self.n_rows)?; - config.assign_with_region( - &mut region, - &self.rows, - &self.updates, - self.n_rows, - randomness, - )?; + config.assign_with_region(&mut region, &self.rows, &self.updates, self.n_rows)?; #[cfg(any(feature = "test", test, feature = "test-circuits"))] { let padding_length = RwMap::padding_len(self.rows.len(), self.n_rows); @@ -528,6 +509,15 @@ fn queries(meta: &mut VirtualCells<'_, F>, c: &StateCircuitConfig) let final_bits_sum = meta.query_advice(first_different_limb.bits[3], Rotation::cur()) + meta.query_advice(first_different_limb.bits[4], Rotation::cur()); let mpt_update_table_expressions = c.mpt_table.table_exprs(meta); + assert_eq!(mpt_update_table_expressions.len(), 12); + + let meta_query_word = + |metap: &mut VirtualCells<'_, F>, word_column: word::Word>, at: Rotation| { + word::Word::new([ + metap.query_advice(word_column.lo(), at), + metap.query_advice(word_column.hi(), at), + ]) + }; Queries { selector: meta.query_fixed(c.selector, Rotation::cur()), @@ -542,20 +532,35 @@ fn queries(meta: &mut VirtualCells<'_, F>, c: &StateCircuitConfig) 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()), - value_prev: meta.query_advice(c.rw_table.value, Rotation::prev()), - value_prev_column: meta.query_advice(c.rw_table.value_prev, Rotation::cur()), + storage_key: meta_query_word(meta, c.rw_table.storage_key, Rotation::cur()), + value: meta_query_word(meta, c.rw_table.value, Rotation::cur()), + value_prev: meta_query_word(meta, c.rw_table.value, Rotation::prev()), + value_prev_column: meta_query_word(meta, c.rw_table.value_prev, Rotation::cur()), }, // TODO: clean this up mpt_update_table: MptUpdateTableQueries { address: mpt_update_table_expressions[0].clone(), - storage_key: mpt_update_table_expressions[1].clone(), - proof_type: mpt_update_table_expressions[2].clone(), - new_root: mpt_update_table_expressions[3].clone(), - old_root: mpt_update_table_expressions[4].clone(), - new_value: mpt_update_table_expressions[5].clone(), - old_value: mpt_update_table_expressions[6].clone(), + storage_key: word::Word::new([ + mpt_update_table_expressions[1].clone(), + mpt_update_table_expressions[2].clone(), + ]), + proof_type: mpt_update_table_expressions[3].clone(), + new_root: word::Word::new([ + mpt_update_table_expressions[4].clone(), + mpt_update_table_expressions[5].clone(), + ]), + old_root: word::Word::new([ + mpt_update_table_expressions[6].clone(), + mpt_update_table_expressions[7].clone(), + ]), + new_value: word::Word::new([ + mpt_update_table_expressions[8].clone(), + mpt_update_table_expressions[9].clone(), + ]), + old_value: word::Word::new([ + mpt_update_table_expressions[10].clone(), + mpt_update_table_expressions[11].clone(), + ]), }, lexicographic_ordering_selector: meta .query_fixed(c.lexicographic_ordering.selector, Rotation::cur()), @@ -576,19 +581,18 @@ fn queries(meta: &mut VirtualCells<'_, F>, c: &StateCircuitConfig) + 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), - storage_key: RlcQueries::new(meta, c.sort_keys.storage_key), - initial_value: meta.query_advice(c.initial_value, Rotation::cur()), - initial_value_prev: meta.query_advice(c.initial_value, Rotation::prev()), + storage_key: MpiQueries::new(meta, c.sort_keys.storage_key), + initial_value: meta_query_word(meta, c.initial_value, Rotation::cur()), + initial_value_prev: meta_query_word(meta, c.initial_value, Rotation::prev()), is_non_exist: meta.query_advice(c.is_non_exist.is_zero, Rotation::cur()), mpt_proof_type: meta.query_advice(c.mpt_proof_type, Rotation::cur()), lookups: LookupsQueries::new(meta, c.lookups), - power_of_randomness: c.power_of_randomness.clone(), first_different_limb: [0, 1, 2, 3] .map(|idx| meta.query_advice(first_different_limb.bits[idx], Rotation::cur())), not_first_access: meta.query_advice(c.not_first_access, Rotation::cur()), last_access: 1.expr() - meta.query_advice(c.not_first_access, Rotation::next()), - state_root: meta.query_advice(c.state_root, Rotation::cur()), - state_root_prev: meta.query_advice(c.state_root, Rotation::prev()), + state_root: meta_query_word(meta, c.state_root, Rotation::cur()), + state_root_prev: meta_query_word(meta, c.state_root, Rotation::prev()), } } @@ -599,13 +603,6 @@ mod state_circuit_stats { stats::{bytecode_prefix_op_big_rws, print_circuit_stats_by_states}, }; - /// Prints the stats of State circuit per execution state. See - /// `print_circuit_stats_by_states` for more details. - /// - /// Run with: - /// `cargo test -p zkevm-circuits --release --all-features - /// get_state_states_stats -- --nocapture --ignored` - #[ignore] #[test] pub fn get_state_states_stats() { print_circuit_stats_by_states( diff --git a/zkevm-circuits/src/state_circuit/constraint_builder.rs b/zkevm-circuits/src/state_circuit/constraint_builder.rs index 58706b5d82..6fc559df2d 100644 --- a/zkevm-circuits/src/state_circuit/constraint_builder.rs +++ b/zkevm-circuits/src/state_circuit/constraint_builder.rs @@ -1,14 +1,10 @@ use super::{ - lookups::Queries as LookupsQueries, multiple_precision_integer::Queries as MpiQueries, - param::*, random_linear_combination::Queries as RlcQueries, + lookups::Queries as LookupsQueries, multiple_precision_integer::Queries as MpiQueries, param::*, }; use crate::{ - evm_circuit::{ - param::N_BYTES_WORD, - util::{math_gadget::generate_lagrange_base_polynomial, not}, - }, + evm_circuit::util::{math_gadget::generate_lagrange_base_polynomial, not}, table::{AccountFieldTag, MPTProofType, RwTableTag}, - util::Expr, + util::{word, Expr}, }; use eth_types::Field; use gadgets::binary_number::BinaryNumberConfig; @@ -26,22 +22,21 @@ pub struct RwTableQueries { pub address: Expression, pub prev_address: Expression, pub field_tag: Expression, - pub storage_key: Expression, - pub value: Expression, - pub value_prev: Expression, // meta.query(value, Rotation::prev()) - pub value_prev_column: Expression, /* meta.query(prev_value, Rotation::cur()) - * TODO: aux1 and aux2 */ + pub storage_key: word::Word>, + pub value: word::Word>, + pub value_prev: word::Word>, // meta.query(value, Rotation::prev()) + pub value_prev_column: word::Word>, // meta.query(prev_value, Rotation::cur()) } #[derive(Clone)] pub struct MptUpdateTableQueries { pub address: Expression, - pub storage_key: Expression, + pub storage_key: word::Word>, pub proof_type: Expression, - pub new_root: Expression, - pub old_root: Expression, - pub new_value: Expression, - pub old_value: Expression, + pub new_root: word::Word>, + pub old_root: word::Word>, + pub new_value: word::Word>, + pub old_value: word::Word>, } #[derive(Clone)] @@ -55,18 +50,17 @@ pub struct Queries { pub id: MpiQueries, pub is_tag_and_id_unchanged: Expression, pub address: MpiQueries, - pub storage_key: RlcQueries, - pub initial_value: Expression, - pub initial_value_prev: Expression, + pub storage_key: MpiQueries, + pub initial_value: word::Word>, + pub initial_value_prev: word::Word>, pub is_non_exist: Expression, pub mpt_proof_type: Expression, pub lookups: LookupsQueries, - pub power_of_randomness: [Expression; N_BYTES_WORD - 1], pub first_different_limb: [Expression; 4], pub not_first_access: Expression, pub last_access: Expression, - pub state_root: Expression, - pub state_root_prev: Expression, + pub state_root: word::Word>, + pub state_root_prev: word::Word>, } type Constraint = (&'static str, Expression); @@ -78,6 +72,25 @@ pub struct ConstraintBuilder { condition: Expression, } +struct LookupBuilder(Vec<(Expression, Expression)>); +impl LookupBuilder { + fn new() -> Self { + Self(vec![]) + } + fn add(mut self, e1: &Expression, e2: &Expression) -> Self { + self.0.push((e1.clone(), e2.clone())); + self + } + fn add_word(mut self, e1: &word::Word>, e2: &word::Word>) -> Self { + self.0.push((e1.lo(), e2.lo())); + self.0.push((e1.hi(), e2.hi())); + self + } + fn build(self) -> Vec<(Expression, Expression)> { + self.0 + } +} + impl ConstraintBuilder { pub fn new() -> Self { Self { @@ -153,10 +166,14 @@ impl ConstraintBuilder { // in the current row differs from the previous row. self.condition(q.first_access(), |cb| { cb.require_zero( - "first access reads don't change value", - q.is_read() * (q.rw_table.value.clone() - q.initial_value()), + "first access reads don't change value (hi)", + q.is_read() * (q.rw_table.value.hi() - q.initial_value().hi()), ); - cb.require_equal( + cb.require_zero( + "first access reads don't change value (lo)", + q.is_read() * (q.rw_table.value.lo() - q.initial_value().lo()), + ); + cb.require_word_equal( "value_prev column is initial_value for first access", q.value_prev_column(), q.initial_value.clone(), @@ -166,12 +183,20 @@ impl ConstraintBuilder { // When all the keys in the current row and previous row are equal. self.condition(q.not_first_access.clone(), |cb| { cb.require_zero( - "non-first access reads don't change value", - q.is_read() * (q.rw_table.value.clone() - q.rw_table.value_prev.clone()), + "non-first access reads don't change value (hi)", + q.is_read() * (q.rw_table.value.hi() - q.rw_table.value_prev.hi()), ); cb.require_zero( - "initial value doesn't change in an access group", - q.initial_value.clone() - q.initial_value_prev(), + "non-first access reads don't change value (lo)", + q.is_read() * (q.rw_table.value.lo() - q.rw_table.value_prev.lo()), + ); + cb.require_zero( + "initial value doesn't change in an access group (hi)", + q.initial_value.hi() - q.initial_value_prev().hi(), + ); + cb.require_zero( + "initial value doesn't change in an access group (lo)", + q.initial_value.lo() - q.initial_value_prev().lo(), ); }); } @@ -181,39 +206,41 @@ impl ConstraintBuilder { self.require_zero("field_tag is 0 for Start", q.field_tag()); 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.rw_table.storage_key.clone()); + self.require_word_zero("storage_key is 0 for Start", q.rw_table.storage_key.clone()); // 1.1. rw_counter increases by 1 for every non-first row self.require_zero( "rw_counter increases by 1 for every non-first row", q.lexicographic_ordering_selector.clone() * (q.rw_counter_change() - 1.expr()), ); // 1.2. Start value is 0 - self.require_zero("Start value is 0", q.value()); + self.require_word_zero("Start value is 0", q.value()); // 1.3. Start initial value is 0 - self.require_zero("Start initial_value is 0", q.initial_value()); + self.require_word_zero("Start initial_value is 0", q.initial_value()); // 1.4. state_root is unchanged for every non-first row self.condition(q.lexicographic_ordering_selector.clone(), |cb| { - cb.require_equal( + cb.require_word_equal( "state_root is unchanged for Start", q.state_root(), q.state_root_prev(), ) }); - self.require_zero("value_prev column is 0 for Start", q.value_prev_column()); + self.require_word_zero("value_prev column is 0 for Start", q.value_prev_column()); } fn build_memory_constraints(&mut self, q: &Queries) { // 2.0. Unused keys are 0 self.require_zero("field_tag is 0 for Memory", q.field_tag()); - self.require_zero( + self.require_word_zero( "storage_key is 0 for Memory", q.rw_table.storage_key.clone(), ); // 2.1. First access for a set of all keys are 0 if READ - self.require_zero( - "first access for a set of all keys are 0 if READ", - q.first_access() * q.is_read() * q.value(), - ); + self.condition(q.first_access() * q.is_read(), |cb| { + cb.require_word_zero( + "first access for a set of all keys are 0 if READ", + q.value(), + ); + }); // could do this more efficiently by just asserting address = limb0 + 2^16 * // limb1? // 2.2. mem_addr in range @@ -222,18 +249,19 @@ impl ConstraintBuilder { } // 2.3. value is a byte self.add_lookup( - "memory value is a byte", - vec![(q.rw_table.value.clone(), q.lookups.u8.clone())], + "memory value is a byte (lo is u8)", + vec![(q.rw_table.value.lo(), q.lookups.u8.clone())], ); + self.require_zero("memory value is a byte (hi is 0)", q.rw_table.value.hi()); // 2.4. Start initial value is 0 - self.require_zero("initial Memory value is 0", q.initial_value()); + self.require_word_zero("initial Memory value is 0", q.initial_value()); // 2.5. state root does not change - self.require_equal( + self.require_word_equal( "state_root is unchanged for Memory", q.state_root(), q.state_root_prev(), ); - self.require_equal( + self.require_word_equal( "value_prev column equals initial_value for Memory", q.value_prev_column(), q.initial_value(), @@ -243,7 +271,7 @@ impl ConstraintBuilder { fn build_stack_constraints(&mut self, q: &Queries) { // 3.0. Unused keys are 0 self.require_zero("field_tag is 0 for Stack", q.field_tag()); - self.require_zero("storage_key is 0 for Stack", q.rw_table.storage_key.clone()); + self.require_word_zero("storage_key is 0 for Stack", q.rw_table.storage_key.clone()); // 3.1. First access for a set of all keys self.require_zero( "first access to new stack address is a write", @@ -262,14 +290,14 @@ impl ConstraintBuilder { ) }); // 3.4. Stack initial value is 0 - self.require_zero("initial Stack value is 0", q.initial_value.clone()); + self.require_word_zero("initial Stack value is 0", q.initial_value.clone()); // 3.5 state root does not change - self.require_equal( + self.require_word_equal( "state_root is unchanged for Stack", q.state_root(), q.state_root_prev(), ); - self.require_equal( + self.require_word_equal( "value_prev column equals initial_value for Stack", q.value_prev_column(), q.initial_value(), @@ -295,26 +323,20 @@ impl ConstraintBuilder { self.condition(q.last_access(), |cb| { cb.add_lookup( "mpt_update exists in mpt circuit for AccountStorage last access", - vec![ - ( - q.rw_table.address.clone(), - q.mpt_update_table.address.clone(), - ), - ( - q.rw_table.storage_key.clone(), - q.mpt_update_table.storage_key.clone(), - ), - (q.mpt_proof_type(), q.mpt_update_table.proof_type.clone()), - (q.state_root(), q.mpt_update_table.new_root.clone()), - (q.state_root_prev(), q.mpt_update_table.old_root.clone()), - (q.value(), q.mpt_update_table.new_value.clone()), - (q.initial_value(), q.mpt_update_table.old_value.clone()), - ], + LookupBuilder::new() + .add(&q.rw_table.address, &q.mpt_update_table.address) + .add_word(&q.rw_table.storage_key, &q.mpt_update_table.storage_key) + .add(&q.mpt_proof_type(), &q.mpt_update_table.proof_type) + .add_word(&q.state_root(), &q.mpt_update_table.new_root) + .add_word(&q.state_root_prev(), &q.mpt_update_table.old_root) + .add_word(&q.value(), &q.mpt_update_table.new_value) + .add_word(&q.initial_value(), &q.mpt_update_table.old_value) + .build(), ); }); self.condition(q.not_first_access.clone(), |cb| { - cb.require_equal( + cb.require_word_equal( "value column at Rotation::prev() equals value_prev at Rotation::cur()", q.rw_table.value_prev.clone(), q.value_prev_column(), @@ -324,24 +346,24 @@ impl ConstraintBuilder { fn build_tx_access_list_account_constraints(&mut self, q: &Queries) { self.require_zero("field_tag is 0 for TxAccessListAccount", q.field_tag()); - self.require_zero( + self.require_word_zero( "storage_key is 0 for TxAccessListAccount", q.rw_table.storage_key.clone(), ); - self.require_boolean("TxAccessListAccount value is boolean", q.value()); - self.require_zero( + self.require_word_boolean("TxAccessListAccount value is boolean", q.value()); + self.require_word_zero( "initial TxAccessListAccount value is false", q.initial_value(), ); - self.require_equal( + self.require_word_equal( "state_root is unchanged for TxAccessListAccount", q.state_root(), q.state_root_prev(), ); self.condition(q.not_first_access.clone(), |cb| { - cb.require_equal( + cb.require_word_equal( "value column at Rotation::prev() equals value_prev at Rotation::cur()", q.rw_table.value_prev.clone(), q.value_prev_column(), @@ -354,20 +376,20 @@ impl ConstraintBuilder { "field_tag is 0 for TxAccessListAccountStorage", q.field_tag(), ); - self.require_boolean("TxAccessListAccountStorage value is boolean", q.value()); - self.require_zero( + self.require_word_boolean("TxAccessListAccountStorage value is boolean", q.value()); + self.require_word_zero( "initial TxAccessListAccountStorage value is false", q.initial_value(), ); - self.require_equal( + self.require_word_equal( "state_root is unchanged for TxAccessListAccountStorage", q.state_root(), q.state_root_prev(), ); self.condition(q.not_first_access.clone(), |cb| { - cb.require_equal( + cb.require_word_equal( "value column at Rotation::prev() equals value_prev at Rotation::cur()", q.rw_table.value_prev.clone(), q.value_prev_column(), @@ -379,32 +401,32 @@ impl ConstraintBuilder { // 7.0. `address`, `field_tag` and `storage_key` are 0 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( + self.require_word_zero( "storage_key is 0 for TxRefund", q.rw_table.storage_key.clone(), ); // 7.1. `state root` is not changed - self.require_equal( + self.require_word_equal( "state_root is unchanged for TxRefund", q.state_root(), q.state_root_prev(), ); self.condition(q.not_first_access.clone(), |cb| { - cb.require_equal( + cb.require_word_equal( "value column at Rotation::prev() equals value_prev at Rotation::cur()", q.rw_table.value_prev.clone(), q.value_prev_column(), ); }); // 7.2. `initial value` is 0 - self.require_zero("initial TxRefund value is 0", q.initial_value()); + self.require_word_zero("initial TxRefund value is 0", q.initial_value()); } fn build_account_constraints(&mut self, q: &Queries) { // ref. spec 6.0. Unused keys are 0 self.require_zero("id is 0 for Account", q.id()); - self.require_zero( + self.require_word_zero( "storage_key is 0 for Account", q.rw_table.storage_key.clone(), ); @@ -443,26 +465,20 @@ impl ConstraintBuilder { self.condition(q.last_access(), |cb| { cb.add_lookup( "mpt_update exists in mpt circuit for Account last access", - vec![ - ( - q.rw_table.address.clone(), - q.mpt_update_table.address.clone(), - ), - ( - q.rw_table.storage_key.clone(), - q.mpt_update_table.storage_key.clone(), - ), - (q.mpt_proof_type(), q.mpt_update_table.proof_type.clone()), - (q.state_root(), q.mpt_update_table.new_root.clone()), - (q.state_root_prev(), q.mpt_update_table.old_root.clone()), - (q.value(), q.mpt_update_table.new_value.clone()), - (q.initial_value(), q.mpt_update_table.old_value.clone()), - ], + LookupBuilder::new() + .add(&q.rw_table.address, &q.mpt_update_table.address) + .add_word(&q.rw_table.storage_key, &q.mpt_update_table.storage_key) + .add(&q.mpt_proof_type(), &q.mpt_update_table.proof_type) + .add_word(&q.state_root(), &q.mpt_update_table.new_root) + .add_word(&q.state_root_prev(), &q.mpt_update_table.old_root) + .add_word(&q.value(), &q.mpt_update_table.new_value) + .add_word(&q.initial_value(), &q.mpt_update_table.old_value) + .build(), ); }); self.condition(q.not_first_access.clone(), |cb| { - cb.require_equal( + cb.require_word_equal( "value column at Rotation::prev() equals value_prev at Rotation::cur()", q.rw_table.value_prev.clone(), q.value_prev_column(), @@ -472,7 +488,7 @@ impl ConstraintBuilder { fn build_call_context_constraints(&mut self, q: &Queries) { self.require_zero("address is 0 for CallContext", q.rw_table.address.clone()); - self.require_zero( + self.require_word_zero( "storage_key is 0 for CallContext", q.rw_table.storage_key.clone(), ); @@ -480,13 +496,13 @@ impl ConstraintBuilder { "field_tag in CallContextFieldTag range", vec![(q.field_tag(), q.lookups.call_context_field_tag.clone())], ); - self.require_zero("initial CallContext value is 0", q.initial_value()); - self.require_equal( + self.require_word_zero("initial CallContext value is 0", q.initial_value()); + self.require_word_equal( "state_root is unchanged for CallContext", q.state_root(), q.state_root_prev(), ); - self.require_zero( + self.require_word_zero( "value_prev column is 0 for CallContext", q.value_prev_column(), ); @@ -498,14 +514,14 @@ impl ConstraintBuilder { q.rw_table.is_write.clone(), 1.expr(), ); - self.require_zero("initial TxLog value is 0", q.initial_value()); + self.require_word_zero("initial TxLog value is 0", q.initial_value()); - self.require_equal( + self.require_word_equal( "state_root is unchanged for TxLog", q.state_root(), q.state_root_prev(), ); - self.require_equal( + self.require_word_equal( "value_prev column equals initial_value for TxLog", q.value_prev_column(), q.initial_value(), @@ -516,12 +532,12 @@ impl ConstraintBuilder { // TODO: implement TxReceipt constraints self.require_equal("TxReceipt rows not implemented", 1.expr(), 0.expr()); - self.require_equal( + self.require_word_equal( "state_root is unchanged for TxReceipt", q.state_root(), q.state_root_prev(), ); - self.require_zero( + self.require_word_zero( "value_prev_column is 0 for TxReceipt", q.value_prev_column(), ); @@ -531,14 +547,38 @@ impl ConstraintBuilder { self.constraints.push((name, self.condition.clone() * e)); } + fn require_word_zero(&mut self, name: &'static str, e: word::Word>) { + let (lo, hi) = e.into_lo_hi(); + self.constraints.push((name, self.condition.clone() * hi)); + self.constraints.push((name, self.condition.clone() * lo)); + } + fn require_equal(&mut self, name: &'static str, left: Expression, right: Expression) { self.require_zero(name, left - right) } + fn require_word_equal( + &mut self, + name: &'static str, + left: word::Word>, + right: word::Word>, + ) { + let (left_lo, left_hi) = left.into_lo_hi(); + let (right_lo, right_hi) = right.into_lo_hi(); + self.require_zero(name, left_hi - right_hi); + self.require_zero(name, left_lo - right_lo); + } + fn require_boolean(&mut self, name: &'static str, e: Expression) { self.require_zero(name, e.clone() * (1.expr() - e)) } + fn require_word_boolean(&mut self, name: &'static str, e: word::Word>) { + let (lo, hi) = e.into_lo_hi(); + self.require_zero(name, hi); + self.require_zero(name, lo.clone() * (1.expr() - lo)); + } + fn require_in_set(&mut self, name: &'static str, item: Expression, set: Vec>) { self.require_zero( name, @@ -593,19 +633,19 @@ impl Queries { self.rw_table.field_tag.clone() } - fn value(&self) -> Expression { + fn value(&self) -> word::Word> { self.rw_table.value.clone() } - fn value_prev(&self) -> Expression { + fn value_prev(&self) -> word::Word> { self.rw_table.value_prev.clone() } - fn initial_value(&self) -> Expression { + fn initial_value(&self) -> word::Word> { self.initial_value.clone() } - fn initial_value_prev(&self) -> Expression { + fn initial_value_prev(&self) -> word::Word> { self.initial_value_prev.clone() } @@ -653,15 +693,15 @@ impl Queries { self.last_access.clone() } - fn state_root(&self) -> Expression { + fn state_root(&self) -> word::Word> { self.state_root.clone() } - fn state_root_prev(&self) -> Expression { + fn state_root_prev(&self) -> word::Word> { self.state_root_prev.clone() } - fn value_prev_column(&self) -> Expression { + fn value_prev_column(&self) -> word::Word> { self.rw_table.value_prev_column.clone() } } diff --git a/zkevm-circuits/src/state_circuit/dev.rs b/zkevm-circuits/src/state_circuit/dev.rs index db8262fcce..ce79871912 100644 --- a/zkevm-circuits/src/state_circuit/dev.rs +++ b/zkevm-circuits/src/state_circuit/dev.rs @@ -48,9 +48,7 @@ where mut layouter: impl Layouter, ) -> Result<(), Error> { let challenges = challenges.values(&mut layouter); - config - .mpt_table - .load(&mut layouter, &self.updates, challenges.evm_word())?; + config.mpt_table.load(&mut layouter, &self.updates)?; self.synthesize_sub(&config, &challenges, &mut layouter) } } @@ -61,11 +59,14 @@ pub enum AdviceColumn { Address, AddressLimb0, AddressLimb1, - StorageKey, - StorageKeyByte0, - StorageKeyByte1, - Value, - ValuePrev, + StorageKeyLo, + StorageKeyHi, + StorageKeyLimb0, + StorageKeyLimb1, + ValueLo, + ValueHi, + ValuePrevLo, + ValuePrevHi, RwCounter, RwCounterLimb0, RwCounterLimb1, @@ -79,7 +80,8 @@ pub enum AdviceColumn { LimbIndexBit2, LimbIndexBit3, LimbIndexBit4, // least significant bit - InitialValue, + InitialValueLo, + InitialValueHi, IsZero, // committed_value and value are 0 // NonEmptyWitness is the BatchedIsZero chip witness that contains the // inverse of the non-zero value if any in [committed_value, value] @@ -93,11 +95,14 @@ impl AdviceColumn { 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.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.rw_table.value, - Self::ValuePrev => config.rw_table.value_prev, + Self::StorageKeyLo => config.rw_table.storage_key.lo(), + Self::StorageKeyHi => config.rw_table.storage_key.hi(), + Self::StorageKeyLimb0 => config.sort_keys.storage_key.limbs[0], + Self::StorageKeyLimb1 => config.sort_keys.storage_key.limbs[1], + Self::ValueLo => config.rw_table.value.lo(), + Self::ValueHi => config.rw_table.value.hi(), + Self::ValuePrevLo => config.rw_table.value_prev.lo(), + Self::ValuePrevHi => config.rw_table.value_prev.hi(), 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], @@ -111,7 +116,8 @@ impl AdviceColumn { Self::LimbIndexBit2 => config.lexicographic_ordering.first_different_limb.bits[2], Self::LimbIndexBit3 => config.lexicographic_ordering.first_different_limb.bits[3], Self::LimbIndexBit4 => config.lexicographic_ordering.first_different_limb.bits[4], - Self::InitialValue => config.initial_value, + Self::InitialValueLo => config.initial_value.lo(), + Self::InitialValueHi => config.initial_value.hi(), Self::IsZero => config.is_non_exist.is_zero, Self::NonEmptyWitness => config.is_non_exist.nonempty_witness, } diff --git a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs index bea63c2d5c..062a85cb00 100644 --- a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs +++ b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs @@ -252,7 +252,7 @@ struct Queries { field_tag: Expression, // 8 bits, so we can pack tag + field_tag into one limb. id_limbs: [Expression; N_LIMBS_ID], address_limbs: [Expression; N_LIMBS_ACCOUNT_ADDRESS], - storage_key_bytes: [Expression; N_BYTES_WORD], + storage_key_limbs: [Expression; N_LIMBS_WORD], rw_counter_limbs: [Expression; N_LIMBS_RW_COUNTER], } @@ -265,18 +265,13 @@ impl Queries { id_limbs: keys.id.limbs.map(&mut query_advice), address_limbs: keys.address.limbs.map(&mut query_advice), field_tag: query_advice(keys.field_tag), - storage_key_bytes: keys.storage_key.bytes.map(&mut query_advice), + storage_key_limbs: keys.storage_key.limbs.map(&mut query_advice), rw_counter_limbs: keys.rw_counter.limbs.map(query_advice), } } fn storage_key_be_limbs(&self) -> Vec> { - self.storage_key_bytes - .iter() - .rev() - .tuples() - .map(|(hi, lo)| (1u64 << 8).expr() * hi.clone() + lo.clone()) - .collect() + self.storage_key_limbs.iter().rev().cloned().collect() } fn be_limbs(&self) -> Vec> { diff --git a/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs b/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs index 2a8d963baa..c6826a3773 100644 --- a/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs +++ b/zkevm-circuits/src/state_circuit/multiple_precision_integer.rs @@ -1,6 +1,6 @@ use super::{lookups, param::*}; use crate::util::Expr; -use eth_types::{Address, Field}; +use eth_types::{Address, Field, ToLittleEndian, Word}; use halo2_proofs::{ circuit::{Layouter, Region, Value}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, @@ -11,22 +11,38 @@ use std::marker::PhantomData; pub trait ToLimbs { fn to_limbs(&self) -> [u16; N]; + fn annotation() -> &'static str; } impl ToLimbs for Address { - fn to_limbs(&self) -> [u16; 10] { + fn to_limbs(&self) -> [u16; N_LIMBS_ACCOUNT_ADDRESS] { // address bytes are be.... maybe just have everything be later? // you will need this in the future later because it makes the key ordering more // obvious let le_bytes: Vec<_> = self.0.iter().rev().cloned().collect(); le_bytes_to_limbs(&le_bytes).try_into().unwrap() } + fn annotation() -> &'static str { + "Address" + } } impl ToLimbs for u32 { - fn to_limbs(&self) -> [u16; 2] { + fn to_limbs(&self) -> [u16; N_LIMBS_RW_COUNTER] { le_bytes_to_limbs(&self.to_le_bytes()).try_into().unwrap() } + fn annotation() -> &'static str { + "u32" + } +} + +impl ToLimbs for Word { + fn to_limbs(&self) -> [u16; N_LIMBS_WORD] { + le_bytes_to_limbs(&self.to_le_bytes()).try_into().unwrap() + } + fn annotation() -> &'static str { + "Word" + } } #[derive(Clone, Copy)] @@ -58,47 +74,19 @@ impl Queries { } } -impl Config { - pub fn assign( - &self, - region: &mut Region<'_, F>, - offset: usize, - value: Address, - ) -> Result<(), Error> { - for (i, &limb) in value.to_limbs().iter().enumerate() { - region.assign_advice( - || format!("limb[{}] in address mpi", i), - self.limbs[i], - offset, - || Value::known(F::from(limb as u64)), - )?; - } - Ok(()) - } - - /// Annotates columns of this gadget embedded within a circuit region. - pub fn annotate_columns_in_region(&self, region: &mut Region, prefix: &str) { - let mut annotations = Vec::new(); - for (i, _) in self.limbs.iter().enumerate() { - annotations.push(format!("MPI_limbs_address_{}", i)); - } - self.limbs - .iter() - .zip(annotations.iter()) - .for_each(|(col, ann)| region.name_column(|| format!("{}_{}", prefix, ann), *col)); - } -} - -impl Config { +impl Config +where + T: ToLimbs, +{ pub fn assign( &self, region: &mut Region<'_, F>, offset: usize, - value: u32, + value: T, ) -> Result<(), Error> { for (i, &limb) in value.to_limbs().iter().enumerate() { region.assign_advice( - || format!("limb[{}] in u32 mpi", i), + || format!("limb[{}] in {} mpi", i, T::annotation()), self.limbs[i], offset, || Value::known(F::from(limb as u64)), @@ -111,7 +99,7 @@ impl Config { pub fn annotate_columns_in_region(&self, region: &mut Region, prefix: &str) { let mut annotations = Vec::new(); for (i, _) in self.limbs.iter().enumerate() { - annotations.push(format!("MPI_limbs_u32_{}", i)); + annotations.push(format!("MPI_limbs_{}_{}", T::annotation(), i)); } self.limbs .iter() @@ -120,19 +108,19 @@ impl Config { } } -pub struct Chip +pub struct Chip where - T: ToLimbs, + T: ToLimbs, { - config: Config, + config: Config, _marker: PhantomData, } -impl Chip +impl Chip where - T: ToLimbs, + T: ToLimbs, { - pub fn construct(config: Config) -> Self { + pub fn construct(config: Config) -> Self { Self { config, _marker: PhantomData, @@ -142,22 +130,31 @@ where pub fn configure( meta: &mut ConstraintSystem, selector: Column, - value: Column, + values: [Column; N_VALUES], lookup: lookups::Config, - ) -> Config { - let limbs = [0; N].map(|_| meta.advice_column()); + ) -> Config { + assert_eq!(N_LIMBS & N_VALUES, 0); + let limbs_per_value = N_LIMBS / N_VALUES; + + let limbs = [0; N_LIMBS].map(|_| meta.advice_column()); for &limb in &limbs { 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()); - let limbs = limbs.map(|limb| meta.query_advice(limb, Rotation::cur())); - vec![selector * (value - value_from_limbs(&limbs))] - }); + + for (n, value) in values.iter().enumerate() { + meta.create_gate("mpi value matches claimed limbs", |meta| { + let selector = meta.query_fixed(selector, Rotation::cur()); + let value_expr = meta.query_advice(*value, Rotation::cur()); + let value_limbs = &limbs[n * limbs_per_value..(n + 1) * limbs_per_value]; + let limbs_expr = value_limbs + .iter() + .map(|limb| meta.query_advice(*limb, Rotation::cur())); + vec![selector * (value_expr - value_from_limbs(&limbs_expr.collect::>()))] + }); + } Config { limbs, diff --git a/zkevm-circuits/src/state_circuit/param.rs b/zkevm-circuits/src/state_circuit/param.rs index fb30860c12..e80cc0ef25 100644 --- a/zkevm-circuits/src/state_circuit/param.rs +++ b/zkevm-circuits/src/state_circuit/param.rs @@ -1,3 +1,4 @@ pub(super) const N_LIMBS_RW_COUNTER: usize = 2; pub(super) const N_LIMBS_ACCOUNT_ADDRESS: usize = 10; pub(super) const N_LIMBS_ID: usize = 2; +pub(super) const N_LIMBS_WORD: usize = 16; diff --git a/zkevm-circuits/src/state_circuit/random_linear_combination.rs b/zkevm-circuits/src/state_circuit/random_linear_combination.rs deleted file mode 100644 index edeed2688f..0000000000 --- a/zkevm-circuits/src/state_circuit/random_linear_combination.rs +++ /dev/null @@ -1,105 +0,0 @@ -use crate::evm_circuit::util::rlc; -use eth_types::{Field, ToLittleEndian, U256}; -use halo2_proofs::{ - circuit::{Layouter, Region, Value}, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, - poly::Rotation, -}; -use std::marker::PhantomData; - -use super::lookups; - -#[derive(Clone, Debug, Copy)] -pub struct Config { - // bytes are little endian - pub bytes: [Column; N], -} - -#[derive(Clone)] -pub struct Queries { - pub bytes: [Expression; N], -} - -impl Queries { - pub fn new(meta: &mut VirtualCells<'_, F>, c: Config) -> Self { - Self { - bytes: c.bytes.map(|byte| meta.query_advice(byte, Rotation::cur())), - } - } -} - -impl Config { - pub fn assign( - &self, - region: &mut Region<'_, F>, - offset: usize, - _randomness: Value, // kept for future use - value: U256, - ) -> Result<(), Error> { - let bytes = value.to_le_bytes(); - for (i, &byte) in bytes.iter().enumerate() { - region.assign_advice( - || format!("byte[{}] in rlc", i), - self.bytes[i], - offset, - || Value::known(F::from(byte as u64)), - )?; - } - Ok(()) - } - - /// Annotates columns of this gadget embedded within a circuit region. - pub fn annotate_columns_in_region(&self, region: &mut Region, prefix: &str) { - let mut annotations = Vec::new(); - for (i, _) in self.bytes.iter().enumerate() { - annotations.push(format!("RLC_byte{}", i)); - } - self.bytes - .iter() - .zip(annotations.iter()) - .for_each(|(col, ann)| region.name_column(|| format!("{}_{}", prefix, ann), *col)); - } -} - -pub struct Chip { - config: Config, - _marker: PhantomData, -} - -impl Chip { - pub fn construct(config: Config) -> Self { - Self { - config, - _marker: PhantomData, - } - } - - pub fn configure( - meta: &mut ConstraintSystem, - selector: Column, - encoded: Column, - lookup: lookups::Config, - randomness: Expression, - ) -> Config { - let bytes = [0; N].map(|_| meta.advice_column()); - - for &byte in &bytes { - lookup.range_check_u8(meta, "rlc bytes fit into u8", |meta| { - meta.query_advice(byte, Rotation::cur()) - }); - } - - meta.create_gate("rlc encoded value matches bytes", |meta| { - 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())); - vec![selector * (encoded - rlc::expr(&bytes, randomness))] - }); - - Config { bytes } - } - - pub fn load(&self, _layouter: &mut impl Layouter) -> Result<(), Error> { - Ok(()) - } -} diff --git a/zkevm-circuits/src/state_circuit/test.rs b/zkevm-circuits/src/state_circuit/test.rs index b8eb3841dc..4721667553 100644 --- a/zkevm-circuits/src/state_circuit/test.rs +++ b/zkevm-circuits/src/state_circuit/test.rs @@ -60,7 +60,7 @@ fn test_state_circuit_ok( fn degree() { let mut meta = ConstraintSystem::::default(); StateCircuit::::configure(&mut meta); - assert_eq!(meta.degree(), 9); + assert_eq!(meta.degree(), 10); } #[test] @@ -281,21 +281,6 @@ fn diff_1_problem_repro() { assert_eq!(verify(rows), Ok(())); } -#[test] -fn storage_key_rlc() { - let rows = vec![Rw::AccountStorage { - rw_counter: 1, - is_write: false, - account_address: Address::default(), - storage_key: U256::from(256), - value: U256::from(300), - value_prev: U256::from(300), - tx_id: 4, - committed_value: U256::from(300), - }]; - assert_eq!(verify(rows), Ok(())); -} - #[test] fn tx_log_ok() { let rows = vec![ @@ -421,38 +406,11 @@ fn storage_key_mismatch() { tx_id: 4, committed_value: U256::from(34), }]; - let overrides = HashMap::from([((AdviceColumn::StorageKeyByte1, 0), Fr::ONE)]); + let overrides = HashMap::from([((AdviceColumn::StorageKeyLimb0, 0), Fr::ONE)]); let result = verify_with_overrides(rows, overrides); - assert_error_matches(result, "rlc encoded value matches bytes"); -} - -#[test] -fn storage_key_byte_out_of_range() { - let rows = vec![Rw::AccountStorage { - rw_counter: 1, - is_write: false, - account_address: Address::default(), - storage_key: U256::from(256), - value: U256::from(500), - value_prev: U256::from(500), - tx_id: 4, - committed_value: U256::from(500), - }]; - let overrides = HashMap::from([ - ((AdviceColumn::StorageKeyByte0, 0), Fr::from(0xcafeu64)), - ((AdviceColumn::StorageKeyByte1, 0), Fr::ZERO), - ]); - - // This will trigger two errors: an RLC encoding error and the "fit into u8", we - // remove the first one - let result = verify_with_overrides(rows, overrides).map_err(|mut err| { - err.remove(0); - err - }); - - assert_error_matches(result, "rlc bytes fit into u8"); + assert_error_matches(result, "mpi value matches claimed limbs"); } #[test] @@ -738,11 +696,14 @@ fn bad_initial_memory_value() { let v = Fr::from(200); let overrides = HashMap::from([ - ((AdviceColumn::Value, 0), v), - ((AdviceColumn::ValuePrev, 0), v), + ((AdviceColumn::ValueLo, 0), v), + ((AdviceColumn::ValueHi, 0), Fr::ZERO), + ((AdviceColumn::ValuePrevLo, 0), v), + ((AdviceColumn::ValuePrevHi, 0), Fr::ZERO), ((AdviceColumn::IsZero, 0), Fr::ZERO), ((AdviceColumn::NonEmptyWitness, 0), v.invert().unwrap()), - ((AdviceColumn::InitialValue, 0), v), + ((AdviceColumn::InitialValueLo, 0), v), + ((AdviceColumn::InitialValueHi, 0), Fr::ZERO), ]); let result = verify_with_overrides(rows, overrides); @@ -761,13 +722,14 @@ fn invalid_memory_value() { }]; let v = Fr::from(256); let overrides = HashMap::from([ - ((AdviceColumn::Value, 0), v), + ((AdviceColumn::ValueHi, 0), Fr::ZERO), + ((AdviceColumn::ValueLo, 0), v), ((AdviceColumn::NonEmptyWitness, 0), v.invert().unwrap()), ]); let result = verify_with_overrides(rows, overrides); - assert_error_matches(result, "memory value is a byte"); + assert_error_matches(result, "memory value is a byte (lo is u8)"); } #[test] @@ -855,8 +817,10 @@ fn bad_initial_stack_value() { }]; let overrides = HashMap::from([ - ((AdviceColumn::InitialValue, 0), Fr::from(10)), - ((AdviceColumn::ValuePrev, 0), Fr::from(10)), + ((AdviceColumn::InitialValueHi, 0), Fr::ZERO), + ((AdviceColumn::InitialValueLo, 0), Fr::from(10)), + ((AdviceColumn::ValuePrevHi, 0), Fr::ZERO), + ((AdviceColumn::ValuePrevLo, 0), Fr::from(10)), ]); assert_error_matches( @@ -877,8 +841,10 @@ fn bad_initial_tx_access_list_account_value() { }]; let overrides = HashMap::from([ - ((AdviceColumn::InitialValue, 0), Fr::from(1)), - ((AdviceColumn::ValuePrev, 0), Fr::from(1)), + ((AdviceColumn::InitialValueHi, 0), Fr::ZERO), + ((AdviceColumn::InitialValueLo, 0), Fr::from(1)), + ((AdviceColumn::ValuePrevHi, 0), Fr::ZERO), + ((AdviceColumn::ValuePrevLo, 0), Fr::from(1)), ]); assert_error_matches( @@ -899,11 +865,14 @@ fn bad_initial_tx_refund_value() { let v = Fr::from(10); let overrides = HashMap::from([ ((AdviceColumn::IsWrite, 0), Fr::from(1)), - ((AdviceColumn::Value, 0), v), - ((AdviceColumn::ValuePrev, 0), v), + ((AdviceColumn::ValueHi, 0), Fr::ZERO), + ((AdviceColumn::ValueLo, 0), v), + ((AdviceColumn::ValuePrevHi, 0), Fr::ZERO), + ((AdviceColumn::ValuePrevLo, 0), v), ((AdviceColumn::IsZero, 0), Fr::ZERO), ((AdviceColumn::NonEmptyWitness, 0), v.invert().unwrap()), - ((AdviceColumn::InitialValue, 0), v), + ((AdviceColumn::InitialValueHi, 0), Fr::ZERO), + ((AdviceColumn::InitialValueLo, 0), v), ]); assert_error_matches( @@ -925,8 +894,10 @@ fn bad_initial_tx_log_value() { }]; let overrides = HashMap::from([ - ((AdviceColumn::InitialValue, 0), Fr::from(10)), - ((AdviceColumn::ValuePrev, 0), Fr::from(10)), + ((AdviceColumn::InitialValueHi, 0), Fr::ZERO), + ((AdviceColumn::InitialValueLo, 0), Fr::from(10)), + ((AdviceColumn::ValuePrevHi, 0), Fr::ZERO), + ((AdviceColumn::ValuePrevLo, 0), Fr::from(10)), ]); assert_error_matches( @@ -1009,8 +980,10 @@ fn bad_initial_tx_receipt_value() { }]; let overrides = HashMap::from([ - ((AdviceColumn::Value, 0), Fr::from(1900)), - ((AdviceColumn::InitialValue, 0), Fr::from(1900)), + ((AdviceColumn::ValueHi, 0), Fr::ZERO), + ((AdviceColumn::ValueLo, 0), Fr::from(1900)), + ((AdviceColumn::InitialValueHi, 0), Fr::ZERO), + ((AdviceColumn::InitialValueLo, 0), Fr::from(1900)), ]); assert_error_matches( diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index f203e5d3cc..f39667797e 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -408,11 +408,9 @@ impl, /// Key3 (StorageKey) - pub storage_key_word: word::Word>, - #[deprecated] - /// Key3 (StorageKey) - pub storage_key: Column, - /// Value - pub value_word: word::Word>, - #[deprecated] + pub storage_key: word::Word>, /// Value - pub value: Column, + pub value: word::Word>, /// Value Previous - pub value_prev_word: word::Word>, - #[deprecated] - /// Value Previous - pub value_prev: Column, - /// Aux1 - pub aux1: Column, - /// Aux2 (Committed Value) - pub aux2: Column, + pub value_prev: word::Word>, + /// InitVal (Committed Value) + pub init_val: word::Word>, } impl LookupTable for RwTable { @@ -476,14 +465,14 @@ impl LookupTable for RwTable { self.id.into(), self.address.into(), self.field_tag.into(), - self.storage_key_word.lo().into(), - self.storage_key_word.hi().into(), - self.value_word.lo().into(), - self.value_word.hi().into(), - self.value_prev_word.lo().into(), - self.value_prev_word.hi().into(), - self.aux1.into(), - self.aux2.into(), + self.storage_key.lo().into(), + self.storage_key.hi().into(), + self.value.lo().into(), + self.value.hi().into(), + self.value_prev.lo().into(), + self.value_prev.hi().into(), + self.init_val.lo().into(), + self.init_val.hi().into(), ] } @@ -501,8 +490,8 @@ impl LookupTable for RwTable { String::from("value_hi"), String::from("value_prev_lo"), String::from("value_prev_hi"), - String::from("aux1"), - String::from("aux2"), + String::from("init_val_lo"), + String::from("init_val_hi"), ] } } @@ -516,16 +505,10 @@ impl RwTable { id: meta.advice_column(), address: meta.advice_column(), field_tag: meta.advice_column(), - storage_key_word: word::Word::new([meta.advice_column(), meta.advice_column()]), - value_word: word::Word::new([meta.advice_column(), meta.advice_column()]), - value_prev_word: word::Word::new([meta.advice_column(), meta.advice_column()]), - storage_key: meta.advice_column(), - value: meta.advice_column(), - value_prev: meta.advice_column(), - // It seems that aux1 for the moment is not using randomness - // TODO check in a future review - aux1: meta.advice_column_in(SecondPhase), - aux2: meta.advice_column_in(SecondPhase), + storage_key: word::Word::new([meta.advice_column(), meta.advice_column()]), + value: word::Word::new([meta.advice_column(), meta.advice_column()]), + value_prev: word::Word::new([meta.advice_column(), meta.advice_column()]), + init_val: word::Word::new([meta.advice_column(), meta.advice_column()]), } } fn assign( @@ -535,20 +518,24 @@ impl RwTable { row: &RwRow>, ) -> Result<(), Error> { for (column, value) in [ + (self.address, row.address), (self.rw_counter, row.rw_counter), (self.is_write, row.is_write), (self.tag, row.tag), (self.id, row.id), - (self.address, row.address), (self.field_tag, row.field_tag), + ] { + region.assign_advice(|| "assign rw row on rw table", column, offset, || value)?; + } + for (column, value) in [ (self.storage_key, row.storage_key), (self.value, row.value), (self.value_prev, row.value_prev), - (self.aux1, row.aux1), - (self.aux2, row.aux2), + (self.init_val, row.init_val), ] { - region.assign_advice(|| "assign rw row on rw table", column, offset, || value)?; + value.assign_advice(region, || "assign rw row on rw table", column, offset)?; } + Ok(()) } @@ -559,11 +546,10 @@ impl RwTable { layouter: &mut impl Layouter, rws: &[Rw], n_rows: usize, - challenges: Value, ) -> Result<(), Error> { layouter.assign_region( || "rw table", - |mut region| self.load_with_region(&mut region, rws, n_rows, challenges), + |mut region| self.load_with_region(&mut region, rws, n_rows), ) } @@ -572,11 +558,10 @@ impl RwTable { region: &mut Region<'_, F>, rws: &[Rw], n_rows: usize, - challenges: Value, ) -> 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(challenges))?; + self.assign(region, offset, &row.table_assignment())?; } Ok(()) } @@ -613,7 +598,7 @@ impl From for MPTProofType { /// The MptTable shared between MPT Circuit and State Circuit #[derive(Clone, Copy, Debug)] -pub struct MptTable([Column; 7]); +pub struct MptTable([Column; 12]); impl LookupTable for MptTable { fn columns(&self) -> Vec> { @@ -623,12 +608,17 @@ impl LookupTable for MptTable { fn annotations(&self) -> Vec { vec![ String::from("address"), - String::from("storage_key"), + String::from("storage_key_lo"), + String::from("storage_key_hi"), String::from("proof_type"), - String::from("new_root"), - String::from("old_root"), - String::from("new_value"), - String::from("old_value"), + String::from("new_root_lo"), + String::from("new_root_hi"), + String::from("old_root_lo"), + String::from("old_root_hi"), + String::from("new_value_lo"), + String::from("new_value_hi"), + String::from("old_value_lo"), + String::from("old_value_hi"), ] } } @@ -637,13 +627,18 @@ impl MptTable { /// Construct a new MptTable pub(crate) fn construct(meta: &mut ConstraintSystem) -> Self { Self([ - meta.advice_column(), // Address - meta.advice_column_in(SecondPhase), // Storage key - meta.advice_column(), // Proof type - meta.advice_column_in(SecondPhase), // New root - meta.advice_column_in(SecondPhase), // Old root - meta.advice_column_in(SecondPhase), // New value - meta.advice_column_in(SecondPhase), // Old value + meta.advice_column(), // Address + meta.advice_column(), // Storage key lo + meta.advice_column(), // Storage key hi + meta.advice_column(), // Proof type + meta.advice_column(), // New root lo + meta.advice_column(), // New root hi + meta.advice_column(), // Old root lo + meta.advice_column(), // Old root hi + meta.advice_column(), // New value lo + meta.advice_column(), // New value hi + meta.advice_column(), // Old value lo + meta.advice_column(), // Old value hi ]) } @@ -663,11 +658,10 @@ impl MptTable { &self, layouter: &mut impl Layouter, updates: &MptUpdates, - randomness: Value, ) -> Result<(), Error> { layouter.assign_region( || "mpt table", - |mut region| self.load_with_region(&mut region, updates, randomness), + |mut region| self.load_with_region(&mut region, updates), ) } @@ -675,9 +669,8 @@ impl MptTable { &self, region: &mut Region<'_, F>, updates: &MptUpdates, - randomness: Value, ) -> Result<(), Error> { - for (offset, row) in updates.table_assignments(randomness).iter().enumerate() { + for (offset, row) in updates.table_assignments().iter().enumerate() { self.assign(region, offset, row)?; } Ok(()) diff --git a/zkevm-circuits/src/util/word.rs b/zkevm-circuits/src/util/word.rs index 2a0ca2e46f..fcc7467228 100644 --- a/zkevm-circuits/src/util/word.rs +++ b/zkevm-circuits/src/util/word.rs @@ -6,8 +6,8 @@ use eth_types::{Field, ToLittleEndian, H160}; use gadgets::util::{not, or, Expr}; use halo2_proofs::{ - circuit::{AssignedCell, Value}, - plonk::{Error, Expression}, + circuit::{AssignedCell, Region, Value}, + plonk::{Advice, Column, Error, Expression}, }; use itertools::Itertools; @@ -214,6 +214,13 @@ impl WordExpr for WordLimbs, N> { } } +impl WordLimbs { + /// Check if zero + pub fn is_zero_vartime(&self) -> bool { + self.limbs.iter().all(|limb| limb.is_zero_vartime()) + } +} + /// `Word`, special alias for Word2. #[derive(Clone, Debug, Copy, Default)] pub struct Word(Word2); @@ -239,6 +246,19 @@ impl Word { pub fn to_lo_hi(&self) -> (T, T) { (self.0.limbs[0].clone(), self.0.limbs[1].clone()) } + + /// Extract (move) lo and hi values + pub fn into_lo_hi(self) -> (T, T) { + let [lo, hi] = self.0.limbs; + (lo, hi) + } + + /// Wrap `Word` into into `Word` + pub fn into_value(self) -> Word> { + let [lo, hi] = self.0.limbs; + Word::new([Value::known(lo), Value::known(hi)]) + } + /// Map the word to other types pub fn map(&self, mut func: impl FnMut(T) -> T2) -> Word { Word(WordLimbs::::new([func(self.lo()), func(self.hi())])) @@ -323,6 +343,27 @@ impl Word> { } } +impl Word> { + /// Assign advice + pub fn assign_advice( + &self, + region: &mut Region<'_, F>, + annotation: A, + column: Word>, + offset: usize, + ) -> Result>, Error> + where + A: Fn() -> AR, + AR: Into, + { + let annotation: String = annotation().into(); + let lo = region.assign_advice(|| &annotation, column.lo(), offset, || self.lo())?; + let hi = region.assign_advice(|| &annotation, column.hi(), offset, || self.hi())?; + + Ok(Word::new([lo, hi])) + } +} + impl WordExpr for Word> { fn to_word(&self) -> Word> { self.word_expr().to_word() diff --git a/zkevm-circuits/src/witness/mpt.rs b/zkevm-circuits/src/witness/mpt.rs index c415feb419..2bd2320e80 100644 --- a/zkevm-circuits/src/witness/mpt.rs +++ b/zkevm-circuits/src/witness/mpt.rs @@ -1,8 +1,9 @@ use crate::{ - evm_circuit::{util::rlc, witness::Rw}, + evm_circuit::witness::Rw, table::{AccountFieldTag, MPTProofType}, + util::word, }; -use eth_types::{Address, Field, ToLittleEndian, ToScalar, Word}; +use eth_types::{Address, Field, ToScalar, Word}; use halo2_proofs::circuit::Value; use itertools::Itertools; use std::collections::BTreeMap; @@ -42,7 +43,7 @@ pub struct MptUpdates { /// The field element encoding of an MPT update, which is used by the MptTable #[derive(Debug, Clone, Copy)] -pub struct MptUpdateRow(pub(crate) [F; 7]); +pub struct MptUpdateRow(pub(crate) [F; 12]); impl MptUpdates { pub(crate) fn old_root(&self) -> Word { @@ -84,51 +85,48 @@ impl MptUpdates { } } - pub(crate) fn table_assignments( - &self, - randomness: Value, - ) -> Vec>> { + pub(crate) fn table_assignments(&self) -> Vec>> { self.updates .values() .map(|update| { - let (new_root, old_root) = randomness - .map(|randomness| update.root_assignments(randomness)) - .unzip(); - let (new_value, old_value) = randomness - .map(|randomness| update.value_assignments(randomness)) - .unzip(); - MptUpdateRow([ - Value::known(update.key.address()), - randomness.map(|randomness| update.key.storage_key(randomness)), - Value::known(update.proof_type()), - new_root, - old_root, - new_value, - old_value, - ]) + let (new_root, old_root) = update.root_assignments(); + let (new_value, old_value) = update.value_assignments(); + let (storage_key_lo, storage_key_hi) = + word::Word::::from(update.key.storage_key()).into_lo_hi(); + let (new_root_lo, new_root_hi) = word::Word::::from(new_root).into_lo_hi(); + let (old_root_lo, old_root_hi) = word::Word::::from(old_root).into_lo_hi(); + let (new_value_lo, new_value_hi) = word::Word::::from(new_value).into_lo_hi(); + let (old_value_lo, old_value_hi) = word::Word::::from(old_value).into_lo_hi(); + let address = update.key.address().to_scalar().unwrap(); + + MptUpdateRow( + [ + address, + storage_key_lo, + storage_key_hi, + update.proof_type(), + new_root_lo, + new_root_hi, + old_root_lo, + old_root_hi, + new_value_lo, + new_value_hi, + old_value_lo, + old_value_hi, + ] + .map(|v| Value::known(v)), + ) }) .collect() } } impl MptUpdate { - pub(crate) fn value_assignments(&self, word_randomness: F) -> (F, F) { - let assign = |x: Word| match self.key { - Key::Account { - field_tag: AccountFieldTag::Nonce | AccountFieldTag::NonExisting, - .. - } => x.to_scalar().unwrap(), - _ => rlc::value(&x.to_le_bytes(), word_randomness), - }; - - (assign(self.new_value), assign(self.old_value)) + pub(crate) fn value_assignments(&self) -> (Word, Word) { + (self.new_value, self.old_value) } - - pub(crate) fn root_assignments(&self, word_randomness: F) -> (F, F) { - ( - rlc::value(&self.new_root.to_le_bytes(), word_randomness), - rlc::value(&self.old_root.to_le_bytes(), word_randomness), - ) + pub(crate) fn root_assignments(&self) -> (Word, Word) { + (self.new_root, self.old_root) } } @@ -179,19 +177,15 @@ impl Key { self } } - fn address(&self) -> F { + fn address(&self) -> Address { match self { - Self::Account { address, .. } | Self::AccountStorage { address, .. } => { - address.to_scalar().unwrap() - } + Self::Account { address, .. } | Self::AccountStorage { address, .. } => *address, } } - fn storage_key(&self, randomness: F) -> F { + fn storage_key(&self) -> Word { match self { - Self::Account { .. } => F::ZERO, - Self::AccountStorage { storage_key, .. } => { - rlc::value(&storage_key.to_le_bytes(), randomness) - } + Self::Account { .. } => Word::zero(), + Self::AccountStorage { storage_key, .. } => *storage_key, } } } diff --git a/zkevm-circuits/src/witness/rw.rs b/zkevm-circuits/src/witness/rw.rs index 994c3c3194..6fd3345da1 100644 --- a/zkevm-circuits/src/witness/rw.rs +++ b/zkevm-circuits/src/witness/rw.rs @@ -2,12 +2,13 @@ use std::collections::HashMap; use bus_mapping::operation::{self, AccountField, CallContextField, TxLogField, TxReceiptField}; -use eth_types::{Address, Field, ToAddress, ToLittleEndian, ToScalar, Word, U256}; -use halo2_proofs::{circuit::Value, halo2curves::bn256::Fr}; +use eth_types::{Address, Field, ToAddress, ToScalar, Word, U256}; +use halo2_proofs::circuit::Value; use itertools::Itertools; +use crate::util::word; + use crate::{ - evm_circuit::util::rlc, table::{AccountFieldTag, CallContextFieldTag, RwTableTag, TxLogFieldTag, TxReceiptFieldTag}, util::build_tx_log_address, }; @@ -42,7 +43,6 @@ impl RwMap { } /// Check value in the same way like StateCircuit pub fn check_value(&self) { - let mock_rand = Fr::from(0x1000u64); let err_msg_first = "first access reads don't change value"; let err_msg_non_first = "non-first access reads don't change value"; let rows = self.table_assignments(); @@ -64,19 +64,19 @@ impl RwMap { key(prev_row) != key(row) }; if !row.is_write() { - let value = row.value_assignment::(mock_rand); + let value = row.value_assignment(); if is_first { // value == init_value let init_value = updates .get(row) - .map(|u| u.value_assignments(mock_rand).1) + .map(|u| u.value_assignments().1) .unwrap_or_default(); if value != init_value { errs.push((idx, err_msg_first, *row, *prev_row)); } } else { // value == prev_value - let prev_value = prev_row.value_assignment::(mock_rand); + let prev_value = prev_row.value_assignment(); if value != prev_value { errs.push((idx, err_msg_non_first, *row, *prev_row)); @@ -253,15 +253,14 @@ pub struct RwRow { pub(crate) id: F, pub(crate) address: F, pub(crate) field_tag: F, - pub(crate) storage_key: F, - pub(crate) value: F, - pub(crate) value_prev: F, - pub(crate) aux1: F, - pub(crate) aux2: F, + pub(crate) storage_key: word::Word, + pub(crate) value: word::Word, + pub(crate) value_prev: word::Word, + pub(crate) init_val: word::Word, } impl RwRow { - pub(crate) fn values(&self) -> [F; 11] { + pub(crate) fn values(&self) -> [F; 14] { [ self.rw_counter, self.is_write, @@ -269,13 +268,17 @@ impl RwRow { self.id, self.address, self.field_tag, - self.storage_key, - self.value, - self.value_prev, - self.aux1, - self.aux2, + self.storage_key.lo(), + self.storage_key.hi(), + self.value.lo(), + self.value.hi(), + self.value_prev.lo(), + self.value_prev.hi(), + self.init_val.lo(), + self.init_val.hi(), ] } + pub(crate) fn rlc(&self, randomness: F) -> F { let values = self.values(); values @@ -289,6 +292,35 @@ impl RwRow { } } +impl RwRow> { + pub(crate) fn unwrap(self) -> RwRow { + let unwrap_f = |f: Value| { + let mut inner = None; + _ = f.map(|v| { + inner = Some(v); + }); + inner.unwrap() + }; + let unwrap_w = |f: word::Word>| { + let (lo, hi) = f.into_lo_hi(); + word::Word::new([unwrap_f(lo), unwrap_f(hi)]) + }; + + RwRow { + rw_counter: unwrap_f(self.rw_counter), + is_write: unwrap_f(self.is_write), + tag: unwrap_f(self.tag), + id: unwrap_f(self.id), + address: unwrap_f(self.address), + field_tag: unwrap_f(self.field_tag), + storage_key: unwrap_w(self.storage_key), + value: unwrap_w(self.value), + value_prev: unwrap_w(self.value_prev), + init_val: unwrap_w(self.init_val), + } + } +} + impl Rw { pub(crate) fn tx_access_list_value_pair(&self) -> (bool, bool) { match self { @@ -383,30 +415,7 @@ impl Rw { } } - // At this moment is a helper for the EVM circuit until EVM challange API is - // applied - pub(crate) fn table_assignment_aux(&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), - 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()), - storage_key: rlc::value( - &self.storage_key().unwrap_or_default().to_le_bytes(), - randomness, - ), - 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. - aux2: self - .committed_value_assignment(randomness) - .unwrap_or_default(), - } - } - - pub(crate) fn table_assignment(&self, randomness: Value) -> RwRow> { + pub(crate) fn table_assignment(&self) -> RwRow> { RwRow { rw_counter: Value::known(F::from(self.rw_counter() as u64)), is_write: Value::known(F::from(self.is_write() as u64)), @@ -414,21 +423,12 @@ impl Rw { id: Value::known(F::from(self.id().unwrap_or_default() as u64)), address: Value::known(self.address().unwrap_or_default().to_scalar().unwrap()), field_tag: Value::known(F::from(self.field_tag().unwrap_or_default())), - storage_key: randomness.map(|randomness| { - rlc::value( - &self.storage_key().unwrap_or_default().to_le_bytes(), - randomness, - ) - }), - value: randomness.map(|randomness| self.value_assignment(randomness)), - value_prev: randomness - .map(|randomness| self.value_prev_assignment(randomness).unwrap_or_default()), - aux1: Value::known(F::ZERO), /* only used for AccountStorage::tx_id, which moved to - * key1. */ - aux2: randomness.map(|randomness| { - self.committed_value_assignment(randomness) - .unwrap_or_default() - }), + storage_key: word::Word::from(self.storage_key().unwrap_or_default()).into_value(), + value: word::Word::from(self.value_assignment()).into_value(), + value_prev: word::Word::from(self.value_prev_assignment().unwrap_or_default()) + .into_value(), + init_val: word::Word::from(self.committed_value_assignment().unwrap_or_default()) + .into_value(), } } @@ -561,69 +561,31 @@ impl Rw { } } - pub(crate) fn value_assignment(&self, randomness: F) -> F { + pub(crate) fn value_assignment(&self) -> Word { match self { - Self::Start { .. } => F::ZERO, - Self::CallContext { - field_tag, value, .. - } => { - match field_tag { - // Only these two tags have values that may not fit into a scalar, so we need to - // RLC. - CallContextFieldTag::CodeHash | CallContextFieldTag::Value => { - rlc::value(&value.to_le_bytes(), randomness) - } - _ => value.to_scalar().unwrap(), - } - } - Self::Account { - value, field_tag, .. - } => match field_tag { - AccountFieldTag::CodeHash | AccountFieldTag::Balance => { - rlc::value(&value.to_le_bytes(), randomness) - } - AccountFieldTag::Nonce | AccountFieldTag::NonExisting => value.to_scalar().unwrap(), - }, - Self::AccountStorage { value, .. } | Self::Stack { value, .. } => { - rlc::value(&value.to_le_bytes(), randomness) - } - - Self::TxLog { - field_tag, value, .. - } => match field_tag { - TxLogFieldTag::Topic => rlc::value(&value.to_le_bytes(), randomness), - _ => value.to_scalar().unwrap(), - }, - + Self::Start { .. } => U256::zero(), + Self::CallContext { value, .. } + | Self::Account { value, .. } + | Self::AccountStorage { value, .. } + | Self::Stack { value, .. } + | Self::TxLog { value, .. } => *value, Self::TxAccessListAccount { is_warm, .. } - | Self::TxAccessListAccountStorage { is_warm, .. } => F::from(*is_warm as u64), - Self::Memory { byte, .. } => F::from(u64::from(*byte)), - Self::TxRefund { value, .. } | Self::TxReceipt { value, .. } => F::from(*value), + | Self::TxAccessListAccountStorage { is_warm, .. } => U256::from(*is_warm as u64), + Self::Memory { byte, .. } => U256::from(u64::from(*byte)), + Self::TxRefund { value, .. } | Self::TxReceipt { value, .. } => U256::from(*value), } } - pub(crate) fn value_prev_assignment(&self, randomness: F) -> Option { + pub(crate) fn value_prev_assignment(&self) -> Option { match self { - Self::Account { - value_prev, - field_tag, - .. - } => Some(match field_tag { - AccountFieldTag::CodeHash | AccountFieldTag::Balance => { - rlc::value(&value_prev.to_le_bytes(), randomness) - } - AccountFieldTag::Nonce | AccountFieldTag::NonExisting => { - value_prev.to_scalar().unwrap() - } - }), - Self::AccountStorage { value_prev, .. } => { - Some(rlc::value(&value_prev.to_le_bytes(), randomness)) + Self::Account { value_prev, .. } | Self::AccountStorage { value_prev, .. } => { + Some(*value_prev) } Self::TxAccessListAccount { is_warm_prev, .. } | Self::TxAccessListAccountStorage { is_warm_prev, .. } => { - Some(F::from(*is_warm_prev as u64)) + Some(U256::from(*is_warm_prev as u64)) } - Self::TxRefund { value_prev, .. } => Some(F::from(*value_prev)), + Self::TxRefund { value_prev, .. } => Some(U256::from(*value_prev)), Self::Start { .. } | Self::Stack { .. } | Self::Memory { .. } @@ -633,11 +595,11 @@ impl Rw { } } - fn committed_value_assignment(&self, randomness: F) -> Option { + fn committed_value_assignment(&self) -> Option { match self { Self::AccountStorage { committed_value, .. - } => Some(rlc::value(&committed_value.to_le_bytes(), randomness)), + } => Some(*committed_value), _ => None, } }