Skip to content

Commit

Permalink
Add challenge API to state circuit (privacy-scaling-explorations#885)
Browse files Browse the repository at this point in the history
  • Loading branch information
adria0 authored Nov 10, 2022
1 parent d2fac3c commit 9972930
Show file tree
Hide file tree
Showing 15 changed files with 178 additions and 106 deletions.
2 changes: 1 addition & 1 deletion circuit-benchmarks/src/state_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ mod tests {
.parse()
.expect("Cannot parse DEGREE env var as u32");

let empty_circuit = StateCircuit::<Fr>::new(Fr::default(), RwMap::default(), 1 << 16);
let empty_circuit = StateCircuit::<Fr>::new(RwMap::default(), 1 << 16);

// Initialize the polynomial commitment parameters
let mut rng = XorShiftRng::from_seed([
Expand Down
7 changes: 2 additions & 5 deletions integration-tests/tests/circuits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,8 @@ async fn test_state_circuit_block(block_num: u64) {

let rw_map = RwMap::from(&builder.block.container);

let randomness = Fr::from(0xcafeu64);
let circuit = StateCircuit::<Fr>::new(randomness, rw_map, 1 << 16);
let power_of_randomness = circuit.instance();

let prover = MockProver::<Fr>::run(DEGREE as u32, &circuit, power_of_randomness).unwrap();
let circuit = StateCircuit::<Fr>::new(rw_map, 1 << 16);
let prover = MockProver::<Fr>::run(DEGREE as u32, &circuit, circuit.instance()).unwrap();
prover
.verify_par()
.expect("state_circuit verification failed");
Expand Down
2 changes: 1 addition & 1 deletion zkevm-circuits/src/copy_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,7 @@ pub mod dev {
&mut layouter,
&self.block.rws.table_assignments(),
self.block.circuits_params.max_rws,
self.randomness,
Value::known(self.randomness),
)?;
config.bytecode_table.load(
&mut layouter,
Expand Down
2 changes: 1 addition & 1 deletion zkevm-circuits/src/evm_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ pub mod test {
&mut layouter,
&self.block.rws.table_assignments(),
self.block.circuits_params.max_rws,
self.block.randomness,
Value::known(self.block.randomness),
)?;
config.bytecode_table.load(
&mut layouter,
Expand Down
7 changes: 5 additions & 2 deletions zkevm-circuits/src/evm_circuit/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1167,7 +1167,10 @@ impl<F: Field> ExecutionConfig<F> {
.rws
.table_assignments()
.iter()
.map(|rw| rw.table_assignment(block.randomness).rlc(block.randomness))
.map(|rw| {
rw.table_assignment_aux(block.randomness)
.rlc(block.randomness)
})
.collect();

for (name, value) in assigned_rw_values.iter() {
Expand All @@ -1178,7 +1181,7 @@ impl<F: Field> ExecutionConfig<F> {
for (idx, assigned_rw_value) in assigned_rw_values.iter().enumerate() {
let rw_idx = step.rw_indices[idx];
let rw = block.rws[rw_idx];
let table_assignments = rw.table_assignment(block.randomness);
let table_assignments = rw.table_assignment_aux(block.randomness);
let rlc = table_assignments.rlc(block.randomness);
if rlc != assigned_rw_value.1 {
log::error!(
Expand Down
2 changes: 1 addition & 1 deletion zkevm-circuits/src/evm_circuit/execution/extcodehash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ impl<F: Field> ExecutionGadget<F> for ExtcodehashGadget<F> {

let [nonce, balance, code_hash] = [5, 6, 7].map(|i| {
block.rws[step.rw_indices[i]]
.table_assignment(block.randomness)
.table_assignment_aux(block.randomness)
.value
});

Expand Down
118 changes: 75 additions & 43 deletions zkevm-circuits/src/state_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,18 @@ mod test;
use crate::{
evm_circuit::param::N_BYTES_WORD,
table::{LookupTable, MptTable, RwTable, RwTableTag},
util::{power_of_randomness_from_instance, Expr},
util::{Challenges, Expr},
witness::{MptUpdates, Rw, RwMap},
};
use constraint_builder::{ConstraintBuilder, Queries};
use eth_types::{Address, Field};
use gadgets::binary_number::{BinaryNumberChip, BinaryNumberConfig};
use halo2_proofs::{
circuit::{Layouter, Region, SimpleFloorPlanner, Value},
plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells},
plonk::{
Advice, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, SecondPhase,
VirtualCells,
},
poly::Rotation,
};
use lexicographic_ordering::Config as LexicographicOrderingConfig;
Expand All @@ -27,7 +30,7 @@ use multiple_precision_integer::{Chip as MpiChip, Config as MpiConfig, Queries a
use random_linear_combination::{Chip as RlcChip, Config as RlcConfig, Queries as RlcQueries};
#[cfg(test)]
use std::collections::HashMap;
use std::iter::once;
use std::{iter::once, marker::PhantomData};

use self::constraint_builder::{MptUpdateTableQueries, RwTableQueries};

Expand Down Expand Up @@ -56,9 +59,9 @@ impl<F: Field> StateCircuitConfig<F> {
/// Configure StateCircuit
pub fn configure(
meta: &mut ConstraintSystem<F>,
power_of_randomness: [Expression<F>; 31],
rw_table: &RwTable,
mpt_table: &MptTable,
challenges: Challenges<Expression<F>>,
) -> Self {
let selector = meta.fixed_column();
let lookups = LookupsChip::configure(meta);
Expand All @@ -67,16 +70,17 @@ impl<F: Field> StateCircuitConfig<F> {
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(),
challenges.evm_word_powers_of_randomness(),
);

let initial_value = meta.advice_column();
let state_root = meta.advice_column();
let initial_value = meta.advice_column_in(SecondPhase);
let state_root = meta.advice_column_in(SecondPhase);

let sort_keys = SortKeysConfig {
tag,
Expand All @@ -91,7 +95,7 @@ impl<F: Field> StateCircuitConfig<F> {
meta,
sort_keys,
lookups,
power_of_randomness.clone(),
challenges.evm_word_powers_of_randomness(),
);

let config = Self {
Expand All @@ -101,7 +105,7 @@ impl<F: Field> StateCircuitConfig<F> {
state_root,
lexicographic_ordering,
lookups,
power_of_randomness,
power_of_randomness: challenges.evm_word_powers_of_randomness(),
rw_table: *rw_table,
mpt_table: *mpt_table,
};
Expand Down Expand Up @@ -129,12 +133,14 @@ impl<F: Field> StateCircuitConfig<F> {
layouter: &mut impl Layouter<F>,
rows: &[Rw],
n_rows: usize, // 0 means dynamically calculated from `rows`.
randomness: F,
challenges: &Challenges<Value<F>>,
) -> 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, randomness),
|mut region| {
self.assign_with_region(&mut region, rows, &updates, n_rows, challenges.evm_word())
},
)
}

Expand All @@ -144,7 +150,7 @@ impl<F: Field> StateCircuitConfig<F> {
rows: &[Rw],
updates: &MptUpdates,
n_rows: usize, // 0 means dynamically calculated from `rows`.
randomness: F,
randomness: Value<F>,
) -> Result<(), Error> {
let tag_chip = BinaryNumberChip::construct(self.sort_keys.tag);

Expand All @@ -153,7 +159,7 @@ impl<F: Field> StateCircuitConfig<F> {
let rows = rows.into_iter();
let prev_rows = once(None).chain(rows.clone().map(Some));

let mut state_root = F::zero();
let mut state_root = randomness.map(|_| F::zero());

for (offset, (row, prev_row)) in rows.zip(prev_rows).enumerate() {
if offset >= padding_length {
Expand All @@ -172,12 +178,15 @@ impl<F: Field> StateCircuitConfig<F> {
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
Expand All @@ -191,28 +200,41 @@ impl<F: Field> StateCircuitConfig<F> {

if is_first_access {
// If previous row was a last access, we need to update the 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 = 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
});
}
}

// The initial value can be determined from the mpt updates or is 0.
let initial_value = updates
.get(&row)
.map(|u| u.value_assignments(randomness).1)
.unwrap_or_default();
let initial_value = randomness.map(|randomness| {
updates
.get(&row)
.map(|u| u.value_assignments(randomness).1)
.unwrap_or_default()
});

region.assign_advice(
|| "initial_value",
self.initial_value,
offset,
|| Value::known(initial_value),
|| initial_value,
)?;

// TODO: Switch from Rw::Start -> Rw::Padding to simplify this logic.
Expand All @@ -223,23 +245,25 @@ impl<F: Field> StateCircuitConfig<F> {
|| "state_root",
self.state_root,
offset - 1,
|| Value::known(state_root),
|| state_root,
)?;
}

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) {
let (new_root, old_root) = update.root_assignments(randomness);
assert_eq!(state_root, old_root);
state_root = new_root;
state_root = randomness.zip(state_root).map(|(randomness, state_root)| {
let (new_root, old_root) = update.root_assignments(randomness);
assert_eq!(state_root, old_root);
new_root
});
}
region.assign_advice(
|| "last row state_root",
self.state_root,
offset,
|| Value::known(state_root),
|| state_root,
)?;
}
}
Expand Down Expand Up @@ -267,39 +291,37 @@ pub struct StateCircuit<F> {
pub(crate) rows: Vec<Rw>,
updates: MptUpdates,
pub(crate) n_rows: usize,
pub(crate) randomness: F,
#[cfg(test)]
overrides: HashMap<(test::AdviceColumn, isize), F>,
_marker: PhantomData<F>,
}

impl<F: Field> StateCircuit<F> {
/// make a new state circuit from an RwMap
pub fn new(randomness: F, rw_map: RwMap, n_rows: usize) -> Self {
pub fn new(rw_map: RwMap, n_rows: usize) -> Self {
let rows = rw_map.table_assignments();
let updates = MptUpdates::mock_from(&rows);
Self {
randomness,
rows,
updates,
n_rows,
#[cfg(test)]
overrides: HashMap::new(),
_marker: PhantomData::default(),
}
}

/// powers of randomness for instance columns
pub fn instance(&self) -> Vec<Vec<F>> {
(1..32)
.map(|exp| vec![self.randomness.pow(&[exp, 0, 0, 0]); self.n_rows])
.collect()
vec![]
}
}

impl<F: Field> Circuit<F> for StateCircuit<F>
where
F: Field,
{
type Config = StateCircuitConfig<F>;
type Config = (StateCircuitConfig<F>, Challenges);
type FloorPlanner = SimpleFloorPlanner;

fn without_witnesses(&self) -> Self {
Expand All @@ -309,17 +331,27 @@ where
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
let rw_table = RwTable::construct(meta);
let mpt_table = MptTable::construct(meta);
let power_of_randomness: [Expression<F>; 31] = power_of_randomness_from_instance(meta);
Self::Config::configure(meta, power_of_randomness, &rw_table, &mpt_table)
let challenges = Challenges::construct(meta);

let config = {
let challenges = challenges.exprs(meta);
StateCircuitConfig::configure(meta, &rw_table, &mpt_table, challenges)
};

(config, challenges)
}

fn synthesize(
&self,
config: Self::Config,
mut layouter: impl Layouter<F>,
) -> Result<(), Error> {
let (config, challenges) = config;

config.load(&mut layouter)?;

let randomness = challenges.values(&mut layouter).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.
Expand All @@ -330,19 +362,19 @@ where
&mut region,
&self.rows,
self.n_rows,
self.randomness,
randomness,
)?;

config
.mpt_table
.load_with_region(&mut region, &self.updates, self.randomness)?;
.load_with_region(&mut region, &self.updates, randomness)?;

config.assign_with_region(
&mut region,
&self.rows,
&self.updates,
self.n_rows,
self.randomness,
randomness,
)?;
#[cfg(test)]
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl<const N: usize> Config<N> {
&self,
region: &mut Region<'_, F>,
offset: usize,
_randomness: F, // kept for future use
_randomness: Value<F>, // kept for future use
value: U256,
) -> Result<(), Error> {
let bytes = value.to_le_bytes();
Expand Down
Loading

0 comments on commit 9972930

Please sign in to comment.