From bcae2ed188cdb856b758bf67a5aeee15b1534157 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Sun, 30 Jul 2023 23:03:05 +0200 Subject: [PATCH 01/43] init --- Cargo.lock | 1 + gadgets/src/util.rs | 46 + zkevm-circuits/Cargo.toml | 1 + zkevm-circuits/src/circuit_tools.rs | 9 + .../src/circuit_tools/cached_region.rs | 233 +++ .../src/circuit_tools/cell_manager.rs | 477 +++++ .../src/circuit_tools/constraint_builder.rs | 1643 +++++++++++++++++ zkevm-circuits/src/circuit_tools/gadgets.rs | 168 ++ zkevm-circuits/src/circuit_tools/memory.rs | 322 ++++ .../src/circuit_tools/test/database.rs | 152 ++ zkevm-circuits/src/circuit_tools/test/mod.rs | 308 +++ .../circuit_tools/test/query_and_branch.rs | 154 ++ .../src/circuit_tools/test/shuffle.rs | 192 ++ .../src/circuit_tools/test/simple_rlp.rs | 78 + zkevm-circuits/src/keccak_circuit.rs | 2 +- zkevm-circuits/src/lib.rs | 1 + zkevm-circuits/src/taiko_pi_circuit.rs | 56 +- 17 files changed, 3835 insertions(+), 8 deletions(-) create mode 100644 zkevm-circuits/src/circuit_tools.rs create mode 100644 zkevm-circuits/src/circuit_tools/cached_region.rs create mode 100644 zkevm-circuits/src/circuit_tools/cell_manager.rs create mode 100644 zkevm-circuits/src/circuit_tools/constraint_builder.rs create mode 100644 zkevm-circuits/src/circuit_tools/gadgets.rs create mode 100644 zkevm-circuits/src/circuit_tools/memory.rs create mode 100644 zkevm-circuits/src/circuit_tools/test/database.rs create mode 100644 zkevm-circuits/src/circuit_tools/test/mod.rs create mode 100644 zkevm-circuits/src/circuit_tools/test/query_and_branch.rs create mode 100644 zkevm-circuits/src/circuit_tools/test/shuffle.rs create mode 100644 zkevm-circuits/src/circuit_tools/test/simple_rlp.rs diff --git a/Cargo.lock b/Cargo.lock index d4746e56aa..4062cc942f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5417,6 +5417,7 @@ dependencies = [ "mock", "num", "num-bigint", + "num_enum", "once_cell", "pretty_assertions", "rand", diff --git a/gadgets/src/util.rs b/gadgets/src/util.rs index e0bfcaa6f1..8ff5e3bcec 100644 --- a/gadgets/src/util.rs +++ b/gadgets/src/util.rs @@ -141,6 +141,35 @@ pub mod select { } } +/// Trait that implements functionality to get a scalar from +/// commonly used types. +pub trait Scalar { + /// Returns a scalar for the type. + fn scalar(&self) -> F; +} + +/// Implementation trait `Scalar` for type able to be casted to u64 +#[macro_export] +macro_rules! impl_scalar { + ($type:ty) => { + impl $crate::util::Scalar for $type { + #[inline] + fn scalar(&self) -> F { + F::from(*self as u64) + } + } + }; + ($type:ty, $method:path) => { + impl $crate::util::Scalar for $type { + #[inline] + fn scalar(&self) -> F { + F::from($method(self) as u64) + } + } + }; +} + + /// Trait that implements functionality to get a constant expression from /// commonly used types. pub trait Expr { @@ -152,6 +181,7 @@ pub trait Expr { #[macro_export] macro_rules! impl_expr { ($type:ty) => { + $crate::impl_scalar!($type); impl $crate::util::Expr for $type { #[inline] fn expr(&self) -> Expression { @@ -160,6 +190,7 @@ macro_rules! impl_expr { } }; ($type:ty, $method:path) => { + $crate::impl_scalar!($type, $method); impl $crate::util::Expr for $type { #[inline] fn expr(&self) -> Expression { @@ -173,9 +204,24 @@ impl_expr!(bool); impl_expr!(u8); impl_expr!(u64); impl_expr!(usize); +impl_expr!(isize); impl_expr!(OpcodeId, OpcodeId::as_u8); impl_expr!(GasCost, GasCost::as_u64); +impl Scalar for i32 { + #[inline] + fn scalar(&self) -> F { + F::from(self.unsigned_abs() as u64) * if self.is_negative() { -F::ONE } else { F::ONE } + } +} + +impl Scalar for &F { + #[inline] + fn scalar(&self) -> F { + *(*self) + } +} + impl Expr for Expression { #[inline] fn expr(&self) -> Expression { diff --git a/zkevm-circuits/Cargo.toml b/zkevm-circuits/Cargo.toml index 6eee0266c5..8d46cc82d3 100644 --- a/zkevm-circuits/Cargo.toml +++ b/zkevm-circuits/Cargo.toml @@ -33,6 +33,7 @@ maingate = { git = "https://github.com/privacy-scaling-explorations/halo2wrong" integer = { git = "https://github.com/privacy-scaling-explorations/halo2wrong", tag = "v2023_04_20" } libsecp256k1 = "0.7" num-bigint = { version = "0.4" } +num_enum = "0.5.7" rand_chacha = "0.3" snark-verifier = { git = "https://github.com/brechtpd/snark-verifier.git", branch = "feat/add-sdk", default-features = false, features = ["loader_halo2", "system_halo2", "loader_evm", "parallel"] } snark-verifier-sdk = { git = "https://github.com/brechtpd/snark-verifier.git", branch = "feat/add-sdk", default-features = false, features = ["loader_halo2", "loader_evm", "parallel", "display", "halo2_circuit_params"] } diff --git a/zkevm-circuits/src/circuit_tools.rs b/zkevm-circuits/src/circuit_tools.rs new file mode 100644 index 0000000000..d775668321 --- /dev/null +++ b/zkevm-circuits/src/circuit_tools.rs @@ -0,0 +1,9 @@ +//! Circuit utilities +#![allow(missing_docs)] +#[macro_use] +pub mod constraint_builder; +pub mod cached_region; +pub mod cell_manager; +pub mod gadgets; +pub mod memory; +mod test; \ No newline at end of file diff --git a/zkevm-circuits/src/circuit_tools/cached_region.rs b/zkevm-circuits/src/circuit_tools/cached_region.rs new file mode 100644 index 0000000000..c9698e2761 --- /dev/null +++ b/zkevm-circuits/src/circuit_tools/cached_region.rs @@ -0,0 +1,233 @@ +use crate::circuit_tools::cell_manager::Cell; +use eth_types::Field; +use halo2_proofs::{ + circuit::{AssignedCell, Region, Value}, + plonk::{Advice, Any, Assigned, Column, Error, Expression, Fixed}, + poly::Rotation, +}; +use std::{ + collections::HashMap, + hash::{Hash, Hasher}, +}; + +use super::{cell_manager::CellType, constraint_builder::ConstraintBuilder}; + +pub trait ChallengeSet { + fn indexed(&self) -> Vec<&Value>; +} + +impl]>> ChallengeSet for V { + fn indexed(&self) -> Vec<&Value> { + self.as_ref().iter().collect() + } +} + +pub struct CachedRegion<'r, 'b, F: Field> { + region: &'r mut Region<'b, F>, + pub advice: HashMap<(usize, usize), F>, + pub fixed: HashMap<(usize, usize), F>, + disable_description: bool, + regions: Vec<(usize, usize)>, + pub r: F, + pub keccak_r: F, +} + +impl<'r, 'b, F: Field> CachedRegion<'r, 'b, F> { + pub(crate) fn new(region: &'r mut Region<'b, F>, r: F, keccak_r: F) -> Self { + Self { + region, + advice: HashMap::new(), + fixed: HashMap::new(), + disable_description: false, + regions: Vec::new(), + r, + keccak_r, + } + } + + pub(crate) fn set_disable_description(&mut self, disable_description: bool) { + self.disable_description = disable_description; + } + + pub(crate) fn push_region(&mut self, offset: usize, region_id: usize) { + self.regions.push((offset, region_id)); + } + + pub(crate) fn pop_region(&mut self) { + // Nothing to do + } + + pub(crate) fn lookup_challenge(&self) -> F { + self.r.clone() + } + + pub(crate) fn assign_stored_expressions>( + &mut self, + cb: &ConstraintBuilder, + challenges: &S, + ) -> Result<(), Error> { + for (offset, region_id) in self.regions.clone() { + for stored_expression in cb.get_stored_expressions(region_id).iter() { + // println!("stored expression: {}", stored_expression.name); + stored_expression.assign(self, challenges, offset)?; + } + } + Ok(()) + } + + /// Assign an advice column value (witness). + pub fn assign_advice<'v, V, VR, A, AR>( + &'v mut self, + annotation: A, + column: Column, + offset: usize, + to: V, + ) -> Result, Error> + where + V: Fn() -> Value + 'v, + for<'vr> Assigned: From<&'vr VR>, + A: Fn() -> AR, + AR: Into, + { + // Actually set the value + let res = self.region.assign_advice(annotation, column, offset, &to); + // Cache the value + // Note that the `value_field` in `AssignedCell` might be `Value::unkonwn` if + // the column has different phase than current one, so we call to `to` + // again here to cache the value. + if res.is_ok() { + to().map(|f: VR| { + let existing = self + .advice + .insert((column.index(), offset), Assigned::from(&f).evaluate()); + assert!(existing.is_none()); + existing + }); + } + res + } + + pub fn name_column(&mut self, annotation: A, column: T) + where + A: Fn() -> AR, + AR: Into, + T: Into>, + { + self.region + .name_column(|| annotation().into(), column.into()); + } + + pub fn assign_fixed<'v, V, VR, A, AR>( + &'v mut self, + annotation: A, + column: Column, + offset: usize, + to: V, + ) -> Result, Error> + where + V: Fn() -> Value + 'v, + for<'vr> Assigned: From<&'vr VR>, + A: Fn() -> AR, + AR: Into, + { + // Actually set the value + let res = self.region.assign_fixed(annotation, column, offset, &to); + // Cache the value + // Note that the `value_field` in `AssignedCell` might be `Value::unkonwn` if + // the column has different phase than current one, so we call to `to` + // again here to cache the value. + if res.is_ok() { + to().map(|f: VR| { + let existing = self + .fixed + .insert((column.index(), offset), Assigned::from(&f).evaluate()); + assert!(existing.is_none()); + existing + }); + } + res + } + + pub fn get_fixed(&self, row_index: usize, column_index: usize, rotation: Rotation) -> F { + let zero = F::ZERO; + *self + .fixed + .get(&(column_index, row_index + rotation.0 as usize)) + .unwrap_or(&zero) + } + + pub fn get_advice(&self, row_index: usize, column_index: usize, rotation: Rotation) -> F { + let zero = F::ZERO; + *self + .advice + .get(&(column_index, row_index + rotation.0 as usize)) + .unwrap_or(&zero) + } + + /// Constrains a cell to have a constant value. + /// + /// Returns an error if the cell is in a column where equality has not been + /// enabled. + pub fn constrain_constant( + &mut self, + cell: AssignedCell, + constant: VR, + ) -> Result<(), Error> + where + VR: Into>, + { + self.region.constrain_constant(cell.cell(), constant.into()) + } +} + +#[derive(Debug, Clone)] +pub struct StoredExpression { + pub(crate) name: String, + pub(crate) cell: Cell, + pub(crate) cell_type: C, + pub(crate) expr: Expression, + pub(crate) expr_id: String, +} + +impl Hash for StoredExpression { + fn hash(&self, state: &mut H) { + self.expr_id.hash(state); + self.cell_type.hash(state); + } +} + +impl StoredExpression { + pub fn assign>( + &self, + region: &mut CachedRegion<'_, '_, F>, + challenges: &S, + offset: usize, + ) -> Result, Error> { + let value = self.expr.evaluate( + &|scalar| Value::known(scalar), + &|_| unimplemented!("selector column"), + &|fixed_query| { + Value::known(region.get_fixed( + offset, + fixed_query.column_index(), + fixed_query.rotation(), + )) + }, + &|advice_query| { + Value::known(region.get_advice( + offset, + advice_query.column_index(), + advice_query.rotation(), + )) + }, + &|_| unimplemented!("instance column"), + &|challenge| *challenges.indexed()[challenge.index()], + &|a| -a, + &|a, b| a + b, + &|a, b| a * b, + &|a, scalar| a * Value::known(scalar), + ); + self.cell.assign_value(region, offset, value)?; + Ok(value) + } +} diff --git a/zkevm-circuits/src/circuit_tools/cell_manager.rs b/zkevm-circuits/src/circuit_tools/cell_manager.rs new file mode 100644 index 0000000000..864c7a5aed --- /dev/null +++ b/zkevm-circuits/src/circuit_tools/cell_manager.rs @@ -0,0 +1,477 @@ +//! Cell manager +use crate::{ + circuit_tools::cached_region::CachedRegion, + util::{query_expression, Expr}, +}; + +use crate::table::LookupTable; +use eth_types::Field; +use halo2_proofs::{ + circuit::{AssignedCell, Value}, + plonk::{ + Advice, Any, Column, ConstraintSystem, Error, Expression, FirstPhase, SecondPhase, + ThirdPhase, VirtualCells, + }, + poly::Rotation, +}; +use lazy_static::__Deref; +use std::{collections::{BTreeMap, HashMap}, fmt::Debug, hash::Hash, cmp::{max, Ordering}}; + +#[derive(Clone, Debug, Default)] +pub(crate) struct Cell { + // expression for constraint + expression: Option>, + pub column: Option>, + // relative position to selector for synthesis + pub rotation: usize, +} + +impl Cell { + pub(crate) fn new(meta: &mut VirtualCells, column: Column, rotation: usize) -> Self { + Self { + expression: Some(meta.query_advice(column, Rotation(rotation as i32))), + column: Some(column), + rotation, + } + } + + pub(crate) fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + value: F, + ) -> Result, Error> { + region.assign_advice( + || { + format!( + "Cell column: {:?} and rotation: {}", + self.column, self.rotation + ) + }, + self.column.unwrap(), + offset + self.rotation, + || Value::known(value), + ) + } + + pub(crate) fn assign_value( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + value: Value, + ) -> Result, Error> { + region.assign_advice( + || { + format!( + "Cell column: {:?} and rotation: {}", + self.column.unwrap(), + self.rotation + ) + }, + self.column.unwrap(), + offset + self.rotation, + || value, + ) + } + + pub(crate) fn column(&self) -> Column { + self.column.unwrap() + } + + pub(crate) fn rotation(&self) -> usize { + self.rotation + } + + pub(crate) fn rot(&self, meta: &mut VirtualCells, rot: usize) -> Expression { + meta.query_advice(self.column.unwrap(), Rotation((self.rotation + rot) as i32)) + } + + pub(crate) fn identifier(&self) -> String { + self.expr().identifier() + } +} + +impl Expr for Cell { + fn expr(&self) -> Expression { + self.expression.as_ref().unwrap().clone() + } +} + +impl Expr for &Cell { + fn expr(&self) -> Expression { + self.expression.as_ref().unwrap().clone() + } +} + +#[derive(Clone, Debug)] +pub struct CellConfig { + pub cell_type: C, + pub num_columns: usize, + pub phase: u8, + pub is_permute: bool, +} + +impl From<(C, usize, u8, bool)> for CellConfig { + fn from((cell_type, num_columns, phase, is_permute): (C, usize, u8, bool)) -> Self { + Self { + cell_type, + num_columns, + phase, + is_permute, + } + } +} + +impl CellConfig { + pub fn init_columns(&self, meta: &mut ConstraintSystem) -> Vec> { + let mut columns = Vec::with_capacity(self.num_columns); + for _ in 0..self.num_columns { + let tmp = match self.phase { + 1 => meta.advice_column_in(FirstPhase), + 2 => meta.advice_column_in(SecondPhase), + 3 => meta.advice_column_in(ThirdPhase), + _ => unreachable!(), + }; + columns.push(tmp); + } + if self.is_permute { + let _ = columns + .iter() + .map(|c| meta.enable_equality(*c)) + .collect::>(); + } + columns + } +} + +pub trait CellType: + Clone + Copy + Debug + PartialEq + Eq + PartialOrd + Ord + Hash + Default +{ + fn byte_type() -> Option; + + // The phase that given `Expression` becomes evaluateable. + fn expr_phase(expr: &Expression) -> u8 { + use Expression::*; + match expr { + Challenge(challenge) => challenge.phase() + 1, + Advice(query) => query.phase(), + Constant(_) | Selector(_) | Fixed(_) | Instance(_) => 0, + Negated(a) | Expression::Scaled(a, _) => Self::expr_phase(a), + Sum(a, b) | Product(a, b) => std::cmp::max(Self::expr_phase(a), Self::expr_phase(b)), + } + } + + /// Return the storage phase of phase + fn storage_for_phase(phase: u8) -> Self; + + /// Return the storage cell of the expression + fn storage_for_expr(expr: &Expression) -> Self { + Self::storage_for_phase(Self::expr_phase::(expr)) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum DefaultCellType { + StoragePhase1, + StoragePhase2, + StoragePhase3, +} + +impl Default for DefaultCellType { + fn default() -> Self { + Self::StoragePhase1 + } +} + +impl CellType for DefaultCellType { + fn byte_type() -> Option { + Some(DefaultCellType::StoragePhase1) + } + + fn storage_for_phase(phase: u8) -> Self { + // println!("phase: {}", phase); + match phase { + 1 => DefaultCellType::StoragePhase1, + 2 => DefaultCellType::StoragePhase2, + 3 => DefaultCellType::StoragePhase3, + _ => unreachable!(), + } + } +} + +#[derive(Clone, Debug)] +pub(crate) struct CellColumn { + pub(crate) column: Column, + index: usize, + pub(crate) cell_type: C, + height: usize, + cells: Vec>, + pub(crate) expr: Expression, +} + + +impl PartialEq for CellColumn { + fn eq(&self, other: &Self) -> bool { + self.index == other.index && self.cell_type == other.cell_type && self.height == other.height + } +} + +impl Eq for CellColumn {} + +impl PartialOrd for CellColumn { + fn partial_cmp(&self, other: &Self) -> Option { + self.height.partial_cmp(&other.height) + } +} + +impl Ord for CellColumn { + fn cmp(&self, other: &Self) -> Ordering { + self.height.cmp(&other.height) + } +} + +impl Expr for CellColumn { + fn expr(&self) -> Expression { + self.expr.clone() + } +} + + +#[derive(Clone, Debug)] +pub struct CellManager { + configs: Vec>, + columns: Vec>, + height: usize, + width: usize, + height_limit: usize, + + // branch ctxs + branch_ctxs: HashMap>, + parent_ctx: Option>, +} + + +#[derive(Default, Clone, Debug)] +struct CmContext{ + parent: Box>>, + columns: Vec>, +} + +impl CellManager { + pub(crate) fn new( + meta: &mut ConstraintSystem, + configs: Vec<(C, usize, u8, bool)>, + offset: usize, + max_height: usize, + ) -> Self { + assert!(max_height >= 1); + let configs = configs + .into_iter() + .map(|c| c.into()) + .collect::>>(); + + let mut width = 0; + let mut columns = Vec::new(); + for config in configs.iter() { + let cols = config.init_columns(meta); + for col in cols.iter() { + let mut cells = Vec::new(); + for r in 0..max_height { + query_expression(meta, |meta| { + cells.push(Cell::new(meta, *col, offset + r)); + }); + } + columns.push(CellColumn { + column: *col, + index: columns.len(), + cell_type: config.cell_type, + height: 0, + expr: cells[0].expr(), + cells, + }); + width += 1; + } + } + Self { + configs, + columns, + height: max_height, + width, + height_limit: max_height, + branch_ctxs: HashMap::new(), + parent_ctx: None, + } + } + + pub(crate) fn cur_to_parent(&mut self) { + let new_parent = match self.parent_ctx.clone() { + // if parent context exists, meaning we are deep in a callstack + // we set it as the parent of new parent + Some(ctx) => CmContext { + parent: Box::new(Some(ctx.clone())), + columns: self.columns.clone(), + }, + // otherwise, this is the fist level of callstack + // the parent of new parent is None + None => CmContext { + parent: Box::new(None), + columns: self.columns.clone(), + } + }; + self.parent_ctx = Some(new_parent); + self.reset(self.height_limit); + } + + pub(crate) fn cur_to_branch(&mut self, name: &str) { + let new_branch = match self.parent_ctx.clone() { + // if parent context exists, meaning we are deep in a callstack + // we set it as the parent of new branch + Some(ctx) => CmContext { + parent: Box::new(Some(ctx.clone())), + columns: self.columns.clone(), + }, + // otherwise, this is the fist level of callstack + // the parent of new branch is None + None => CmContext { + parent: Box::new(None), + columns: self.columns.clone(), + } + }; + self.branch_ctxs.insert(name.to_string(), new_branch); + self.reset(self.height_limit); + } + + pub(crate) fn recover_max_branch(&mut self) { + let mut new_cols = self.columns.clone(); + let parent = self.parent_ctx.clone().expect("Retruning context needs parent"); + self.branch_ctxs + .iter() + .for_each(|(name, ctx)| { + for c in 0..self.width { + new_cols[c] = max(&new_cols[c], &ctx.columns[c]).clone(); + new_cols[c] = max(&new_cols[c], &parent.columns[c]).clone(); + } + }); + self.columns = new_cols; + self.branch_ctxs.clear(); + self.parent_ctx = self.parent_ctx + .clone() + .map(|ctx| ctx.parent.deref().clone()) + .unwrap(); + } + + pub(crate) fn recover_parent(&mut self) { + assert!(self.parent_ctx.is_some(), "No parent context to recover"); + self.columns = self.parent_ctx.clone().unwrap().columns.clone(); + self.parent_ctx + .clone() + .map(|ctx| self.parent_ctx = ctx.parent.deref().clone()) + .unwrap(); + self.branch_ctxs.clear(); + } + + pub(crate) fn query_cells(&mut self, cell_type: C, count: usize) -> Vec> { + let mut cells = Vec::with_capacity(count); + while cells.len() < count { + let column_idx = self.next_column(cell_type); + let column = &mut self.columns[column_idx]; + cells.push(column.cells[column.height].clone()); + column.height += 1; + } + cells + } + + pub(crate) fn query_cell(&mut self, cell_type: C) -> Cell { + self.query_cells(cell_type, 1)[0].clone() + } + + pub(crate) fn reset(&mut self, height_limit: usize) { + assert!(height_limit <= self.height); + self.height_limit = height_limit; + for column in self.columns.iter_mut() { + column.height = 0; + } + } + + fn next_column(&self, cell_type: C) -> usize { + let mut best_index: Option = None; + let mut best_height = self.height; + for column in self.columns.iter() { + if column.cell_type == cell_type && column.height < best_height { + best_index = Some(column.index); + best_height = column.height; + } + } + if best_height >= self.height_limit { + best_index = None; + } + match best_index { + Some(index) => index, + None => unreachable!("not enough cells for query: {:?}", cell_type), + } + } + + pub(crate) fn get_height(&self) -> usize { + self.columns + .iter() + .map(|column| column.height) + .max() + .unwrap() + } + + /// Returns a map of CellType -> (width, height, num_cells) + pub(crate) fn get_stats(&self) -> BTreeMap { + let mut data = BTreeMap::new(); + for column in self.columns.iter() { + let (mut count, mut height, mut num_cells) = + data.get(&column.cell_type).unwrap_or(&(0, 0, 0)); + count += 1; + height = height.max(column.height); + num_cells += column.height; + data.insert(column.cell_type, (count, height, num_cells)); + } + data + } + + pub(crate) fn columns(&self) -> &[CellColumn] { + &self.columns + } + + pub(crate) fn get_typed_columns(&self, cell_type: C) -> Vec> { + let mut columns = Vec::new(); + for column in self.columns.iter() { + if column.cell_type == cell_type { + columns.push(column.clone()); + } + } + columns + } +} + +/// LookupTable created dynamically and stored in an advice column +#[derive(Clone, Debug)] +pub struct DynamicLookupTable { + /// Table + pub table: Column, +} + +impl DynamicLookupTable { + /// Construct a new BlockTable + pub fn from(cm: &CellManager, cell_type: C) -> Self { + let table_columns = cm.get_typed_columns(cell_type); + assert_eq!(table_columns.len(), 1); + Self { + table: table_columns[0].column, + } + } +} + +impl LookupTable for DynamicLookupTable { + fn columns(&self) -> Vec> { + vec![self.table.into()] + } + + fn annotations(&self) -> Vec { + vec![String::from("generated")] + } +} diff --git a/zkevm-circuits/src/circuit_tools/constraint_builder.rs b/zkevm-circuits/src/circuit_tools/constraint_builder.rs new file mode 100644 index 0000000000..460b61aa30 --- /dev/null +++ b/zkevm-circuits/src/circuit_tools/constraint_builder.rs @@ -0,0 +1,1643 @@ +//! Circuit utilities +use std::{ + collections::HashMap, + ops::{Add, Mul}, + vec, +}; + +use crate::{evm_circuit::util::rlc, table::LookupTable, util::Expr}; +use eth_types::Field; +use gadgets::util::{and, sum, Scalar}; +use halo2_proofs::{ + plonk::{ConstraintSystem, Expression}, + poly::Rotation, +}; +use itertools::Itertools; + +use super::{ + cached_region::StoredExpression, + cell_manager::{Cell, CellManager, CellType}, +}; + +fn get_condition_expr(conditions: &Vec>) -> Expression { + if conditions.is_empty() { + 1.expr() + } else { + and::expr(conditions) + } +} + +/// Data for dynamic lookup +#[derive(Clone, Debug)] +pub struct DynamicData { + /// Desciption + pub description: &'static str, + /// Condition under which the lookup needs to be done + pub condition: Expression, + /// The values to lookup + pub values: Vec>, + /// region + pub region_id: usize, + /// If is fixed, use static table for lookup + pub is_fixed: bool, + /// Use rlc + pub compress: bool, +} + +/// Constraint builder +#[derive(Clone)] +pub struct ConstraintBuilder { + /// Constraints to be returned to meta + constraints: Vec<(&'static str, Expression)>, + /// Max global degree of constraints + max_global_degree: usize, + /// Max local degree of constraints inside the current region + max_degree: usize, + /// conditions for constraints + conditions: Vec>, + /// The lookups generated during synthesis + /// assembles runtime access to RAM + pub dynamic_lookups: HashMap>>, + /// The tables written during synthesis + /// write to RAM + pub dynamic_tables: HashMap>>, + /// All stored expressions + pub stored_expressions: HashMap>>, + /// CellManager + pub cell_manager: Option>, + /// Disable macro-generated description for constraints & lookups + /// for graph display + pub disable_description: bool, + /// region id + pub region_id: usize, + /// lookup input challenge + pub lookup_challenge: Option>, + /// state contect + pub state_context: Vec>, + /// state constraints start + pub state_constraints_start: usize, + /// Lookups + pub lookups: HashMap>>, +} + +impl ConstraintBuilder { + pub(crate) fn new( + max_degree: usize, + cell_manager: Option>, + lookup_challenge: Option>, + ) -> Self { + ConstraintBuilder { + constraints: Vec::new(), + max_global_degree: max_degree, + max_degree, + conditions: Vec::new(), + dynamic_lookups: HashMap::new(), + dynamic_tables: HashMap::new(), + cell_manager, + disable_description: false, + stored_expressions: HashMap::new(), + region_id: 0, + lookup_challenge, + state_context: Vec::new(), + state_constraints_start: 0, + lookups: HashMap::new(), + } + } + + pub(crate) fn set_cell_manager(&mut self, cell_manager: CellManager) { + self.cell_manager = Some(cell_manager); + } + + pub(crate) fn set_max_degree(&mut self, max_degree: usize) { + self.max_global_degree = max_degree; + } + + pub(crate) fn push_region(&mut self, region_id: usize) { + assert!(region_id != 0); + self.region_id = region_id; + self.state_context = self.conditions.clone(); + self.max_degree = self.max_global_degree - self.get_condition_expr().degree(); + self.conditions.clear(); + self.state_constraints_start = self.constraints.len(); + } + + pub(crate) fn pop_region(&mut self) { + let condition = get_condition_expr(&self.state_context); + for idx in self.state_constraints_start..self.constraints.len() { + self.constraints[idx].1 = condition.expr() * self.constraints[idx].1.clone(); + } + for (_, values) in self.dynamic_lookups.iter_mut() { + for value in values { + if value.region_id == self.region_id { + value.condition = condition.expr() * value.condition.expr(); + } + } + } + for (_key, values) in self.dynamic_tables.iter_mut() { + for value in values { + if value.region_id == self.region_id { + value.condition = condition.expr() * value.condition.expr(); + } + } + } + self.conditions = self.state_context.clone(); + self.max_degree = self.max_global_degree - self.get_condition_expr().degree(); + self.region_id = 0; + } + + pub(crate) fn set_disable_description(&mut self, disable_description: bool) { + self.disable_description = disable_description; + } + + pub(crate) fn require_zero(&mut self, name: &'static str, constraint: Expression) { + self.add_constraint(name, constraint); + } + + pub(crate) fn require_equal( + &mut self, + name: &'static str, + lhs: Expression, + rhs: Expression, + ) { + self.add_constraint(name, lhs - rhs); + } + + pub(crate) fn require_boolean(&mut self, name: &'static str, value: Expression) { + self.add_constraint(name, value.clone() * (1.expr() - value)); + } + + pub(crate) fn require_in_set( + &mut self, + name: &'static str, + value: Expression, + set: Vec>, + ) { + self.add_constraint( + name, + set.iter() + .fold(1.expr(), |acc, item| acc * (value.clone() - item.clone())), + ); + } + + pub(crate) fn condition( + &mut self, + condition: Expression, + constraint: impl FnOnce(&mut Self) -> R, + ) -> R { + self.push_condition(condition); + let ret = constraint(self); + self.pop_condition(); + ret + } + + pub(crate) fn push_condition(&mut self, condition: Expression) { + self.conditions.push(condition); + } + + pub(crate) fn pop_condition(&mut self) { + self.conditions.pop(); + } + + pub(crate) fn add_constraints(&mut self, constraints: Vec<(&'static str, Expression)>) { + for (name, constraint) in constraints { + self.add_constraint(name, constraint); + } + } + + pub(crate) fn add_constraint(&mut self, name: &'static str, constraint: Expression) { + if self.max_global_degree == 0 { + return; + } + let constraint = match self.get_condition() { + Some(condition) => condition * constraint, + None => constraint, + }; + let constraint = self.split_expression(name, constraint); + self.validate_degree(constraint.degree(), name); + self.constraints.push((name, constraint)); + } + + // Query + + pub(crate) fn query_bool(&mut self) -> Cell { + let cell = self.query_default(); + self.require_boolean("Constrain cell to be a bool", cell.expr()); + cell + } + + pub(crate) fn query_default(&mut self) -> Cell { + self.query_cells_dyn(C::default(), 1) + .get(0) + .expect("No cell found") + .clone() + } + + pub(crate) fn query_one(&mut self, cell_type: C) -> Cell { + self.query_cells_dyn(cell_type, 1).first().unwrap().clone() + } + + pub(crate) fn query_bytes(&mut self) -> [Cell; N] { + self.query_cells_dyn( + C::byte_type().expect("No byte type for this CellManager"), + N, + ) + .try_into() + .unwrap() + } + + pub(crate) fn query_cells_dyn(&mut self, cell_type: C, count: usize) -> Vec> { + self.cell_manager + .as_mut() + .expect("Cell manager not set") + .query_cells(cell_type, count) + } + + pub(crate) fn query_cell_with_type(&mut self, cell_type: C) -> Cell { + self.query_cells_dyn(cell_type, 1).first().unwrap().clone() + } + + pub(crate) fn validate_degree(&self, degree: usize, name: &'static str) { + if self.max_global_degree > 0 && self.region_id != 0 { + debug_assert!( + degree <= self.max_degree, + "Expression {} degree too high: {} > {}", + name, + degree, + self.max_degree, + ); + } + } + + pub(crate) fn split_constraints_expression(&mut self) { + self.constraints = self.constraints.clone() + .into_iter() + .map(|(name, c)| { + (name, self.split_expression(name, c.clone())) + }) + .collect::>(); + } + + pub(crate) fn build_constraints(&self) -> Vec<(&'static str, Expression)> { + self.constraints.clone() + } + + pub(crate) fn build_lookups( + &self, + meta: &mut ConstraintSystem, + cell_managers: Vec>, + tables: Vec<(C, &dyn LookupTable)>, + ) { + for cm in cell_managers { + for (cell_type, table) in &tables { + for col in cm.get_typed_columns(*cell_type) { + let name = format!("{:?}", cell_type); + meta.lookup_any(Box::leak(name.into_boxed_str()), |meta| { + vec![( + col.expr, + rlc::expr( + &table.table_exprs(meta), + self.lookup_challenge.clone().unwrap(), + ), + )] + }); + } + } + } + } + + pub(crate) fn build_dynamic_lookups( + &mut self, + meta: &mut ConstraintSystem, + lookup_names: &[C], + fixed_table: Vec<(C, &dyn LookupTable)>, + ) { + let lookups = self.dynamic_lookups.clone(); + for lookup_name in lookup_names.iter() { + if let Some(lookups) = lookups.get(lookup_name) { + for lookup in lookups.iter() { + meta.lookup_any(lookup.description, |meta| { + // Fixed lookup is a direct lookup into the pre-difined fixed tables + // i.e. cond * (v1, v2, v3) => (t1, t2, t3) + // equivalent to the vanilla lookup operation of Halo2. + // Dynamic lookup applies condition to the advice values stored at + // configuration time i.e. cond * (v1, v2, v3) => + // cond * (t1, t2, t3) the dynamic lookup in a ifx! + // branch would become trivial 0 => 0 + // when the elsex! branch evaluates to true + + let table = if lookup.is_fixed { + let table_cols = fixed_table + .iter() + .find(|(name, _)| name == lookup_name) + .unwrap() + .1 + .columns(); + table_cols + .iter() + .map(|col| meta.query_any(*col, Rotation(0))) + .collect() + } else { + self.get_dynamic_table_values(*lookup_name) + }; + + let mut values: Vec<_> = lookup + .values + .iter() + .map(|value| value.expr() * lookup.condition.clone()) + .collect(); + // align the length of values and table + assert!(table.len() >= values.len()); + while values.len() < table.len() { + values.push(0.expr()); + } + + // Perform rlc if specified + // i.e. (v1*r + v2*r^2 + v3*r^3) => (t1*r + t2*r^2 + t3*r^3) + // lastly is_split had been fulfilled at insertion time + + let ret = if lookup.compress { + vec![( + rlc::expr(&values, self.lookup_challenge.clone().unwrap()), + rlc::expr(&table, self.lookup_challenge.clone().unwrap()), + )] + } else { + values + .iter() + .zip(table.iter()) + .map(|(v, t)| (v.expr(), t.expr())) + .collect() + }; + ret + }); + } + } else { + unreachable!("lookup not found: {:?}", lookup_name); + } + } + } + + pub(crate) fn get_condition(&self) -> Option> { + if self.conditions.is_empty() { + None + } else { + Some(and::expr(self.conditions.iter())) + } + } + + pub(crate) fn get_condition_expr(&self) -> Expression { + self.get_condition().unwrap_or_else(|| 1.expr()) + } + + pub(crate) fn store_dynamic_table( + &mut self, + description: &'static str, + tag: C, + values: Vec>, + compress: bool, + store: bool, + ) { + let condition = self.get_condition_expr(); + let mut values = if compress { + vec![rlc::expr(&values, self.lookup_challenge.clone().unwrap())] + } else { + values + }; + if store { + values.iter_mut().for_each(|v| { + *v = self.split_expression( + Box::leak(format!("compression value - {:?}", tag).into_boxed_str()), + v.clone(), + ) + }); + // values = vec![self.store_expression(description, values[0].expr(), tag)]; + } + let lookup = DynamicData { + description, + condition, + values, + region_id: self.region_id, + // cannot be is_fixed + is_fixed: false, + compress: false, + }; + if let Some(table_data) = self.dynamic_tables.get_mut(&tag) { + table_data.push(lookup); + } else { + self.dynamic_tables.insert(tag, vec![lookup]); + } + } + + pub(crate) fn add_dynamic_lookup( + &mut self, + description: &'static str, + tag: C, + values: Vec>, + is_fixed: bool, + compress: bool, + store: bool, + ) { + let condition = self.get_condition_expr(); + let mut values = if compress { + vec![rlc::expr(&values, self.lookup_challenge.clone().unwrap())] + } else { + values + }; + if store { + values.iter_mut().for_each(|v| { + *v = self.split_expression( + Box::leak(format!("compression value - {:?}", tag).into_boxed_str()), + v.clone(), + ) + }); + } + let lookup = DynamicData { + description, + condition, + values, + region_id: self.region_id, + is_fixed, + compress, + }; + if let Some(lookup_data) = self.dynamic_lookups.get_mut(&tag) { + lookup_data.push(lookup); + } else { + self.dynamic_lookups.insert(tag, vec![lookup]); + } + } + + pub(crate) fn add_lookup( + &mut self, + description: &str, + cell_type: C, + values: Vec>, + ) { + let condition = self.get_condition_expr(); + let values = values + .iter() + .map(|value| condition.expr() * value.expr()) + .collect_vec(); + let compressed_expr = self.split_expression( + "compression", + rlc::expr(&values, self.lookup_challenge.clone().unwrap().expr()), + ); + self.store_expression(description, compressed_expr, cell_type, None); + + let lookup = DynamicData { + description: Box::leak(description.to_string().into_boxed_str()), + condition, + values, + region_id: self.region_id, + is_fixed: true, + compress: true, + }; + if let Some(lookup_data) = self.lookups.get_mut(&cell_type) { + lookup_data.push(lookup); + } else { + self.lookups.insert(cell_type, vec![lookup]); + } + } + + pub(crate) fn get_stored_expressions(&self, region_id: usize) -> Vec> { + self.stored_expressions + .get(®ion_id) + .cloned() + .unwrap_or_default() + } + + pub(crate) fn get_dynamic_table(&self, tag: C) -> (Expression, Vec>) { + let table_values = self + .dynamic_tables + .get(&tag) + .unwrap_or_else(|| panic!("Dynamic table {:?} not found", tag)); + merge_values_unsafe( + table_values + .iter() + .map(|table| (table.condition.clone(), table.values.clone())) + .collect::>(), + ) + } + + pub(crate) fn get_dynamic_table_values(&self, tag: C) -> Vec> { + let condition_and_values = self.get_dynamic_table(tag); + condition_and_values + .1 + .iter() + .map(|value| value.expr() * condition_and_values.0.expr()) + .collect::>() + } + + pub(crate) fn generate_lookup_table_checks(&mut self, tag: C) { + let table_values = self + .dynamic_tables + .get(&tag) + .unwrap_or_else(|| panic!("Dynamic table {:?} not found", tag)) + .clone(); + let selectors = table_values + .into_iter() + .map(|value| { + let sel = value.condition.expr(); + self.require_boolean("lookup table condition needs to be boolean", sel.clone()); + sel + }) + .collect::>(); + let selector = sum::expr(&selectors); + self.require_boolean( + "lookup table conditions sum needs to be boolean", + selector.expr(), + ); + } + + pub(crate) fn store_expression( + &mut self, + name: &str, + expr: Expression, + cell_type: C, + target_cell: Option>, + ) -> Expression { + // Check if we already stored the expression somewhere + let stored_expression = self.find_stored_expression(&expr, cell_type); + match stored_expression { + Some(stored_expression) => stored_expression.cell.expr(), + None => { + // Require the stored value to equal the value of the expression + let cell = if let Some(tc) = target_cell { + tc + } else { + self.query_one(cell_type) + }; + let name = format!("{} (stored expression)", name); + self.constraints.push(( + Box::leak(name.clone().into_boxed_str()), + cell.expr() - expr.clone(), + )); + self.stored_expressions + .entry(self.region_id) + .or_insert_with(Vec::new) + .push(StoredExpression { + name, + cell: cell.clone(), + cell_type, + expr_id: expr.identifier(), + expr, + }); + cell.expr() + } + } + } + + pub(crate) fn find_stored_expression( + &self, + expr: &Expression, + cell_type: C, + ) -> Option<&StoredExpression> { + let expr_id = expr.identifier(); + if let Some(stored_expressions) = self.stored_expressions.get(&self.region_id) { + stored_expressions + .iter() + .find(|&e| e.cell_type == cell_type && e.expr_id == expr_id) + } else { + None + } + } + + pub(crate) fn split_expression( + &mut self, + name: &'static str, + expr: Expression, + ) -> Expression { + if expr.degree() > self.max_degree && self.region_id != 0 { + match expr { + Expression::Negated(poly) => { + Expression::Negated(Box::new(self.split_expression(name, *poly))) + } + Expression::Scaled(poly, v) => { + Expression::Scaled(Box::new(self.split_expression(name, *poly)), v) + } + Expression::Sum(a, b) => { + let a = self.split_expression(name, *a); + let b = self.split_expression(name, *b); + a + b + } + Expression::Product(a, b) => { + let (mut a, mut b) = (*a, *b); + while a.degree() + b.degree() > self.max_degree { + let mut split = |expr: Expression| { + if expr.degree() > self.max_degree { + self.split_expression(name, expr) + } else { + let cell_type = C::storage_for_expr(&expr); + self.store_expression(name, expr, cell_type, None) + } + }; + if a.degree() >= b.degree() { + a = split(a); + } else { + b = split(b); + } + } + a * b + } + _ => expr.clone(), + } + } else { + expr.clone() + } + } + + pub(crate) fn print_stats(&self) { + let mut expressions = self.constraints.clone(); + expressions.sort_by(|a, b| a.1.degree().cmp(&b.1.degree())); + for (name, expr) in expressions.iter() { + println!("'{}': {}", name, expr.degree()); + } + } +} + +pub(crate) fn merge_lookups( + cb: &mut ConstraintBuilder, + lookups: Vec>, +) -> (Expression, Vec>) { + merge_values( + cb, + lookups + .iter() + .map(|lookup| (lookup.condition.clone(), lookup.values.clone())) + .collect::>(), + ) +} + +pub(crate) fn merge_values( + cb: &mut ConstraintBuilder, + values: Vec<(Expression, Vec>)>, +) -> (Expression, Vec>) { + let selector = sum::expr(values.iter().map(|(condition, _)| condition.expr())); + crate::circuit!([meta, cb], { + require!(selector => bool); + }); + merge_values_unsafe(values) +} + +pub(crate) fn merge_values_unsafe( + values: Vec<(Expression, Vec>)>, +) -> (Expression, Vec>) { + if values.is_empty() { + return (0.expr(), Vec::new()); + } + let selector = sum::expr(values.iter().map(|(condition, _)| condition.expr())); + // Merge + let max_length = values.iter().map(|(_, values)| values.len()).max().unwrap(); + let mut merged_values = vec![0.expr(); max_length]; + let default_value = 0.expr(); + for (idx, value) in merged_values.iter_mut().enumerate() { + *value = sum::expr(values.iter().map(|(condition, values)| { + condition.expr() * values.get(idx).unwrap_or(&default_value).expr() + })); + } + (selector, merged_values) +} + +/// General trait to convert to a vec +pub trait ToVec { + /// Converts a tuple to a vector + fn to_vec(&self) -> Vec; +} + +impl ToVec for Vec { + fn to_vec(&self) -> Vec { + self.clone() + } +} + +impl ToVec for [T] { + fn to_vec(&self) -> Vec { + self.to_owned() + } +} + +impl ToVec for &[T] { + fn to_vec(&self) -> Vec { + <&[T]>::clone(self).to_owned() + } +} + +macro_rules! impl_to_vec { + (($($t:ty),*), ($($v:ident),*)) => { + impl ToVec for ($($t,)*) { + fn to_vec(&self) -> Vec { + let ($($v,)*) = self; + vec![$($v.clone()),*] + } + } + }; +} + +impl_to_vec!((T, T), (a, b)); +impl_to_vec!((T, T, T), (a, b, c)); +impl_to_vec!((T, T, T, T), (a, b, c, d)); +impl_to_vec!((T, T, T, T, T), (a, b, c, d, e)); +impl_to_vec!((T, T, T, T, T, T), (a, b, c, d, e, f)); +impl_to_vec!((T, T, T, T, T, T, T), (a, b, c, d, e, f, g)); +impl_to_vec!((T, T, T, T, T, T, T, T), (a, b, c, d, e, f, g, h)); + +/// Trait that generates a vector of expressions +pub trait ExprVec { + /// Returns a vector of the expressions from itself + fn to_expr_vec(&self) -> Vec>; +} + +impl ExprVec for std::ops::Range { + fn to_expr_vec(&self) -> Vec> { + self.clone().map(|e| e.expr()).collect::>() + } +} + +impl> ExprVec for Vec { + fn to_expr_vec(&self) -> Vec> { + self.iter().map(|e| e.expr()).collect::>() + } +} + +impl> ExprVec for [E] { + fn to_expr_vec(&self) -> Vec> { + self.iter().map(|e| e.expr()).collect::>() + } +} + +impl> ExprVec for &[E] { + fn to_expr_vec(&self) -> Vec> { + self.iter().map(|e| e.expr()).collect::>() + } +} + +/// Implementation trait `ExprVec` for type able to be casted to an +/// Expression +#[macro_export] +macro_rules! impl_expr_vec { + ($type:ty) => { + impl ExprVec for $type { + #[inline] + fn to_expr_vec(&self) -> Vec> { + vec![self.expr()] + } + } + }; +} + +impl_expr_vec!(bool); +impl_expr_vec!(u8); +impl_expr_vec!(i32); +impl_expr_vec!(u64); +impl_expr_vec!(usize); +impl_expr_vec!(isize); +impl_expr_vec!(Expression); +impl_expr_vec!(Cell); + +/// Newtype wrapper for `Vec>` +#[derive(Clone)] +pub struct ExpressionVec(pub Vec>); + +impl Add for ExpressionVec { + type Output = ExpressionVec; + + fn add(self, rhs: ExpressionVec) -> Self::Output { + ExpressionVec( + self.0 + .iter() + .zip(rhs.0.iter()) + .map(|(a, b)| a.expr() + b.expr()) + .collect(), + ) + } +} + +impl Mul for ExpressionVec { + type Output = ExpressionVec; + + fn mul(self, rhs: ExpressionVec) -> Self::Output { + ExpressionVec( + self.0 + .iter() + .zip(rhs.0.iter()) + .map(|(a, b)| a.expr() * b.expr()) + .collect(), + ) + } +} + +/// Trait for doing math on Expressions, no matter the type they are stored in +pub trait ExprResult { + /// Adds two values together + fn add(&self, other: &Self) -> Self; + /// Multiply with a scalar + fn mul(&self, other: &Expression) -> Self; +} + +impl ExprResult for () { + fn add(&self, _other: &Self) -> Self {} + fn mul(&self, _other: &Expression) -> Self {} +} + +impl ExprResult for Vec> { + fn add(&self, other: &Self) -> Self { + (ExpressionVec(self.clone()) + ExpressionVec(other.clone())).0 + } + fn mul(&self, other: &Expression) -> Self { + (ExpressionVec(self.clone()) * ExpressionVec(vec![other.clone(); self.len()])).0 + } +} + +impl ExprResult for Expression { + fn add(&self, other: &Self) -> Self { + vec![self.clone()].add(&vec![other.clone()])[0].clone() + } + fn mul(&self, other: &Expression) -> Self { + vec![self.clone()].mul(other)[0].clone() + } +} + +/// Implement `ExprResult` for tupples +#[macro_export] +macro_rules! impl_expr_result { + ($($type:ty),*) => { + impl ExprResult for ($($type),*) { + fn add(&self, other: &Self) -> Self { + self.to_vec().add(&other.to_vec()).into_iter().collect_tuple().unwrap() + } + fn mul(&self, other: &Expression) -> Self { + self.to_vec().mul(other).into_iter().collect_tuple().unwrap() + } + } + }; +} + +impl_expr_result!(Expression, Expression); +impl_expr_result!(Expression, Expression, Expression); +impl_expr_result!(Expression, Expression, Expression, Expression); +impl_expr_result!( + Expression, + Expression, + Expression, + Expression, + Expression +); +impl_expr_result!( + Expression, + Expression, + Expression, + Expression, + Expression, + Expression +); +impl_expr_result!( + Expression, + Expression, + Expression, + Expression, + Expression, + Expression, + Expression +); +impl_expr_result!( + Expression, + Expression, + Expression, + Expression, + Expression, + Expression, + Expression, + Expression +); + +/// Trait around RLC +pub trait RLCable { + /// Returns the RLC of itself + fn rlc(&self, r: &Expression) -> Expression; + /// Returns the RLC of the reverse of itself + fn rlc_rev(&self, r: &Expression) -> Expression; +} + +impl + ?Sized> RLCable for E { + fn rlc(&self, r: &Expression) -> Expression { + rlc::expr(&self.to_expr_vec(), r.expr()) + } + + fn rlc_rev(&self, r: &Expression) -> Expression { + rlc::expr( + &self.to_expr_vec().iter().rev().cloned().collect_vec(), + r.expr(), + ) + } +} + +/// Trait around RLC +pub trait RLCChainable { + /// Returns the RLC of itself with a starting rlc/multiplier + fn rlc_chain(&self, other: Expression) -> Expression; +} + +impl RLCChainable for (Expression, Expression) { + fn rlc_chain(&self, other: Expression) -> Expression { + self.0.expr() + self.1.expr() * other.expr() + } +} + +/// Trait around RLC +pub trait RLCChainable2 { + /// Returns the RLC of itself with a starting rlc/multiplier + fn rlc_chain2(&self, other: (Expression, Expression)) -> Expression; +} + +impl RLCChainable2 for Expression { + fn rlc_chain2(&self, other: (Expression, Expression)) -> Expression { + self.expr() * other.1.expr() + other.0.expr() + } +} + +/// Trait around RLC +pub trait RLCableValue { + /// Returns the RLC of itself + fn rlc_value(&self, r: F) -> F; +} + +impl RLCableValue for Vec { + fn rlc_value(&self, r: F) -> F { + rlc::value(self, r) + } +} + +impl RLCableValue for [u8] { + fn rlc_value(&self, r: F) -> F { + rlc::value(self, r) + } +} + +/// Trait around RLC +pub trait RLCChainableValue { + /// Returns the RLC of itself with a starting rlc/multiplier + fn rlc_chain_value(&self, values: I, r: F) -> (F, F); +} + +impl, I: IntoIterator> RLCChainableValue for (F, F) { + fn rlc_chain_value(&self, values: I, r: F) -> (F, F) { + let mut rlc = self.0; + let mut mult = self.1; + for value in values.into_iter().map(|byte| byte.scalar()) { + rlc += value * mult; + mult *= r; + } + (rlc, mult) + } +} +/// require_parser +#[macro_export] +macro_rules! require_parser { + { + $cb:expr, + lhs = ($($lhs:tt)*) + rest = (== $($rhs:tt)*) + } => { + let description = $crate::concat_with_preamble!( + stringify!($($lhs)*), + " == ", + stringify!($($rhs)*) + ); + $crate::_require!($cb, description, $($lhs)* => $($rhs)*) + }; + + { + $cb:expr, + lhs = ($($lhs:tt)*) + rest = ($next:tt $($rest:tt)*) + } => { + $crate::require_parser! { + $cb, + lhs = ($($lhs)* $next) + rest = ($($rest)*) + } + }; +} + +/// _require2 +#[macro_export] +macro_rules! _require2 { + ($cb:expr, $($rest:tt)*) => {{ + $crate::require_parser! { + $cb, + lhs = () + rest = ($($rest)*) + } + }}; +} + +/// Creates a dummy constraint builder that cannot be used to add constraints. +#[macro_export] +macro_rules! _cb { + () => {{ + use $crate::circuit_tools::cell_manager::DefaultCellType; + ConstraintBuilder::::new(0, None, None) + }}; +} + +/// Concats arguments with preamble consisting of the originating file and line. +#[macro_export] +macro_rules! concat_with_preamble { + ($($args:expr),* $(,)?) => {{ + concat!( + file!(), + ":", + line!(), + ": ", + $( + $args, + )* + ) + }}; +} + +/// Can be used to mark a specific branch as unreachable +#[macro_export] +macro_rules! _unreachablex { + ($cb:expr $(,$descr:expr)?) => {{ + let descr = concat_with_preamble!( + "unreachable executed", + $( + ": ", + $descr, + )* + ); + _require!($cb, descr, true => false) + }}; +} + +/// _require +#[macro_export] +macro_rules! _require { + ($cb:expr, $lhs:expr => bool) => {{ + let description = concat_with_preamble!( + stringify!($lhs), + " => ", + "bool", + ); + $cb.require_boolean(description, $lhs.expr()); + }}; + + ($cb:expr, $lhs:expr => $rhs:expr) => {{ + let description = concat_with_preamble!( + stringify!($lhs), + " => ", + stringify!($rhs) + ); + _require!($cb, description, $lhs => $rhs) + }}; + + ($cb:expr, $descr:expr, $lhs:expr => $rhs:expr) => {{ + let rhs = $rhs.to_expr_vec(); + if rhs.len() == 1 { + $cb.require_equal( + Box::leak($descr.to_string().into_boxed_str()), + $lhs.expr(), + rhs[0].expr(), + ); + } else { + $cb.require_in_set( + Box::leak($descr.to_string().into_boxed_str()), + $lhs.expr(), + rhs.clone(), + ); + } + }}; + + // Lookup using a tuple + ($cb:expr, ($($v:expr),+) => @$tag:expr) => {{ + let description = concat_with_preamble!( + "(", + $( + stringify!($v), + ", ", + )* + ") => @", + stringify!($tag), + ); + $cb.add_lookup( + description, + $tag, + vec![$($v.expr(),)*], + ); + }}; + ($cb:expr, $descr:expr, ($($v:expr),+) => @$tag:expr) => {{ + $cb.add_lookup( + Box::leak($descr.into_boxed_str()), + $tag, + vec![$($v.expr(),)*], + ); + }}; + + // Lookup using an array + ($cb:expr, $values:expr => @$tag:expr) => {{ + let description = concat_with_preamble!( + stringify!($values), + " => @", + stringify!($tag), + ); + $cb.add_lookup( + description, + $tag, + $values.clone(), + ); + }}; + ($cb:expr, $descr:expr, $values:expr => @$tag:expr) => {{ + $cb.add_lookup( + Box::leak($descr.to_string().into_boxed_str()), + $tag, + $values.clone(), + ); + }}; + + // Lookup using a tuple + ($cb:expr, ($($v:expr),+) => @$tag:expr, $is_fixed:expr, $compress:expr, $is_split:expr) => {{ + let description = concat_with_preamble!( + "(", + $( + stringify!($v), + ", ", + )* + ") => @", + stringify!($tag), + ); + $cb.add_dynamic_lookup( + description, + $tag, + vec![$($v.expr(),)*], + $is_fixed, + $compress, + $is_split, + ); + }}; + ($cb:expr, $descr:expr, ($($v:expr),+) => @$tag:expr, $is_fixed:expr, $compress:expr, $is_split:expr) => {{ + $cb.add_dynamic_lookup( + Box::leak($descr.into_boxed_str()), + $tag, + vec![$($v.expr(),)*], + $is_fixed, + $compress, + $is_split, + ); + }}; + + + // Lookup using an array + ($cb:expr, $values:expr => @$tag:expr, $is_fixed:expr, $compress:expr, $is_split:expr) => {{ + let description = concat_with_preamble!( + stringify!($values), + " => @", + stringify!($tag), + ); + $cb.add_dynamic_lookup( + description, + $tag, + $values.clone(), + $is_fixed, + $compress, + $is_split, + ); + }}; + ($cb:expr, $descr:expr, $values:expr => @$tag:expr, $is_fixed:expr, $compress:expr, $is_split:expr) => {{ + $cb.add_dynamic_lookup( + Box::leak($descr.into_boxed_str()), + $tag, + $values.clone(), + $is_fixed, + $compress, + $is_split, + ); + }}; + + // Put values in a lookup table using a tuple + ($cb:expr, @$tag:expr => ($($v:expr),+)) => {{ + let description = concat_with_preamble!( + "@", + stringify!($tag), + " => (", + $( + stringify!($v), + ", ", + )* + ")", + ); + $cb.store_dynamic_table( + description, + $tag, + vec![$($v.expr(),)*], + false, + false, + ); + }}; + // Put values in a lookup table using an array + ($cb:expr, @$tag:expr => $values:expr) => {{ + let description = concat_with_preamble!( + "@", + stringify!($tag), + " => (", + stringify!($values), + ")", + ); + $cb.store_dynamic_table( + description, + $tag, + $values, + false, + false, + ); + }}; + + // Put values in a lookup table using a tuple + ($cb:expr, @$tag:expr => ($($v:expr),+), $compress:expr, $is_split:expr) => {{ + let description = concat_with_preamble!( + "@", + stringify!($tag), + " => (", + $( + stringify!($v), + ", ", + )* + ")", + ); + $cb.store_dynamic_table( + description, + $tag, + vec![$($v.expr(),)*], + $compress, + $is_split, + ); + }}; + // Put values in a lookup table using an array + ($cb:expr, @$tag:expr => $values:expr, $compress:expr, $is_split:expr) => {{ + let description = concat_with_preamble!( + "@", + stringify!($tag), + " => (", + stringify!($values), + ")", + ); + $cb.store_dynamic_table( + description, + $tag, + $values, + $compress, + $is_split, + ); + }}; +} + +/// matchx +/// Supports `_` which works the same as in the normal `match`: if none of the +/// other arms are active the `_` arm will be executed and so can be used to +/// return some default values or could also be marked as unreachable (using the +/// unreachablex! macro). +#[macro_export] +macro_rules! _matchx { + ($cb:expr, $($condition:expr => $when:expr),* $(, _ => $catch_all:expr)? $(,)?) => {{ + let mut conditions = Vec::new(); + let mut cases = Vec::new(); + $( + $cb.push_condition($condition.expr()); + let ret = $when.clone(); + $cb.pop_condition(); + cases.push(($condition.expr(), ret)); + conditions.push($condition.expr()); + )* + + $( + let catch_all_condition = not::expr(sum::expr(&conditions)); + $cb.push_condition(catch_all_condition.expr()); + let ret = $catch_all; + $cb.pop_condition(); + cases.push((catch_all_condition.expr(), ret)); + conditions.push(catch_all_condition.expr()); + )* + + // All conditions need to be boolean + for condition in conditions.iter() { + _require!($cb, condition => bool); + } + // Exactly 1 case needs to be enabled + _require!($cb, sum::expr(&conditions) => 1); + + // Apply the conditions to all corresponding values + let mut res = cases[0].1.mul(&cases[0].0.expr()); + for pair in cases.iter().skip(1) { + res = <_ as ExprResult>::add(&res, &pair.1.mul(&pair.0.expr())); + } + res + }}; +} + +/// ifx +#[macro_export] +macro_rules! _ifx { + ($cb:expr, $($condition:expr),* => $when_true:block $(elsex $when_false:block)?) => {{ + let condition = and::expr([$($condition.expr()),*]); + + $cb.push_condition(condition.expr()); + let ret_true = $when_true; + $cb.pop_condition(); + + #[allow(unused_assignments, unused_mut)] + let mut ret = ret_true.mul(&condition.expr()); + $( + // In if/else cases, the condition needs to be boolean + _require!($cb, condition => bool); + + $cb.push_condition(not::expr(condition.expr())); + let ret_false = $when_false; + $cb.pop_condition(); + + ret = <_ as ExprResult>::add(&ret_true.mul(&condition), &ret_false.mul(¬::expr(condition.expr()))); + )* + ret + }}; +} + +/// matchw - Resembles matchx so that the witness generation can look like the +/// circuit code. +#[macro_export] +macro_rules! matchw { + ($($condition:expr => $when:expr),* $(, _ => $catch_all:expr)? $(,)?) => {{ + if false { + unreachable!() + } + $(else if $condition { + $when + } + )* + else { + $( + $catch_all + )* + unreachable!() + } + }}; +} + +/// assign advice +#[macro_export] +macro_rules! assign { + // Column + ($region:expr, ($column:expr, $offset:expr) => $value:expr) => {{ + use halo2_proofs::circuit::Value; + let description = + $crate::concat_with_preamble!(stringify!($column), " => ", stringify!($value)); + let value: F = $value; + $region.assign_advice(|| description, $column, $offset, || Value::known(value)) + }}; + ($region:expr, ($column:expr, $offset:expr) => $annotation:expr, $value:expr) => {{ + use halo2_proofs::circuit::Value; + let value: F = $value; + $region.name_column(|| $annotation, $column); + $region.assign_advice(|| "", $column, $offset, || Value::known(value)) + }}; + // Cell + ($region:expr, $cell:expr, $offset:expr => $value:expr) => {{ + use halo2_proofs::circuit::Value; + let description = + $crate::concat_with_preamble!(stringify!($cell), " => ", stringify!($value)); + let value: F = $value; + $region.assign_advice( + || description, + $cell.column(), + $offset + $cell.rotation(), + || Value::known(value), + ) + }}; + ($region:expr, $cell:expr, $offset:expr => $annotation:expr, $value:expr) => {{ + use halo2_proofs::circuit::Value; + let value: F = $value; + $region.assign_advice( + || $annotation, + $cell.column(), + $offset + $cell.rotation(), + || Value::known(value), + ) + }}; +} + +/// assign fixed +#[macro_export] +macro_rules! assignf { + ($region:expr, ($column:expr, $offset:expr) => $value:expr) => {{ + let description = + $crate::concat_with_preamble!(stringify!($column), " => ", stringify!($value)); + let value: F = $value; + $region.assign_fixed(|| description, $column, $offset, || Value::known(value)) + }}; +} + +/// Circuit builder macros +/// Nested macro's can't do repetition +/// so we expose a couple of permutations here manually. +#[macro_export] +macro_rules! circuit { + ([$meta:expr, $cb:expr], $content:block) => {{ + #[allow(unused_imports)] + use $crate::{concat_with_preamble, _require, _matchx, _ifx, _unreachablex}; + #[allow(unused_imports)] + use gadgets::util::{and, not, or, sum, Expr}; + #[allow(unused_imports)] + use $crate::circuit_tools::constraint_builder::{ExprVec, ExprResult}; + + #[allow(unused_macros)] + macro_rules! f { + ($column:expr, $rot:expr) => {{ + $meta.query_fixed($column.clone(), Rotation($rot as i32)) + }}; + ($column:expr) => {{ + $meta.query_fixed($column.clone(), Rotation::cur()) + }}; + } + + #[allow(unused_macros)] + macro_rules! a { + ($column:expr, $rot:expr) => {{ + $meta.query_advice($column.clone(), Rotation($rot as i32)) + }}; + ($column:expr) => {{ + $meta.query_advice($column.clone(), Rotation::cur()) + }}; + } + + #[allow(unused_macros)] + macro_rules! c { + ($column:expr) => {{ + $meta.query_challenge($column.clone()) + }}; + } + + #[allow(unused_macros)] + macro_rules! x { + ($column:expr, $rot:expr) => {{ + $meta.query_any($column.clone(), Rotation($rot as i32)) + }}; + ($column:expr) => {{ + $meta.query_any($column.clone(), Rotation::cur()) + }}; + } + + #[allow(unused_macros)] + macro_rules! not { + ($expr:expr) => {{ + gadgets::util::not::expr($expr.expr()) + }}; + } + + #[allow(unused_macros)] + macro_rules! invert { + ($expr:expr) => {{ + Expression::Constant(F::from($expr as u64).invert().unwrap()) + }}; + } + + #[allow(unused_macros)] + macro_rules! require { + ($lhs:expr => bool) => {{ + _require!($cb, $lhs => bool); + }}; + + ($lhs:expr => $rhs:expr) => {{ + _require!($cb, $lhs => $rhs); + }}; + + ($name:expr, $lhs:expr => $rhs:expr) => {{ + _require!($cb, $name, $lhs => $rhs); + }}; + + (($a:expr) => @$tag:expr) => {{ + _require!($cb, ($a) => @$tag); + }}; + + (($a:expr, $b:expr) => @$tag:expr) => {{ + _require!($cb, ($a, $b) => @$tag); + }}; + + (($a:expr, $b:expr, $c:expr) => @$tag:expr) => {{ + _require!($cb, ($a, $b, $c) => @$tag); + }}; + + (($a:expr, $b:expr, $c:expr, $d:expr) => @$tag:expr) => {{ + _require!($cb, ($a, $b, $c, $d) => @$tag); + }}; + + ($values:expr => @$tag:expr) => {{ + _require!($cb, $values => @$tag); + }}; + + ($descr:expr, $values:expr => @$tag:expr) => {{ + _require!($cb, $descr, $values => @$tag); + }}; + + (($a:expr) => @$tag:expr, $is_fixed:expr, $compress:expr, $is_split:expr) => {{ + _require!($cb, ($a) => @$tag, $is_fixed, $compress, $is_split); + }}; + + (($a:expr, $b:expr) => @$tag:expr, $is_fixed:expr, $compress:expr, $is_split:expr) => {{ + _require!($cb, ($a, $b) => @$tag, $is_fixed, $compress, $is_split); + }}; + + (($a:expr, $b:expr, $c:expr) => @$tag:expr, $is_fixed:expr, $compress:expr, $is_split:expr) => {{ + _require!($cb, ($a, $b, $c) => @$tag, $is_fixed, $compress, $is_split); + }}; + + (($a:expr, $b:expr, $c:expr, $d:expr) => @$tag:expr, $is_fixed:expr, $compress:expr, $is_split:expr) => {{ + _require!($cb, ($a, $b, $c, $d) => @$tag, $is_fixed, $compress, $is_split); + }}; + + ($values:expr => @$tag:expr, $is_fixed:expr, $compress:expr, $is_split:expr) => {{ + _require!($cb, $values => @$tag, $is_fixed, $compress, $is_split); + }}; + + ($descr:expr, $values:expr => @$tag:expr, $is_fixed:expr, $compress:expr, $is_split:expr) => {{ + _require!($cb, $descr, $values => @$tag, $is_fixed, $compress, $is_split); + }}; + + (@$tag:expr => ($a:expr, $b:expr, $c:expr)) => {{ + _require!($cb, @$tag => ($a, $b, $c)); + }}; + + (@$tag:expr => $values:expr) => {{ + _require!($cb, @$tag => $values); + }}; + } + + #[allow(unused_macros)] + macro_rules! ifx { + ($condition:expr => $when_true:block elsex $when_false:block) => {{ + _ifx!($cb, $condition => $when_true elsex $when_false) + }}; + ($condition_a:expr, $condition_b:expr => $when_true:block elsex $when_false:block) => {{ + _ifx!($cb, $condition_a, $condition_b => $when_true elsex $when_false) + }}; + ($condition_a:expr, $condition_b:expr, $condition_c:expr => $when_true:block elsex $when_false:block) => {{ + _ifx!($cb, $condition_a, $condition_b, $condition_c => $when_true elsex $when_false) + }}; + ($condition_a:expr, $condition_b:expr, $condition_c:expr, $condition_d:expr => $when_true:block elsex $when_false:block) => {{ + _ifx!($cb, $condition_a, $condition_b, $condition_c, $condition_d => $when_true elsex $when_false) + }}; + + ($condition:expr => $when_true:block) => {{ + _ifx!($cb, $condition => $when_true) + }}; + ($condition_a:expr, $condition_b:expr => $when_true:block) => {{ + _ifx!($cb, $condition_a, $condition_b => $when_true) + }}; + ($condition_a:expr, $condition_b:expr, $condition_c:expr => $when_true:block) => {{ + _ifx!($cb, $condition_a, $condition_b, $condition_c => $when_true) + }}; + ($condition_a:expr, $condition_b:expr, $condition_c:expr, $condition_d:expr => $when_true:block) => {{ + _ifx!($cb, $condition_a, $condition_b, $condition_c, $condition_d => $when_true) + }}; + ($condition_a:expr, $condition_b:expr, $condition_c:expr, $condition_d:expr, $condition_e:expr => $when_true:block) => {{ + _ifx!($cb, $condition_a, $condition_b, $condition_c, $condition_d, $condition_e => $when_true) + }}; + } + + #[allow(unused_macros)] + macro_rules! matchx { + ($condition_a:expr => $when_a:expr,) => {{ + _matchx!($cb, $condition_a => $when_a) + }}; + ($condition_a:expr => $when_a:expr, $condition_b:expr => $when_b:expr,) => {{ + _matchx!($cb, $condition_a => $when_a, $condition_b => $when_b) + }}; + ($condition_a:expr => $when_a:expr, $condition_b:expr => $when_b:expr, $condition_c:expr => $when_c:expr,) => {{ + _matchx!($cb, $condition_a => $when_a, $condition_b => $when_b, $condition_c => $when_c) + }}; + ($condition_a:expr => $when_a:expr, $condition_b:expr => $when_b:expr, $condition_c:expr => $when_c:expr, $condition_d:expr => $when_d:expr,) => {{ + _matchx!($cb, $condition_a => $when_a, $condition_b => $when_b, $condition_c => $when_c, $condition_d => $when_d,) + }}; + + ($condition_a:expr => $when_a:expr, _ => $catch_all:expr,) => {{ + _matchx!($cb, $condition_a => $when_a, _ => $catch_all,) + }}; + ($condition_a:expr => $when_a:expr, $condition_b:expr => $when_b:expr, _ => $catch_all:expr,) => {{ + _matchx!($cb, $condition_a => $when_a, $condition_b => $when_b, _ => $catch_all,) + }}; + ($condition_a:expr => $when_a:expr, $condition_b:expr => $when_b:expr, $condition_c:expr => $when_c:expr, _ => $catch_all:expr,) => {{ + _matchx!($cb, $condition_a => $when_a, $condition_b => $when_b, $condition_c => $when_c, _ => $catch_all,) + }}; + ($condition_a:expr => $when_a:expr, $condition_b:expr => $when_b:expr, $condition_c:expr => $when_c:expr, $condition_d:expr => $when_d:expr, _ => $catch_all:expr,) => {{ + _matchx!($cb, $condition_a => $when_a, $condition_b => $when_b, $condition_c => $when_c, $condition_d => $when_d, _ => $catch_all,) + }}; + } + + #[allow(unused_macros)] + macro_rules! unreachablex { + () => {{ + _unreachablex!($cb) + }}; + ($arg:expr) => {{ + _unreachablex!($cb, $arg) + }}; + } + + $content + }}; +} diff --git a/zkevm-circuits/src/circuit_tools/gadgets.rs b/zkevm-circuits/src/circuit_tools/gadgets.rs new file mode 100644 index 0000000000..9299957d67 --- /dev/null +++ b/zkevm-circuits/src/circuit_tools/gadgets.rs @@ -0,0 +1,168 @@ +//! Circuit gadgets +use eth_types::Field; +use gadgets::util::Expr; +use halo2_proofs::plonk::{Error, Expression}; + +use crate::evm_circuit::util::{from_bytes, pow_of_two}; + +use super::{ + cached_region::CachedRegion, + cell_manager::{Cell, CellType}, + constraint_builder::ConstraintBuilder, +}; + +/// Returns `1` when `value == 0`, and returns `0` otherwise. +#[derive(Clone, Debug, Default)] +pub struct IsZeroGadget { + inverse: Option>, + is_zero: Option>, +} + +impl IsZeroGadget { + pub(crate) fn construct( + cb: &mut ConstraintBuilder, + value: Expression, + ) -> Self { + circuit!([meta, cb], { + let inverse = cb.query_default(); + + let is_zero = 1.expr() - (value.expr() * inverse.expr()); + // `value != 0` => check `inverse = a.invert()`: value * (1 - value * inverse) + require!(value * is_zero.clone() => 0); + // `value == 0` => check `inverse = 0`: `inverse ⋅ (1 - value * inverse)` + require!(inverse.expr() * is_zero.expr() => 0); + + Self { + inverse: Some(inverse), + is_zero: Some(is_zero), + } + }) + } + + pub(crate) fn expr(&self) -> Expression { + self.is_zero.as_ref().unwrap().clone() + } + + pub(crate) fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + value: F, + ) -> Result { + let inverse = value.invert().unwrap_or(F::ZERO); + self.inverse + .as_ref() + .unwrap() + .assign(region, offset, inverse)?; + Ok(if value.is_zero().into() { + F::ONE + } else { + F::ZERO + }) + } +} + +/// Returns `1` when `lhs == rhs`, and returns `0` otherwise. +#[derive(Clone, Debug, Default)] +pub struct IsEqualGadget { + is_zero: IsZeroGadget, +} + +impl IsEqualGadget { + pub(crate) fn construct( + cb: &mut ConstraintBuilder, + lhs: Expression, + rhs: Expression, + ) -> Self { + let is_zero = IsZeroGadget::construct(cb, lhs - rhs); + + Self { is_zero } + } + + pub(crate) fn expr(&self) -> Expression { + self.is_zero.expr() + } + + pub(crate) fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + lhs: F, + rhs: F, + ) -> Result { + self.is_zero.assign(region, offset, lhs - rhs) + } +} + +/// Returns `1` when `lhs < rhs`, and returns `0` otherwise. +/// lhs and rhs `< 256**N_BYTES` +/// `N_BYTES` is required to be `<= MAX_N_BYTES_INTEGER` to prevent overflow: +/// values are stored in a single field element and two of these are added +/// together. +/// The equation that is enforced is `lhs - rhs == diff - (lt * range)`. +/// Because all values are `<= 256**N_BYTES` and `lt` is boolean, `lt` can only +/// be `1` when `lhs < rhs`. +#[derive(Clone, Debug, Default)] +pub struct LtGadget { + lt: Option>, // `1` when `lhs < rhs`, `0` otherwise. + diff: Option<[Cell; N_BYTES]>, /* The byte values of `diff`. + * `diff` equals `lhs - rhs` if `lhs >= rhs`, + * `lhs - rhs + range` otherwise. */ + range: F, // The range of the inputs, `256**N_BYTES` +} + +impl LtGadget { + pub(crate) fn construct( + cb: &mut ConstraintBuilder, + lhs: Expression, + rhs: Expression, + ) -> Self { + let lt = cb.query_bool(); + let diff = cb.query_bytes(); + let range = pow_of_two(N_BYTES * 8); + + // The equation we require to hold: `lhs - rhs == diff - (lt * range)`. + cb.require_equal( + "lhs - rhs == diff - (lt ⋅ range)", + lhs - rhs, + from_bytes::expr(&diff) - (lt.expr() * range), + ); + + Self { + lt: Some(lt), + diff: Some(diff), + range, + } + } + + pub(crate) fn expr(&self) -> Expression { + self.lt.as_ref().unwrap().expr() + } + + pub(crate) fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + lhs: F, + rhs: F, + ) -> Result<(F, Vec), Error> { + // Set `lt` + let lt = lhs < rhs; + self.lt + .as_ref() + .unwrap() + .assign(region, offset, if lt { F::ONE } else { F::ZERO })?; + // Set the bytes of diff + let diff = (lhs - rhs) + (if lt { self.range } else { F::ZERO }); + let diff_bytes = diff.to_repr(); + for (idx, diff) in self.diff.as_ref().unwrap().iter().enumerate() { + diff.assign(region, offset, F::from(diff_bytes[idx] as u64))?; + } + + Ok((if lt { F::ONE } else { F::ZERO }, diff_bytes.to_vec())) + } + + pub(crate) fn diff_bytes(&self) -> Vec> { + self.diff.as_ref().unwrap().to_vec() + } +} diff --git a/zkevm-circuits/src/circuit_tools/memory.rs b/zkevm-circuits/src/circuit_tools/memory.rs new file mode 100644 index 0000000000..a8f3ec78df --- /dev/null +++ b/zkevm-circuits/src/circuit_tools/memory.rs @@ -0,0 +1,322 @@ +//! Memory +use crate::{ + evm_circuit::util::rlc, + util::{query_expression, Expr}, +}; +use eth_types::Field; +use halo2_proofs::{ + circuit::Value, + plonk::{ + Advice, Column, ConstraintSystem, Error, Expression, FirstPhase, SecondPhase, ThirdPhase, + }, + poly::Rotation, +}; +use itertools::Itertools; +use std::{ + collections::HashMap, + ops::{Index, IndexMut}, +}; + +use super::{ + cached_region::CachedRegion, + cell_manager::{Cell, CellType}, + constraint_builder::ConstraintBuilder, +}; + +#[derive(Clone, Debug, Default)] +pub(crate) struct Memory { + height: usize, + banks: HashMap>, + rw_records: HashMap, Column)>, +} + +impl Memory { + pub(crate) fn new( + meta: &mut ConstraintSystem, + tags: Vec<(C, usize)>, + offset: usize, + height: usize, + ) -> Self { + let mut rw_records = HashMap::new(); + let banks = tags + .iter() + .map(|(tag, phase)| { + let [key, reads, writes] = match phase { + 1 => [(); 3].map(|_| meta.advice_column_in(FirstPhase)), + 2 => [(); 3].map(|_| meta.advice_column_in(SecondPhase)), + 3 => [(); 3].map(|_| meta.advice_column_in(ThirdPhase)), + _ => unreachable!(), + }; + rw_records.insert(tag.clone(), (reads, writes)); + (tag.clone(), MemoryBank::new(meta, tag.clone(), height, offset, key, reads, writes)) + }) + .collect::>>(); + Self { + banks, + height, + rw_records, + } + } + + pub(crate) fn get_bank(&self, tag: C) -> &MemoryBank { + self.banks.get(&tag).unwrap() + } + + pub(crate) fn get_mut_bank(&mut self, tag: C) -> &mut MemoryBank { + self.banks.get_mut(&tag).unwrap() + } + + pub(crate) fn get_records(&self) -> Vec<(Column, Column)> { + self.rw_records.clone().into_values().collect() + } + + pub(crate) fn build_constraints( + &self, + cb: &mut ConstraintBuilder, + is_first_row: Expression, + ) { + for (_, bank) in self.banks.iter() { + bank.build_constraints(cb, is_first_row.expr()); + } + } + + pub(crate) fn build_lookups(&self, meta: &mut ConstraintSystem) { + for (cell_type, (reads, writes)) in &self.rw_records { + let name = format!("{:?}", cell_type); + meta.lookup_any(Box::leak(name.into_boxed_str()), |meta| { + vec![( + meta.query_advice(*reads, Rotation(0)), + meta.query_advice(*writes, Rotation(0)), + )] + }); + } + } + + pub(crate) fn clear_witness_data(&mut self) { + for (_, bank) in self.banks.iter_mut() { + bank.clear_witness_data(); + } + } + + pub(crate) fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + height: usize, + ) -> Result<(), Error> { + for (_, bank) in self.banks.iter() { + bank.assign(region, height)?; + } + Ok(()) + } + + pub(crate) fn tags(&self) -> Vec { + self.banks.iter().map(|(_, bank)| bank.tag()).collect() + } +} + +impl Index for Memory { + type Output = MemoryBank; + + fn index(&self, tag: C) -> &Self::Output { + &self.banks[&tag] + } +} + +impl IndexMut for Memory { + fn index_mut(&mut self, tag: C) -> &mut Self::Output { + self.banks.get_mut(&tag).unwrap() + } +} + +#[derive(Clone, Debug)] +pub(crate) struct MemoryBank { + tag: C, + key: Column, + reads: (Vec>, usize), + writes: (Vec>, usize), + cur: Expression, + next: Expression, + table_conditions: Vec<(usize, Expression)>, + store_offsets: Vec, + stored_values: Vec>, +} + +impl MemoryBank { + pub(crate) fn new( + meta: &mut ConstraintSystem, + tag: C, + height: usize, + offset: usize, + key: Column, + read_col: Column, + write_col: Column, + ) -> Self { + let mut cur = 0.expr(); + let mut next = 0.expr(); + query_expression(meta, |meta| { + cur = meta.query_advice(key, Rotation::cur()); + next = meta.query_advice(key, Rotation::next()); + }); + let mut reads = Vec::new(); + let mut writes = Vec::new(); + for h in 0..height { + query_expression(meta, |meta| { + reads.push(Cell::new(meta, read_col, offset + h)); + writes.push(Cell::new(meta, write_col, offset + h)); + }); + } + Self { + tag, + key, + reads: (reads, 0), + writes: (writes, 0), + cur, + next, + table_conditions: Vec::new(), + store_offsets: Vec::new(), + stored_values: Vec::new(), + } + } + + pub(crate) fn key(&self) -> Expression { + self.cur.expr() + } + + fn query_write(&mut self) -> Cell { + let cell = self.writes.0[self.writes.1].clone(); + self.writes.1 += 1; + cell + } + + fn query_read(&mut self) -> Cell { + let cell = self.reads.0[self.reads.1].clone(); + self.reads.1 += 1; + cell + } + + pub(crate) fn store( + &mut self, + cb: &mut ConstraintBuilder, + values: &[Expression], + ) -> Expression { + let key = self.key() + 1.expr(); + let condition = cb.get_condition_expr(); + let values = self + .prepend_key(key.clone(), values) + .iter() + .map(|value| condition.expr() * value.expr()) + .collect_vec(); + let compressed_expr = cb.split_expression( + "compression", + rlc::expr(&values, cb.lookup_challenge.clone().unwrap().expr()), + ); + let name = format!("{:?} write #{:?}", self.tag, self.writes.1); + cb.store_expression( + name.as_str(), + compressed_expr.expr(), + C::default(), + Some(self.query_write()), + ); + self.table_conditions.push((cb.region_id, condition)); + key + } + + pub(crate) fn load( + &mut self, + _description: &'static str, + cb: &mut ConstraintBuilder, + load_offset: Expression, + values: &[Expression], + ) { + let key = self.key() - load_offset; + let condition = cb.get_condition_expr(); + let values = self + .prepend_key(key, values) + .iter() + .map(|value| condition.expr() * value.expr()) + .collect_vec(); + let compressed_expr = cb.split_expression( + "compression", + rlc::expr(&values, cb.lookup_challenge.clone().unwrap().expr()), + ); + let name = format!("{:?} write #{:?}", self.tag, self.writes.1); + cb.store_expression( + name.as_str(), + compressed_expr.expr(), + C::default(), + Some(self.query_read()), + ); + } + + pub(crate) fn witness_store(&mut self, offset: usize, values: &[F]) { + self.stored_values.push(values.to_vec()); + self.store_offsets.push(offset); + } + + pub(crate) fn witness_load(&self, offset: usize) -> Vec { + self.stored_values[self.stored_values.len() - 1 - offset].clone() + } + + pub(crate) fn clear_witness_data(&mut self) { + self.store_offsets.clear(); + } + + pub(crate) fn build_constraints( + &self, + cb: &mut ConstraintBuilder, + is_first_row: Expression, + ) { + let condition = self + .table_conditions + .iter() + .filter(|tc| tc.0 == cb.region_id) + .fold(0.expr(), |acc, tc| acc + tc.1.expr()); + crate::circuit!([meta, cb], { + ifx! {is_first_row => { + require!(self.cur.expr() => 0); + }} + let description = format!("Dynamic lookup table {:?}", self.tag()); + require!(condition => bool); + require!(description, self.next => self.cur.expr() + condition.expr()); + // TODO(Brecht): add constraint that makes sure the table value remains the same when + // not written + ifx!(not!(is_first_row) * not!(condition) => { + // TODO(Cecilia): Only works with Halo2 query API update + // require!(self.writes.0[0].column().expr() => self.writes.0[0].column().prev()); + }); + }); + } + + pub(crate) fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + height: usize, + ) -> Result<(), Error> { + // Pad to the full circuit (necessary for reads) + let mut store_offsets = self.store_offsets.clone(); + store_offsets.push(height); + + // TODO(Brecht): partial updates + let mut offset = 0; + for (store_index, &stored_offset) in store_offsets.iter().enumerate() { + while offset <= stored_offset { + region.assign_advice( + || "assign memory index".to_string(), + self.key, + offset, + || Value::known(F::from(store_index as u64)), + )?; + offset += 1; + } + } + Ok(()) + } + + pub(crate) fn tag(&self) -> C { + self.tag + } + + pub(crate) fn prepend_key(&self, key: V, values: &[V]) -> Vec { + [vec![key], values.to_owned()].concat().to_vec() + } +} diff --git a/zkevm-circuits/src/circuit_tools/test/database.rs b/zkevm-circuits/src/circuit_tools/test/database.rs new file mode 100644 index 0000000000..4e813aefee --- /dev/null +++ b/zkevm-circuits/src/circuit_tools/test/database.rs @@ -0,0 +1,152 @@ + +use eth_types::Field; +use gadgets::util::Scalar; +use halo2_proofs::{ + plonk::{Circuit, ConstraintSystem, Advice, Fixed, Column, FirstPhase, Challenge, Error, SecondPhase, Selector, Expression, VirtualCells}, + circuit::{SimpleFloorPlanner, Layouter, layouter, Value}, + poly::Rotation, +}; +use sha3::digest::typenum::IsEqual; +use crate::circuit_tools::{memory::{Memory}, constraint_builder::ConstraintBuilder, cell_manager::{CellManager, CellType, Cell}, gadgets::IsEqualGadget}; + +const MAX_DEG: usize = 5; + +pub struct DBConfig { + pub(crate) q_first: Column, + pub(crate) operation: Operation, + pub(crate) id: Column, + pub(crate) balance: Column, + pub(crate) db_read: Option> +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum DBCell { + PhaseOne, + PhaseTwo, +} + +impl CellType for DBCell { + fn byte_type() -> Option { + None + } + + fn storage_for_phase(phase: u8) -> Self { + match phase { + 1 => DBCell::PhaseOne, + 2 => DBCell::PhaseTwo, + _ => unreachable!() + } + } +} + +impl Default for DBCell { + fn default() -> Self { + DBCell::PhaseOne + } +} + +impl DBConfig { + pub fn new( + meta: &mut ConstraintSystem, + ) -> Self { + let q_first = meta.fixed_column(); + let mut operation = Operation::default(); + let op_col = meta.advice_column(); + let amt_col = meta.advice_column(); + let id = meta.advice_column(); + let balance = meta.advice_column(); + let mut db_read = DBRead::default(); + + let mut memory = Memory::new( + meta, + vec![(DBCell::PhaseOne, 1)], + 0, + 5 + ); + let cm = CellManager::new( + meta, + vec![ + (DBCell::PhaseOne, 3, 1, false), + (DBCell::PhaseTwo, 3, 2, false), + ], + 0, + 5, + ); + let mut cb = ConstraintBuilder::new(MAX_DEG, Some(cm), None); + meta.create_gate("DB", |meta| { + circuit!([meta, cb], { + operation.init(meta, &mut cb, op_col, amt_col); + let bd = memory.get_mut_bank(DBCell::PhaseOne); + matchx!( + operation.is_deposit() => { + require!(a!(balance) => a!(balance, -1) + a!(amt_col)); + bd.store(&mut cb, &[a!(id), a!(balance)]); + }, + operation.is_withdraw() => { + require!(a!(balance) => a!(balance, -1) - a!(amt_col)); + bd.store(&mut cb, &[a!(id), a!(balance)]); + }, + _ => { + let id = cb.query_default(); + let balance = cb.query_default(); + bd.load( + "Check Balance", + &mut cb, + a!(operation.check_offset()), &[id.expr(), balance.expr()] + ); + db_read.id = Some(id); + db_read.balance = Some(balance); + }, + ); + memory.build_constraints(&mut cb, f!(q_first)); + cb.build_constraints() + }) + }); + memory.build_lookups(meta); + DBConfig { q_first, operation, id, balance, db_read: Some(db_read)} + } + + +} + +#[derive(Default)] +pub struct Operation { + pub(crate) operation: Option>, + pub(crate) amount: Option>, + is_deposit: IsEqualGadget, + is_withdraw: IsEqualGadget, +} + +impl Operation { + fn init( + &mut self, + meta: &mut VirtualCells<'_, F>, + cb: &mut ConstraintBuilder, + op_col: Column, + amt_col: Column, + ) { + circuit!([meta, cb], { + self.operation = Some(op_col); + self.amount = Some(amt_col); + // 0 => deposit, 1 => withdraw, + // others => check balance with amount representing offset + self.is_deposit = IsEqualGadget::construct(cb, a!(op_col), 0.expr()); + self.is_withdraw = IsEqualGadget::construct(cb, a!(op_col), 1.expr()); + }); + } + fn is_deposit(&self) -> Expression { + self.is_deposit.expr() + } + fn is_withdraw(&self) -> Expression { + self.is_withdraw.expr() + } + fn check_offset(&self) -> Column { + self.amount.unwrap().clone() + } +} + +#[derive(Default, Clone)] +pub struct DBRead{ + id: Option>, + balance: Option>, +} \ No newline at end of file diff --git a/zkevm-circuits/src/circuit_tools/test/mod.rs b/zkevm-circuits/src/circuit_tools/test/mod.rs new file mode 100644 index 0000000000..a0284193e7 --- /dev/null +++ b/zkevm-circuits/src/circuit_tools/test/mod.rs @@ -0,0 +1,308 @@ + +use std::vec; + + +use crate::circuit_tools::cell_manager::{Cell, CellManager, CellType}; +use crate::circuit_tools::constraint_builder:: ConstraintBuilder; +use crate::circuit_tools::cached_region::CachedRegion; +use crate::{table::LookupTable, util::Expr}; + + +use eth_types::Field; +use gadgets::util::Scalar; +use halo2_proofs::circuit::{SimpleFloorPlanner, Layouter}; + + +use halo2_proofs::dev::MockProver; +use halo2_proofs::halo2curves::bn256::Fr; +use halo2_proofs::plonk::{Any, Circuit, FirstPhase, Challenge, Fixed, Selector}; +use halo2_proofs::{ + circuit::{Value}, + plonk::{ConstraintSystem, Advice, Column, Error}, + poly::Rotation, +}; + +mod query_and_branch; +mod shuffle; +mod simple_rlp; +mod database; + +/// To Test: +/// 1. Constrain advices with cells +/// 2. Lookup (advices <--> advices) with cells (RAM) +/// 3. Lookup (advices <--> fixed) with cells (ROM) +/// + +const MAX_DEG: usize = 5; +const CM_HEIGHT: usize = 10; +const COPY_COL_NUM: usize = 1; +const REGION_HEIGHT: usize = 10; + +#[derive(Clone)] +pub struct TestTable { + pub a: Column, + pub b: Column, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum TestCellType { + LookupTestTable, + PhaseOne, + PhaseTwo +} + +impl Default for TestCellType { + fn default() -> Self { + TestCellType::PhaseOne + } +} + +impl CellType for TestCellType { + fn byte_type() -> Option { + Some(TestCellType::PhaseOne) + } + + fn storage_for_phase(phase: u8) -> Self { + match phase { + 0 => TestCellType::PhaseOne, + 1 => TestCellType::PhaseTwo, + _ => unreachable!(), + } + } +} + +impl LookupTable for TestTable { + + fn columns(&self) -> Vec> { + vec![self.a.into(), self.b.into()] + } + fn annotations(&self) -> Vec { + vec![String::from("a"), String::from("b")] + } +} + +#[derive(Clone)] +pub struct TestConfig { + pub(crate) sel: Selector, + pub(crate) q_enable: Column, + pub(crate) q_count: Column, + pub(crate) cell_gadget: CellGadget, + pub(crate) table: TestTable, +} + + +impl TestConfig { + pub fn new( + meta: &mut ConstraintSystem, + table: TestTable, + randomness: Challenge + ) -> Self { + + // Get columns + let sel = meta.selector(); + let q_enable = meta.fixed_column(); + let q_count = meta.advice_column(); + + // Init cell manager and constraint builder + let cm = CellManager::new( + meta, + vec![ + (TestCellType::PhaseOne, 3, 1, false), + (TestCellType::PhaseTwo, 3, 2, false), + (TestCellType::LookupTestTable, 3, 1, false), + ], + 0, + 5, + ); + let mut cb = ConstraintBuilder::new(MAX_DEG, Some(cm), None); + + let mut cell_gadget = CellGadget::default(); + meta.create_gate("Test Gate", |meta| { + // Config row counts + circuit!([meta, cb], { + cb.lookup_challenge = Some(meta.query_challenge(randomness)); + // All configuration of inner gadgets must be wrapped in ifx! + // it pushes a condition into cb, which is gonna be multiplied with the upcoming constraints. + // then if you turn off q_enable, your inner gadgets will be disabled. + // otherwise you'll see missing selector error. + ifx!(f!(q_enable) => { + require!(a!(q_count, 1) => a!(q_count) + 1.expr()); + // Init Gadgets + cell_gadget = CellGadget::configure(&mut cb, ); + }) + }); + cb.build_constraints() + }); + + Self { + sel, + q_enable, + q_count, + cell_gadget, + table, + } + } + + pub fn assign( + &self, + layouter: &mut impl Layouter, + randomness: Value, + ) -> Result<(), Error> { + layouter.assign_region( + || "cell gadget", + |mut region| { + + self.sel.enable(&mut region, 0)?; + + for offset in 0..20 { + assignf!(region, (self.q_enable, offset) => 1.scalar())?; + assign!(region, (self.q_count, offset) => offset.scalar())?; + } + assign!(region, (self.q_count, 20) => 20.scalar())?; + + // Value of challenge is obtained from layouter. + // We query it once during synthesis and + // make it accessable across Config through CachedRegion. + let mut lookup_challenge = F::ZERO; + randomness.map(|r| lookup_challenge = r); + let mut cached_region = CachedRegion::new(&mut region, lookup_challenge, 12345.scalar()); + self.cell_gadget.assign(&mut cached_region, 0) + }, + ) + } + + fn load_fixed_table( + &self, + layouter: &mut impl Layouter, + ) -> Result<(), Error> { + // We can use different region for lookup tables, + // since they are not related to cell gadgets. + layouter.assign_region( + || "fixed table", + |mut region| { + for offset in 0..10 { + // Don't need CachedRegion here since we don't cache intermediate values. + assignf!(region, (self.table.a, offset) => offset.scalar())?; + assignf!(region, (self.table.b, offset) => (offset + 1).scalar())?; + } + Ok(()) + }, + ) + } + + +} + +#[derive(Clone, Debug, Default)] +pub struct CellGadget { + // (a, b) in lookup + // a, randomness * b == c + // where randomness is phase1 challenge + // a == d + a: Cell, + b: Cell, + c: Cell, + d: Cell, +} + + +impl CellGadget { + pub fn configure(cb: &mut ConstraintBuilder) -> Self { + let a = cb.query_default(); + let b = cb.query_default(); + // c depends on Phase1 Challenge randomness + let c = cb.query_one(TestCellType::PhaseTwo); + let d = cb.query_default(); + circuit!([meta, cb], { + //require!((a, b) => @format!("test_lookup")); + require!(c => a.expr() + b.expr() * cb.lookup_challenge.clone().unwrap()); + require!(a => d.expr()); + }); + + CellGadget { a, b, c, d } + } + + pub fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + ) -> Result<(), Error>{ + // Assign values to cells + self.a.assign(region, offset, 2u64.scalar())?; + self.b.assign(region, offset, 3u64.scalar())?; + self.c.assign( + region, + offset, + F::from(2u64) + F::from(3u64) * region.lookup_challenge() + )?; + self.d.assign(region, offset, 2u64.scalar())?; + Ok(()) + } +} + +#[derive(Clone, Debug, Default)] +struct TestCircuit { + // Don't need anything in this struct, + // since we don't have precomputed data from outside + // and Config & Challenges are passed to synthesize. + _phantom: F, +} + +impl Circuit for TestCircuit { + type Config = (TestConfig, Challenge); + type FloorPlanner = SimpleFloorPlanner; + type Params = (); + + fn without_witnesses(&self) -> Self { + unimplemented!() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + + // Build the table and challenge outside + // since zkevm use them accross circuits + let table = TestTable { + a: meta.fixed_column(), + b: meta.fixed_column(), + }; + let _dummy_phase1 = meta.advice_column_in(FirstPhase); + let randomness = meta.challenge_usable_after(FirstPhase); + + let config = TestConfig::new(meta, table, randomness); + (config, randomness) + } + + fn synthesize( + &self, + (config, randomness): Self::Config, + mut layouter: impl halo2_proofs::circuit::Layouter + ) -> Result<(), Error> { + let randomness = layouter.get_challenge(randomness); + config.load_fixed_table(&mut layouter)?; + config.assign(&mut layouter, randomness) + } +} + +// #[cfg(feature = "dev-graph")] +#[test] +fn test() { + let circuit = TestCircuit::::default(); + let prover = MockProver::::run(6, &circuit, vec![]).unwrap(); + prover.assert_satisfied_par(); + + // use plotters::prelude::*; + // let root = BitMapBackend::new("test.png", (1024, 768)).into_drawing_area(); + // root.fill(&WHITE).unwrap(); + // let root = root + // .titled("Test Layout", ("sans-serif", 60)) + // .unwrap(); + + // halo2_proofs::dev::CircuitLayout::default() + // // Render the circuit onto your area! + // // The first argument is the size parameter for the circuit. + // .show_cell_annotations(true) + // .region_by_name("cell gadget") + // .render(6, &circuit.clone(), &root) + // .unwrap(); + +} \ No newline at end of file diff --git a/zkevm-circuits/src/circuit_tools/test/query_and_branch.rs b/zkevm-circuits/src/circuit_tools/test/query_and_branch.rs new file mode 100644 index 0000000000..ca0ce965ac --- /dev/null +++ b/zkevm-circuits/src/circuit_tools/test/query_and_branch.rs @@ -0,0 +1,154 @@ +use std::{marker::PhantomData}; + +use eth_types::Field; +use gadgets::util::Scalar; +use halo2_proofs::{ + plonk::{Circuit, ConstraintSystem, Advice, Fixed, Column, FirstPhase, Challenge, Error, SecondPhase}, + circuit::{SimpleFloorPlanner, Layouter, layouter, Value}, + poly::Rotation, +}; + +use crate::circuit_tools::{constraint_builder:: ConstraintBuilder, cell_manager::CellType}; + +#[derive(Clone)] +pub struct TestConfig { + pub(crate) q_enable: Column, + pub(crate) a: Column, + pub(crate) b: Column, + pub(crate) c: Column, + pub(crate) res: Column, +} + +#[derive(Clone, Copy, Debug, num_enum::Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(u8)] +pub enum TestCellType { + #[num_enum(default)] + Storage, +} +impl CellType for TestCellType{ + fn byte_type() -> Option { + unimplemented!() + } + fn storage_for_phase(phase: u8) -> Self { + unimplemented!() + } +} + + +impl TestConfig { + pub fn new(meta: &mut ConstraintSystem, r: Challenge) -> Self { + let q_enable = meta.fixed_column(); + let a = meta.advice_column(); + let b = meta.advice_column_in(SecondPhase); + let c = meta.fixed_column(); + let res = meta.advice_column(); + + + let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, None, None); + + meta.create_gate("Test", |meta| { + circuit!([meta, cb], { + // Fixed column with ifx! equivalents to selector + // BUT Advice does not + ifx!(f!(q_enable) => { + ifx!(a!(a) => { + require!(a!(res) => a!(b) + f!(c)); + } elsex { + require!(a!(res) => a!(b) + c!(r)); + + }); + // Matchx! adds the same set of constraints as ifx! + matchx!( + a!(a) => { + require!(a!(res) => a!(b) + f!(c)); + }, + not!(a!(a)) => { + require!(a!(res) => a!(b) + c!(r)); + + }, + _ => unreachablex!(), + ); + }) + }); + cb.build_constraints() + }); + TestConfig { + q_enable, + a, + b, + c, + res, + } + } + + pub fn assign( + &self, + layouter: &mut impl Layouter, + r: F, + ) -> Result<(), Error> { + layouter.assign_region( + || "Test", + |mut region| { + for offset in 0..10 { + assignf!(region, (self.q_enable, offset) => true.scalar()); + + let is_even = (offset / 2) * 2 == offset; + assign!(region, (self.a, offset) => is_even.scalar()); + assign!(region, (self.b, offset) => 887766.scalar()); + assignf!(region, (self.c, offset) => 112233.scalar()); + if is_even { + assign!(region, (self.res, offset) => (887766 + 112233).scalar()); + } else { + assign!(region, (self.res, offset) => Scalar::::scalar(&887766) + r); + } + } + Ok(()) + } + ) + } +} + +#[derive(Clone, Debug, Default)] +struct TestCircuit { + _phantom: F, +} + +impl Circuit for TestCircuit { + type Config = (TestConfig, Challenge); + type FloorPlanner = SimpleFloorPlanner; + type Params = (); + + fn without_witnesses(&self) -> Self { + unimplemented!() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + // dummy column for phase1 challange + meta.advice_column_in(FirstPhase); + let randomness = meta.challenge_usable_after(FirstPhase); + let config = TestConfig::new(meta, randomness); + + (config, randomness) + } + + fn synthesize( + &self, + (config, randomness): Self::Config, + mut layouter: impl Layouter + ) -> Result<(), halo2_proofs::plonk::Error> { + let mut r = F::ZERO; + layouter.get_challenge(randomness).map(|randomness| r = randomness); + config.assign(&mut layouter, r)?; + Ok(()) + } +} + +#[test] +fn test() { + + use halo2_proofs::{ dev::MockProver, halo2curves::bn256::Fr}; + + let circuit = TestCircuit::::default(); + let prover = MockProver::::run(6, &circuit, vec![]).unwrap(); + prover.assert_satisfied_par(); +} \ No newline at end of file diff --git a/zkevm-circuits/src/circuit_tools/test/shuffle.rs b/zkevm-circuits/src/circuit_tools/test/shuffle.rs new file mode 100644 index 0000000000..a421f5615d --- /dev/null +++ b/zkevm-circuits/src/circuit_tools/test/shuffle.rs @@ -0,0 +1,192 @@ +use std::{marker::PhantomData}; + +use eth_types::Field; +use gadgets::util::Scalar; +use halo2_proofs::{ + plonk::{Circuit, ConstraintSystem, Advice, Fixed, Column, FirstPhase, Challenge, Error, SecondPhase, Selector, Expression}, + circuit::{SimpleFloorPlanner, Layouter, layouter, Value}, + poly::Rotation, +}; +use rand::RngCore; +use rand_chacha::rand_core::OsRng; + +use crate::circuit_tools::{constraint_builder:: ConstraintBuilder, cell_manager::{CellType, CellManager, Cell}, cached_region::CachedRegion}; + + +#[derive(Clone)] +pub struct ShuffleConfig { + q_shuffle: Column, + original: [Column; W], + shuffled: [Column; W], + cb: ConstraintBuilder +} + +#[derive(Clone, Copy, Debug, num_enum::Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(u8)] +pub enum ShuffleCells { + Original, + Shuffled, + #[num_enum(default)] + Storage, +} + +impl CellType for ShuffleCells { + fn byte_type() -> Option { + unimplemented!() + } + fn storage_for_phase(phase: u8) -> Self { + match phase { + 0 => ShuffleCells::Storage, + _ => unreachable!() + } + } +} + + +impl ShuffleConfig { + pub fn new(meta: &mut ConstraintSystem) -> Self { + let q_shuffle = meta.fixed_column(); + let cm = CellManager::new( + meta, + vec![ + (ShuffleCells::Original, W, 1, false), + (ShuffleCells::Shuffled, W, 1, false), + (ShuffleCells::Storage, 10, 1, false), + ], + 0, + H + ); + let original = array_init::from_iter( + cm.get_typed_columns(ShuffleCells::Original) + .iter() + .map(|c| c.column.clone()) + ).unwrap(); + let shuffled = array_init::from_iter( + cm.get_typed_columns(ShuffleCells::Shuffled) + .iter() + .map(|c| c.column.clone()) + ).unwrap(); + + let mut cb: ConstraintBuilder = ConstraintBuilder::new(10 , Some(cm), None); + + meta.create_gate("Shuffle", |meta| { + // Rest of the gate we handle in macro + circuit!([meta, cb], { + ifx!(f!(q_shuffle) => { + let original = cb.query_cells_dyn(ShuffleCells::Original, W * H); + let shuffled = cb.query_cells_dyn(ShuffleCells::Shuffled, W * H); + + for (i, o) in original.iter().enumerate() { + let mut dest_set = Vec::new(); + for (j, s) in shuffled.iter().enumerate() { + if i == j { continue; } + dest_set.push(s.expr()); + } + require!(o.expr() => dest_set); + } + cb.split_constraints_expression(); + }); + cb.build_constraints() + }) + }); + + ShuffleConfig { + q_shuffle, + original, + shuffled, + cb + } + } +} + +#[derive(Clone)] +struct ShuffleCircuit { + original: [[F; H]; W], + shuffled: [[F; H]; W], +} + +impl ShuffleCircuit { + pub fn new(rng: &mut R) -> Self { + let original = [(); W].map(|_| [(); H].map(|_| F::random(&mut *rng))); + Self { + original, + shuffled: Self::shuffled(original, rng) + } + } + + fn shuffled( + original: [[F; H]; W], + rng: &mut R, + ) -> [[F; H]; W] { + let mut shuffled = original; + + for row in (1..H).rev() { + for column in shuffled.iter_mut() { + let rand_row = (rng.next_u32() as usize) % row; + column.swap(row, rand_row); + } + } + for col in (1..W).rev() { + let rand_col = (rng.next_u32() as usize) % col; + shuffled.swap(col, rand_col); + } + + shuffled + } +} + +impl Circuit for ShuffleCircuit { + type Config = ShuffleConfig<5, 6, F>; + type FloorPlanner = SimpleFloorPlanner; + type Params = (); + + fn without_witnesses(&self) -> Self { + unimplemented!() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + // dummy column for phase1 challange + meta.advice_column_in(FirstPhase); + ShuffleConfig::new(meta) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter + ) -> Result<(), halo2_proofs::plonk::Error> { + layouter.assign_region(|| "Shuffle", |mut region| { + let mut region = CachedRegion::new(&mut region, 1.scalar(), 2.scalar()); + assignf!(region, (config.q_shuffle, 0) => true.scalar()); + for h in (0..H) { + config.original + .iter() + .zip(self.original.iter()) + .for_each(| (col, val) | { + assign!(region, (col.clone(), h) => val[h]); + }); + config.shuffled + .iter() + .zip(self.shuffled.iter()) + .for_each(| (col, val) | { + assign!(region, (col.clone(), h) => val[h]); + }) + } + region.assign_stored_expressions(&config.cb, &[Value::known(1.scalar())]) + }) + } +} + +#[test] +fn test() { + + use halo2_proofs::{ dev::MockProver, halo2curves::bn256::Fr}; + + const W: usize = 3; + const H: usize = 4; + + let circuit = ShuffleCircuit::::new(&mut OsRng); + let prover = MockProver::::run(6, &circuit, vec![]).unwrap(); + prover.assert_satisfied_par(); +} + diff --git a/zkevm-circuits/src/circuit_tools/test/simple_rlp.rs b/zkevm-circuits/src/circuit_tools/test/simple_rlp.rs new file mode 100644 index 0000000000..95316e96a6 --- /dev/null +++ b/zkevm-circuits/src/circuit_tools/test/simple_rlp.rs @@ -0,0 +1,78 @@ +use std::default; + +use eth_types::Field; +use gadgets::util::Scalar; +use halo2_proofs::{ + plonk::{Circuit, ConstraintSystem, Advice, Fixed, Column, FirstPhase, Challenge, Error, SecondPhase, Selector, Expression}, + circuit::{SimpleFloorPlanner, Layouter, layouter, Value}, + poly::Rotation, +}; + +use crate::circuit_tools::{memory::{Memory}, constraint_builder::ConstraintBuilder, cell_manager::{CellManager, CellType}}; + +const MAX_DEG: usize = 5; + +#[derive(Clone)] +pub struct RlpConfig { + pub(crate) sel: Selector, + pub(crate) bytes: Column, + pub(crate) rlc: Column, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum RlpCell { + PhaseOne, + PhaseTwo, + Stack, + Byte, +} + +impl CellType for RlpCell { + fn byte_type() -> Option { + Some(RlpCell::Byte) + } + + fn storage_for_phase(phase: u8) -> Self { + match phase { + 1 => RlpCell::PhaseOne, + 2 => RlpCell::PhaseTwo, + _ => unreachable!() + } + } +} + +impl Default for RlpCell { + fn default() -> Self { + RlpCell::PhaseOne + } +} + +impl RlpConfig { + pub fn new( + meta: &mut ConstraintSystem, + gamma: Challenge, + sigma: Challenge, + ) -> Self { + let sel = meta.selector(); + let bytes = meta.advice_column(); + let rlc = meta.advice_column(); + + let memory = Memory::new( + meta, + vec![(RlpCell::Stack, 2)], + 0, + 5 + ); + let cm = CellManager::new( + meta, + vec![ + (RlpCell::PhaseOne, 3, 1, false), + (RlpCell::PhaseTwo, 3, 2, false), + ], + 0, + 5, + ); + let mut cb = ConstraintBuilder::new(MAX_DEG, Some(cm), None); + todo!() + } +} diff --git a/zkevm-circuits/src/keccak_circuit.rs b/zkevm-circuits/src/keccak_circuit.rs index 029ec196a6..16d4185abb 100644 --- a/zkevm-circuits/src/keccak_circuit.rs +++ b/zkevm-circuits/src/keccak_circuit.rs @@ -1,6 +1,6 @@ //! The keccak circuit implementation. mod cell_manager; -/// Keccak packed multi +/// Keccak packed multiz pub mod keccak_packed_multi; mod param; mod table; diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index 62c796821d..41a22ef556 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -32,6 +32,7 @@ pub mod taiko_super_circuit; #[cfg(any(feature = "test", test))] pub mod test_util; +pub mod circuit_tools; pub mod anchor_tx_circuit; pub mod tx_circuit; diff --git a/zkevm-circuits/src/taiko_pi_circuit.rs b/zkevm-circuits/src/taiko_pi_circuit.rs index 125e35c1ff..22944898df 100644 --- a/zkevm-circuits/src/taiko_pi_circuit.rs +++ b/zkevm-circuits/src/taiko_pi_circuit.rs @@ -4,7 +4,11 @@ use crate::{ evm_circuit::util::constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}, util::{random_linear_combine_word as rlc, Challenges, SubCircuit, SubCircuitConfig}, - witness::{self, BlockContext}, + circuit_tools::{ + constraint_builder::ConstraintBuilder, + cell_manager::{CellManager, CellType}, + }, + witness::{self, BlockContext}, circuit, }; use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256}; use ethers_core::utils::keccak256; @@ -71,6 +75,7 @@ pub struct PublicData { } impl PublicData { + // 需要的信息全部串起来,(名字,block#,raw_bytes) fn assignments(&self) -> [(&'static str, Option, [u8; 32]); 10] { [ ( @@ -110,6 +115,7 @@ impl PublicData { ] } + // 取 raw_bytes 串起来 /// get rpi bytes pub fn rpi_bytes(&self) -> Vec { self.assignments().iter().flat_map(|v| v.2).collect() @@ -151,6 +157,7 @@ impl PublicData { } } + // raw_bytes -> keccak -> H256 整数 fn get_pi(&self) -> H256 { let rpi_bytes = self.rpi_bytes(); let rpi_keccak = keccak256(rpi_bytes); @@ -196,6 +203,23 @@ pub struct TaikoPiCircuitConfigArgs { pub challenges: Challenges>, } +/// +#[derive(Clone, Copy, Debug, num_enum::Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(u8)] +pub enum PiCellType { + /// + #[num_enum(default)] + Storage, +} +impl CellType for PiCellType { + fn byte_type() -> Option { + unimplemented!() + } + fn storage_for_phase(phase: u8) -> Self { + unimplemented!() + } +} + impl SubCircuitConfig for TaikoPiCircuitConfig { type ConfigArgs = TaikoPiCircuitConfigArgs; @@ -229,11 +253,29 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { meta.enable_equality(block_table.value); meta.enable_equality(pi); + let cm = CellManager::new( + meta, + vec![ + (PiCellType::Storage, 3, 1, false), + ], + 0, + 1, + ); + let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(cm), Some(challenges.evm_word())); + + + meta.create_gate("Pi Gate", |meta| { + circuit!([meta, cb], { + + }); + cb.build_constraints() + }); + // field bytes meta.create_gate( "rpi_field_bytes_acc[i+1] = rpi_field_bytes_acc[i] * t + rpi_bytes[i+1]", |meta| { - let mut cb = BaseConstraintBuilder::new(MAX_DEGREE); + let mut bcb = BaseConstraintBuilder::new(MAX_DEGREE); let q_field_step = meta.query_selector(q_field_step); let rpi_field_bytes_acc_next = @@ -243,27 +285,27 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { let is_field_rlc = meta.query_fixed(is_field_rlc, Rotation::next()); let randomness = challenges.evm_word(); let t = select::expr(is_field_rlc, randomness, BYTE_POW_BASE.expr()); - cb.require_equal( + bcb.require_equal( "rpi_field_bytes_acc[i+1] = rpi_field_bytes_acc[i] * t + rpi_bytes[i+1]", rpi_field_bytes_acc_next, rpi_field_bytes_acc * t + rpi_field_bytes_next, ); - cb.gate(q_field_step) + bcb.gate(q_field_step) }, ); meta.create_gate("rpi_field_bytes_acc[0] = rpi_field_bytes[0]", |meta| { - let mut cb = BaseConstraintBuilder::new(MAX_DEGREE); + let mut bcb = BaseConstraintBuilder::new(MAX_DEGREE); let q_field_start = meta.query_selector(q_field_start); let rpi_field_bytes_acc = meta.query_advice(rpi_field_bytes_acc, Rotation::cur()); let rpi_field_bytes = meta.query_advice(rpi_field_bytes, Rotation::cur()); - cb.require_equal( + bcb.require_equal( "rpi_field_bytes_acc[0] = rpi_field_bytes[0]", rpi_field_bytes_acc, rpi_field_bytes, ); - cb.gate(q_field_start) + bcb.gate(q_field_start) }); // keccak in rpi From 4b891d7015ef8b20e60ef3a581d7fdf3edab85d5 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Sun, 30 Jul 2023 23:11:04 +0200 Subject: [PATCH 02/43] runs --- zkevm-circuits/src/taiko_pi_circuit.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/zkevm-circuits/src/taiko_pi_circuit.rs b/zkevm-circuits/src/taiko_pi_circuit.rs index 22944898df..faa61e87d5 100644 --- a/zkevm-circuits/src/taiko_pi_circuit.rs +++ b/zkevm-circuits/src/taiko_pi_circuit.rs @@ -8,6 +8,7 @@ use crate::{ constraint_builder::ConstraintBuilder, cell_manager::{CellManager, CellType}, }, + witness::{self, BlockContext}, circuit, }; use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256}; @@ -75,7 +76,6 @@ pub struct PublicData { } impl PublicData { - // 需要的信息全部串起来,(名字,block#,raw_bytes) fn assignments(&self) -> [(&'static str, Option, [u8; 32]); 10] { [ ( @@ -115,7 +115,6 @@ impl PublicData { ] } - // 取 raw_bytes 串起来 /// get rpi bytes pub fn rpi_bytes(&self) -> Vec { self.assignments().iter().flat_map(|v| v.2).collect() @@ -157,7 +156,6 @@ impl PublicData { } } - // raw_bytes -> keccak -> H256 整数 fn get_pi(&self) -> H256 { let rpi_bytes = self.rpi_bytes(); let rpi_keccak = keccak256(rpi_bytes); @@ -266,7 +264,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { meta.create_gate("Pi Gate", |meta| { circuit!([meta, cb], { - + cb.add_constraint("", 0.expr()); }); cb.build_constraints() }); From 998d71abc70a9391a447a679fb6d74eca37c6ca7 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Wed, 2 Aug 2023 03:51:39 +0200 Subject: [PATCH 03/43] conversion macro --- zkevm-circuits/src/circuit_tools.rs | 1 - .../src/circuit_tools/cached_region.rs | 4 - .../src/circuit_tools/cell_manager.rs | 120 +-- .../src/circuit_tools/constraint_builder.rs | 970 ++++++++---------- zkevm-circuits/src/circuit_tools/memory.rs | 42 +- .../src/circuit_tools/test/database.rs | 152 --- zkevm-circuits/src/circuit_tools/test/mod.rs | 308 ------ .../circuit_tools/test/query_and_branch.rs | 154 --- .../src/circuit_tools/test/shuffle.rs | 192 ---- .../src/circuit_tools/test/simple_rlp.rs | 78 -- zkevm-circuits/src/taiko_pi_circuit.rs | 209 ++-- 11 files changed, 614 insertions(+), 1616 deletions(-) delete mode 100644 zkevm-circuits/src/circuit_tools/test/database.rs delete mode 100644 zkevm-circuits/src/circuit_tools/test/mod.rs delete mode 100644 zkevm-circuits/src/circuit_tools/test/query_and_branch.rs delete mode 100644 zkevm-circuits/src/circuit_tools/test/shuffle.rs delete mode 100644 zkevm-circuits/src/circuit_tools/test/simple_rlp.rs diff --git a/zkevm-circuits/src/circuit_tools.rs b/zkevm-circuits/src/circuit_tools.rs index d775668321..2c8535d22e 100644 --- a/zkevm-circuits/src/circuit_tools.rs +++ b/zkevm-circuits/src/circuit_tools.rs @@ -6,4 +6,3 @@ pub mod cached_region; pub mod cell_manager; pub mod gadgets; pub mod memory; -mod test; \ No newline at end of file diff --git a/zkevm-circuits/src/circuit_tools/cached_region.rs b/zkevm-circuits/src/circuit_tools/cached_region.rs index c9698e2761..189708df2e 100644 --- a/zkevm-circuits/src/circuit_tools/cached_region.rs +++ b/zkevm-circuits/src/circuit_tools/cached_region.rs @@ -57,10 +57,6 @@ impl<'r, 'b, F: Field> CachedRegion<'r, 'b, F> { // Nothing to do } - pub(crate) fn lookup_challenge(&self) -> F { - self.r.clone() - } - pub(crate) fn assign_stored_expressions>( &mut self, cb: &ConstraintBuilder, diff --git a/zkevm-circuits/src/circuit_tools/cell_manager.rs b/zkevm-circuits/src/circuit_tools/cell_manager.rs index 864c7a5aed..56d78a8768 100644 --- a/zkevm-circuits/src/circuit_tools/cell_manager.rs +++ b/zkevm-circuits/src/circuit_tools/cell_manager.rs @@ -1,7 +1,7 @@ //! Cell manager use crate::{ circuit_tools::cached_region::CachedRegion, - util::{query_expression, Expr}, + util::{query_expression, Expr}, evm_circuit::util::rlc, }; use crate::table::LookupTable; @@ -14,8 +14,7 @@ use halo2_proofs::{ }, poly::Rotation, }; -use lazy_static::__Deref; -use std::{collections::{BTreeMap, HashMap}, fmt::Debug, hash::Hash, cmp::{max, Ordering}}; +use std::{cmp::Ordering, collections::BTreeMap, fmt::Debug, hash::Hash}; #[derive(Clone, Debug, Default)] pub(crate) struct Cell { @@ -209,10 +208,11 @@ pub(crate) struct CellColumn { pub(crate) expr: Expression, } - impl PartialEq for CellColumn { fn eq(&self, other: &Self) -> bool { - self.index == other.index && self.cell_type == other.cell_type && self.height == other.height + self.index == other.index + && self.cell_type == other.cell_type + && self.height == other.height } } @@ -236,25 +236,12 @@ impl Expr for CellColumn { } } - #[derive(Clone, Debug)] pub struct CellManager { configs: Vec>, columns: Vec>, height: usize, - width: usize, height_limit: usize, - - // branch ctxs - branch_ctxs: HashMap>, - parent_ctx: Option>, -} - - -#[derive(Default, Clone, Debug)] -struct CmContext{ - parent: Box>>, - columns: Vec>, } impl CellManager { @@ -264,13 +251,11 @@ impl CellManager { offset: usize, max_height: usize, ) -> Self { - assert!(max_height >= 1); let configs = configs .into_iter() .map(|c| c.into()) .collect::>>(); - - let mut width = 0; + let mut columns = Vec::new(); for config in configs.iter() { let cols = config.init_columns(meta); @@ -289,85 +274,21 @@ impl CellManager { expr: cells[0].expr(), cells, }); - width += 1; } } Self { configs, columns, height: max_height, - width, height_limit: max_height, - branch_ctxs: HashMap::new(), - parent_ctx: None, } } - pub(crate) fn cur_to_parent(&mut self) { - let new_parent = match self.parent_ctx.clone() { - // if parent context exists, meaning we are deep in a callstack - // we set it as the parent of new parent - Some(ctx) => CmContext { - parent: Box::new(Some(ctx.clone())), - columns: self.columns.clone(), - }, - // otherwise, this is the fist level of callstack - // the parent of new parent is None - None => CmContext { - parent: Box::new(None), - columns: self.columns.clone(), - } - }; - self.parent_ctx = Some(new_parent); - self.reset(self.height_limit); - } - - pub(crate) fn cur_to_branch(&mut self, name: &str) { - let new_branch = match self.parent_ctx.clone() { - // if parent context exists, meaning we are deep in a callstack - // we set it as the parent of new branch - Some(ctx) => CmContext { - parent: Box::new(Some(ctx.clone())), - columns: self.columns.clone(), - }, - // otherwise, this is the fist level of callstack - // the parent of new branch is None - None => CmContext { - parent: Box::new(None), - columns: self.columns.clone(), - } - }; - self.branch_ctxs.insert(name.to_string(), new_branch); - self.reset(self.height_limit); - } - - pub(crate) fn recover_max_branch(&mut self) { - let mut new_cols = self.columns.clone(); - let parent = self.parent_ctx.clone().expect("Retruning context needs parent"); - self.branch_ctxs - .iter() - .for_each(|(name, ctx)| { - for c in 0..self.width { - new_cols[c] = max(&new_cols[c], &ctx.columns[c]).clone(); - new_cols[c] = max(&new_cols[c], &parent.columns[c]).clone(); - } - }); - self.columns = new_cols; - self.branch_ctxs.clear(); - self.parent_ctx = self.parent_ctx - .clone() - .map(|ctx| ctx.parent.deref().clone()) - .unwrap(); - } - - pub(crate) fn recover_parent(&mut self) { - assert!(self.parent_ctx.is_some(), "No parent context to recover"); - self.columns = self.parent_ctx.clone().unwrap().columns.clone(); - self.parent_ctx - .clone() - .map(|ctx| self.parent_ctx = ctx.parent.deref().clone()) - .unwrap(); - self.branch_ctxs.clear(); + pub(crate) fn restart(&mut self) { + self.height = self.height_limit; + for col in self.columns.iter_mut() { + col.height = 0; + } } pub(crate) fn query_cells(&mut self, cell_type: C, count: usize) -> Vec> { @@ -446,6 +367,25 @@ impl CellManager { } columns } + + pub(crate) fn build_lookups_from_table( + &self, + meta: &mut ConstraintSystem, + tables: &[(C, &dyn LookupTable)], + challenge: Expression, + ) { + for (cell_type, table) in tables { + for col in self.get_typed_columns(*cell_type) { + let name = format!("{:?}", cell_type); + meta.lookup_any(Box::leak(name.into_boxed_str()), |meta| { + vec![( + col.expr, + rlc::expr(&table.table_exprs(meta), challenge.expr()), + )] + }); + } + } + } } /// LookupTable created dynamically and stored in an advice column diff --git a/zkevm-circuits/src/circuit_tools/constraint_builder.rs b/zkevm-circuits/src/circuit_tools/constraint_builder.rs index 460b61aa30..5c2ca4e892 100644 --- a/zkevm-circuits/src/circuit_tools/constraint_builder.rs +++ b/zkevm-circuits/src/circuit_tools/constraint_builder.rs @@ -2,10 +2,10 @@ use std::{ collections::HashMap, ops::{Add, Mul}, - vec, + vec, marker::PhantomData, }; -use crate::{evm_circuit::util::rlc, table::LookupTable, util::Expr}; +use crate::{evm_circuit::util::rlc, table::LookupTable, util::{Expr, query_expression}}; use eth_types::Field; use gadgets::util::{and, sum, Scalar}; use halo2_proofs::{ @@ -13,6 +13,7 @@ use halo2_proofs::{ poly::Rotation, }; use itertools::Itertools; +use num::complex::ComplexFloat; use super::{ cached_region::StoredExpression, @@ -29,19 +30,90 @@ fn get_condition_expr(conditions: &Vec>) -> Expression { +pub struct LookupData { /// Desciption pub description: &'static str, /// Condition under which the lookup needs to be done - pub condition: Expression, + pub regional_condition: Expression, + /// Need to store local condition for dyn table checks + pub local_condition: Expression, /// The values to lookup pub values: Vec>, /// region pub region_id: usize, - /// If is fixed, use static table for lookup - pub is_fixed: bool, - /// Use rlc - pub compress: bool, + /// If true lookup to fixed table + pub to_fixed: bool, +} + +/// Data for dynamic lookup +#[derive(Clone, Debug)] +pub struct TableData { + /// Condition under which the lookup needs to be done + pub regional_condition: Expression, + /// Need to store local condition for dyn table checks + pub local_condition: Expression, + /// The values to lookup + pub values: Vec>, + /// region + pub region_id: usize, +} + +impl TableData { + fn condition(&self) -> Expression { + self.regional_condition.expr() * self.local_condition.expr() + } +} + +struct TabelMerger{ + data: Vec>, + _phantom: PhantomData, +} + +impl TabelMerger { + fn merge_check(&self, cb: &mut ConstraintBuilder) { + let selector = sum::expr(self.data.iter().map(|t| t.condition())); + crate::circuit!([meta, cb], { + require!(selector => bool); + }); + } + + fn merge_unsafe(&self,) -> (Expression, Vec>) { + if self.data.is_empty() { + return (0.expr(), Vec::new()); + } + let selector = sum::expr(self.data.iter().map(|v| v.condition())); + // Merge + let max_length = self.data.iter().map(|t| t.values.len()).max().unwrap(); + let mut merged_values = vec![0.expr(); max_length]; + let default_value = 0.expr(); + merged_values + .iter_mut() + .enumerate() + .for_each(|(idx, v)| { + *v = sum::expr( + self.data + .iter() + .map(|t| t.condition() * t.values.get(idx).unwrap_or(&default_value).expr()), + ); + }); + (selector, merged_values) + } + + fn check_and_merge( + &self, + cb: &mut ConstraintBuilder, + ) -> (Expression, Vec>) { + self.merge_check(cb); + self.merge_unsafe() + } + + fn merge_and_select( + &self, + cb: &mut ConstraintBuilder, + ) -> Vec> { + let (selector, v) = self.merge_unsafe(); + v.iter().map(|v| selector.expr() * v.expr()).collect() + } } /// Constraint builder @@ -57,10 +129,10 @@ pub struct ConstraintBuilder { conditions: Vec>, /// The lookups generated during synthesis /// assembles runtime access to RAM - pub dynamic_lookups: HashMap>>, + pub lookups: HashMap>>, /// The tables written during synthesis /// write to RAM - pub dynamic_tables: HashMap>>, + pub dynamic_tables: HashMap>>, /// All stored expressions pub stored_expressions: HashMap>>, /// CellManager @@ -76,8 +148,6 @@ pub struct ConstraintBuilder { pub state_context: Vec>, /// state constraints start pub state_constraints_start: usize, - /// Lookups - pub lookups: HashMap>>, } impl ConstraintBuilder { @@ -91,7 +161,7 @@ impl ConstraintBuilder { max_global_degree: max_degree, max_degree, conditions: Vec::new(), - dynamic_lookups: HashMap::new(), + lookups: HashMap::new(), dynamic_tables: HashMap::new(), cell_manager, disable_description: false, @@ -100,7 +170,20 @@ impl ConstraintBuilder { lookup_challenge, state_context: Vec::new(), state_constraints_start: 0, - lookups: HashMap::new(), + } + } + + pub(crate) fn restart(&mut self) { + self.constraints.clear(); + self.conditions.clear(); + self.dynamic_tables.clear(); + self.stored_expressions.clear(); + self.region_id = 0; + self.state_context.clear(); + self.state_constraints_start = 0; + self.lookups.clear(); + if let Some(cell_manager) = &mut self.cell_manager { + cell_manager.restart(); } } @@ -126,17 +209,17 @@ impl ConstraintBuilder { for idx in self.state_constraints_start..self.constraints.len() { self.constraints[idx].1 = condition.expr() * self.constraints[idx].1.clone(); } - for (_, values) in self.dynamic_lookups.iter_mut() { + for (_, values) in self.lookups.iter_mut() { for value in values { if value.region_id == self.region_id { - value.condition = condition.expr() * value.condition.expr(); + value.regional_condition = value.regional_condition.expr() * condition.expr(); } } } for (_key, values) in self.dynamic_tables.iter_mut() { for value in values { if value.region_id == self.region_id { - value.condition = condition.expr() * value.condition.expr(); + value.regional_condition = value.regional_condition.expr() * condition.expr(); } } } @@ -217,6 +300,18 @@ impl ConstraintBuilder { self.constraints.push((name, constraint)); } + pub(crate) fn get_condition(&self) -> Option> { + if self.conditions.is_empty() { + None + } else { + Some(and::expr(self.conditions.iter())) + } + } + + pub(crate) fn get_condition_expr(&self) -> Expression { + self.get_condition().unwrap_or_else(|| 1.expr()) + } + // Query pub(crate) fn query_bool(&mut self) -> Cell { @@ -268,283 +363,203 @@ impl ConstraintBuilder { } } - pub(crate) fn split_constraints_expression(&mut self) { - self.constraints = self.constraints.clone() - .into_iter() - .map(|(name, c)| { - (name, self.split_expression(name, c.clone())) - }) - .collect::>(); - } - pub(crate) fn build_constraints(&self) -> Vec<(&'static str, Expression)> { + if self.constraints.is_empty() { + return vec![("No constraints", 0.expr())]; + } self.constraints.clone() } pub(crate) fn build_lookups( + &mut self, + meta: &mut ConstraintSystem, + cell_managers: &[CellManager], + fixed_path: &[(C, &dyn LookupTable)], + dynamic_path: &[(C, Option<&dyn LookupTable>)], + ) { + self.build_from_table(meta, cell_managers, fixed_path); + self.build_from_data(meta, dynamic_path); + } + + pub(crate) fn build_from_table( &self, meta: &mut ConstraintSystem, - cell_managers: Vec>, - tables: Vec<(C, &dyn LookupTable)>, + cell_managers: &[CellManager], + tables: &[(C, &dyn LookupTable)], ) { + let challenge = self.lookup_challenge.clone().unwrap(); for cm in cell_managers { - for (cell_type, table) in &tables { - for col in cm.get_typed_columns(*cell_type) { - let name = format!("{:?}", cell_type); - meta.lookup_any(Box::leak(name.into_boxed_str()), |meta| { - vec![( - col.expr, - rlc::expr( - &table.table_exprs(meta), - self.lookup_challenge.clone().unwrap(), - ), - )] - }); - } - } + cm.build_lookups_from_table(meta, tables.clone(), challenge.expr()); } } - pub(crate) fn build_dynamic_lookups( + pub(crate) fn build_from_data( &mut self, meta: &mut ConstraintSystem, - lookup_names: &[C], - fixed_table: Vec<(C, &dyn LookupTable)>, + tables: &[(C, Option<&dyn LookupTable>)], ) { - let lookups = self.dynamic_lookups.clone(); - for lookup_name in lookup_names.iter() { - if let Some(lookups) = lookups.get(lookup_name) { - for lookup in lookups.iter() { - meta.lookup_any(lookup.description, |meta| { - // Fixed lookup is a direct lookup into the pre-difined fixed tables - // i.e. cond * (v1, v2, v3) => (t1, t2, t3) - // equivalent to the vanilla lookup operation of Halo2. - // Dynamic lookup applies condition to the advice values stored at - // configuration time i.e. cond * (v1, v2, v3) => - // cond * (t1, t2, t3) the dynamic lookup in a ifx! - // branch would become trivial 0 => 0 - // when the elsex! branch evaluates to true - - let table = if lookup.is_fixed { - let table_cols = fixed_table - .iter() - .find(|(name, _)| name == lookup_name) - .unwrap() - .1 - .columns(); - table_cols - .iter() - .map(|col| meta.query_any(*col, Rotation(0))) - .collect() - } else { - self.get_dynamic_table_values(*lookup_name) - }; - - let mut values: Vec<_> = lookup - .values + let lookups = self.lookups.clone(); + + for (tag, table) in tables.iter() { + if let Some(lookups) = lookups.get(tag) { + for data in lookups.iter() { + let LookupData { + description, + values, + local_condition, + regional_condition, + to_fixed, + .. + } = data.clone(); + let table = if to_fixed { + // (v1, v2, v3) => (t1, t2, t3) + // Direct lookup into the pre-difined fixed tables, vanilla lookup of + // Halo2. + table + .expect(&format!( + "Fixed table tag {:?} not provided for lookup data", + tag + )) + .columns() .iter() - .map(|value| value.expr() * lookup.condition.clone()) - .collect(); - // align the length of values and table - assert!(table.len() >= values.len()); - while values.len() < table.len() { - values.push(0.expr()); - } - - // Perform rlc if specified - // i.e. (v1*r + v2*r^2 + v3*r^3) => (t1*r + t2*r^2 + t3*r^3) - // lastly is_split had been fulfilled at insertion time - - let ret = if lookup.compress { - vec![( - rlc::expr(&values, self.lookup_challenge.clone().unwrap()), - rlc::expr(&table, self.lookup_challenge.clone().unwrap()), - )] - } else { - values - .iter() - .zip(table.iter()) - .map(|(v, t)| (v.expr(), t.expr())) - .collect() - }; - ret + .map(|col| { + query_expression(meta, |meta| + meta.query_any(*col, Rotation(0)) + ) + }) + .collect() + } else { + // (v1, v2, v3) => cond * (t1, t2, t3) + // Applies condition to the advice values stored at configuration time + self.dynamic_table_merged(*tag) + }; + // Apply the conditions added from popping regions + let mut values: Vec<_> = values + .iter() + .map(|value| value.expr() * regional_condition.clone()) + .collect(); + // align the length of values and table + assert!(table.len() >= values.len()); + while values.len() < table.len() { + values.push(0.expr()); + } + meta.lookup_any(description, |meta| { + values + .iter() + .zip(table.iter()) + .map(|(v, t)| (v.expr(), t.expr())) + .collect() }); } } else { - unreachable!("lookup not found: {:?}", lookup_name); + unreachable!("Lookup not found: {:?}", tag); } } } - pub(crate) fn get_condition(&self) -> Option> { - if self.conditions.is_empty() { - None - } else { - Some(and::expr(self.conditions.iter())) - } - } - - pub(crate) fn get_condition_expr(&self) -> Expression { - self.get_condition().unwrap_or_else(|| 1.expr()) - } - pub(crate) fn store_dynamic_table( &mut self, description: &'static str, tag: C, values: Vec>, compress: bool, - store: bool, + reduce: bool, ) { - let condition = self.get_condition_expr(); - let mut values = if compress { - vec![rlc::expr(&values, self.lookup_challenge.clone().unwrap())] - } else { - values + let values = match (compress, reduce) { + (true, true) | (true, false) => vec![self.local_compression(description, &values, tag, None, reduce)], + (false, true) => values + .iter() + .map(|v| self.local_compression(description, &[v.clone()], tag, None, reduce)) + .collect(), + (false, false) => values + .iter() + .map(|v| v.expr() * self.get_condition_expr()) + .collect(), }; - if store { - values.iter_mut().for_each(|v| { - *v = self.split_expression( - Box::leak(format!("compression value - {:?}", tag).into_boxed_str()), - v.clone(), - ) - }); - // values = vec![self.store_expression(description, values[0].expr(), tag)]; - } - let lookup = DynamicData { - description, - condition, + let data = TableData { + regional_condition: 1.expr(), + local_condition: self.get_condition_expr(), values, region_id: self.region_id, - // cannot be is_fixed - is_fixed: false, - compress: false, }; - if let Some(table_data) = self.dynamic_tables.get_mut(&tag) { - table_data.push(lookup); + if let Some(tables) = self.dynamic_tables.get_mut(&tag) { + tables.push(data); } else { - self.dynamic_tables.insert(tag, vec![lookup]); + self.dynamic_tables.insert(tag, vec![data]); } } - pub(crate) fn add_dynamic_lookup( + pub(crate) fn add_lookup( &mut self, description: &'static str, tag: C, values: Vec>, - is_fixed: bool, + to_fixed: bool, compress: bool, - store: bool, + reduce: bool, + fixed_path: bool, ) { - let condition = self.get_condition_expr(); - let mut values = if compress { - vec![rlc::expr(&values, self.lookup_challenge.clone().unwrap())] - } else { - values - }; - if store { - values.iter_mut().for_each(|v| { - *v = self.split_expression( - Box::leak(format!("compression value - {:?}", tag).into_boxed_str()), - v.clone(), - ) - }); - } - let lookup = DynamicData { - description, - condition, - values, - region_id: self.region_id, - is_fixed, - compress, + let values = match (compress, reduce) { + (true, true) | (true, false) => vec![self.local_compression(description, &values, tag, None, reduce)], + (false, true) => values + .iter() + .map(|v| self.local_compression(description, &[v.clone()], tag, None, reduce)) + .collect(), + (false, false) => values + .iter() + .map(|v| v.expr() * self.get_condition_expr()) + .collect(), }; - if let Some(lookup_data) = self.dynamic_lookups.get_mut(&tag) { - lookup_data.push(lookup); - } else { - self.dynamic_lookups.insert(tag, vec![lookup]); + // Incase of fixed_path + // Buildig lookup from typed columns -> fixed table + // no need to store the lookup + if !fixed_path { + let data = LookupData { + description, + local_condition: self.get_condition_expr(), + regional_condition: 1.expr(), + values, + region_id: self.region_id, + to_fixed, + }; + if let Some(lookups) = self.lookups.get_mut(&tag) { + lookups.push(data); + } else { + self.lookups.insert(tag, vec![data]); + } } + } - pub(crate) fn add_lookup( + pub(crate) fn local_compression( &mut self, - description: &str, + name: &str, + values: &[Expression], cell_type: C, - values: Vec>, - ) { - let condition = self.get_condition_expr(); - let values = values - .iter() - .map(|value| condition.expr() * value.expr()) - .collect_vec(); - let compressed_expr = self.split_expression( - "compression", - rlc::expr(&values, self.lookup_challenge.clone().unwrap().expr()), - ); - self.store_expression(description, compressed_expr, cell_type, None); - - let lookup = DynamicData { - description: Box::leak(description.to_string().into_boxed_str()), - condition, - values, - region_id: self.region_id, - is_fixed: true, - compress: true, - }; - if let Some(lookup_data) = self.lookups.get_mut(&cell_type) { - lookup_data.push(lookup); + target_cell: Option>, + reduce: bool, + ) -> Expression { + let local_condition = self.get_condition_expr(); + let challenge = self.lookup_challenge.clone().unwrap(); + let rlc = rlc::expr(&values, challenge) * local_condition; + let compressed_expr = if reduce { + self.split_expression("compression", rlc) } else { - self.lookups.insert(cell_type, vec![lookup]); - } - } - - pub(crate) fn get_stored_expressions(&self, region_id: usize) -> Vec> { - self.stored_expressions - .get(®ion_id) - .cloned() - .unwrap_or_default() - } - - pub(crate) fn get_dynamic_table(&self, tag: C) -> (Expression, Vec>) { - let table_values = self - .dynamic_tables - .get(&tag) - .unwrap_or_else(|| panic!("Dynamic table {:?} not found", tag)); - merge_values_unsafe( - table_values - .iter() - .map(|table| (table.condition.clone(), table.values.clone())) - .collect::>(), - ) - } - - pub(crate) fn get_dynamic_table_values(&self, tag: C) -> Vec> { - let condition_and_values = self.get_dynamic_table(tag); - condition_and_values - .1 - .iter() - .map(|value| value.expr() * condition_and_values.0.expr()) - .collect::>() + rlc + }; + self.store_expression(name, compressed_expr, cell_type, target_cell) } - pub(crate) fn generate_lookup_table_checks(&mut self, tag: C) { - let table_values = self - .dynamic_tables + pub(crate) fn dynamic_table_merged(&mut self, tag: C) -> Vec> { + let data = self.dynamic_tables .get(&tag) .unwrap_or_else(|| panic!("Dynamic table {:?} not found", tag)) .clone(); - let selectors = table_values - .into_iter() - .map(|value| { - let sel = value.condition.expr(); - self.require_boolean("lookup table condition needs to be boolean", sel.clone()); - sel - }) - .collect::>(); - let selector = sum::expr(&selectors); - self.require_boolean( - "lookup table conditions sum needs to be boolean", - selector.expr(), - ); + let table_merger = TabelMerger{ + data, + _phantom: PhantomData, + }; + table_merger.merge_and_select(self) } pub(crate) fn store_expression( @@ -585,6 +600,13 @@ impl ConstraintBuilder { } } + pub(crate) fn get_stored_expressions(&self, region_id: usize) -> Vec> { + self.stored_expressions + .get(®ion_id) + .cloned() + .unwrap_or_default() + } + pub(crate) fn find_stored_expression( &self, expr: &Expression, @@ -653,48 +675,16 @@ impl ConstraintBuilder { } } -pub(crate) fn merge_lookups( - cb: &mut ConstraintBuilder, - lookups: Vec>, -) -> (Expression, Vec>) { - merge_values( - cb, - lookups - .iter() - .map(|lookup| (lookup.condition.clone(), lookup.values.clone())) - .collect::>(), - ) +#[derive(PartialEq)] +pub enum LookupOption { + ToFixed, + Compress, + Reduce, } -pub(crate) fn merge_values( - cb: &mut ConstraintBuilder, - values: Vec<(Expression, Vec>)>, -) -> (Expression, Vec>) { - let selector = sum::expr(values.iter().map(|(condition, _)| condition.expr())); - crate::circuit!([meta, cb], { - require!(selector => bool); - }); - merge_values_unsafe(values) -} - -pub(crate) fn merge_values_unsafe( - values: Vec<(Expression, Vec>)>, -) -> (Expression, Vec>) { - if values.is_empty() { - return (0.expr(), Vec::new()); - } - let selector = sum::expr(values.iter().map(|(condition, _)| condition.expr())); - // Merge - let max_length = values.iter().map(|(_, values)| values.len()).max().unwrap(); - let mut merged_values = vec![0.expr(); max_length]; - let default_value = 0.expr(); - for (idx, value) in merged_values.iter_mut().enumerate() { - *value = sum::expr(values.iter().map(|(condition, values)| { - condition.expr() * values.get(idx).unwrap_or(&default_value).expr() - })); - } - (selector, merged_values) -} +pub const TO_FIX: LookupOption = LookupOption::ToFixed; +pub const COMPRESS: LookupOption = LookupOption::Compress; +pub const REDUCE: LookupOption = LookupOption::Reduce; /// General trait to convert to a vec pub trait ToVec { @@ -1107,33 +1097,47 @@ macro_rules! _require { } }}; - // Lookup using a tuple - ($cb:expr, ($($v:expr),+) => @$tag:expr) => {{ + // ----------------------------------------------------- + // Lookups build from table + // only reduce flag is allowed + + // Lookup using a array + ($cb:expr, $values:expr =>> @$tag:expr, $($reduce:expr)?) => {{ + use $crate::circuit_tools::constraint_builder::REDUCE; let description = concat_with_preamble!( - "(", - $( - stringify!($v), - ", ", - )* - ") => @", + stringify!($values), + " =>> @", stringify!($tag), ); + // let values = _to_vec!($values); $cb.add_lookup( description, $tag, - vec![$($v.expr(),)*], + $values, + bool::default(), + bool::default(), + vec![$($reduce)?].contains(&REDUCE), + true ); }}; - ($cb:expr, $descr:expr, ($($v:expr),+) => @$tag:expr) => {{ + // Lookup using a tuple + ($cb:expr, $descr:expr, $values:expr =>> @$tag:expr, $($reduce:expr)?) => {{ + use $crate::circuit_tools::constraint_builder::REDUCE; $cb.add_lookup( - Box::leak($descr.into_boxed_str()), + Box::leak($descr.to_string().into_boxed_str()), $tag, - vec![$($v.expr(),)*], + $values, + bool::default(), + bool::default(), + vec![$($reduce)?].contains(&REDUCE), + true ); }}; - // Lookup using an array - ($cb:expr, $values:expr => @$tag:expr) => {{ + // ----------------------------------------------------- + // Lookup using a tuple + ($cb:expr, $values:expr => @$tag:expr, $options:expr) => {{ + use $crate::circuit_tools::constraint_builder::{REDUCE, COMPRESS, TO_FIX}; let description = concat_with_preamble!( stringify!($values), " => @", @@ -1142,136 +1146,31 @@ macro_rules! _require { $cb.add_lookup( description, $tag, - $values.clone(), + $values, + $options.contains(&TO_FIX), + $options.contains(&COMPRESS), + $options.contains(&REDUCE), + false ); }}; - ($cb:expr, $descr:expr, $values:expr => @$tag:expr) => {{ + ($cb:expr, $descr:expr, $values:expr => @$tag:expr, $options:expr) => {{ + use $crate::circuit_tools::constraint_builder::{REDUCE, COMPRESS, TO_FIX}; $cb.add_lookup( - Box::leak($descr.to_string().into_boxed_str()), + Box::leak($descr.into_boxed_str()), $tag, - $values.clone(), + $values, + $options.contains(&TO_FIX), + $options.contains(&COMPRESS), + $options.contains(&REDUCE), + false ); }}; - // Lookup using a tuple - ($cb:expr, ($($v:expr),+) => @$tag:expr, $is_fixed:expr, $compress:expr, $is_split:expr) => {{ - let description = concat_with_preamble!( - "(", - $( - stringify!($v), - ", ", - )* - ") => @", - stringify!($tag), - ); - $cb.add_dynamic_lookup( - description, - $tag, - vec![$($v.expr(),)*], - $is_fixed, - $compress, - $is_split, - ); - }}; - ($cb:expr, $descr:expr, ($($v:expr),+) => @$tag:expr, $is_fixed:expr, $compress:expr, $is_split:expr) => {{ - $cb.add_dynamic_lookup( - Box::leak($descr.into_boxed_str()), - $tag, - vec![$($v.expr(),)*], - $is_fixed, - $compress, - $is_split, - ); - }}; - - - // Lookup using an array - ($cb:expr, $values:expr => @$tag:expr, $is_fixed:expr, $compress:expr, $is_split:expr) => {{ - let description = concat_with_preamble!( - stringify!($values), - " => @", - stringify!($tag), - ); - $cb.add_dynamic_lookup( - description, - $tag, - $values.clone(), - $is_fixed, - $compress, - $is_split, - ); - }}; - ($cb:expr, $descr:expr, $values:expr => @$tag:expr, $is_fixed:expr, $compress:expr, $is_split:expr) => {{ - $cb.add_dynamic_lookup( - Box::leak($descr.into_boxed_str()), - $tag, - $values.clone(), - $is_fixed, - $compress, - $is_split, - ); - }}; + // ----------------------------------------------------- - // Put values in a lookup table using a tuple - ($cb:expr, @$tag:expr => ($($v:expr),+)) => {{ - let description = concat_with_preamble!( - "@", - stringify!($tag), - " => (", - $( - stringify!($v), - ", ", - )* - ")", - ); - $cb.store_dynamic_table( - description, - $tag, - vec![$($v.expr(),)*], - false, - false, - ); - }}; - // Put values in a lookup table using an array - ($cb:expr, @$tag:expr => $values:expr) => {{ - let description = concat_with_preamble!( - "@", - stringify!($tag), - " => (", - stringify!($values), - ")", - ); - $cb.store_dynamic_table( - description, - $tag, - $values, - false, - false, - ); - }}; // Put values in a lookup table using a tuple - ($cb:expr, @$tag:expr => ($($v:expr),+), $compress:expr, $is_split:expr) => {{ - let description = concat_with_preamble!( - "@", - stringify!($tag), - " => (", - $( - stringify!($v), - ", ", - )* - ")", - ); - $cb.store_dynamic_table( - description, - $tag, - vec![$($v.expr(),)*], - $compress, - $is_split, - ); - }}; - // Put values in a lookup table using an array - ($cb:expr, @$tag:expr => $values:expr, $compress:expr, $is_split:expr) => {{ + ($cb:expr, @$tag:expr, $options:expr => $values:expr) => {{ let description = concat_with_preamble!( "@", stringify!($tag), @@ -1283,8 +1182,8 @@ macro_rules! _require { description, $tag, $values, - $compress, - $is_split, + $options.contains(&COMPRESS), + $options.contains(&REDUCE), ); }}; } @@ -1296,7 +1195,7 @@ macro_rules! _require { /// unreachablex! macro). #[macro_export] macro_rules! _matchx { - ($cb:expr, $($condition:expr => $when:expr),* $(, _ => $catch_all:expr)? $(,)?) => {{ + ($cb:expr, ($($condition:expr => $when:expr),* $(, _ => $catch_all:expr)? $(,)?)) => {{ let mut conditions = Vec::new(); let mut cases = Vec::new(); $( @@ -1332,11 +1231,20 @@ macro_rules! _matchx { }}; } +#[macro_export] +macro_rules! _to_and { + (($($condition:expr),*)) => { + and::expr([$($condition.expr()),*]) + }; + ($condition:expr) => { + $condition.expr() + } +} /// ifx #[macro_export] macro_rules! _ifx { - ($cb:expr, $($condition:expr),* => $when_true:block $(elsex $when_false:block)?) => {{ - let condition = and::expr([$($condition.expr()),*]); + ($cb:expr,$condition:tt => $when_true:block $(elsex $when_false:block)?) => {{ + let condition = _to_and!($condition); $cb.push_condition(condition.expr()); let ret_true = $when_true; @@ -1432,18 +1340,33 @@ macro_rules! assignf { }}; } +#[macro_export] +macro_rules! _to_values_vec { + (($($tts:expr), *)) => { + vec![$($tts.expr()), *] + }; +} + +#[macro_export] +macro_rules! _to_options_vec { + (($($tts:expr), *)) => { + vec![$($tts), *] + }; +} /// Circuit builder macros /// Nested macro's can't do repetition /// so we expose a couple of permutations here manually. #[macro_export] macro_rules! circuit { ([$meta:expr, $cb:expr], $content:block) => {{ - #[allow(unused_imports)] - use $crate::{concat_with_preamble, _require, _matchx, _ifx, _unreachablex}; #[allow(unused_imports)] use gadgets::util::{and, not, or, sum, Expr}; #[allow(unused_imports)] - use $crate::circuit_tools::constraint_builder::{ExprVec, ExprResult}; + use $crate::circuit_tools::constraint_builder::{ExprResult, ExprVec}; + #[allow(unused_imports)] + use $crate::{ + _ifx, _matchx, _require, _to_and, _to_values_vec, _to_options_vec, _unreachablex, concat_with_preamble, + }; #[allow(unused_macros)] macro_rules! f { @@ -1472,6 +1395,13 @@ macro_rules! circuit { }}; } + #[allow(unused_macros)] + macro_rules! q { + ($column:expr) => {{ + $meta.query_selector($column.clone()) + }}; + } + #[allow(unused_macros)] macro_rules! x { ($column:expr, $rot:expr) => {{ @@ -1498,133 +1428,95 @@ macro_rules! circuit { #[allow(unused_macros)] macro_rules! require { - ($lhs:expr => bool) => {{ - _require!($cb, $lhs => bool); - }}; + ($lhs:expr => bool) => {{ + _require!($cb, $lhs => bool); + }}; + + ($lhs:expr => $rhs:expr) => {{ + _require!($cb, $lhs => $rhs); + }}; + + ($name:expr, $lhs:expr => $rhs:expr) => {{ + _require!($cb, $name, $lhs => $rhs); + }}; + + // Lookups build from table + // only reduce flag is allowed + ($values:tt =>> @$tag:expr, $reduce:expr) => {{ + let values = _to_values_vec!($values); + _require!($cb, values =>> @$tag, $reduce); + }}; + ($values:tt =>> @$tag:expr) => {{ + let values = _to_values_vec!($values); + _require!($cb, values =>> @$tag); + }}; + ($descr:expr, $values:tt =>> @$tag:expr, $reduce:expr) => {{ + let values = _to_values_vec!($values); + _require!($cb, $descr, values =>> @$tag, $reduce); + }}; + ($descr:expr, $values:tt =>> @$tag:expr) => {{ + let values = _to_values_vec!($values); + _require!($cb, $descr, values =>> @$tag); + }}; + + + ($values:tt => @$tag:expr, $options:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, values => @$tag, options); + }}; + ($values:tt => @$tag:expr) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, values => @$tag, options); + }}; + ($descr:expr, $values:tt => @$tag:expr, $options:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, $descr, values => @$tag, options); + }}; + ($descr:expr, $values:tt => @$tag:expr) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, $descr, values => @$tag, options); + }}; + + + (@$tag:expr, $options:tt => $values:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, @$tag, options => values); + }}; + (@$tag:expr => $values:tt) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, @$tag, options => values); + }}; - ($lhs:expr => $rhs:expr) => {{ - _require!($cb, $lhs => $rhs); - }}; - ($name:expr, $lhs:expr => $rhs:expr) => {{ - _require!($cb, $name, $lhs => $rhs); - }}; - - (($a:expr) => @$tag:expr) => {{ - _require!($cb, ($a) => @$tag); - }}; - - (($a:expr, $b:expr) => @$tag:expr) => {{ - _require!($cb, ($a, $b) => @$tag); - }}; - - (($a:expr, $b:expr, $c:expr) => @$tag:expr) => {{ - _require!($cb, ($a, $b, $c) => @$tag); - }}; - - (($a:expr, $b:expr, $c:expr, $d:expr) => @$tag:expr) => {{ - _require!($cb, ($a, $b, $c, $d) => @$tag); - }}; - - ($values:expr => @$tag:expr) => {{ - _require!($cb, $values => @$tag); - }}; - - ($descr:expr, $values:expr => @$tag:expr) => {{ - _require!($cb, $descr, $values => @$tag); - }}; - - (($a:expr) => @$tag:expr, $is_fixed:expr, $compress:expr, $is_split:expr) => {{ - _require!($cb, ($a) => @$tag, $is_fixed, $compress, $is_split); - }}; - - (($a:expr, $b:expr) => @$tag:expr, $is_fixed:expr, $compress:expr, $is_split:expr) => {{ - _require!($cb, ($a, $b) => @$tag, $is_fixed, $compress, $is_split); - }}; - - (($a:expr, $b:expr, $c:expr) => @$tag:expr, $is_fixed:expr, $compress:expr, $is_split:expr) => {{ - _require!($cb, ($a, $b, $c) => @$tag, $is_fixed, $compress, $is_split); - }}; - - (($a:expr, $b:expr, $c:expr, $d:expr) => @$tag:expr, $is_fixed:expr, $compress:expr, $is_split:expr) => {{ - _require!($cb, ($a, $b, $c, $d) => @$tag, $is_fixed, $compress, $is_split); - }}; - - ($values:expr => @$tag:expr, $is_fixed:expr, $compress:expr, $is_split:expr) => {{ - _require!($cb, $values => @$tag, $is_fixed, $compress, $is_split); - }}; - - ($descr:expr, $values:expr => @$tag:expr, $is_fixed:expr, $compress:expr, $is_split:expr) => {{ - _require!($cb, $descr, $values => @$tag, $is_fixed, $compress, $is_split); - }}; - - (@$tag:expr => ($a:expr, $b:expr, $c:expr)) => {{ - _require!($cb, @$tag => ($a, $b, $c)); - }}; - - (@$tag:expr => $values:expr) => {{ - _require!($cb, @$tag => $values); - }}; - } + } #[allow(unused_macros)] macro_rules! ifx { - ($condition:expr => $when_true:block elsex $when_false:block) => {{ - _ifx!($cb, $condition => $when_true elsex $when_false) - }}; - ($condition_a:expr, $condition_b:expr => $when_true:block elsex $when_false:block) => {{ - _ifx!($cb, $condition_a, $condition_b => $when_true elsex $when_false) - }}; - ($condition_a:expr, $condition_b:expr, $condition_c:expr => $when_true:block elsex $when_false:block) => {{ - _ifx!($cb, $condition_a, $condition_b, $condition_c => $when_true elsex $when_false) - }}; - ($condition_a:expr, $condition_b:expr, $condition_c:expr, $condition_d:expr => $when_true:block elsex $when_false:block) => {{ - _ifx!($cb, $condition_a, $condition_b, $condition_c, $condition_d => $when_true elsex $when_false) - }}; - - ($condition:expr => $when_true:block) => {{ - _ifx!($cb, $condition => $when_true) - }}; - ($condition_a:expr, $condition_b:expr => $when_true:block) => {{ - _ifx!($cb, $condition_a, $condition_b => $when_true) - }}; - ($condition_a:expr, $condition_b:expr, $condition_c:expr => $when_true:block) => {{ - _ifx!($cb, $condition_a, $condition_b, $condition_c => $when_true) - }}; - ($condition_a:expr, $condition_b:expr, $condition_c:expr, $condition_d:expr => $when_true:block) => {{ - _ifx!($cb, $condition_a, $condition_b, $condition_c, $condition_d => $when_true) - }}; - ($condition_a:expr, $condition_b:expr, $condition_c:expr, $condition_d:expr, $condition_e:expr => $when_true:block) => {{ - _ifx!($cb, $condition_a, $condition_b, $condition_c, $condition_d, $condition_e => $when_true) - }}; - } + ($condition:tt => $when_true:block elsex $when_false:block) => {{ + _ifx!($cb, ($condition) => $when_true elsex $when_false) + }}; + ($condition:expr => $when_true:block elsex $when_false:block) => {{ + _ifx!($cb, $condition => $when_true elsex $when_false) + }}; + + ($condition:tt => $when_true:block) => {{ + _ifx!($cb, $condition => $when_true) + }}; + ($condition:expr => $when_true:block) => {{ + _ifx!($cb, $condition => $when_true) + }}; + } #[allow(unused_macros)] macro_rules! matchx { - ($condition_a:expr => $when_a:expr,) => {{ - _matchx!($cb, $condition_a => $when_a) - }}; - ($condition_a:expr => $when_a:expr, $condition_b:expr => $when_b:expr,) => {{ - _matchx!($cb, $condition_a => $when_a, $condition_b => $when_b) - }}; - ($condition_a:expr => $when_a:expr, $condition_b:expr => $when_b:expr, $condition_c:expr => $when_c:expr,) => {{ - _matchx!($cb, $condition_a => $when_a, $condition_b => $when_b, $condition_c => $when_c) - }}; - ($condition_a:expr => $when_a:expr, $condition_b:expr => $when_b:expr, $condition_c:expr => $when_c:expr, $condition_d:expr => $when_d:expr,) => {{ - _matchx!($cb, $condition_a => $when_a, $condition_b => $when_b, $condition_c => $when_c, $condition_d => $when_d,) - }}; - - ($condition_a:expr => $when_a:expr, _ => $catch_all:expr,) => {{ - _matchx!($cb, $condition_a => $when_a, _ => $catch_all,) - }}; - ($condition_a:expr => $when_a:expr, $condition_b:expr => $when_b:expr, _ => $catch_all:expr,) => {{ - _matchx!($cb, $condition_a => $when_a, $condition_b => $when_b, _ => $catch_all,) - }}; - ($condition_a:expr => $when_a:expr, $condition_b:expr => $when_b:expr, $condition_c:expr => $when_c:expr, _ => $catch_all:expr,) => {{ - _matchx!($cb, $condition_a => $when_a, $condition_b => $when_b, $condition_c => $when_c, _ => $catch_all,) - }}; - ($condition_a:expr => $when_a:expr, $condition_b:expr => $when_b:expr, $condition_c:expr => $when_c:expr, $condition_d:expr => $when_d:expr, _ => $catch_all:expr,) => {{ - _matchx!($cb, $condition_a => $when_a, $condition_b => $when_b, $condition_c => $when_c, $condition_d => $when_d, _ => $catch_all,) + ($condition_to_when:tt) => {{ + _matchx!($cb, $condition_to_when) }}; } diff --git a/zkevm-circuits/src/circuit_tools/memory.rs b/zkevm-circuits/src/circuit_tools/memory.rs index a8f3ec78df..6b919ad96f 100644 --- a/zkevm-circuits/src/circuit_tools/memory.rs +++ b/zkevm-circuits/src/circuit_tools/memory.rs @@ -26,7 +26,7 @@ use super::{ #[derive(Clone, Debug, Default)] pub(crate) struct Memory { height: usize, - banks: HashMap>, + banks: Vec>, rw_records: HashMap, Column)>, } @@ -48,9 +48,9 @@ impl Memory { _ => unreachable!(), }; rw_records.insert(tag.clone(), (reads, writes)); - (tag.clone(), MemoryBank::new(meta, tag.clone(), height, offset, key, reads, writes)) + MemoryBank::new(meta, tag.clone(), height, offset, key, reads, writes) }) - .collect::>>(); + .collect::>>(); Self { banks, height, @@ -59,11 +59,21 @@ impl Memory { } pub(crate) fn get_bank(&self, tag: C) -> &MemoryBank { - self.banks.get(&tag).unwrap() + for bank in self.banks.iter() { + if bank.tag() == tag { + return bank; + } + } + unreachable!() } pub(crate) fn get_mut_bank(&mut self, tag: C) -> &mut MemoryBank { - self.banks.get_mut(&tag).unwrap() + for bank in self.banks.iter_mut() { + if bank.tag() == tag { + return bank; + } + } + unreachable!() } pub(crate) fn get_records(&self) -> Vec<(Column, Column)> { @@ -75,7 +85,7 @@ impl Memory { cb: &mut ConstraintBuilder, is_first_row: Expression, ) { - for (_, bank) in self.banks.iter() { + for bank in self.banks.iter() { bank.build_constraints(cb, is_first_row.expr()); } } @@ -93,7 +103,7 @@ impl Memory { } pub(crate) fn clear_witness_data(&mut self) { - for (_, bank) in self.banks.iter_mut() { + for bank in self.banks.iter_mut() { bank.clear_witness_data(); } } @@ -103,14 +113,14 @@ impl Memory { region: &mut CachedRegion<'_, '_, F>, height: usize, ) -> Result<(), Error> { - for (_, bank) in self.banks.iter() { + for bank in self.banks.iter() { bank.assign(region, height)?; } Ok(()) } pub(crate) fn tags(&self) -> Vec { - self.banks.iter().map(|(_, bank)| bank.tag()).collect() + self.banks.iter().map(|bank| bank.tag()).collect() } } @@ -118,13 +128,23 @@ impl Index for Memory { type Output = MemoryBank; fn index(&self, tag: C) -> &Self::Output { - &self.banks[&tag] + for bank in self.banks.iter() { + if bank.tag() == tag { + return bank; + } + } + unreachable!() } } impl IndexMut for Memory { fn index_mut(&mut self, tag: C) -> &mut Self::Output { - self.banks.get_mut(&tag).unwrap() + for bank in self.banks.iter_mut() { + if bank.tag() == tag { + return bank; + } + } + unreachable!() } } diff --git a/zkevm-circuits/src/circuit_tools/test/database.rs b/zkevm-circuits/src/circuit_tools/test/database.rs deleted file mode 100644 index 4e813aefee..0000000000 --- a/zkevm-circuits/src/circuit_tools/test/database.rs +++ /dev/null @@ -1,152 +0,0 @@ - -use eth_types::Field; -use gadgets::util::Scalar; -use halo2_proofs::{ - plonk::{Circuit, ConstraintSystem, Advice, Fixed, Column, FirstPhase, Challenge, Error, SecondPhase, Selector, Expression, VirtualCells}, - circuit::{SimpleFloorPlanner, Layouter, layouter, Value}, - poly::Rotation, -}; -use sha3::digest::typenum::IsEqual; -use crate::circuit_tools::{memory::{Memory}, constraint_builder::ConstraintBuilder, cell_manager::{CellManager, CellType, Cell}, gadgets::IsEqualGadget}; - -const MAX_DEG: usize = 5; - -pub struct DBConfig { - pub(crate) q_first: Column, - pub(crate) operation: Operation, - pub(crate) id: Column, - pub(crate) balance: Column, - pub(crate) db_read: Option> -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum DBCell { - PhaseOne, - PhaseTwo, -} - -impl CellType for DBCell { - fn byte_type() -> Option { - None - } - - fn storage_for_phase(phase: u8) -> Self { - match phase { - 1 => DBCell::PhaseOne, - 2 => DBCell::PhaseTwo, - _ => unreachable!() - } - } -} - -impl Default for DBCell { - fn default() -> Self { - DBCell::PhaseOne - } -} - -impl DBConfig { - pub fn new( - meta: &mut ConstraintSystem, - ) -> Self { - let q_first = meta.fixed_column(); - let mut operation = Operation::default(); - let op_col = meta.advice_column(); - let amt_col = meta.advice_column(); - let id = meta.advice_column(); - let balance = meta.advice_column(); - let mut db_read = DBRead::default(); - - let mut memory = Memory::new( - meta, - vec![(DBCell::PhaseOne, 1)], - 0, - 5 - ); - let cm = CellManager::new( - meta, - vec![ - (DBCell::PhaseOne, 3, 1, false), - (DBCell::PhaseTwo, 3, 2, false), - ], - 0, - 5, - ); - let mut cb = ConstraintBuilder::new(MAX_DEG, Some(cm), None); - meta.create_gate("DB", |meta| { - circuit!([meta, cb], { - operation.init(meta, &mut cb, op_col, amt_col); - let bd = memory.get_mut_bank(DBCell::PhaseOne); - matchx!( - operation.is_deposit() => { - require!(a!(balance) => a!(balance, -1) + a!(amt_col)); - bd.store(&mut cb, &[a!(id), a!(balance)]); - }, - operation.is_withdraw() => { - require!(a!(balance) => a!(balance, -1) - a!(amt_col)); - bd.store(&mut cb, &[a!(id), a!(balance)]); - }, - _ => { - let id = cb.query_default(); - let balance = cb.query_default(); - bd.load( - "Check Balance", - &mut cb, - a!(operation.check_offset()), &[id.expr(), balance.expr()] - ); - db_read.id = Some(id); - db_read.balance = Some(balance); - }, - ); - memory.build_constraints(&mut cb, f!(q_first)); - cb.build_constraints() - }) - }); - memory.build_lookups(meta); - DBConfig { q_first, operation, id, balance, db_read: Some(db_read)} - } - - -} - -#[derive(Default)] -pub struct Operation { - pub(crate) operation: Option>, - pub(crate) amount: Option>, - is_deposit: IsEqualGadget, - is_withdraw: IsEqualGadget, -} - -impl Operation { - fn init( - &mut self, - meta: &mut VirtualCells<'_, F>, - cb: &mut ConstraintBuilder, - op_col: Column, - amt_col: Column, - ) { - circuit!([meta, cb], { - self.operation = Some(op_col); - self.amount = Some(amt_col); - // 0 => deposit, 1 => withdraw, - // others => check balance with amount representing offset - self.is_deposit = IsEqualGadget::construct(cb, a!(op_col), 0.expr()); - self.is_withdraw = IsEqualGadget::construct(cb, a!(op_col), 1.expr()); - }); - } - fn is_deposit(&self) -> Expression { - self.is_deposit.expr() - } - fn is_withdraw(&self) -> Expression { - self.is_withdraw.expr() - } - fn check_offset(&self) -> Column { - self.amount.unwrap().clone() - } -} - -#[derive(Default, Clone)] -pub struct DBRead{ - id: Option>, - balance: Option>, -} \ No newline at end of file diff --git a/zkevm-circuits/src/circuit_tools/test/mod.rs b/zkevm-circuits/src/circuit_tools/test/mod.rs deleted file mode 100644 index a0284193e7..0000000000 --- a/zkevm-circuits/src/circuit_tools/test/mod.rs +++ /dev/null @@ -1,308 +0,0 @@ - -use std::vec; - - -use crate::circuit_tools::cell_manager::{Cell, CellManager, CellType}; -use crate::circuit_tools::constraint_builder:: ConstraintBuilder; -use crate::circuit_tools::cached_region::CachedRegion; -use crate::{table::LookupTable, util::Expr}; - - -use eth_types::Field; -use gadgets::util::Scalar; -use halo2_proofs::circuit::{SimpleFloorPlanner, Layouter}; - - -use halo2_proofs::dev::MockProver; -use halo2_proofs::halo2curves::bn256::Fr; -use halo2_proofs::plonk::{Any, Circuit, FirstPhase, Challenge, Fixed, Selector}; -use halo2_proofs::{ - circuit::{Value}, - plonk::{ConstraintSystem, Advice, Column, Error}, - poly::Rotation, -}; - -mod query_and_branch; -mod shuffle; -mod simple_rlp; -mod database; - -/// To Test: -/// 1. Constrain advices with cells -/// 2. Lookup (advices <--> advices) with cells (RAM) -/// 3. Lookup (advices <--> fixed) with cells (ROM) -/// - -const MAX_DEG: usize = 5; -const CM_HEIGHT: usize = 10; -const COPY_COL_NUM: usize = 1; -const REGION_HEIGHT: usize = 10; - -#[derive(Clone)] -pub struct TestTable { - pub a: Column, - pub b: Column, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum TestCellType { - LookupTestTable, - PhaseOne, - PhaseTwo -} - -impl Default for TestCellType { - fn default() -> Self { - TestCellType::PhaseOne - } -} - -impl CellType for TestCellType { - fn byte_type() -> Option { - Some(TestCellType::PhaseOne) - } - - fn storage_for_phase(phase: u8) -> Self { - match phase { - 0 => TestCellType::PhaseOne, - 1 => TestCellType::PhaseTwo, - _ => unreachable!(), - } - } -} - -impl LookupTable for TestTable { - - fn columns(&self) -> Vec> { - vec![self.a.into(), self.b.into()] - } - fn annotations(&self) -> Vec { - vec![String::from("a"), String::from("b")] - } -} - -#[derive(Clone)] -pub struct TestConfig { - pub(crate) sel: Selector, - pub(crate) q_enable: Column, - pub(crate) q_count: Column, - pub(crate) cell_gadget: CellGadget, - pub(crate) table: TestTable, -} - - -impl TestConfig { - pub fn new( - meta: &mut ConstraintSystem, - table: TestTable, - randomness: Challenge - ) -> Self { - - // Get columns - let sel = meta.selector(); - let q_enable = meta.fixed_column(); - let q_count = meta.advice_column(); - - // Init cell manager and constraint builder - let cm = CellManager::new( - meta, - vec![ - (TestCellType::PhaseOne, 3, 1, false), - (TestCellType::PhaseTwo, 3, 2, false), - (TestCellType::LookupTestTable, 3, 1, false), - ], - 0, - 5, - ); - let mut cb = ConstraintBuilder::new(MAX_DEG, Some(cm), None); - - let mut cell_gadget = CellGadget::default(); - meta.create_gate("Test Gate", |meta| { - // Config row counts - circuit!([meta, cb], { - cb.lookup_challenge = Some(meta.query_challenge(randomness)); - // All configuration of inner gadgets must be wrapped in ifx! - // it pushes a condition into cb, which is gonna be multiplied with the upcoming constraints. - // then if you turn off q_enable, your inner gadgets will be disabled. - // otherwise you'll see missing selector error. - ifx!(f!(q_enable) => { - require!(a!(q_count, 1) => a!(q_count) + 1.expr()); - // Init Gadgets - cell_gadget = CellGadget::configure(&mut cb, ); - }) - }); - cb.build_constraints() - }); - - Self { - sel, - q_enable, - q_count, - cell_gadget, - table, - } - } - - pub fn assign( - &self, - layouter: &mut impl Layouter, - randomness: Value, - ) -> Result<(), Error> { - layouter.assign_region( - || "cell gadget", - |mut region| { - - self.sel.enable(&mut region, 0)?; - - for offset in 0..20 { - assignf!(region, (self.q_enable, offset) => 1.scalar())?; - assign!(region, (self.q_count, offset) => offset.scalar())?; - } - assign!(region, (self.q_count, 20) => 20.scalar())?; - - // Value of challenge is obtained from layouter. - // We query it once during synthesis and - // make it accessable across Config through CachedRegion. - let mut lookup_challenge = F::ZERO; - randomness.map(|r| lookup_challenge = r); - let mut cached_region = CachedRegion::new(&mut region, lookup_challenge, 12345.scalar()); - self.cell_gadget.assign(&mut cached_region, 0) - }, - ) - } - - fn load_fixed_table( - &self, - layouter: &mut impl Layouter, - ) -> Result<(), Error> { - // We can use different region for lookup tables, - // since they are not related to cell gadgets. - layouter.assign_region( - || "fixed table", - |mut region| { - for offset in 0..10 { - // Don't need CachedRegion here since we don't cache intermediate values. - assignf!(region, (self.table.a, offset) => offset.scalar())?; - assignf!(region, (self.table.b, offset) => (offset + 1).scalar())?; - } - Ok(()) - }, - ) - } - - -} - -#[derive(Clone, Debug, Default)] -pub struct CellGadget { - // (a, b) in lookup - // a, randomness * b == c - // where randomness is phase1 challenge - // a == d - a: Cell, - b: Cell, - c: Cell, - d: Cell, -} - - -impl CellGadget { - pub fn configure(cb: &mut ConstraintBuilder) -> Self { - let a = cb.query_default(); - let b = cb.query_default(); - // c depends on Phase1 Challenge randomness - let c = cb.query_one(TestCellType::PhaseTwo); - let d = cb.query_default(); - circuit!([meta, cb], { - //require!((a, b) => @format!("test_lookup")); - require!(c => a.expr() + b.expr() * cb.lookup_challenge.clone().unwrap()); - require!(a => d.expr()); - }); - - CellGadget { a, b, c, d } - } - - pub fn assign( - &self, - region: &mut CachedRegion<'_, '_, F>, - offset: usize, - ) -> Result<(), Error>{ - // Assign values to cells - self.a.assign(region, offset, 2u64.scalar())?; - self.b.assign(region, offset, 3u64.scalar())?; - self.c.assign( - region, - offset, - F::from(2u64) + F::from(3u64) * region.lookup_challenge() - )?; - self.d.assign(region, offset, 2u64.scalar())?; - Ok(()) - } -} - -#[derive(Clone, Debug, Default)] -struct TestCircuit { - // Don't need anything in this struct, - // since we don't have precomputed data from outside - // and Config & Challenges are passed to synthesize. - _phantom: F, -} - -impl Circuit for TestCircuit { - type Config = (TestConfig, Challenge); - type FloorPlanner = SimpleFloorPlanner; - type Params = (); - - fn without_witnesses(&self) -> Self { - unimplemented!() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - - // Build the table and challenge outside - // since zkevm use them accross circuits - let table = TestTable { - a: meta.fixed_column(), - b: meta.fixed_column(), - }; - let _dummy_phase1 = meta.advice_column_in(FirstPhase); - let randomness = meta.challenge_usable_after(FirstPhase); - - let config = TestConfig::new(meta, table, randomness); - (config, randomness) - } - - fn synthesize( - &self, - (config, randomness): Self::Config, - mut layouter: impl halo2_proofs::circuit::Layouter - ) -> Result<(), Error> { - let randomness = layouter.get_challenge(randomness); - config.load_fixed_table(&mut layouter)?; - config.assign(&mut layouter, randomness) - } -} - -// #[cfg(feature = "dev-graph")] -#[test] -fn test() { - let circuit = TestCircuit::::default(); - let prover = MockProver::::run(6, &circuit, vec![]).unwrap(); - prover.assert_satisfied_par(); - - // use plotters::prelude::*; - // let root = BitMapBackend::new("test.png", (1024, 768)).into_drawing_area(); - // root.fill(&WHITE).unwrap(); - // let root = root - // .titled("Test Layout", ("sans-serif", 60)) - // .unwrap(); - - // halo2_proofs::dev::CircuitLayout::default() - // // Render the circuit onto your area! - // // The first argument is the size parameter for the circuit. - // .show_cell_annotations(true) - // .region_by_name("cell gadget") - // .render(6, &circuit.clone(), &root) - // .unwrap(); - -} \ No newline at end of file diff --git a/zkevm-circuits/src/circuit_tools/test/query_and_branch.rs b/zkevm-circuits/src/circuit_tools/test/query_and_branch.rs deleted file mode 100644 index ca0ce965ac..0000000000 --- a/zkevm-circuits/src/circuit_tools/test/query_and_branch.rs +++ /dev/null @@ -1,154 +0,0 @@ -use std::{marker::PhantomData}; - -use eth_types::Field; -use gadgets::util::Scalar; -use halo2_proofs::{ - plonk::{Circuit, ConstraintSystem, Advice, Fixed, Column, FirstPhase, Challenge, Error, SecondPhase}, - circuit::{SimpleFloorPlanner, Layouter, layouter, Value}, - poly::Rotation, -}; - -use crate::circuit_tools::{constraint_builder:: ConstraintBuilder, cell_manager::CellType}; - -#[derive(Clone)] -pub struct TestConfig { - pub(crate) q_enable: Column, - pub(crate) a: Column, - pub(crate) b: Column, - pub(crate) c: Column, - pub(crate) res: Column, -} - -#[derive(Clone, Copy, Debug, num_enum::Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(u8)] -pub enum TestCellType { - #[num_enum(default)] - Storage, -} -impl CellType for TestCellType{ - fn byte_type() -> Option { - unimplemented!() - } - fn storage_for_phase(phase: u8) -> Self { - unimplemented!() - } -} - - -impl TestConfig { - pub fn new(meta: &mut ConstraintSystem, r: Challenge) -> Self { - let q_enable = meta.fixed_column(); - let a = meta.advice_column(); - let b = meta.advice_column_in(SecondPhase); - let c = meta.fixed_column(); - let res = meta.advice_column(); - - - let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, None, None); - - meta.create_gate("Test", |meta| { - circuit!([meta, cb], { - // Fixed column with ifx! equivalents to selector - // BUT Advice does not - ifx!(f!(q_enable) => { - ifx!(a!(a) => { - require!(a!(res) => a!(b) + f!(c)); - } elsex { - require!(a!(res) => a!(b) + c!(r)); - - }); - // Matchx! adds the same set of constraints as ifx! - matchx!( - a!(a) => { - require!(a!(res) => a!(b) + f!(c)); - }, - not!(a!(a)) => { - require!(a!(res) => a!(b) + c!(r)); - - }, - _ => unreachablex!(), - ); - }) - }); - cb.build_constraints() - }); - TestConfig { - q_enable, - a, - b, - c, - res, - } - } - - pub fn assign( - &self, - layouter: &mut impl Layouter, - r: F, - ) -> Result<(), Error> { - layouter.assign_region( - || "Test", - |mut region| { - for offset in 0..10 { - assignf!(region, (self.q_enable, offset) => true.scalar()); - - let is_even = (offset / 2) * 2 == offset; - assign!(region, (self.a, offset) => is_even.scalar()); - assign!(region, (self.b, offset) => 887766.scalar()); - assignf!(region, (self.c, offset) => 112233.scalar()); - if is_even { - assign!(region, (self.res, offset) => (887766 + 112233).scalar()); - } else { - assign!(region, (self.res, offset) => Scalar::::scalar(&887766) + r); - } - } - Ok(()) - } - ) - } -} - -#[derive(Clone, Debug, Default)] -struct TestCircuit { - _phantom: F, -} - -impl Circuit for TestCircuit { - type Config = (TestConfig, Challenge); - type FloorPlanner = SimpleFloorPlanner; - type Params = (); - - fn without_witnesses(&self) -> Self { - unimplemented!() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - // dummy column for phase1 challange - meta.advice_column_in(FirstPhase); - let randomness = meta.challenge_usable_after(FirstPhase); - let config = TestConfig::new(meta, randomness); - - (config, randomness) - } - - fn synthesize( - &self, - (config, randomness): Self::Config, - mut layouter: impl Layouter - ) -> Result<(), halo2_proofs::plonk::Error> { - let mut r = F::ZERO; - layouter.get_challenge(randomness).map(|randomness| r = randomness); - config.assign(&mut layouter, r)?; - Ok(()) - } -} - -#[test] -fn test() { - - use halo2_proofs::{ dev::MockProver, halo2curves::bn256::Fr}; - - let circuit = TestCircuit::::default(); - let prover = MockProver::::run(6, &circuit, vec![]).unwrap(); - prover.assert_satisfied_par(); -} \ No newline at end of file diff --git a/zkevm-circuits/src/circuit_tools/test/shuffle.rs b/zkevm-circuits/src/circuit_tools/test/shuffle.rs deleted file mode 100644 index a421f5615d..0000000000 --- a/zkevm-circuits/src/circuit_tools/test/shuffle.rs +++ /dev/null @@ -1,192 +0,0 @@ -use std::{marker::PhantomData}; - -use eth_types::Field; -use gadgets::util::Scalar; -use halo2_proofs::{ - plonk::{Circuit, ConstraintSystem, Advice, Fixed, Column, FirstPhase, Challenge, Error, SecondPhase, Selector, Expression}, - circuit::{SimpleFloorPlanner, Layouter, layouter, Value}, - poly::Rotation, -}; -use rand::RngCore; -use rand_chacha::rand_core::OsRng; - -use crate::circuit_tools::{constraint_builder:: ConstraintBuilder, cell_manager::{CellType, CellManager, Cell}, cached_region::CachedRegion}; - - -#[derive(Clone)] -pub struct ShuffleConfig { - q_shuffle: Column, - original: [Column; W], - shuffled: [Column; W], - cb: ConstraintBuilder -} - -#[derive(Clone, Copy, Debug, num_enum::Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(u8)] -pub enum ShuffleCells { - Original, - Shuffled, - #[num_enum(default)] - Storage, -} - -impl CellType for ShuffleCells { - fn byte_type() -> Option { - unimplemented!() - } - fn storage_for_phase(phase: u8) -> Self { - match phase { - 0 => ShuffleCells::Storage, - _ => unreachable!() - } - } -} - - -impl ShuffleConfig { - pub fn new(meta: &mut ConstraintSystem) -> Self { - let q_shuffle = meta.fixed_column(); - let cm = CellManager::new( - meta, - vec![ - (ShuffleCells::Original, W, 1, false), - (ShuffleCells::Shuffled, W, 1, false), - (ShuffleCells::Storage, 10, 1, false), - ], - 0, - H - ); - let original = array_init::from_iter( - cm.get_typed_columns(ShuffleCells::Original) - .iter() - .map(|c| c.column.clone()) - ).unwrap(); - let shuffled = array_init::from_iter( - cm.get_typed_columns(ShuffleCells::Shuffled) - .iter() - .map(|c| c.column.clone()) - ).unwrap(); - - let mut cb: ConstraintBuilder = ConstraintBuilder::new(10 , Some(cm), None); - - meta.create_gate("Shuffle", |meta| { - // Rest of the gate we handle in macro - circuit!([meta, cb], { - ifx!(f!(q_shuffle) => { - let original = cb.query_cells_dyn(ShuffleCells::Original, W * H); - let shuffled = cb.query_cells_dyn(ShuffleCells::Shuffled, W * H); - - for (i, o) in original.iter().enumerate() { - let mut dest_set = Vec::new(); - for (j, s) in shuffled.iter().enumerate() { - if i == j { continue; } - dest_set.push(s.expr()); - } - require!(o.expr() => dest_set); - } - cb.split_constraints_expression(); - }); - cb.build_constraints() - }) - }); - - ShuffleConfig { - q_shuffle, - original, - shuffled, - cb - } - } -} - -#[derive(Clone)] -struct ShuffleCircuit { - original: [[F; H]; W], - shuffled: [[F; H]; W], -} - -impl ShuffleCircuit { - pub fn new(rng: &mut R) -> Self { - let original = [(); W].map(|_| [(); H].map(|_| F::random(&mut *rng))); - Self { - original, - shuffled: Self::shuffled(original, rng) - } - } - - fn shuffled( - original: [[F; H]; W], - rng: &mut R, - ) -> [[F; H]; W] { - let mut shuffled = original; - - for row in (1..H).rev() { - for column in shuffled.iter_mut() { - let rand_row = (rng.next_u32() as usize) % row; - column.swap(row, rand_row); - } - } - for col in (1..W).rev() { - let rand_col = (rng.next_u32() as usize) % col; - shuffled.swap(col, rand_col); - } - - shuffled - } -} - -impl Circuit for ShuffleCircuit { - type Config = ShuffleConfig<5, 6, F>; - type FloorPlanner = SimpleFloorPlanner; - type Params = (); - - fn without_witnesses(&self) -> Self { - unimplemented!() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - // dummy column for phase1 challange - meta.advice_column_in(FirstPhase); - ShuffleConfig::new(meta) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter - ) -> Result<(), halo2_proofs::plonk::Error> { - layouter.assign_region(|| "Shuffle", |mut region| { - let mut region = CachedRegion::new(&mut region, 1.scalar(), 2.scalar()); - assignf!(region, (config.q_shuffle, 0) => true.scalar()); - for h in (0..H) { - config.original - .iter() - .zip(self.original.iter()) - .for_each(| (col, val) | { - assign!(region, (col.clone(), h) => val[h]); - }); - config.shuffled - .iter() - .zip(self.shuffled.iter()) - .for_each(| (col, val) | { - assign!(region, (col.clone(), h) => val[h]); - }) - } - region.assign_stored_expressions(&config.cb, &[Value::known(1.scalar())]) - }) - } -} - -#[test] -fn test() { - - use halo2_proofs::{ dev::MockProver, halo2curves::bn256::Fr}; - - const W: usize = 3; - const H: usize = 4; - - let circuit = ShuffleCircuit::::new(&mut OsRng); - let prover = MockProver::::run(6, &circuit, vec![]).unwrap(); - prover.assert_satisfied_par(); -} - diff --git a/zkevm-circuits/src/circuit_tools/test/simple_rlp.rs b/zkevm-circuits/src/circuit_tools/test/simple_rlp.rs deleted file mode 100644 index 95316e96a6..0000000000 --- a/zkevm-circuits/src/circuit_tools/test/simple_rlp.rs +++ /dev/null @@ -1,78 +0,0 @@ -use std::default; - -use eth_types::Field; -use gadgets::util::Scalar; -use halo2_proofs::{ - plonk::{Circuit, ConstraintSystem, Advice, Fixed, Column, FirstPhase, Challenge, Error, SecondPhase, Selector, Expression}, - circuit::{SimpleFloorPlanner, Layouter, layouter, Value}, - poly::Rotation, -}; - -use crate::circuit_tools::{memory::{Memory}, constraint_builder::ConstraintBuilder, cell_manager::{CellManager, CellType}}; - -const MAX_DEG: usize = 5; - -#[derive(Clone)] -pub struct RlpConfig { - pub(crate) sel: Selector, - pub(crate) bytes: Column, - pub(crate) rlc: Column, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum RlpCell { - PhaseOne, - PhaseTwo, - Stack, - Byte, -} - -impl CellType for RlpCell { - fn byte_type() -> Option { - Some(RlpCell::Byte) - } - - fn storage_for_phase(phase: u8) -> Self { - match phase { - 1 => RlpCell::PhaseOne, - 2 => RlpCell::PhaseTwo, - _ => unreachable!() - } - } -} - -impl Default for RlpCell { - fn default() -> Self { - RlpCell::PhaseOne - } -} - -impl RlpConfig { - pub fn new( - meta: &mut ConstraintSystem, - gamma: Challenge, - sigma: Challenge, - ) -> Self { - let sel = meta.selector(); - let bytes = meta.advice_column(); - let rlc = meta.advice_column(); - - let memory = Memory::new( - meta, - vec![(RlpCell::Stack, 2)], - 0, - 5 - ); - let cm = CellManager::new( - meta, - vec![ - (RlpCell::PhaseOne, 3, 1, false), - (RlpCell::PhaseTwo, 3, 2, false), - ], - 0, - 5, - ); - let mut cb = ConstraintBuilder::new(MAX_DEG, Some(cm), None); - todo!() - } -} diff --git a/zkevm-circuits/src/taiko_pi_circuit.rs b/zkevm-circuits/src/taiko_pi_circuit.rs index faa61e87d5..9c5f949aaf 100644 --- a/zkevm-circuits/src/taiko_pi_circuit.rs +++ b/zkevm-circuits/src/taiko_pi_circuit.rs @@ -1,11 +1,12 @@ //! Use the hash value as public input. use crate::{ - evm_circuit::util::constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, + evm_circuit::table::Table::*, + evm_circuit::{util::constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, table::Table}, table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}, util::{random_linear_combine_word as rlc, Challenges, SubCircuit, SubCircuitConfig}, circuit_tools::{ - constraint_builder::ConstraintBuilder, + constraint_builder::{ConstraintBuilder, TO_FIX}, cell_manager::{CellManager, CellType}, }, @@ -13,7 +14,7 @@ use crate::{ }; use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256}; use ethers_core::utils::keccak256; -use gadgets::util::{or, select, Expr}; +use gadgets::util::{or, select, Expr, and}; use halo2_proofs::{ circuit::{AssignedCell, Layouter, Region, SimpleFloorPlanner, Value}, plonk::{ @@ -202,12 +203,12 @@ pub struct TaikoPiCircuitConfigArgs { } /// -#[derive(Clone, Copy, Debug, num_enum::Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(u8)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum PiCellType { /// - #[num_enum(default)] Storage, + /// + Lookup(Table) } impl CellType for PiCellType { fn byte_type() -> Option { @@ -217,6 +218,13 @@ impl CellType for PiCellType { unimplemented!() } } +impl Default for PiCellType { + fn default() -> Self { + Self::Storage + } +} + + impl SubCircuitConfig for TaikoPiCircuitConfig { type ConfigArgs = TaikoPiCircuitConfigArgs; @@ -261,91 +269,102 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { ); let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(cm), Some(challenges.evm_word())); - - meta.create_gate("Pi Gate", |meta| { + // field bytes + meta.create_gate( + "rpi_field_bytes_acc[i+1] = rpi_field_bytes_acc[i] * t + rpi_bytes[i+1]", + |meta| { circuit!([meta, cb], { - cb.add_constraint("", 0.expr()); + ifx!(q!(q_field_step) => { + let t = ifx!(f!(is_field_rlc) => { + challenges.evm_word() + } elsex { + BYTE_POW_BASE.expr() + }); + require!( + a!(rpi_field_bytes_acc, 1) => a!(rpi_field_bytes_acc) * t + a!(rpi_field_bytes, 1) + ); + }); }); cb.build_constraints() }); - - // field bytes - meta.create_gate( - "rpi_field_bytes_acc[i+1] = rpi_field_bytes_acc[i] * t + rpi_bytes[i+1]", - |meta| { - let mut bcb = BaseConstraintBuilder::new(MAX_DEGREE); - - let q_field_step = meta.query_selector(q_field_step); - let rpi_field_bytes_acc_next = - meta.query_advice(rpi_field_bytes_acc, Rotation::next()); - let rpi_field_bytes_acc = meta.query_advice(rpi_field_bytes_acc, Rotation::cur()); - let rpi_field_bytes_next = meta.query_advice(rpi_field_bytes, Rotation::next()); - let is_field_rlc = meta.query_fixed(is_field_rlc, Rotation::next()); - let randomness = challenges.evm_word(); - let t = select::expr(is_field_rlc, randomness, BYTE_POW_BASE.expr()); - bcb.require_equal( - "rpi_field_bytes_acc[i+1] = rpi_field_bytes_acc[i] * t + rpi_bytes[i+1]", - rpi_field_bytes_acc_next, - rpi_field_bytes_acc * t + rpi_field_bytes_next, - ); - bcb.gate(q_field_step) - }, - ); - meta.create_gate("rpi_field_bytes_acc[0] = rpi_field_bytes[0]", |meta| { - let mut bcb = BaseConstraintBuilder::new(MAX_DEGREE); - - let q_field_start = meta.query_selector(q_field_start); - let rpi_field_bytes_acc = meta.query_advice(rpi_field_bytes_acc, Rotation::cur()); - let rpi_field_bytes = meta.query_advice(rpi_field_bytes, Rotation::cur()); - - bcb.require_equal( - "rpi_field_bytes_acc[0] = rpi_field_bytes[0]", - rpi_field_bytes_acc, - rpi_field_bytes, - ); - bcb.gate(q_field_start) + meta.create_gate("rpi_field_bytes_acc[0] = rpi_field_bytes[0]", + |meta| { + cb.restart(); + circuit!([meta, cb], { + ifx!(q!(q_field_start) => { + require!(a!(rpi_field_bytes_acc) => a!(rpi_field_bytes)); + }); + }); + cb.build_constraints() }); - // keccak in rpi - meta.lookup_any("keccak(rpi)", |meta| { - let q_keccak = meta.query_selector(q_keccak); - let rpi_rlc = meta.query_advice(rpi_field_bytes_acc, Rotation::cur()); - let output = meta.query_advice(rpi_rlc_acc, Rotation::cur()); - [1.expr(), rpi_rlc, RPI_BYTES_LEN.expr(), output] - .into_iter() - .zip(keccak_table.table_exprs(meta).into_iter()) - .map(|(arg, table)| (q_keccak.expr() * arg, table)) - .collect::>() + meta.create_gate("keccak(rpi)", |meta| { + cb.restart(); + circuit!([meta, cb], { + ifx!(q!(q_keccak) => { + require!( + (1.expr(), a!(rpi_field_bytes_acc), RPI_BYTES_LEN.expr(), a!(rpi_rlc_acc)) + => @PiCellType::Lookup(Table::Keccak), (TO_FIX) + ); + }); + + // TODO(Cecilia): All lookups should work in one gate + // ifx!(q!(q_block_table) => { + // require!( + // ( + // BlockContextFieldTag::BlockHash.expr(), + // a!(block_index), + // a!(rpi_field_bytes_acc) + // ) => @PiCellType::Lookup(Table::Block), (TO_FIX) + // ); + // }); + // ifx!(or::expr([q!(q_field_start), q!(q_field_end)]) => { + // require!( + // (a!(rpi_field_bytes)) => @PiCellType::Lookup(Table::Bytecode), (TO_FIX) + // ); + // }); + + }); + cb.build_constraints() }); - // in block table - meta.lookup_any("in block table", |meta| { - let q_block_table = meta.query_selector(q_block_table); - let block_index = meta.query_advice(block_index, Rotation::cur()); - let block_hash = meta.query_advice(rpi_field_bytes_acc, Rotation::cur()); - [ - BlockContextFieldTag::BlockHash.expr(), - block_index, - block_hash, - ] - .into_iter() - .zip(block_table.table_exprs(meta).into_iter()) - .map(|(arg, table)| (q_block_table.expr() * arg, table)) - .collect::>() + meta.create_gate("in block table", |meta| { + circuit!([meta, cb], { + ifx!(q!(q_block_table) => { + require!( + ( + BlockContextFieldTag::BlockHash.expr(), + a!(block_index), + a!(rpi_field_bytes_acc) + ) => @PiCellType::Lookup(Table::Block), (TO_FIX) + ); + }); + }); + cb.build_constraints() }); - // is byte - meta.lookup_any("is_byte", |meta| { - let q_field_step = meta.query_selector(q_field_start); - let q_field_end = meta.query_selector(q_field_end); - let is_field = or::expr([q_field_step, q_field_end]); - let rpi_field_bytes = meta.query_advice(rpi_field_bytes, Rotation::cur()); - [rpi_field_bytes] - .into_iter() - .zip(byte_table.table_exprs(meta).into_iter()) - .map(|(arg, table)| (is_field.expr() * arg, table)) - .collect::>() + + meta.create_gate("is_byte", |meta| { + circuit!([meta, cb], { + ifx!(or::expr([q!(q_field_start), q!(q_field_end)]) => { + require!( + (a!(rpi_field_bytes)) => @PiCellType::Lookup(Table::Bytecode), (TO_FIX) + ); + }); + }); + cb.build_constraints() }); + cb.build_lookups( + meta, + &[cb.cell_manager.clone().unwrap()], + &[], + &[ + (PiCellType::Lookup(Table::Keccak), Some(&keccak_table)), + (PiCellType::Lookup(Table::Block), Some(&block_table)), + (PiCellType::Lookup(Table::Bytecode), Some(&byte_table)), + ], + ); + Self { rpi_field_bytes, rpi_field_bytes_acc, @@ -371,6 +390,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { } } +// input -> kecceck, output -> evm impl TaikoPiCircuitConfig { #[allow(clippy::too_many_arguments)] fn assign_pi_field( @@ -379,22 +399,26 @@ impl TaikoPiCircuitConfig { offset: &mut usize, _annotation: &'static str, field_bytes: &[u8], - rpi_rlc_acc: &mut Value, + rpi_rlc_acc: &mut Value, // 总的 challenges: &Challenges>, keccak_hi_lo: bool, block_number: Option, ) -> Result>, Error> { let len = field_bytes.len(); - let mut field_rlc_acc = Value::known(F::ZERO); + let mut field_rlc_acc = Value::known(F::ZERO); // field 的 let (use_rlc, t) = if len * 8 > F::CAPACITY as usize { + // 正常字段 (F::ONE, challenges.evm_word()) } else { + // lo hi (F::ZERO, Value::known(F::from(BYTE_POW_BASE))) }; let randomness = if keccak_hi_lo { + // input challenges.evm_word() } else { + // keccak input challenges.keccak_input() }; let mut cells = vec![None; field_bytes.len() + 2]; @@ -417,19 +441,24 @@ impl TaikoPiCircuitConfig { )?; field_rlc_acc = field_rlc_acc * t + Value::known(F::from(*byte as u64)); + // 字段 let rpi_cell = region.assign_advice( || "field bytes acc", self.rpi_field_bytes_acc, row_offset, || field_rlc_acc, )?; + *rpi_rlc_acc = *rpi_rlc_acc * randomness + Value::known(F::from(*byte as u64)); + // 总体 let rpi_rlc_acc_cell = region.assign_advice( || "rpi_rlc_acc", self.rpi_rlc_acc, row_offset, || *rpi_rlc_acc, )?; + + // setup selector if i == 0 { self.q_field_start.enable(region, row_offset)?; @@ -495,16 +524,16 @@ impl TaikoPiCircuitConfig { keccak_row, )?; let keccak = public_data.get_pi(); - let mut keccak_input = keccak.to_fixed_bytes(); - keccak_input.reverse(); - let keccak_rlc = challenges + let mut keccak_output = keccak.to_fixed_bytes(); + keccak_output.reverse(); + let keccak_output_rlc = challenges .evm_word() - .map(|randomness| rlc(keccak_input, randomness)); + .map(|randomness| rlc(keccak_output, randomness)); let keccak_output_cell = region.assign_advice( || "keccak(rpi)_output", self.rpi_rlc_acc, keccak_row, - || keccak_rlc, + || keccak_output_rlc, )?; self.q_keccak.enable(region, keccak_row)?; @@ -512,6 +541,12 @@ impl TaikoPiCircuitConfig { offset += 1; let mut pi = Vec::with_capacity(2); + // kck_output = rlc( + // r = evm_word(), + // keccak(concat([l1_signal, l2_signal, metadata, ...])) + // ) + // kck_output -> keccak_output_cell + for (idx, (annotation, field_bytes)) in [ ( "high_16_bytes_of_keccak_rpi", From 0a66dfd9da91b346fd4e530c87e20993cab4292b Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Thu, 3 Aug 2023 21:41:22 +0200 Subject: [PATCH 04/43] fixes from mpt --- .../src/circuit_tools/constraint_builder.rs | 84 +++++++++++-------- 1 file changed, 50 insertions(+), 34 deletions(-) diff --git a/zkevm-circuits/src/circuit_tools/constraint_builder.rs b/zkevm-circuits/src/circuit_tools/constraint_builder.rs index 5c2ca4e892..d0d3d22aa8 100644 --- a/zkevm-circuits/src/circuit_tools/constraint_builder.rs +++ b/zkevm-circuits/src/circuit_tools/constraint_builder.rs @@ -13,7 +13,6 @@ use halo2_proofs::{ poly::Rotation, }; use itertools::Itertools; -use num::complex::ComplexFloat; use super::{ cached_region::StoredExpression, @@ -41,6 +40,8 @@ pub struct LookupData { pub values: Vec>, /// region pub region_id: usize, + /// If the values are in rlc + pub compressed: bool, /// If true lookup to fixed table pub to_fixed: bool, } @@ -109,7 +110,7 @@ impl TabelMerger { fn merge_and_select( &self, - cb: &mut ConstraintBuilder, + _cb: &mut ConstraintBuilder, ) -> Vec> { let (selector, v) = self.merge_unsafe(); v.iter().map(|v| selector.expr() * v.expr()).collect() @@ -399,19 +400,18 @@ impl ConstraintBuilder { tables: &[(C, Option<&dyn LookupTable>)], ) { let lookups = self.lookups.clone(); - for (tag, table) in tables.iter() { if let Some(lookups) = lookups.get(tag) { for data in lookups.iter() { let LookupData { description, values, - local_condition, + compressed, regional_condition, to_fixed, .. } = data.clone(); - let table = if to_fixed { + let mut table = if to_fixed { // (v1, v2, v3) => (t1, t2, t3) // Direct lookup into the pre-difined fixed tables, vanilla lookup of // Halo2. @@ -433,6 +433,10 @@ impl ConstraintBuilder { // Applies condition to the advice values stored at configuration time self.dynamic_table_merged(*tag) }; + if compressed { + let challenge = self.lookup_challenge.clone().unwrap(); + table = vec![rlc::expr(&table, challenge)]; + } // Apply the conditions added from popping regions let mut values: Vec<_> = values .iter() @@ -443,7 +447,7 @@ impl ConstraintBuilder { while values.len() < table.len() { values.push(0.expr()); } - meta.lookup_any(description, |meta| { + meta.lookup_any(description, |_meta| { values .iter() .zip(table.iter()) @@ -451,9 +455,7 @@ impl ConstraintBuilder { .collect() }); } - } else { - unreachable!("Lookup not found: {:?}", tag); - } + } } } @@ -466,7 +468,8 @@ impl ConstraintBuilder { reduce: bool, ) { let values = match (compress, reduce) { - (true, true) | (true, false) => vec![self.local_compression(description, &values, tag, None, reduce)], + (true, true) => vec![self.local_compression(description, &values, tag, None, true)], + (true, false) => vec![self.local_compression(description, &values, tag, None, false)], (false, true) => values .iter() .map(|v| self.local_compression(description, &[v.clone()], tag, None, reduce)) @@ -499,9 +502,11 @@ impl ConstraintBuilder { reduce: bool, fixed_path: bool, ) { + // Process the value with conpression and reduction flags + // also apply the local condition let values = match (compress, reduce) { - (true, true) | (true, false) => vec![self.local_compression(description, &values, tag, None, reduce)], - (false, true) => values + (true, true) => vec![self.local_compression(description, &values, tag, None, true)], + (true, false) => vec![self.local_compression(description, &values, tag, None, false)], (false, true) => values .iter() .map(|v| self.local_compression(description, &[v.clone()], tag, None, reduce)) .collect(), @@ -510,9 +515,9 @@ impl ConstraintBuilder { .map(|v| v.expr() * self.get_condition_expr()) .collect(), }; - // Incase of fixed_path + // Incase of fixed_path, =>> // Buildig lookup from typed columns -> fixed table - // no need to store the lookup + // no need to store the lookup, also to_fixed flag become useless if !fixed_path { let data = LookupData { description, @@ -520,6 +525,7 @@ impl ConstraintBuilder { regional_condition: 1.expr(), values, region_id: self.region_id, + compressed: compress, to_fixed, }; if let Some(lookups) = self.lookups.get_mut(&tag) { @@ -542,12 +548,18 @@ impl ConstraintBuilder { let local_condition = self.get_condition_expr(); let challenge = self.lookup_challenge.clone().unwrap(); let rlc = rlc::expr(&values, challenge) * local_condition; - let compressed_expr = if reduce { - self.split_expression("compression", rlc) - } else { - rlc - }; - self.store_expression(name, compressed_expr, cell_type, target_cell) + match reduce { + true => { + let reduced_rlc = self.split_expression("compression", rlc); + self.store_expression( + name, + reduced_rlc, + cell_type, + target_cell + ) + }, + false => rlc + } } pub(crate) fn dynamic_table_merged(&mut self, tag: C) -> Vec> { @@ -1102,34 +1114,33 @@ macro_rules! _require { // only reduce flag is allowed // Lookup using a array - ($cb:expr, $values:expr =>> @$tag:expr, $($reduce:expr)?) => {{ + ($cb:expr, $values:expr =>> @$tag:expr, $options:expr) => {{ use $crate::circuit_tools::constraint_builder::REDUCE; let description = concat_with_preamble!( stringify!($values), " =>> @", stringify!($tag), ); - // let values = _to_vec!($values); $cb.add_lookup( description, $tag, $values, bool::default(), - bool::default(), - vec![$($reduce)?].contains(&REDUCE), + $options.contains(&COMPRESS), + $options.contains(&REDUCE), true ); }}; // Lookup using a tuple - ($cb:expr, $descr:expr, $values:expr =>> @$tag:expr, $($reduce:expr)?) => {{ + ($cb:expr, $descr:expr, $values:expr =>> @$tag:expr, $options:expr) => {{ use $crate::circuit_tools::constraint_builder::REDUCE; $cb.add_lookup( Box::leak($descr.to_string().into_boxed_str()), $tag, $values, bool::default(), - bool::default(), - vec![$($reduce)?].contains(&REDUCE), + $options.contains(&COMPRESS), + $options.contains(&REDUCE), true ); }}; @@ -1442,25 +1453,30 @@ macro_rules! circuit { // Lookups build from table // only reduce flag is allowed - ($values:tt =>> @$tag:expr, $reduce:expr) => {{ + ($values:tt =>> @$tag:expr, $options:tt) => {{ let values = _to_values_vec!($values); - _require!($cb, values =>> @$tag, $reduce); + let options = _to_options_vec!($options); + _require!($cb, values =>> @$tag, options); }}; ($values:tt =>> @$tag:expr) => {{ let values = _to_values_vec!($values); - _require!($cb, values =>> @$tag); + let options = Vec::new(); + _require!($cb, values =>> @$tag, options); }}; - ($descr:expr, $values:tt =>> @$tag:expr, $reduce:expr) => {{ + ($descr:expr, $values:tt =>> @$tag:expr, $options:tt) => {{ let values = _to_values_vec!($values); - _require!($cb, $descr, values =>> @$tag, $reduce); + let options = _to_options_vec!($options); + _require!($cb, $descr, values =>> @$tag, options); }}; ($descr:expr, $values:tt =>> @$tag:expr) => {{ let values = _to_values_vec!($values); - _require!($cb, $descr, values =>> @$tag); + let options = Vec::new(); + _require!($cb, $descr, values =>> @$tag, options); }}; - ($values:tt => @$tag:expr, $options:tt) => {{ + + ($values:tt => @$tag:expr, $options:tt) => {{ let values = _to_values_vec!($values); let options = _to_options_vec!($options); _require!($cb, values => @$tag, options); From bddf633b7325e4a704acf2f9f7b1f32d2294176c Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Tue, 15 Aug 2023 18:50:33 +0800 Subject: [PATCH 05/43] latest circuit tools --- .../src/circuit_tools/cell_manager.rs | 87 ++-- .../src/circuit_tools/constraint_builder.rs | 318 +++++++++------ zkevm-circuits/src/circuit_tools/memory.rs | 380 ++++++++---------- 3 files changed, 384 insertions(+), 401 deletions(-) diff --git a/zkevm-circuits/src/circuit_tools/cell_manager.rs b/zkevm-circuits/src/circuit_tools/cell_manager.rs index 56d78a8768..a307ef0421 100644 --- a/zkevm-circuits/src/circuit_tools/cell_manager.rs +++ b/zkevm-circuits/src/circuit_tools/cell_manager.rs @@ -188,7 +188,6 @@ impl CellType for DefaultCellType { } fn storage_for_phase(phase: u8) -> Self { - // println!("phase: {}", phase); match phase { 1 => DefaultCellType::StoragePhase1, 2 => DefaultCellType::StoragePhase2, @@ -201,11 +200,11 @@ impl CellType for DefaultCellType { #[derive(Clone, Debug)] pub(crate) struct CellColumn { pub(crate) column: Column, - index: usize, pub(crate) cell_type: C, - height: usize, - cells: Vec>, + pub(crate) cells: Vec>, pub(crate) expr: Expression, + height: usize, + index: usize, } impl PartialEq for CellColumn { @@ -236,7 +235,8 @@ impl Expr for CellColumn { } } -#[derive(Clone, Debug)] + +#[derive(Clone, Debug, Default)] pub struct CellManager { configs: Vec>, columns: Vec>, @@ -251,37 +251,42 @@ impl CellManager { offset: usize, max_height: usize, ) -> Self { - let configs = configs + let mut cm = CellManager::default(); + cm.height_limit = max_height; + configs .into_iter() - .map(|c| c.into()) - .collect::>>(); + .for_each(|c| cm.add_celltype(meta, c, offset)); + cm.height = max_height; + cm + } - let mut columns = Vec::new(); - for config in configs.iter() { - let cols = config.init_columns(meta); - for col in cols.iter() { - let mut cells = Vec::new(); - for r in 0..max_height { - query_expression(meta, |meta| { - cells.push(Cell::new(meta, *col, offset + r)); - }); - } - columns.push(CellColumn { - column: *col, - index: columns.len(), - cell_type: config.cell_type, - height: 0, - expr: cells[0].expr(), - cells, + pub(crate) fn add_celltype( + &mut self, + meta: &mut ConstraintSystem, + config: (C, usize, u8, bool), + offset: usize, + ) { + if self.get_typed_columns(config.0).len() != 0 { + panic!("CellManager: cell type {:?} already exists", config.0); + } + let config = CellConfig::from(config); + for col in config.init_columns(meta).iter() { + let mut cells = Vec::new(); + for r in 0..self.height_limit { + query_expression(meta, |meta| { + cells.push(Cell::new(meta, *col, offset + r)); }); } + self.columns.push(CellColumn { + column: *col, + index: self.columns.len(), + cell_type: config.cell_type, + height: 0, + expr: cells[0].expr(), + cells, + }); } - Self { - configs, - columns, - height: max_height, - height_limit: max_height, - } + self.configs.push(config); } pub(crate) fn restart(&mut self) { @@ -368,23 +373,13 @@ impl CellManager { columns } - pub(crate) fn build_lookups_from_table( - &self, - meta: &mut ConstraintSystem, - tables: &[(C, &dyn LookupTable)], - challenge: Expression, - ) { - for (cell_type, table) in tables { - for col in self.get_typed_columns(*cell_type) { - let name = format!("{:?}", cell_type); - meta.lookup_any(Box::leak(name.into_boxed_str()), |meta| { - vec![( - col.expr, - rlc::expr(&table.table_exprs(meta), challenge.expr()), - )] - }); + pub(crate) fn get_config(&self, cell_type: C) -> Option> { + for config in self.configs.iter() { + if config.cell_type == cell_type { + return Some(config.clone()); } } + None } } diff --git a/zkevm-circuits/src/circuit_tools/constraint_builder.rs b/zkevm-circuits/src/circuit_tools/constraint_builder.rs index d0d3d22aa8..4db26963a4 100644 --- a/zkevm-circuits/src/circuit_tools/constraint_builder.rs +++ b/zkevm-circuits/src/circuit_tools/constraint_builder.rs @@ -10,7 +10,6 @@ use eth_types::Field; use gadgets::util::{and, sum, Scalar}; use halo2_proofs::{ plonk::{ConstraintSystem, Expression}, - poly::Rotation, }; use itertools::Itertools; @@ -65,12 +64,12 @@ impl TableData { } } -struct TabelMerger{ +struct TableMerger{ data: Vec>, _phantom: PhantomData, } -impl TabelMerger { +impl TableMerger { fn merge_check(&self, cb: &mut ConstraintBuilder) { let selector = sum::expr(self.data.iter().map(|t| t.condition())); crate::circuit!([meta, cb], { @@ -134,6 +133,9 @@ pub struct ConstraintBuilder { /// The tables written during synthesis /// write to RAM pub dynamic_tables: HashMap>>, + /// The tables preloaded before configuration + /// Read-only memory + pub fixed_tables: HashMap>>, /// All stored expressions pub stored_expressions: HashMap>>, /// CellManager @@ -164,6 +166,7 @@ impl ConstraintBuilder { conditions: Vec::new(), lookups: HashMap::new(), dynamic_tables: HashMap::new(), + fixed_tables: HashMap::new(), cell_manager, disable_description: false, stored_expressions: HashMap::new(), @@ -196,6 +199,18 @@ impl ConstraintBuilder { self.max_global_degree = max_degree; } + pub(crate) fn preload_tables( + &mut self, + meta: &mut ConstraintSystem, + tables: &[(C, &dyn LookupTable)] + ) { + query_expression(meta, |meta| { + for (tag, table) in tables { + self.fixed_tables.insert(tag.clone(), table.table_exprs(meta)); + } + }) + } + pub(crate) fn push_region(&mut self, region_id: usize) { assert!(region_id != 0); self.region_id = region_id; @@ -371,124 +386,121 @@ impl ConstraintBuilder { self.constraints.clone() } - pub(crate) fn build_lookups( + pub(crate) fn build_fixed_path( &mut self, meta: &mut ConstraintSystem, cell_managers: &[CellManager], - fixed_path: &[(C, &dyn LookupTable)], - dynamic_path: &[(C, Option<&dyn LookupTable>)], - ) { - self.build_from_table(meta, cell_managers, fixed_path); - self.build_from_data(meta, dynamic_path); + tag:&(C, C) + ){ + let (data_tag, table_tag) = tag; + let challenge = self.lookup_challenge.clone().unwrap(); + if let Some(table) = self.fixed_tables.get(table_tag) { + let table_expr = rlc::expr(&table, challenge.expr()); + for cm in cell_managers { + for col in cm.get_typed_columns(*data_tag) { + meta.lookup_any(format!("{:?}", data_tag), |_meta| { + vec![(col.expr(), table_expr.clone())] + }); + } + } + } } - pub(crate) fn build_from_table( - &self, + pub(crate) fn build_dynamic_path( + &mut self, meta: &mut ConstraintSystem, - cell_managers: &[CellManager], - tables: &[(C, &dyn LookupTable)], - ) { - let challenge = self.lookup_challenge.clone().unwrap(); - for cm in cell_managers { - cm.build_lookups_from_table(meta, tables.clone(), challenge.expr()); - } + tag: &(C, C) + ){ + let (data_tag, table_tag) = tag; + if let Some(lookups) = self.lookups.clone().get(data_tag) { + for data in lookups.iter() { + let LookupData { + description, + values, + compressed, + regional_condition, + to_fixed, + .. + } = data.clone(); + let mut table = if to_fixed { + // (v1, v2, v3) => (t1, t2, t3) + // Direct lookup into the pre-difined fixed tables, vanilla lookup of + // Halo2. + self.fixed_tables + .get(table_tag) + .expect(&format!( + "Fixed table {:?} not found for dynamic lookup", + table_tag + )) + .clone() + } else { + // (v1, v2, v3) => cond * (t1, t2, t3) + // Applies condition to the advice values stored at configuration time + self.dynamic_table_merged(*table_tag) + }; + if compressed { + let challenge = self.lookup_challenge.clone().unwrap(); + table = vec![rlc::expr(&table, challenge)]; + } + // Apply the conditions added from popping regions + let mut values: Vec<_> = values + .iter() + .map(|value| value.expr() * regional_condition.clone()) + .collect(); + // align the length of values and table + assert!(table.len() >= values.len()); + while values.len() < table.len() { + values.push(0.expr()); + } + meta.lookup_any(description, |_meta| { + values + .iter() + .zip(table.iter()) + .map(|(v, t)| (v.expr(), t.expr())) + .collect() + }); + } + } } - pub(crate) fn build_from_data( + + pub(crate) fn build_lookups( &mut self, meta: &mut ConstraintSystem, - tables: &[(C, Option<&dyn LookupTable>)], + cell_managers: &[CellManager], + tags: &[(C, C)], ) { - let lookups = self.lookups.clone(); - for (tag, table) in tables.iter() { - if let Some(lookups) = lookups.get(tag) { - for data in lookups.iter() { - let LookupData { - description, - values, - compressed, - regional_condition, - to_fixed, - .. - } = data.clone(); - let mut table = if to_fixed { - // (v1, v2, v3) => (t1, t2, t3) - // Direct lookup into the pre-difined fixed tables, vanilla lookup of - // Halo2. - table - .expect(&format!( - "Fixed table tag {:?} not provided for lookup data", - tag - )) - .columns() - .iter() - .map(|col| { - query_expression(meta, |meta| - meta.query_any(*col, Rotation(0)) - ) - }) - .collect() - } else { - // (v1, v2, v3) => cond * (t1, t2, t3) - // Applies condition to the advice values stored at configuration time - self.dynamic_table_merged(*tag) - }; - if compressed { - let challenge = self.lookup_challenge.clone().unwrap(); - table = vec![rlc::expr(&table, challenge)]; - } - // Apply the conditions added from popping regions - let mut values: Vec<_> = values - .iter() - .map(|value| value.expr() * regional_condition.clone()) - .collect(); - // align the length of values and table - assert!(table.len() >= values.len()); - while values.len() < table.len() { - values.push(0.expr()); - } - meta.lookup_any(description, |_meta| { - values - .iter() - .zip(table.iter()) - .map(|(v, t)| (v.expr(), t.expr())) - .collect() - }); - } - } + let _challenge = self.lookup_challenge.clone().unwrap(); + for tag in tags { + self.build_fixed_path(meta, cell_managers, tag); + self.build_dynamic_path(meta, tag); } } - pub(crate) fn store_dynamic_table( + pub(crate) fn store_table( &mut self, description: &'static str, tag: C, values: Vec>, compress: bool, reduce: bool, + dyn_path: bool, ) { - let values = match (compress, reduce) { - (true, true) => vec![self.local_compression(description, &values, tag, None, true)], - (true, false) => vec![self.local_compression(description, &values, tag, None, false)], - (false, true) => values - .iter() - .map(|v| self.local_compression(description, &[v.clone()], tag, None, reduce)) - .collect(), - (false, false) => values - .iter() - .map(|v| v.expr() * self.get_condition_expr()) - .collect(), - }; - let data = TableData { - regional_condition: 1.expr(), - local_condition: self.get_condition_expr(), - values, - region_id: self.region_id, - }; - if let Some(tables) = self.dynamic_tables.get_mut(&tag) { - tables.push(data); + let values = self.local_processing(description, &values, tag, None, compress, reduce); + if dyn_path { + let data = TableData { + regional_condition: 1.expr(), + local_condition: self.get_condition_expr(), + values, + region_id: self.region_id, + }; + if let Some(tables) = self.dynamic_tables.get_mut(&tag) { + tables.push(data); + } else { + self.dynamic_tables.insert(tag, vec![data]); + } } else { - self.dynamic_tables.insert(tag, vec![data]); + self.fixed_tables.insert(tag, values); } } @@ -500,25 +512,15 @@ impl ConstraintBuilder { to_fixed: bool, compress: bool, reduce: bool, - fixed_path: bool, + dyn_path: bool, ) { // Process the value with conpression and reduction flags // also apply the local condition - let values = match (compress, reduce) { - (true, true) => vec![self.local_compression(description, &values, tag, None, true)], - (true, false) => vec![self.local_compression(description, &values, tag, None, false)], (false, true) => values - .iter() - .map(|v| self.local_compression(description, &[v.clone()], tag, None, reduce)) - .collect(), - (false, false) => values - .iter() - .map(|v| v.expr() * self.get_condition_expr()) - .collect(), - }; + let values = self.local_processing(description, &values, tag, None, compress, reduce); // Incase of fixed_path, =>> // Buildig lookup from typed columns -> fixed table // no need to store the lookup, also to_fixed flag become useless - if !fixed_path { + if dyn_path { let data = LookupData { description, local_condition: self.get_condition_expr(), @@ -534,31 +536,48 @@ impl ConstraintBuilder { self.lookups.insert(tag, vec![data]); } } - } - pub(crate) fn local_compression( + pub(crate) fn local_processing( &mut self, name: &str, values: &[Expression], cell_type: C, target_cell: Option>, + compress: bool, reduce: bool, - ) -> Expression { + ) -> Vec >{ + let local_condition = self.get_condition_expr(); let challenge = self.lookup_challenge.clone().unwrap(); - let rlc = rlc::expr(&values, challenge) * local_condition; - match reduce { - true => { - let reduced_rlc = self.split_expression("compression", rlc); - self.store_expression( - name, - reduced_rlc, - cell_type, - target_cell - ) - }, - false => rlc + + let mut local_compression = | values: &[Expression]| -> Expression { + let rlc = rlc::expr(&values, challenge.expr()) * local_condition.expr(); + match reduce { + true => { + let reduced_rlc = self.split_expression("compression", rlc); + self.store_expression( + name, + reduced_rlc, + cell_type, + target_cell.clone() + ) + }, + false => rlc + } + }; + + match (compress, reduce) { + (true, true) => vec![local_compression(&values)], + (true, false) => vec![local_compression(&values)], + (false, true) => values + .iter() + .map(|_v| local_compression(&values)) + .collect(), + (false, false) => values + .iter() + .map(|v| v.expr() * local_condition.expr()) + .collect(), } } @@ -567,7 +586,7 @@ impl ConstraintBuilder { .get(&tag) .unwrap_or_else(|| panic!("Dynamic table {:?} not found", tag)) .clone(); - let table_merger = TabelMerger{ + let table_merger = TableMerger{ data, _phantom: PhantomData, }; @@ -1128,7 +1147,7 @@ macro_rules! _require { bool::default(), $options.contains(&COMPRESS), $options.contains(&REDUCE), - true + false ); }}; // Lookup using a tuple @@ -1141,7 +1160,7 @@ macro_rules! _require { bool::default(), $options.contains(&COMPRESS), $options.contains(&REDUCE), - true + false ); }}; @@ -1161,7 +1180,7 @@ macro_rules! _require { $options.contains(&TO_FIX), $options.contains(&COMPRESS), $options.contains(&REDUCE), - false + true ); }}; ($cb:expr, $descr:expr, $values:expr => @$tag:expr, $options:expr) => {{ @@ -1173,7 +1192,7 @@ macro_rules! _require { $options.contains(&TO_FIX), $options.contains(&COMPRESS), $options.contains(&REDUCE), - false + true ); }}; @@ -1189,12 +1208,31 @@ macro_rules! _require { stringify!($values), ")", ); - $cb.store_dynamic_table( + $cb.store_table( description, $tag, $values, $options.contains(&COMPRESS), $options.contains(&REDUCE), + true + ); + }}; + // Put values in a lookup table using a tuple + ($cb:expr, @$tag:expr, $options:expr =>> $values:expr) => {{ + let description = concat_with_preamble!( + "@", + stringify!($tag), + " => (", + stringify!($values), + ")", + ); + $cb.store_table( + description, + $tag, + $values, + $options.contains(&COMPRESS), + $options.contains(&REDUCE), + false ); }}; } @@ -1356,6 +1394,9 @@ macro_rules! _to_values_vec { (($($tts:expr), *)) => { vec![$($tts.expr()), *] }; + ($tts:expr)=> { + $tts + } } #[macro_export] @@ -1508,7 +1549,16 @@ macro_rules! circuit { let options = Vec::new(); _require!($cb, @$tag, options => values); }}; - + (@$tag:expr, $options:tt =>> $values:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, @$tag, options =>> values); + }}; + (@$tag:expr =>> $values:tt) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, @$tag, options =>> values); + }}; } diff --git a/zkevm-circuits/src/circuit_tools/memory.rs b/zkevm-circuits/src/circuit_tools/memory.rs index 6b919ad96f..8d2ec50db1 100644 --- a/zkevm-circuits/src/circuit_tools/memory.rs +++ b/zkevm-circuits/src/circuit_tools/memory.rs @@ -1,110 +1,95 @@ //! Memory use crate::{ - evm_circuit::util::rlc, util::{query_expression, Expr}, }; -use eth_types::Field; +use eth_types::{Field}; use halo2_proofs::{ circuit::Value, plonk::{ - Advice, Column, ConstraintSystem, Error, Expression, FirstPhase, SecondPhase, ThirdPhase, + Advice, Column, ConstraintSystem, Error, Expression, }, poly::Rotation, }; use itertools::Itertools; use std::{ collections::HashMap, - ops::{Index, IndexMut}, + ops::{Index, IndexMut}, marker::PhantomData, }; use super::{ cached_region::CachedRegion, - cell_manager::{Cell, CellType}, + cell_manager::{CellType, CellManager}, constraint_builder::ConstraintBuilder, }; #[derive(Clone, Debug, Default)] -pub(crate) struct Memory { - height: usize, - banks: Vec>, - rw_records: HashMap, Column)>, +pub(crate) struct Memory> { + // TODO(Cecilia): want to use dynamic dispatch + // i.e. dyn MemoryBank but trait with generic param is not object safe + pub(crate) banks: HashMap, + _phantom: PhantomData } -impl Memory { - pub(crate) fn new( - meta: &mut ConstraintSystem, - tags: Vec<(C, usize)>, - offset: usize, - height: usize, - ) -> Self { - let mut rw_records = HashMap::new(); - let banks = tags - .iter() - .map(|(tag, phase)| { - let [key, reads, writes] = match phase { - 1 => [(); 3].map(|_| meta.advice_column_in(FirstPhase)), - 2 => [(); 3].map(|_| meta.advice_column_in(SecondPhase)), - 3 => [(); 3].map(|_| meta.advice_column_in(ThirdPhase)), - _ => unreachable!(), - }; - rw_records.insert(tag.clone(), (reads, writes)); - MemoryBank::new(meta, tag.clone(), height, offset, key, reads, writes) - }) - .collect::>>(); - Self { - banks, - height, - rw_records, +impl> Index for Memory { + type Output = MB; + + fn index(&self, tag: C) -> &Self::Output { + if let Some(bank) = self.banks.get(&tag) { + bank + } else { + unreachable!() } } +} - pub(crate) fn get_bank(&self, tag: C) -> &MemoryBank { - for bank in self.banks.iter() { - if bank.tag() == tag { - return bank; - } +impl> IndexMut for Memory { + fn index_mut(&mut self, tag: C) -> &mut Self::Output { + if let Some(bank) = self.banks.get_mut(&tag) { + bank + } else { + unreachable!() } - unreachable!() } +} - pub(crate) fn get_mut_bank(&mut self, tag: C) -> &mut MemoryBank { - for bank in self.banks.iter_mut() { - if bank.tag() == tag { - return bank; - } +impl> Memory { + pub(crate) fn new( + cm: &mut CellManager, + meta: &mut ConstraintSystem, + tags: Vec<(C, C, u8)>, + offset: usize, + ) -> Self { + let mut banks = HashMap::new(); + tags + .into_iter() + .for_each(|(data_tag, table_tag, phase)| { + banks.insert( + data_tag, + MB::new(meta, cm, (data_tag, table_tag), phase, offset) + ); + }); + Self { + banks, + _phantom: PhantomData } - unreachable!() } - pub(crate) fn get_records(&self) -> Vec<(Column, Column)> { - self.rw_records.clone().into_values().collect() + pub(crate) fn get_columns(&self) -> Vec> { + self.banks + .values() + .fold( Vec::new(),|mut acc, bank| { + acc.extend(bank.columns().iter()); + acc + }) } pub(crate) fn build_constraints( &self, cb: &mut ConstraintBuilder, - is_first_row: Expression, + q_start: Expression, ) { - for bank in self.banks.iter() { - bank.build_constraints(cb, is_first_row.expr()); - } - } - - pub(crate) fn build_lookups(&self, meta: &mut ConstraintSystem) { - for (cell_type, (reads, writes)) in &self.rw_records { - let name = format!("{:?}", cell_type); - meta.lookup_any(Box::leak(name.into_boxed_str()), |meta| { - vec![( - meta.query_advice(*reads, Rotation(0)), - meta.query_advice(*writes, Rotation(0)), - )] - }); - } - } - - pub(crate) fn clear_witness_data(&mut self) { - for bank in self.banks.iter_mut() { - bank.clear_witness_data(); + for (_, bank) in self.banks.iter() { + bank.build_constraints(cb, q_start.expr()); } } @@ -113,201 +98,162 @@ impl Memory { region: &mut CachedRegion<'_, '_, F>, height: usize, ) -> Result<(), Error> { - for bank in self.banks.iter() { + for (_, bank) in self.banks.iter() { bank.assign(region, height)?; } Ok(()) } pub(crate) fn tags(&self) -> Vec { - self.banks.iter().map(|bank| bank.tag()).collect() + self.banks.iter().map(|(_, bank)| bank.tag().0).collect() } } -impl Index for Memory { - type Output = MemoryBank; - - fn index(&self, tag: C) -> &Self::Output { - for bank in self.banks.iter() { - if bank.tag() == tag { - return bank; - } - } - unreachable!() - } -} -impl IndexMut for Memory { - fn index_mut(&mut self, tag: C) -> &mut Self::Output { - for bank in self.banks.iter_mut() { - if bank.tag() == tag { - return bank; - } - } - unreachable!() - } +pub(crate) trait MemoryBank: Clone { + fn new(meta: &mut ConstraintSystem, cm: &mut CellManager, tag: (C, C), phase: u8, offset: usize) -> Self; + fn store(&mut self, cb: &mut ConstraintBuilder, values: &[Expression]) -> Expression; + fn load(&mut self, cb: &mut ConstraintBuilder, load_offset: Expression, values: &[Expression]); + fn columns(&self) -> Vec>; + fn tag(&self) -> (C, C); + fn witness_store(&mut self, offset: usize, values: &[F]); + fn witness_load(&self, offset: usize) -> Vec; + fn build_constraints(&self, cb: &mut ConstraintBuilder, q_start: Expression); + fn assign(&self, region: &mut CachedRegion<'_, '_, F>, height: usize) -> Result<(), Error>; } #[derive(Clone, Debug)] -pub(crate) struct MemoryBank { - tag: C, +pub(crate) struct RwBank { + tag: (C, C), key: Column, - reads: (Vec>, usize), - writes: (Vec>, usize), - cur: Expression, - next: Expression, - table_conditions: Vec<(usize, Expression)>, + reads: Column, + writes: Column, store_offsets: Vec, stored_values: Vec>, + cur: Expression, + next: Expression, + // TODO(Cecilia): get rid of this when we kill regions + local_conditions: Vec<(usize, Expression)>, } -impl MemoryBank { - pub(crate) fn new( +impl RwBank { + pub(crate) fn prepend_key(&self, values: &[Expression]) -> Vec> { + [&[self.cur.expr() + 1.expr()], values].concat().to_vec() + } + + pub(crate) fn prepend_offset(&self, values: &[Expression], offset: Expression) -> Vec> { + [&[self.cur.expr() - offset], values].concat().to_vec() + } +} + +impl MemoryBank for RwBank { + fn new( meta: &mut ConstraintSystem, - tag: C, - height: usize, + cm: &mut CellManager, + tag: (C, C), + phase: u8, offset: usize, - key: Column, - read_col: Column, - write_col: Column, ) -> Self { - let mut cur = 0.expr(); - let mut next = 0.expr(); - query_expression(meta, |meta| { - cur = meta.query_advice(key, Rotation::cur()); - next = meta.query_advice(key, Rotation::next()); + let rw: Vec> = [tag.0, tag.1].iter() + .map(|t| { + let config = (t.clone(), 1usize, phase, false); + cm.add_celltype(meta, config, offset); + cm.get_typed_columns(t.clone())[0].column + }).collect(); + let key = meta.advice_column(); + let (cur, next) = query_expression(meta, |meta| { + ( + meta.query_advice(key, Rotation(0)), + meta.query_advice(key, Rotation(1)) + ) }); - let mut reads = Vec::new(); - let mut writes = Vec::new(); - for h in 0..height { - query_expression(meta, |meta| { - reads.push(Cell::new(meta, read_col, offset + h)); - writes.push(Cell::new(meta, write_col, offset + h)); - }); - } - Self { - tag, - key, - reads: (reads, 0), - writes: (writes, 0), + Self { + tag, + key, + reads: rw[0], + writes: rw[1], + store_offsets: Vec::new(), + stored_values: Vec::new(), cur, next, - table_conditions: Vec::new(), - store_offsets: Vec::new(), - stored_values: Vec::new(), + local_conditions: Vec::new(), } } - pub(crate) fn key(&self) -> Expression { - self.cur.expr() - } - - fn query_write(&mut self) -> Cell { - let cell = self.writes.0[self.writes.1].clone(); - self.writes.1 += 1; - cell - } - - fn query_read(&mut self) -> Cell { - let cell = self.reads.0[self.reads.1].clone(); - self.reads.1 += 1; - cell - } - - pub(crate) fn store( - &mut self, - cb: &mut ConstraintBuilder, - values: &[Expression], + fn store( + &mut self, + cb: &mut ConstraintBuilder, + values: &[Expression] ) -> Expression { - let key = self.key() + 1.expr(); - let condition = cb.get_condition_expr(); - let values = self - .prepend_key(key.clone(), values) - .iter() - .map(|value| condition.expr() * value.expr()) - .collect_vec(); - let compressed_expr = cb.split_expression( - "compression", - rlc::expr(&values, cb.lookup_challenge.clone().unwrap().expr()), + let values = self.prepend_key(values); + cb.store_table( + Box::leak(format!("{:?} store", self.tag.1).into_boxed_str()), + self.tag.1, + values.clone(), + true, + true, + false ); - let name = format!("{:?} write #{:?}", self.tag, self.writes.1); - cb.store_expression( - name.as_str(), - compressed_expr.expr(), - C::default(), - Some(self.query_write()), - ); - self.table_conditions.push((cb.region_id, condition)); - key + self.local_conditions.push((cb.region_id, cb.get_condition_expr())); + values[0].expr() } - pub(crate) fn load( - &mut self, - _description: &'static str, - cb: &mut ConstraintBuilder, - load_offset: Expression, - values: &[Expression], + fn load( + &mut self, + cb: &mut ConstraintBuilder, + load_offset: Expression, + values: &[Expression] ) { - let key = self.key() - load_offset; - let condition = cb.get_condition_expr(); - let values = self - .prepend_key(key, values) - .iter() - .map(|value| condition.expr() * value.expr()) - .collect_vec(); - let compressed_expr = cb.split_expression( - "compression", - rlc::expr(&values, cb.lookup_challenge.clone().unwrap().expr()), + let values = self.prepend_offset(values, load_offset); + cb.add_lookup( + Box::leak(format!("{:?} load", self.tag.0).into_boxed_str()), + self.tag.0, + values, + false, + true, + true, + false ); - let name = format!("{:?} write #{:?}", self.tag, self.writes.1); - cb.store_expression( - name.as_str(), - compressed_expr.expr(), - C::default(), - Some(self.query_read()), - ); - } - - pub(crate) fn witness_store(&mut self, offset: usize, values: &[F]) { - self.stored_values.push(values.to_vec()); - self.store_offsets.push(offset); } - pub(crate) fn witness_load(&self, offset: usize) -> Vec { - self.stored_values[self.stored_values.len() - 1 - offset].clone() + fn tag(&self) -> (C, C) { + self.tag } - pub(crate) fn clear_witness_data(&mut self) { - self.store_offsets.clear(); + fn columns(&self) -> Vec> { + vec![self.key, self.reads, self.writes] } - pub(crate) fn build_constraints( - &self, - cb: &mut ConstraintBuilder, - is_first_row: Expression, + fn build_constraints( + &self, + cb: &mut ConstraintBuilder, + q_start: Expression ) { let condition = self - .table_conditions + .local_conditions .iter() .filter(|tc| tc.0 == cb.region_id) .fold(0.expr(), |acc, tc| acc + tc.1.expr()); crate::circuit!([meta, cb], { - ifx! {is_first_row => { + ifx! {q_start => { require!(self.cur.expr() => 0); }} let description = format!("Dynamic lookup table {:?}", self.tag()); require!(condition => bool); require!(description, self.next => self.cur.expr() + condition.expr()); - // TODO(Brecht): add constraint that makes sure the table value remains the same when - // not written - ifx!(not!(is_first_row) * not!(condition) => { - // TODO(Cecilia): Only works with Halo2 query API update - // require!(self.writes.0[0].column().expr() => self.writes.0[0].column().prev()); - }); - }); + }); } - pub(crate) fn assign( + fn witness_store(&mut self, offset: usize, values: &[F]) { + self.stored_values.push(values.to_vec()); + self.store_offsets.push(offset); + } + + fn witness_load(&self, offset: usize) -> Vec { + self.stored_values[self.stored_values.len() - 1 - offset].clone() + } + + fn assign( &self, region: &mut CachedRegion<'_, '_, F>, height: usize, @@ -331,12 +277,4 @@ impl MemoryBank { } Ok(()) } - - pub(crate) fn tag(&self) -> C { - self.tag - } - - pub(crate) fn prepend_key(&self, key: V, values: &[V]) -> Vec { - [vec![key], values.to_owned()].concat().to_vec() - } -} +} \ No newline at end of file From e803f77a6fd9f255d2e869166403aa72da0ea7c2 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Tue, 15 Aug 2023 18:51:11 +0800 Subject: [PATCH 06/43] rewrite --- zkevm-circuits/src/lib.rs | 1 + zkevm-circuits/src/taiko_pi_circuit.rs | 75 ++-- zkevm-circuits/src/taiko_pi_circuit__.rs | 433 +++++++++++++++++++++++ 3 files changed, 470 insertions(+), 39 deletions(-) create mode 100644 zkevm-circuits/src/taiko_pi_circuit__.rs diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index 41a22ef556..cf049455c6 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -28,6 +28,7 @@ pub mod state_circuit; pub mod super_circuit; pub mod table; pub mod taiko_pi_circuit; +pub mod taiko_pi_circuit__; pub mod taiko_super_circuit; #[cfg(any(feature = "test", test))] diff --git a/zkevm-circuits/src/taiko_pi_circuit.rs b/zkevm-circuits/src/taiko_pi_circuit.rs index 9c5f949aaf..e231b8076d 100644 --- a/zkevm-circuits/src/taiko_pi_circuit.rs +++ b/zkevm-circuits/src/taiko_pi_circuit.rs @@ -268,12 +268,20 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { 1, ); let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(cm), Some(challenges.evm_word())); + cb.preload_tables(meta, + &[ + (PiCellType::Lookup(Keccak), &keccak_table), + (PiCellType::Lookup(Bytecode), &byte_table), + (PiCellType::Lookup(Block), &block_table) + ] + ); // field bytes meta.create_gate( - "rpi_field_bytes_acc[i+1] = rpi_field_bytes_acc[i] * t + rpi_bytes[i+1]", + "PI acc constraints", |meta| { circuit!([meta, cb], { + // rpi_field_bytes_acc[i+1] = rpi_field_bytes_acc[i] * t + rpi_bytes[i+1] ifx!(q!(q_field_step) => { let t = ifx!(f!(is_field_rlc) => { challenges.evm_word() @@ -284,13 +292,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { a!(rpi_field_bytes_acc, 1) => a!(rpi_field_bytes_acc) * t + a!(rpi_field_bytes, 1) ); }); - }); - cb.build_constraints() - }); - meta.create_gate("rpi_field_bytes_acc[0] = rpi_field_bytes[0]", - |meta| { - cb.restart(); - circuit!([meta, cb], { + // rpi_field_bytes_acc[0] = rpi_field_bytes[0] ifx!(q!(q_field_start) => { require!(a!(rpi_field_bytes_acc) => a!(rpi_field_bytes)); }); @@ -302,28 +304,12 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { cb.restart(); circuit!([meta, cb], { ifx!(q!(q_keccak) => { + // require!( (1.expr(), a!(rpi_field_bytes_acc), RPI_BYTES_LEN.expr(), a!(rpi_rlc_acc)) => @PiCellType::Lookup(Table::Keccak), (TO_FIX) ); }); - - // TODO(Cecilia): All lookups should work in one gate - // ifx!(q!(q_block_table) => { - // require!( - // ( - // BlockContextFieldTag::BlockHash.expr(), - // a!(block_index), - // a!(rpi_field_bytes_acc) - // ) => @PiCellType::Lookup(Table::Block), (TO_FIX) - // ); - // }); - // ifx!(or::expr([q!(q_field_start), q!(q_field_end)]) => { - // require!( - // (a!(rpi_field_bytes)) => @PiCellType::Lookup(Table::Bytecode), (TO_FIX) - // ); - // }); - }); cb.build_constraints() }); @@ -331,11 +317,12 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { meta.create_gate("in block table", |meta| { circuit!([meta, cb], { ifx!(q!(q_block_table) => { + // (BlockHashTag, block#, f0*c^0+f1*c^1+...), field rlc with challenge require!( ( BlockContextFieldTag::BlockHash.expr(), a!(block_index), - a!(rpi_field_bytes_acc) + a!(rpi_field_bytes_acc) // 此时等于 block hash ) => @PiCellType::Lookup(Table::Block), (TO_FIX) ); }); @@ -357,11 +344,10 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { cb.build_lookups( meta, &[cb.cell_manager.clone().unwrap()], - &[], &[ - (PiCellType::Lookup(Table::Keccak), Some(&keccak_table)), - (PiCellType::Lookup(Table::Block), Some(&block_table)), - (PiCellType::Lookup(Table::Bytecode), Some(&byte_table)), + (PiCellType::Lookup(Table::Keccak), PiCellType::Lookup(Table::Keccak)), + (PiCellType::Lookup(Table::Block),PiCellType::Lookup(Table::Block)), + (PiCellType::Lookup(Table::Bytecode), PiCellType::Lookup(Table::Bytecode)), ], ); @@ -406,21 +392,23 @@ impl TaikoPiCircuitConfig { ) -> Result>, Error> { let len = field_bytes.len(); let mut field_rlc_acc = Value::known(F::ZERO); // field 的 - let (use_rlc, t) = if len * 8 > F::CAPACITY as usize { + let (use_rlc, t, t_) = if len * 8 > F::CAPACITY as usize { // 正常字段 - (F::ONE, challenges.evm_word()) + (F::ONE, challenges.evm_word(), "evm_work") } else { + println!("use_rlc {:?}, t={:?}", len, BYTE_POW_BASE); // lo hi - (F::ZERO, Value::known(F::from(BYTE_POW_BASE))) + (F::ZERO, Value::known(F::from(BYTE_POW_BASE)), "BYTE_POW_BASE") }; - let randomness = if keccak_hi_lo { + let (randomness, r_) = if keccak_hi_lo { // input - challenges.evm_word() + (challenges.evm_word(), "evm_word") } else { // keccak input - challenges.keccak_input() + (challenges.keccak_input(), "keccak_input") }; + println!(" t={:?}, r={:?}", t_, r_); let mut cells = vec![None; field_bytes.len() + 2]; for (i, byte) in field_bytes.iter().enumerate() { let row_offset = *offset + i; @@ -432,7 +420,7 @@ impl TaikoPiCircuitConfig { || Value::known(use_rlc), )?; - // assign field bytes + // 直接写 Byte let field_byte_cell = region.assign_advice( || "field bytes", self.rpi_field_bytes, @@ -440,8 +428,8 @@ impl TaikoPiCircuitConfig { || Value::known(F::from(*byte as u64)), )?; - field_rlc_acc = field_rlc_acc * t + Value::known(F::from(*byte as u64)); // 字段 + field_rlc_acc = field_rlc_acc * t + Value::known(F::from(*byte as u64)); let rpi_cell = region.assign_advice( || "field bytes acc", self.rpi_field_bytes_acc, @@ -449,8 +437,10 @@ impl TaikoPiCircuitConfig { || field_rlc_acc, )?; - *rpi_rlc_acc = *rpi_rlc_acc * randomness + Value::known(F::from(*byte as u64)); // 总体 + // 到了 keccak hi & low 那俩行,用 evm_word + // 否则用 keccak_input + *rpi_rlc_acc = *rpi_rlc_acc * randomness + Value::known(F::from(*byte as u64)); let rpi_rlc_acc_cell = region.assign_advice( || "rpi_rlc_acc", self.rpi_rlc_acc, @@ -469,6 +459,7 @@ impl TaikoPiCircuitConfig { cells[RPI_CELL_IDX] = Some(rpi_cell); cells[RPI_RLC_ACC_CELL_IDX] = Some(rpi_rlc_acc_cell); if let Some(block_number) = block_number { + println!("---self.q_block_table.enable---"); self.q_block_table.enable(region, row_offset)?; region.assign_advice( || "block_index", @@ -498,7 +489,9 @@ impl TaikoPiCircuitConfig { let mut rpi_rlc_acc = Value::known(F::ZERO); let mut offset = 0; let mut rpi_rlc_acc_cell = None; + println!("\n==================="); for (annotation, block_number, field_bytes) in public_data.assignments() { + println!("{:?}, len {:?}, offset {:?}", annotation, field_bytes.len(), offset); let cells = self.assign_pi_field( region, &mut offset, @@ -535,8 +528,11 @@ impl TaikoPiCircuitConfig { keccak_row, || keccak_output_rlc, )?; + println!("---self.q_keccak.enable---"); self.q_keccak.enable(region, keccak_row)?; + // 设成零 !!!!! + rpi_rlc_acc = Value::known(F::ZERO); offset += 1; let mut pi = Vec::with_capacity(2); @@ -557,6 +553,7 @@ impl TaikoPiCircuitConfig { .into_iter() .enumerate() { + println!("high_16_bytes_of_keccak_rpi"); let cells = self.assign_pi_field( region, &mut offset, diff --git a/zkevm-circuits/src/taiko_pi_circuit__.rs b/zkevm-circuits/src/taiko_pi_circuit__.rs new file mode 100644 index 0000000000..c60edcafab --- /dev/null +++ b/zkevm-circuits/src/taiko_pi_circuit__.rs @@ -0,0 +1,433 @@ +//! Use the hash value as public input. + +use crate::{ + evm_circuit::table::Table::*, + evm_circuit::{util::{constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, rlc}, table::Table}, + table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}, + util::{random_linear_combine_word as rlc, Challenges, SubCircuit, SubCircuitConfig}, + circuit_tools::{ + constraint_builder::{ConstraintBuilder, TO_FIX, COMPRESS, REDUCE}, + cell_manager::{CellManager, CellType, Cell}, gadgets::IsEqualGadget, + }, + + witness::{self, BlockContext}, circuit, +}; +use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256}; +use ethers_core::utils::keccak256; +use gadgets::{util::{or, select, Expr, and}, impl_expr}; +use halo2_proofs::{ + circuit::{AssignedCell, Layouter, Region, SimpleFloorPlanner, Value}, + plonk::{ + Advice, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, Instance, SecondPhase, + Selector, VirtualCells, + }, + poly::Rotation, +}; +use std::{marker::PhantomData, ops::Range, usize, default}; + +const BYTE_POW_BASE: u64 = 1 << 8; +const N: usize = 32; +const RPI_BYTES_LEN: usize = 32 * 10; + + +/// PublicData contains all the values that the PiCircuit receives as input +#[derive(Debug, Clone, Default)] +pub struct PublicData { + /// l1 signal service address + pub l1_signal_service: Word, + /// l2 signal service address + pub l2_signal_service: Word, + /// l2 contract address + pub l2_contract: Word, + /// meta hash + pub meta_hash: Word, + /// block hash value + pub block_hash: Word, + /// the parent block hash + pub parent_hash: Word, + /// signal root + pub signal_root: Word, + /// extra message + pub graffiti: Word, + /// union field + pub field9: Word, // prover[96:256]+parentGasUsed[64:96]+gasUsed[32:64] + /// union field + pub field10: Word, /* blockMaxGasLimit[192:256]+maxTransactionsPerBlock[128: + * 192]+maxBytesPerTxList[64:128] */ + + // privates + // Prover address + prover: Address, + // parent block gas used + parent_gas_used: u32, + // block gas used + gas_used: u32, + // blockMaxGasLimit + block_max_gas_limit: u64, + // maxTransactionsPerBlock: u64, + max_transactions_per_block: u64, + // maxBytesPerTxList: u64, + max_bytes_per_tx_list: u64, + + block_context: BlockContext, + chain_id: Word, +} + +impl PublicData { + fn assignments(&self) -> [(&'static str, Option, [u8; 32]); 10] { + let res = [ + ( + "l1_signal_service", + None, + self.l1_signal_service.to_be_bytes(), + ), + ( + "l2_signal_service", + None, + self.l2_signal_service.to_be_bytes(), + ), + ("l2_contract", None, self.l2_contract.to_be_bytes()), + ("meta_hash", None, self.meta_hash.to_be_bytes()), + ( + "parent_hash", + Some(self.block_context.number - 1), + self.parent_hash.to_be_bytes(), + ), + ( + "block_hash", + Some(self.block_context.number), + self.block_hash.to_be_bytes(), + ), + ("signal_root", None, self.signal_root.to_be_bytes()), + ("graffiti", None, self.graffiti.to_be_bytes()), + ( + "prover+parentGasUsed+gasUsed", + None, + self.field9.to_be_bytes(), + ), + ( + "blockMaxGasLimit+maxTransactionsPerBlock+maxBytesPerTxList", + None, + self.field10.to_be_bytes(), + ), + ]; + res.iter().for_each(|field|{ + println!("{:?}, len {:?}", field.0, field.2.len()); + }); + res + } + + /// get rpi bytes + pub fn rpi_bytes(&self) -> Vec { + self.assignments().iter().flat_map(|v| v.2).collect() + } + + fn default() -> Self { + Self::new::(&witness::Block::default()) + } + + /// create PublicData from block and taiko + pub fn new(block: &witness::Block) -> Self { + use witness::left_shift; + let field9 = left_shift(block.protocol_instance.prover, 96) + + left_shift(block.protocol_instance.parent_gas_used as u64, 64) + + left_shift(block.protocol_instance.gas_used as u64, 32); + + let field10 = left_shift(block.protocol_instance.block_max_gas_limit, 192) + + left_shift(block.protocol_instance.max_transactions_per_block, 128) + + left_shift(block.protocol_instance.max_bytes_per_tx_list, 64); + PublicData { + l1_signal_service: block.protocol_instance.l1_signal_service.to_word(), + l2_signal_service: block.protocol_instance.l2_signal_service.to_word(), + l2_contract: block.protocol_instance.l2_contract.to_word(), + meta_hash: block.protocol_instance.meta_hash.hash().to_word(), + block_hash: block.protocol_instance.block_hash.to_word(), + parent_hash: block.protocol_instance.parent_hash.to_word(), + signal_root: block.protocol_instance.signal_root.to_word(), + graffiti: block.protocol_instance.graffiti.to_word(), + prover: block.protocol_instance.prover, + parent_gas_used: block.protocol_instance.parent_gas_used, + gas_used: block.protocol_instance.gas_used, + block_max_gas_limit: block.protocol_instance.block_max_gas_limit, + max_transactions_per_block: block.protocol_instance.max_transactions_per_block, + max_bytes_per_tx_list: block.protocol_instance.max_bytes_per_tx_list, + field9, + field10, + block_context: block.context.clone(), + chain_id: block.context.chain_id, + } + } + + fn get_pi(&self) -> H256 { + let rpi_bytes = self.rpi_bytes(); + let rpi_keccak = keccak256(rpi_bytes); + H256(rpi_keccak) + } +} + +/// Field Bytes +#[derive(Clone, Debug, Default)] +pub struct FieldBytesGadget { + bytes: [Cell; N], + block_idx: Option>, + keccak_input: Option>, + // len: Cell, + // acc: Cell, + // is_keccak: IsEqualGadget, + // is_block: IsEqualGadget, +} + +impl FieldBytesGadget { + + fn config(&mut self, cb: &mut ConstraintBuilder) { + self.bytes = cb.query_bytes(); + } + + fn bytes_expr(&self, range: Range) -> Vec> { + assert!(range.end < N); + range.map(|i| self.bytes[i].expr()).collect() + } + + fn hi_low_fields(&self) -> (Expression, Expression){ + ( + rlc::expr(&self.bytes_expr(0..N/2), BYTE_POW_BASE.expr()), + rlc::expr(&self.bytes_expr(N/2..N), BYTE_POW_BASE.expr()), + ) + } + + fn evm_word_field(&self, challenges: Challenges>) -> Expression { + rlc::expr(&self.bytes_expr(0..N), challenges.evm_word().expr()) + } + + fn keccak_field(&self, challenges: Challenges>) -> Expression { + rlc::expr(&self.bytes_expr(0..N), challenges.keccak_input().expr()) + } + + fn keccak_input_acc(&self, challenges: Challenges>, prev_field: Expression) -> Expression { + let raise_exp = (0..self.bytes.len()) + .fold(1.expr(), |acc: Expression, _|acc * challenges.keccak_input().expr()); + prev_field * raise_exp + self.keccak_field(challenges.clone()) + } + + fn keccak_output_acc(&self, challenges: Challenges>) -> Expression { + let raise_exp = (0..self.bytes.len()) + .fold(1.expr(), |acc: Expression, _|acc * challenges.evm_word().expr()); + let (hi, low) = self.hi_low_fields(); + hi * raise_exp + low + } + + fn set_keccak_input( + &mut self, + cb: &mut ConstraintBuilder, + keccak_input_acc: Expression, + challenges: Challenges>, + ) { + let keccak_input = cb.query_cell_with_type(PiCellType::Storage2); + cb.require_equal( + "Copy keccak input from acc column to temporary cell", + keccak_input_acc, + keccak_input.expr() + ); + } + + fn keccak_lookup( + &mut self, + cb: &mut ConstraintBuilder, + challenges: Challenges>, + ) -> (Expression, Expression, Expression, Expression) { + if let Some(input) = self.keccak_input.clone() { + return ( + 1.expr(), + input.expr(), + RPI_BYTES_LEN.expr(), + self.keccak_output_acc(challenges) + ) + } else { + panic!("Cannot configure keccak lookup without input"); + } + } + + fn block_hash_lookup( + &mut self, + cb: &mut ConstraintBuilder, + challenges: Challenges>, + ) -> (Expression, Expression, Expression) { + let block_idx = cb.query_default(); + self.block_idx = Some(block_idx.clone()); + ( + BlockContextFieldTag::BlockHash.expr(), + block_idx.expr(), + self.evm_word_field(challenges) + ) + } + + // fn bytes_lookup( + // &mut self, + // cb: &mut ConstraintBuilder, + // ) { + // circuit!([meta, cb], { + // for byte in self.bytes { + // require!((byte.expr()) => @PiCellType::Lookup(Table::Bytecode), (TO_FIX)); + // } + // }); + // } +} + + +/// Config for PiCircuit +#[derive(Clone, Debug)] +pub struct TaikoPiCircuitConfig { + field_idx: Column, + block_acc: Column, + field_gadget: FieldBytesGadget, +} + +/// Circuit configuration arguments +pub struct TaikoPiCircuitConfigArgs { + /// BlockTable + pub block_table: BlockTable, + /// KeccakTable + pub keccak_table: KeccakTable, + /// ByteTable + pub byte_table: ByteTable, + /// Challenges + pub challenges: Challenges>, +} + +/// +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum PiCellType { + /// + Storage1, + /// + Storage2, + /// + Byte, + /// + Lookup(Table) +} +impl CellType for PiCellType { + fn byte_type() -> Option { + Some(Self::Byte) + } + fn storage_for_phase(phase: u8) -> Self { + match phase { + 1 => PiCellType::Storage1, + 2 => PiCellType::Storage2, + _ => unimplemented!() + } + } +} +impl Default for PiCellType { + fn default() -> Self { + Self::Storage1 + } +} + +/// +#[derive(Clone, Copy)] +pub enum PiState { + /// + Start, + /// + Block, + /// + Last, +} +impl_expr!(PiState); + + +impl SubCircuitConfig for TaikoPiCircuitConfig { + type ConfigArgs = TaikoPiCircuitConfigArgs; + + /// Return a new TaikoPiCircuitConfig + fn new( + meta: &mut ConstraintSystem, + Self::ConfigArgs { + block_table, + keccak_table, + byte_table, + challenges, + }: Self::ConfigArgs, + ) -> Self { + let cm = CellManager::new( + meta, + vec![ + (PiCellType::Byte, 32, 1, false), + (PiCellType::Storage1, 3, 2, false), + ], + 0, + 1, + ); + let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(cm), Some(challenges.evm_word())); + cb.preload_tables(meta, + &[ + (PiCellType::Lookup(Keccak), &keccak_table), + (PiCellType::Lookup(Bytecode), &byte_table), + (PiCellType::Lookup(Block), &block_table) + ] + ); + + + let field_idx = meta.advice_column(); + let block_acc = meta.advice_column(); + let mut field_gadget = FieldBytesGadget::default(); + meta.create_gate( + "PI acc constraints", + |meta| { + circuit!([meta, cb], { + let q_block = IsEqualGadget::construct(&mut cb, a!(field_idx), PiState::Block.expr()); + let q_last_field = IsEqualGadget::construct(&mut cb, a!(field_idx), PiState::Last.expr()); + field_gadget.config(&mut cb); + ifx! (Self::is_block_field(&mut cb, a!(field_idx)) => { + // do this directly in column + // field_gadget.bytes_lookup(&mut cb); + require!( + a!(block_acc, 1) => field_gadget.keccak_input_acc(challenges.clone(), a!(block_acc)) + ); + ifx!(q_block.expr() => { + let data = field_gadget.block_hash_lookup(&mut cb, challenges.clone()); + require!((data.0, data.1, data.2) => @PiCellType::Lookup(Table::Block), (COMPRESS, REDUCE)); + }); + ifx!(q_last_field.expr() => { + field_gadget.set_keccak_input(&mut cb, a!(block_acc), challenges.clone()); + }); + } elsex { + let data = field_gadget.keccak_lookup(&mut cb, challenges.clone()); + require!((data.0, data.1, data.2, data.3) => @PiCellType::Lookup(Table::Keccak), (COMPRESS, REDUCE)); + }); + }); + cb.build_constraints() + } + ); + Self { + field_idx, + block_acc, + field_gadget + } + } + +} + +impl TaikoPiCircuitConfig { + + fn is_block_field( + cb: &mut ConstraintBuilder, + field_idx: Expression + ) -> Expression { + let mut expr = 0.expr(); + circuit!([meta, cb], { + let is_block_field = (0..9) + .fold(1.expr(), |acc, item| acc * (field_idx.clone() - item.expr())); + expr = cb.local_processing( + "is_block_field range check", + &[is_block_field], + PiCellType::Storage1, + None, + true, + true + )[0].clone(); + }); + expr + } +} \ No newline at end of file From ca4e47cdf466c2c7378fd52d09902e87bcb4857a Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Wed, 16 Aug 2023 05:01:13 +0800 Subject: [PATCH 07/43] apis done --- zkevm-circuits/src/lib.rs | 1 + zkevm-circuits/src/taiko_pi_circuit__.rs | 384 +++++++++++++++-------- 2 files changed, 246 insertions(+), 139 deletions(-) diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index cf049455c6..b07c812575 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -28,6 +28,7 @@ pub mod state_circuit; pub mod super_circuit; pub mod table; pub mod taiko_pi_circuit; +#[macro_use] pub mod taiko_pi_circuit__; pub mod taiko_super_circuit; diff --git a/zkevm-circuits/src/taiko_pi_circuit__.rs b/zkevm-circuits/src/taiko_pi_circuit__.rs index c60edcafab..0799719341 100644 --- a/zkevm-circuits/src/taiko_pi_circuit__.rs +++ b/zkevm-circuits/src/taiko_pi_circuit__.rs @@ -1,17 +1,19 @@ //! Use the hash value as public input. use crate::{ + assign, evm_circuit::table::Table::*, evm_circuit::{util::{constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, rlc}, table::Table}, table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}, - util::{random_linear_combine_word as rlc, Challenges, SubCircuit, SubCircuitConfig}, + util::{Challenges, SubCircuitConfig}, circuit_tools::{ - constraint_builder::{ConstraintBuilder, TO_FIX, COMPRESS, REDUCE}, - cell_manager::{CellManager, CellType, Cell}, gadgets::IsEqualGadget, + constraint_builder::{ConstraintBuilder, RLCable, RLCChainable, TO_FIX, COMPRESS, REDUCE, RLCableValue}, + cell_manager::{CellManager, CellType, Cell}, gadgets::IsEqualGadget, cached_region::CachedRegion, }, witness::{self, BlockContext}, circuit, }; +use gadgets::util::Scalar; use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256}; use ethers_core::utils::keccak256; use gadgets::{util::{or, select, Expr, and}, impl_expr}; @@ -21,7 +23,7 @@ use halo2_proofs::{ Advice, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, Instance, SecondPhase, Selector, VirtualCells, }, - poly::Rotation, + poly::{Rotation}, }; use std::{marker::PhantomData, ops::Range, usize, default}; @@ -165,113 +167,6 @@ impl PublicData { } } -/// Field Bytes -#[derive(Clone, Debug, Default)] -pub struct FieldBytesGadget { - bytes: [Cell; N], - block_idx: Option>, - keccak_input: Option>, - // len: Cell, - // acc: Cell, - // is_keccak: IsEqualGadget, - // is_block: IsEqualGadget, -} - -impl FieldBytesGadget { - - fn config(&mut self, cb: &mut ConstraintBuilder) { - self.bytes = cb.query_bytes(); - } - - fn bytes_expr(&self, range: Range) -> Vec> { - assert!(range.end < N); - range.map(|i| self.bytes[i].expr()).collect() - } - - fn hi_low_fields(&self) -> (Expression, Expression){ - ( - rlc::expr(&self.bytes_expr(0..N/2), BYTE_POW_BASE.expr()), - rlc::expr(&self.bytes_expr(N/2..N), BYTE_POW_BASE.expr()), - ) - } - - fn evm_word_field(&self, challenges: Challenges>) -> Expression { - rlc::expr(&self.bytes_expr(0..N), challenges.evm_word().expr()) - } - - fn keccak_field(&self, challenges: Challenges>) -> Expression { - rlc::expr(&self.bytes_expr(0..N), challenges.keccak_input().expr()) - } - - fn keccak_input_acc(&self, challenges: Challenges>, prev_field: Expression) -> Expression { - let raise_exp = (0..self.bytes.len()) - .fold(1.expr(), |acc: Expression, _|acc * challenges.keccak_input().expr()); - prev_field * raise_exp + self.keccak_field(challenges.clone()) - } - - fn keccak_output_acc(&self, challenges: Challenges>) -> Expression { - let raise_exp = (0..self.bytes.len()) - .fold(1.expr(), |acc: Expression, _|acc * challenges.evm_word().expr()); - let (hi, low) = self.hi_low_fields(); - hi * raise_exp + low - } - - fn set_keccak_input( - &mut self, - cb: &mut ConstraintBuilder, - keccak_input_acc: Expression, - challenges: Challenges>, - ) { - let keccak_input = cb.query_cell_with_type(PiCellType::Storage2); - cb.require_equal( - "Copy keccak input from acc column to temporary cell", - keccak_input_acc, - keccak_input.expr() - ); - } - - fn keccak_lookup( - &mut self, - cb: &mut ConstraintBuilder, - challenges: Challenges>, - ) -> (Expression, Expression, Expression, Expression) { - if let Some(input) = self.keccak_input.clone() { - return ( - 1.expr(), - input.expr(), - RPI_BYTES_LEN.expr(), - self.keccak_output_acc(challenges) - ) - } else { - panic!("Cannot configure keccak lookup without input"); - } - } - - fn block_hash_lookup( - &mut self, - cb: &mut ConstraintBuilder, - challenges: Challenges>, - ) -> (Expression, Expression, Expression) { - let block_idx = cb.query_default(); - self.block_idx = Some(block_idx.clone()); - ( - BlockContextFieldTag::BlockHash.expr(), - block_idx.expr(), - self.evm_word_field(challenges) - ) - } - - // fn bytes_lookup( - // &mut self, - // cb: &mut ConstraintBuilder, - // ) { - // circuit!([meta, cb], { - // for byte in self.bytes { - // require!((byte.expr()) => @PiCellType::Lookup(Table::Bytecode), (TO_FIX)); - // } - // }); - // } -} /// Config for PiCircuit @@ -324,15 +219,21 @@ impl Default for PiCellType { } } -/// #[derive(Clone, Copy)] -pub enum PiState { - /// - Start, - /// - Block, - /// - Last, +enum PiState { + L1Signal, + L2Signal, + L2Contract, + MetaHash, + ParentHash, + BlockHash, + SignalRoot, + Graffiti, + Field9, + Field10, + + KeccakHi, + KeccakLow, } impl_expr!(PiState); @@ -350,16 +251,22 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { challenges, }: Self::ConfigArgs, ) -> Self { - let cm = CellManager::new( + let field_cm = CellManager::new( meta, - vec![ - (PiCellType::Byte, 32, 1, false), - (PiCellType::Storage1, 3, 2, false), - ], + vec![(PiCellType::Byte, 32, 1, false)], 0, 1, ); - let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(cm), Some(challenges.evm_word())); + let state_cm = CellManager::new( + meta, + vec![ + (PiCellType::Storage1, 2, 1, false), + (PiCellType::Storage2, 2, 2, false), + ], + 0, + 5, + ); + let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(field_cm), Some(challenges.evm_word())); cb.preload_tables(meta, &[ (PiCellType::Lookup(Keccak), &keccak_table), @@ -372,38 +279,72 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { let field_idx = meta.advice_column(); let block_acc = meta.advice_column(); let mut field_gadget = FieldBytesGadget::default(); + field_gadget.challenges = Some(challenges.clone()); meta.create_gate( "PI acc constraints", |meta| { circuit!([meta, cb], { - let q_block = IsEqualGadget::construct(&mut cb, a!(field_idx), PiState::Block.expr()); - let q_last_field = IsEqualGadget::construct(&mut cb, a!(field_idx), PiState::Last.expr()); + cb.set_cell_manager(state_cm.clone()); + let is_block_fields = Self::is_block_field(&mut cb, a!(field_idx)); + let q_block = Self::idx_range(&mut cb, a!(field_idx), &[PiState::ParentHash, PiState::BlockHash]); + let q_keccak = Self::idx_range(&mut cb, a!(field_idx), &[PiState::KeccakLow]); + let q_last_field = Self::idx_range(&mut cb, a!(field_idx), &[PiState::Field10]); + field_gadget.config(&mut cb); - ifx! (Self::is_block_field(&mut cb, a!(field_idx)) => { + ifx! (is_block_fields => { // do this directly in column // field_gadget.bytes_lookup(&mut cb); require!( - a!(block_acc, 1) => field_gadget.keccak_input_acc(challenges.clone(), a!(block_acc)) + a!(block_acc, 1) => field_gadget.block_input_acc(a!(block_acc)) ); ifx!(q_block.expr() => { - let data = field_gadget.block_hash_lookup(&mut cb, challenges.clone()); - require!((data.0, data.1, data.2) => @PiCellType::Lookup(Table::Block), (COMPRESS, REDUCE)); + let block_number = field_gadget.set_block_number(&mut cb); + require!( + ( + BlockContextFieldTag::BlockHash.expr(), + block_number.expr(), + field_gadget.evm_word_field() + ) => @PiCellType::Lookup(Table::Block), (COMPRESS, REDUCE) + ); }); + // On the last bytes of the last field + // the we copy block_acc to a tmp cell in field_gadget ifx!(q_last_field.expr() => { - field_gadget.set_keccak_input(&mut cb, a!(block_acc), challenges.clone()); + field_gadget.set_keccak_input(&mut cb, a!(block_acc)); }); } elsex { - let data = field_gadget.keccak_lookup(&mut cb, challenges.clone()); - require!((data.0, data.1, data.2, data.3) => @PiCellType::Lookup(Table::Keccak), (COMPRESS, REDUCE)); + // block_acc should be reset to 0 + require!( + a!(block_acc, 1) => field_gadget.keccak_output_acc(a!(block_acc)) + ); + ifx!(q_keccak.expr() => { + require!( + ( + 1.expr(), + field_gadget.keccak_input.clone().unwrap().expr(), + RPI_BYTES_LEN.expr(), + a!(block_acc) + ) + => @PiCellType::Lookup(Table::Keccak), (COMPRESS, REDUCE)); + }); }); }); cb.build_constraints() } ); + cb.build_lookups( + meta, + &[state_cm], + &[ + (PiCellType::Byte, PiCellType::Lookup(Bytecode)), + (PiCellType::Lookup(Table::Keccak), PiCellType::Lookup(Table::Keccak)), + (PiCellType::Lookup(Table::Block), PiCellType::Lookup(Table::Block)), + ] + ); Self { field_idx, block_acc, - field_gadget + field_gadget: field_gadget } } @@ -411,16 +352,17 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { impl TaikoPiCircuitConfig { - fn is_block_field( + fn idx_range( cb: &mut ConstraintBuilder, - field_idx: Expression + field_idx: Expression, + range: &[PiState] ) -> Expression { let mut expr = 0.expr(); circuit!([meta, cb], { - let is_block_field = (0..9) + let is_block_field = range.iter() .fold(1.expr(), |acc, item| acc * (field_idx.clone() - item.expr())); expr = cb.local_processing( - "is_block_field range check", + "field idx range check", &[is_block_field], PiCellType::Storage1, None, @@ -430,4 +372,168 @@ impl TaikoPiCircuitConfig { }); expr } + + fn is_block_field( + cb: &mut ConstraintBuilder, + field_idx: Expression + ) -> Expression { + Self::idx_range( + cb, + field_idx, + &[ + PiState::L1Signal, + PiState::L2Signal, + PiState::L2Contract, + PiState::MetaHash, + PiState::BlockHash, + PiState::SignalRoot, + PiState::Graffiti, + PiState::Field9, + PiState::Field10 + ] + ) + } + + pub(crate) fn assign( + &self, + layouter: &mut impl Layouter, + public_data: &PublicData, + challenges: &Challenges>, + ) -> Result<(), Error> { + layouter.assign_region( + || "Pi", + |mut region| { + let mut block_acc = Value::known(F::ZERO); + let mut offset = 0; + + Ok(()) + }, + ) + } +} + +/// Field Bytes +#[derive(Clone, Debug, Default)] +pub struct FieldBytesGadget { + challenges: Option>>, + bytes: [Cell; N], + block_number: Option>, + keccak_input: Option>, +} + +impl FieldBytesGadget { + + fn config(&mut self, cb: &mut ConstraintBuilder) { + self.bytes = cb.query_bytes(); + } + + fn bytes_expr(&self) -> Vec> { + self.bytes.iter().map(|b| b.expr()).collect() + } + + /// RLC of bytes of a field with evm_word 1<<8 + fn hi_low_fields_(&self) -> Expression { + self.bytes_expr().rlc(&BYTE_POW_BASE.expr()) + } + + /// RLC of bytes of a field with evm_word + fn evm_word_field(&self) -> Expression { + let r = self.challenges.clone().unwrap().evm_word().expr(); + self.bytes_expr().rlc(&r) + } + + /// RLC of bytes of a field with keccak_input + fn keccak_field(&self) -> Expression { + let r = self.challenges.clone().unwrap().keccak_input().expr(); + self.bytes_expr().rlc(&r) + } + + // ------------------ Acc ------------------ + + /// Next value of block field bytes RLC accumulator for keccak input + fn block_input_acc(&self, prev_field: Expression) -> Expression { + let r = self.challenges.clone().unwrap().keccak_input().expr(); + let raise_exp = (0..self.bytes.len()) + .fold(1.expr(), |acc: Expression, _|acc * r.clone()); + prev_field * raise_exp + self.keccak_field() + } + + /// Next value of keccak output hi low bytes accumulator + fn keccak_output_acc(&self, hi: Expression) -> Expression { + let r = self.challenges.clone().unwrap().evm_word().expr(); + let raise_exp = (0..self.bytes.len()) + .fold(1.expr(), |acc: Expression, _|acc * r.clone()); + let low = self.hi_low_fields_(); + hi * raise_exp + low + } + + // ------------------ Set Cell ------------------ + + + /// Init a cell for keccak input at the last row of all block fields + fn set_keccak_input( + &mut self, + cb: &mut ConstraintBuilder, + keccak_input_acc: Expression, + ) -> Expression { + let keccak_input = cb.query_cell_with_type(PiCellType::Storage2); + cb.require_equal( + "Copy keccak input from acc column to temporary cell", + keccak_input_acc, + keccak_input.expr() + ); + keccak_input.expr() + } + + /// Init a cell for block idx when we need to lookup block table + fn set_block_number(&mut self, cb: &mut ConstraintBuilder) -> Expression { + let block_number = cb.query_default(); + self.block_number = Some(block_number.clone()); + block_number.expr() + } + + // ------------------ Assign ------------------ + + pub(crate) fn assign_bytes( + &self, + region: &mut CachedRegion<'_, '_, F>, + r: F, + offset: usize, + bytes: &[u8], + ) -> Result { + // Assign the bytes + for (byte, column) in bytes.iter().zip(self.bytes.iter()) { + assign!(region, (column.column(), offset) => byte.scalar())?; + } + let mut rlc = bytes.rlc_value(r); + Ok(rlc) + } + + pub(crate) fn assign_block_number( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + block_number: F, + ) -> Result<(), Error> { + if let Some(cell) = &self.block_number { + cell.assign(region, offset, block_number)?; + Ok(()) + } else { + Err(Error::Synthesis) + } + } + + pub(crate) fn assign_keccak_acc( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + keccak_input: F + ) -> Result<(), Error> { + if let Some(cell) = &self.keccak_input { + cell.assign(region, offset, keccak_input)?; + Ok(()) + } else { + Err(Error::Synthesis) + } + } } \ No newline at end of file From 0f52e56f228b216bde71f8f898f71ead9caf396f Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Wed, 16 Aug 2023 16:47:56 +0800 Subject: [PATCH 08/43] cb.equalities --- zkevm-circuits/src/circuit_tools/cached_region.rs | 12 +++--------- .../src/circuit_tools/constraint_builder.rs | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/zkevm-circuits/src/circuit_tools/cached_region.rs b/zkevm-circuits/src/circuit_tools/cached_region.rs index 189708df2e..b4339a3558 100644 --- a/zkevm-circuits/src/circuit_tools/cached_region.rs +++ b/zkevm-circuits/src/circuit_tools/cached_region.rs @@ -26,27 +26,21 @@ pub struct CachedRegion<'r, 'b, F: Field> { region: &'r mut Region<'b, F>, pub advice: HashMap<(usize, usize), F>, pub fixed: HashMap<(usize, usize), F>, - disable_description: bool, regions: Vec<(usize, usize)>, - pub r: F, - pub keccak_r: F, } impl<'r, 'b, F: Field> CachedRegion<'r, 'b, F> { - pub(crate) fn new(region: &'r mut Region<'b, F>, r: F, keccak_r: F) -> Self { + pub(crate) fn new(region: &'r mut Region<'b, F>) -> Self { Self { region, advice: HashMap::new(), fixed: HashMap::new(), - disable_description: false, regions: Vec::new(), - r, - keccak_r, } } - pub(crate) fn set_disable_description(&mut self, disable_description: bool) { - self.disable_description = disable_description; + pub(crate) fn inner(&mut self) -> &mut Region<'b, F> { + self.region } pub(crate) fn push_region(&mut self, offset: usize, region_id: usize) { diff --git a/zkevm-circuits/src/circuit_tools/constraint_builder.rs b/zkevm-circuits/src/circuit_tools/constraint_builder.rs index 4db26963a4..e4ff617e5e 100644 --- a/zkevm-circuits/src/circuit_tools/constraint_builder.rs +++ b/zkevm-circuits/src/circuit_tools/constraint_builder.rs @@ -9,7 +9,7 @@ use crate::{evm_circuit::util::rlc, table::LookupTable, util::{Expr, query_expre use eth_types::Field; use gadgets::util::{and, sum, Scalar}; use halo2_proofs::{ - plonk::{ConstraintSystem, Expression}, + plonk::{ConstraintSystem, Expression, Column, Advice}, }; use itertools::Itertools; @@ -127,6 +127,8 @@ pub struct ConstraintBuilder { max_degree: usize, /// conditions for constraints conditions: Vec>, + /// Columns whoes equality constraints needed to be enable + equalities: Vec>, /// The lookups generated during synthesis /// assembles runtime access to RAM pub lookups: HashMap>>, @@ -164,6 +166,7 @@ impl ConstraintBuilder { max_global_degree: max_degree, max_degree, conditions: Vec::new(), + equalities: Vec::new(), lookups: HashMap::new(), dynamic_tables: HashMap::new(), fixed_tables: HashMap::new(), @@ -328,6 +331,10 @@ impl ConstraintBuilder { self.get_condition().unwrap_or_else(|| 1.expr()) } + pub(crate) fn enable_equality(&mut self, column: Column){ + self.equalities.push(column); + } + // Query pub(crate) fn query_bool(&mut self) -> Cell { @@ -386,6 +393,12 @@ impl ConstraintBuilder { self.constraints.clone() } + pub(crate) fn build_equalities(&self, meta: &mut ConstraintSystem) { + self.equalities + .iter() + .for_each(|c| meta.enable_equality(c.clone())); + } + pub(crate) fn build_fixed_path( &mut self, meta: &mut ConstraintSystem, From 362346dac969fa012db16e4889b9fd6d7cd0221f Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Wed, 16 Aug 2023 16:48:17 +0800 Subject: [PATCH 09/43] assignments done --- zkevm-circuits/src/taiko_pi_circuit__.rs | 73 ++++++++++++++++++++---- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/zkevm-circuits/src/taiko_pi_circuit__.rs b/zkevm-circuits/src/taiko_pi_circuit__.rs index 0799719341..4cfc91232a 100644 --- a/zkevm-circuits/src/taiko_pi_circuit__.rs +++ b/zkevm-circuits/src/taiko_pi_circuit__.rs @@ -8,7 +8,7 @@ use crate::{ util::{Challenges, SubCircuitConfig}, circuit_tools::{ constraint_builder::{ConstraintBuilder, RLCable, RLCChainable, TO_FIX, COMPRESS, REDUCE, RLCableValue}, - cell_manager::{CellManager, CellType, Cell}, gadgets::IsEqualGadget, cached_region::CachedRegion, + cell_manager::{CellManager, CellType, Cell}, gadgets::IsEqualGadget, cached_region::{CachedRegion, self}, }, witness::{self, BlockContext}, circuit, @@ -236,7 +236,11 @@ enum PiState { KeccakLow, } impl_expr!(PiState); - +impl Into for PiState { + fn into(self) -> usize { + self as usize + } +} impl SubCircuitConfig for TaikoPiCircuitConfig { type ConfigArgs = TaikoPiCircuitConfigArgs; @@ -332,6 +336,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { cb.build_constraints() } ); + cb.build_equalities(meta); cb.build_lookups( meta, &[state_cm], @@ -385,6 +390,7 @@ impl TaikoPiCircuitConfig { PiState::L2Signal, PiState::L2Contract, PiState::MetaHash, + PiState::ParentHash, PiState::BlockHash, PiState::SignalRoot, PiState::Graffiti, @@ -403,9 +409,47 @@ impl TaikoPiCircuitConfig { layouter.assign_region( || "Pi", |mut region| { - let mut block_acc = Value::known(F::ZERO); + let mut cached_region = CachedRegion::new(&mut region); + let mut r = F::ZERO; + let mut block_acc = F::ZERO; let mut offset = 0; - + println!("\n==================="); + for (i, (annotation, block_number, bytes)) in public_data + .assignments() + .iter() + .enumerate() { + println!("{:?}, len {:?}, offset {:?}", annotation, bytes.len(), offset); + match i { + i if i <= PiState::Field10.into() => { + challenges.keccak_input().map(|v| r = v); + block_acc += self.field_gadget.assign_bytes(&mut cached_region, r, offset, bytes.as_ref())?; + let block_acc_cell = assign!(cached_region, (self.block_acc, offset) => block_acc)?; + if let Some(block_number) = block_number { + self.field_gadget.assign_block_number( + &mut cached_region, + offset, + block_number.as_u64().scalar() + ); + } + + if i == PiState::Field10 as usize { + self.field_gadget.assign_keccak_acc( + &mut cached_region, + offset, + block_acc_cell + ); + block_acc = F::ZERO; + } + }, + i if i >= PiState::KeccakHi.into() => { + challenges.evm_word().map(|v| r = v); + block_acc += self.field_gadget.assign_bytes(&mut cached_region, r, offset, bytes.as_ref())?; + assign!(cached_region, (self.block_acc, offset) => block_acc)?; + + }, + _ => unreachable!(), + } + } Ok(()) }, ) @@ -477,11 +521,12 @@ impl FieldBytesGadget { keccak_input_acc: Expression, ) -> Expression { let keccak_input = cb.query_cell_with_type(PiCellType::Storage2); - cb.require_equal( - "Copy keccak input from acc column to temporary cell", - keccak_input_acc, - keccak_input.expr() - ); + cb.enable_equality(keccak_input.column()); + // cb.require_equal( + // "Copy keccak input from acc column to temporary cell", + // keccak_input_acc, + // keccak_input.expr() + // ); keccak_input.expr() } @@ -494,6 +539,7 @@ impl FieldBytesGadget { // ------------------ Assign ------------------ + /// Returns the rlc of given bytes pub(crate) fn assign_bytes( &self, region: &mut CachedRegion<'_, '_, F>, @@ -527,10 +573,15 @@ impl FieldBytesGadget { &self, region: &mut CachedRegion<'_, '_, F>, offset: usize, - keccak_input: F + block_acc_cell: AssignedCell, ) -> Result<(), Error> { if let Some(cell) = &self.keccak_input { - cell.assign(region, offset, keccak_input)?; + block_acc_cell.copy_advice( + || "Copy block acc into cell for keccak input", + region.inner(), + cell.column(), + offset + )?; Ok(()) } else { Err(Error::Synthesis) From d91e1b1ed28a346d2b80694c522d344a32e792ad Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Thu, 17 Aug 2023 18:45:45 +0800 Subject: [PATCH 10/43] cb build_equalities & build_constraints with sel --- .../src/circuit_tools/constraint_builder.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/zkevm-circuits/src/circuit_tools/constraint_builder.rs b/zkevm-circuits/src/circuit_tools/constraint_builder.rs index e4ff617e5e..48dae34476 100644 --- a/zkevm-circuits/src/circuit_tools/constraint_builder.rs +++ b/zkevm-circuits/src/circuit_tools/constraint_builder.rs @@ -386,17 +386,27 @@ impl ConstraintBuilder { } } - pub(crate) fn build_constraints(&self) -> Vec<(&'static str, Expression)> { + pub(crate) fn build_constraints(&self, selector: Option>) -> Vec<(&'static str, Expression)> { if self.constraints.is_empty() { return vec![("No constraints", 0.expr())]; } - self.constraints.clone() + selector.map_or( + self.constraints.clone(), + |s| { + self.constraints + .iter() + .map(|(name, c)| (*name, s.expr() * c.clone())) + .collect() + } + ) } pub(crate) fn build_equalities(&self, meta: &mut ConstraintSystem) { self.equalities .iter() - .for_each(|c| meta.enable_equality(c.clone())); + .for_each(|c| { + println!("Enable equality for {:?}", c.index()); + meta.enable_equality(c.clone())}); } pub(crate) fn build_fixed_path( From 059eeba177e672478668e79000ce32e1e533542d Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Thu, 17 Aug 2023 18:46:18 +0800 Subject: [PATCH 11/43] debug constraints --- zkevm-circuits/src/taiko_pi_circuit.rs | 8 +- zkevm-circuits/src/taiko_pi_circuit__.rs | 604 ++++++++++++++++------- 2 files changed, 423 insertions(+), 189 deletions(-) diff --git a/zkevm-circuits/src/taiko_pi_circuit.rs b/zkevm-circuits/src/taiko_pi_circuit.rs index e231b8076d..04f7340583 100644 --- a/zkevm-circuits/src/taiko_pi_circuit.rs +++ b/zkevm-circuits/src/taiko_pi_circuit.rs @@ -297,7 +297,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { require!(a!(rpi_field_bytes_acc) => a!(rpi_field_bytes)); }); }); - cb.build_constraints() + cb.build_constraints(None) }); meta.create_gate("keccak(rpi)", |meta| { @@ -311,7 +311,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { ); }); }); - cb.build_constraints() + cb.build_constraints(None) }); meta.create_gate("in block table", |meta| { @@ -327,7 +327,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { ); }); }); - cb.build_constraints() + cb.build_constraints(None) }); meta.create_gate("is_byte", |meta| { @@ -338,7 +338,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { ); }); }); - cb.build_constraints() + cb.build_constraints(None) }); cb.build_lookups( diff --git a/zkevm-circuits/src/taiko_pi_circuit__.rs b/zkevm-circuits/src/taiko_pi_circuit__.rs index 4cfc91232a..d8fbd339bc 100644 --- a/zkevm-circuits/src/taiko_pi_circuit__.rs +++ b/zkevm-circuits/src/taiko_pi_circuit__.rs @@ -5,10 +5,10 @@ use crate::{ evm_circuit::table::Table::*, evm_circuit::{util::{constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, rlc}, table::Table}, table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}, - util::{Challenges, SubCircuitConfig}, + util::{Challenges, SubCircuitConfig, SubCircuit}, circuit_tools::{ constraint_builder::{ConstraintBuilder, RLCable, RLCChainable, TO_FIX, COMPRESS, REDUCE, RLCableValue}, - cell_manager::{CellManager, CellType, Cell}, gadgets::IsEqualGadget, cached_region::{CachedRegion, self}, + cell_manager::{CellManager, CellType, Cell}, gadgets::{IsEqualGadget, IsZeroGadget}, cached_region::{CachedRegion, self}, }, witness::{self, BlockContext}, circuit, @@ -25,11 +25,21 @@ use halo2_proofs::{ }, poly::{Rotation}, }; +use rand_chacha::rand_core::block; use std::{marker::PhantomData, ops::Range, usize, default}; const BYTE_POW_BASE: u64 = 1 << 8; const N: usize = 32; +const H: usize = 12; const RPI_BYTES_LEN: usize = 32 * 10; +const USED_ROWS: usize = RPI_BYTES_LEN + 64; +const L1SIGNAL_IDX: usize = 0; +const PARENT_HASH: usize = 4; +const BLOCK_HASH: usize = 5; +const FIELD9_IDX: usize = 8; +const FIELD10_IDX: usize = 9; +const KECCAK_HI: usize = 10; +const KECCAK_LOW: usize = 11; /// PublicData contains all the values that the PiCircuit receives as input @@ -166,15 +176,19 @@ impl PublicData { H256(rpi_keccak) } } - - - /// Config for PiCircuit #[derive(Clone, Debug)] pub struct TaikoPiCircuitConfig { - field_idx: Column, + q_enable: Selector, + public_input: Column, + q_state: Column, block_acc: Column, field_gadget: FieldBytesGadget, + + + block_table: BlockTable, + keccak_table: KeccakTable, + byte_table: ByteTable, } /// Circuit configuration arguments @@ -189,16 +203,11 @@ pub struct TaikoPiCircuitConfigArgs { pub challenges: Challenges>, } -/// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum PiCellType { - /// +enum PiCellType { Storage1, - /// Storage2, - /// Byte, - /// Lookup(Table) } impl CellType for PiCellType { @@ -219,28 +228,6 @@ impl Default for PiCellType { } } -#[derive(Clone, Copy)] -enum PiState { - L1Signal, - L2Signal, - L2Contract, - MetaHash, - ParentHash, - BlockHash, - SignalRoot, - Graffiti, - Field9, - Field10, - - KeccakHi, - KeccakLow, -} -impl_expr!(PiState); -impl Into for PiState { - fn into(self) -> usize { - self as usize - } -} impl SubCircuitConfig for TaikoPiCircuitConfig { type ConfigArgs = TaikoPiCircuitConfigArgs; @@ -255,22 +242,17 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { challenges, }: Self::ConfigArgs, ) -> Self { - let field_cm = CellManager::new( - meta, - vec![(PiCellType::Byte, 32, 1, false)], - 0, - 1, - ); - let state_cm = CellManager::new( + let cm = CellManager::new( meta, vec![ + (PiCellType::Byte, 32, 1, false), (PiCellType::Storage1, 2, 1, false), (PiCellType::Storage2, 2, 2, false), ], 0, - 5, + H, ); - let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(field_cm), Some(challenges.evm_word())); + let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(cm.clone()), Some(challenges.evm_word())); cb.preload_tables(meta, &[ (PiCellType::Lookup(Keccak), &keccak_table), @@ -278,68 +260,65 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { (PiCellType::Lookup(Block), &block_table) ] ); - - let field_idx = meta.advice_column(); - let block_acc = meta.advice_column(); - let mut field_gadget = FieldBytesGadget::default(); - field_gadget.challenges = Some(challenges.clone()); + let q_enable = meta.selector(); + let public_input = meta.instance_column(); + let q_state = meta.advice_column(); + let block_acc = meta.advice_column_in(SecondPhase); + let mut field_gadget = FieldBytesGadget::config(&mut cb, challenges.clone()); meta.create_gate( "PI acc constraints", |meta| { circuit!([meta, cb], { - cb.set_cell_manager(state_cm.clone()); - let is_block_fields = Self::is_block_field(&mut cb, a!(field_idx)); - let q_block = Self::idx_range(&mut cb, a!(field_idx), &[PiState::ParentHash, PiState::BlockHash]); - let q_keccak = Self::idx_range(&mut cb, a!(field_idx), &[PiState::KeccakLow]); - let q_last_field = Self::idx_range(&mut cb, a!(field_idx), &[PiState::Field10]); - - field_gadget.config(&mut cb); - ifx! (is_block_fields => { - // do this directly in column - // field_gadget.bytes_lookup(&mut cb); - require!( - a!(block_acc, 1) => field_gadget.block_input_acc(a!(block_acc)) - ); - ifx!(q_block.expr() => { - let block_number = field_gadget.set_block_number(&mut cb); - require!( - ( - BlockContextFieldTag::BlockHash.expr(), - block_number.expr(), - field_gadget.evm_word_field() - ) => @PiCellType::Lookup(Table::Block), (COMPRESS, REDUCE) - ); - }); - // On the last bytes of the last field - // the we copy block_acc to a tmp cell in field_gadget - ifx!(q_last_field.expr() => { - field_gadget.set_keccak_input(&mut cb, a!(block_acc)); - }); - } elsex { - // block_acc should be reset to 0 - require!( - a!(block_acc, 1) => field_gadget.keccak_output_acc(a!(block_acc)) - ); - ifx!(q_keccak.expr() => { - require!( - ( - 1.expr(), - field_gadget.keccak_input.clone().unwrap().expr(), - RPI_BYTES_LEN.expr(), - a!(block_acc) - ) - => @PiCellType::Lookup(Table::Keccak), (COMPRESS, REDUCE)); - }); - }); + for idx in 0..H { + match idx { + L1SIGNAL_IDX => { + require!(a!(block_acc, idx) => field_gadget.evm_word_field(idx)); + }, + L1SIGNAL_IDX..=FIELD9_IDX => { + require!(a!(block_acc, idx + 1) => field_gadget.block_input_acc(a!(block_acc), idx + 1)); + if idx == PARENT_HASH || idx == BLOCK_HASH { + let block_number = field_gadget.set_block_number(&mut cb); + // require!( + // ( + // BlockContextFieldTag::BlockHash.expr(), + // block_number.expr(), + // field_gadget.evm_word_field(idx) + // ) => @PiCellType::Lookup(Table::Block), (TO_FIX) + // ); + } + } + FIELD10_IDX => { + field_gadget.set_keccak_input(&mut cb, block_acc.clone()); + require!(a!(block_acc, idx + 1) => field_gadget.keccak_field(KECCAK_HI)); + }, + KECCAK_HI => { + require!(a!(block_acc, idx + 1) => field_gadget.keccak_output_acc(a!(block_acc), idx + 1)); + require!(x!(public_input) => a!(block_acc, idx)); + }, + KECCAK_LOW => { + // require!( + // ( + // 1.expr(), + // field_gadget.keccak_input.clone().unwrap().expr(), + // RPI_BYTES_LEN.expr(), + // a!(block_acc) + // ) + // => @PiCellType::Lookup(Table::Keccak), (TO_FIX) + // ); + require!(x!(public_input) => a!(block_acc, idx)); + } + _ => unimplemented!() + } + } }); - cb.build_constraints() + cb.build_constraints(Some(meta.query_selector(q_enable))) } ); cb.build_equalities(meta); cb.build_lookups( meta, - &[state_cm], + &[cm], &[ (PiCellType::Byte, PiCellType::Lookup(Bytecode)), (PiCellType::Lookup(Table::Keccak), PiCellType::Lookup(Table::Keccak)), @@ -347,9 +326,14 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { ] ); Self { - field_idx, + q_enable, + public_input, + q_state, block_acc, - field_gadget: field_gadget + field_gadget, + block_table, + keccak_table, + byte_table, } } @@ -357,49 +341,6 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { impl TaikoPiCircuitConfig { - fn idx_range( - cb: &mut ConstraintBuilder, - field_idx: Expression, - range: &[PiState] - ) -> Expression { - let mut expr = 0.expr(); - circuit!([meta, cb], { - let is_block_field = range.iter() - .fold(1.expr(), |acc, item| acc * (field_idx.clone() - item.expr())); - expr = cb.local_processing( - "field idx range check", - &[is_block_field], - PiCellType::Storage1, - None, - true, - true - )[0].clone(); - }); - expr - } - - fn is_block_field( - cb: &mut ConstraintBuilder, - field_idx: Expression - ) -> Expression { - Self::idx_range( - cb, - field_idx, - &[ - PiState::L1Signal, - PiState::L2Signal, - PiState::L2Contract, - PiState::MetaHash, - PiState::ParentHash, - PiState::BlockHash, - PiState::SignalRoot, - PiState::Graffiti, - PiState::Field9, - PiState::Field10 - ] - ) - } - pub(crate) fn assign( &self, layouter: &mut impl Layouter, @@ -409,9 +350,10 @@ impl TaikoPiCircuitConfig { layouter.assign_region( || "Pi", |mut region| { + self.q_enable.enable(&mut region, 0); let mut cached_region = CachedRegion::new(&mut region); - let mut r = F::ZERO; let mut block_acc = F::ZERO; + let mut r = F::ZERO; let mut offset = 0; println!("\n==================="); for (i, (annotation, block_number, bytes)) in public_data @@ -420,35 +362,46 @@ impl TaikoPiCircuitConfig { .enumerate() { println!("{:?}, len {:?}, offset {:?}", annotation, bytes.len(), offset); match i { - i if i <= PiState::Field10.into() => { + i if i <= FIELD10_IDX => { challenges.keccak_input().map(|v| r = v); - block_acc += self.field_gadget.assign_bytes(&mut cached_region, r, offset, bytes.as_ref())?; + block_acc += self.field_gadget.assign_bytes(&mut cached_region, r, offset, i, bytes.as_ref())?; let block_acc_cell = assign!(cached_region, (self.block_acc, offset) => block_acc)?; if let Some(block_number) = block_number { + println!(" block_number {:?}", block_number); self.field_gadget.assign_block_number( &mut cached_region, offset, block_number.as_u64().scalar() - ); + )?; } - if i == PiState::Field10 as usize { + if i == FIELD10_IDX { + println!(" PiState::Field10 {:?}", block_acc); self.field_gadget.assign_keccak_acc( &mut cached_region, offset, block_acc_cell - ); + )?; block_acc = F::ZERO; } }, - i if i >= PiState::KeccakHi.into() => { + i if i == KECCAK_HI => { + println!(" PiState::Keccak {:?}", block_acc); challenges.evm_word().map(|v| r = v); - block_acc += self.field_gadget.assign_bytes(&mut cached_region, r, offset, bytes.as_ref())?; + block_acc += self.field_gadget.assign_bytes(&mut cached_region, r, offset, i, bytes.as_ref())?; + assign!(cached_region, (self.block_acc, offset) => block_acc)?; + + }, + i if i == KECCAK_LOW => { + println!(" PiState::Keccak {:?}", block_acc); + challenges.evm_word().map(|v| r = v); + block_acc += self.field_gadget.assign_bytes(&mut cached_region, r, offset, i, bytes.as_ref())?; assign!(cached_region, (self.block_acc, offset) => block_acc)?; }, _ => unreachable!(), } + offset += 1; } Ok(()) }, @@ -456,59 +409,75 @@ impl TaikoPiCircuitConfig { } } -/// Field Bytes -#[derive(Clone, Debug, Default)] -pub struct FieldBytesGadget { +#[derive(Clone, Debug)] +struct FieldBytesGadget { challenges: Option>>, - bytes: [Cell; N], + bytes: [[Cell; N]; H], block_number: Option>, keccak_input: Option>, + + keccak_exp: Expression, + evm_exp: Expression } impl FieldBytesGadget { - fn config(&mut self, cb: &mut ConstraintBuilder) { - self.bytes = cb.query_bytes(); + fn config(cb: &mut ConstraintBuilder, challenges: Challenges>) -> Self { + let mut bytes = Vec::new(); + for idx in 0..H { + bytes.push(cb.query_bytes()); + } + let keccak_exp = (0..N) + .fold(1.expr(), |acc: Expression, _|{ + acc * challenges.keccak_input() + }); + let evm_exp = (0..N) + .fold(1.expr(), |acc: Expression, _|{ + acc * challenges.evm_word() + }); + Self { + challenges: Some(challenges), + bytes: bytes.try_into().unwrap(), + block_number: None, + keccak_input: None, + keccak_exp, + evm_exp, + } } - fn bytes_expr(&self) -> Vec> { - self.bytes.iter().map(|b| b.expr()).collect() + fn bytes_expr(&self, idx: usize) -> Vec> { + self.bytes[idx].iter().map(|b| b.expr()).collect() } /// RLC of bytes of a field with evm_word 1<<8 - fn hi_low_fields_(&self) -> Expression { - self.bytes_expr().rlc(&BYTE_POW_BASE.expr()) + fn hi_low_fields_(&self, idx: usize) -> Expression { + self.bytes_expr(idx).rlc(&BYTE_POW_BASE.expr()) } /// RLC of bytes of a field with evm_word - fn evm_word_field(&self) -> Expression { + fn evm_word_field(&self, idx: usize) -> Expression { let r = self.challenges.clone().unwrap().evm_word().expr(); - self.bytes_expr().rlc(&r) + self.bytes_expr(idx).rlc(&r) } /// RLC of bytes of a field with keccak_input - fn keccak_field(&self) -> Expression { + fn keccak_field(&self, idx: usize) -> Expression { let r = self.challenges.clone().unwrap().keccak_input().expr(); - self.bytes_expr().rlc(&r) + self.bytes_expr(idx).rlc(&r) } // ------------------ Acc ------------------ - /// Next value of block field bytes RLC accumulator for keccak input - fn block_input_acc(&self, prev_field: Expression) -> Expression { + /// Next value of block field bytes RLC accumulator for keccak input, per row without offset + fn block_input_acc(&self, prev_field: Expression, idx: usize) -> Expression { let r = self.challenges.clone().unwrap().keccak_input().expr(); - let raise_exp = (0..self.bytes.len()) - .fold(1.expr(), |acc: Expression, _|acc * r.clone()); - prev_field * raise_exp + self.keccak_field() + prev_field * self.keccak_exp.expr() + self.keccak_field(idx) } - /// Next value of keccak output hi low bytes accumulator - fn keccak_output_acc(&self, hi: Expression) -> Expression { - let r = self.challenges.clone().unwrap().evm_word().expr(); - let raise_exp = (0..self.bytes.len()) - .fold(1.expr(), |acc: Expression, _|acc * r.clone()); - let low = self.hi_low_fields_(); - hi * raise_exp + low + /// Next value of keccak output hi low bytes accumulator, per row without offset + fn keccak_output_acc(&self, hi: Expression, idx: usize) -> Expression { + let low = self.hi_low_fields_(idx); + hi * self.evm_exp.expr() + low } // ------------------ Set Cell ------------------ @@ -518,15 +487,12 @@ impl FieldBytesGadget { fn set_keccak_input( &mut self, cb: &mut ConstraintBuilder, - keccak_input_acc: Expression, + block_acc: Column, ) -> Expression { let keccak_input = cb.query_cell_with_type(PiCellType::Storage2); cb.enable_equality(keccak_input.column()); - // cb.require_equal( - // "Copy keccak input from acc column to temporary cell", - // keccak_input_acc, - // keccak_input.expr() - // ); + cb.enable_equality(block_acc); + self.keccak_input = Some(keccak_input.clone()); keccak_input.expr() } @@ -545,10 +511,11 @@ impl FieldBytesGadget { region: &mut CachedRegion<'_, '_, F>, r: F, offset: usize, + idx: usize, bytes: &[u8], ) -> Result { // Assign the bytes - for (byte, column) in bytes.iter().zip(self.bytes.iter()) { + for (byte, column) in bytes.iter().zip(self.bytes[idx].iter()) { assign!(region, (column.column(), offset) => byte.scalar())?; } let mut rlc = bytes.rlc_value(r); @@ -561,8 +528,8 @@ impl FieldBytesGadget { offset: usize, block_number: F, ) -> Result<(), Error> { - if let Some(cell) = &self.block_number { - cell.assign(region, offset, block_number)?; + if let Some(c) = &self.block_number { + c.assign(region, offset, block_number)?; Ok(()) } else { Err(Error::Synthesis) @@ -575,11 +542,11 @@ impl FieldBytesGadget { offset: usize, block_acc_cell: AssignedCell, ) -> Result<(), Error> { - if let Some(cell) = &self.keccak_input { + if let Some(c) = &self.keccak_input { block_acc_cell.copy_advice( || "Copy block acc into cell for keccak input", region.inner(), - cell.column(), + c.column(), offset )?; Ok(()) @@ -587,4 +554,271 @@ impl FieldBytesGadget { Err(Error::Synthesis) } } -} \ No newline at end of file +} + + +/// Public Inputs Circuit +#[derive(Clone, Default, Debug)] +pub struct TaikoPiCircuit { + /// PublicInputs data known by the verifier + pub public_data: PublicData, + _marker: PhantomData, +} + +impl TaikoPiCircuit { + /// Creates a new TaikoPiCircuit + pub fn new(public_data: PublicData) -> Self { + Self { + public_data, + _marker: PhantomData, + } + } +} + + +impl SubCircuit for TaikoPiCircuit { + type Config = TaikoPiCircuitConfig; + + fn unusable_rows() -> usize { + // No column queried at more than 3 distinct rotations, so returns 6 as + // minimum unusable rows. + 6 + } + + fn min_num_rows_block(_block: &witness::Block) -> (usize, usize) { + (USED_ROWS, USED_ROWS) + } + + fn new_from_block(block: &witness::Block) -> Self { + TaikoPiCircuit::new(PublicData::new(block)) + } + + /// Compute the public inputs for this circuit. + fn instance(&self) -> Vec> { + let keccak_rpi = self.public_data.get_pi(); + let keccak_hi = keccak_rpi + .to_fixed_bytes() + .iter() + .take(16) + .fold(F::ZERO, |acc, byte| { + acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) + }); + + let keccak_lo = keccak_rpi + .to_fixed_bytes() + .iter() + .skip(16) + .fold(F::ZERO, |acc, byte| { + acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) + }); + + let public_inputs = vec![keccak_hi, keccak_lo]; + vec![public_inputs] + } + + /// Make the assignments to the PiCircuit + fn synthesize_sub( + &self, + config: &Self::Config, + challenges: &Challenges>, + layouter: &mut impl Layouter, + ) -> Result<(), Error> { + config.byte_table.load(layouter)?; + config.assign(layouter, &self.public_data, challenges) + } +} + + +// We define the PiTestCircuit as a wrapper over PiCircuit extended to take the +// generic const parameters MAX_TXS and MAX_CALLDATA. This is necessary because +// the trait Circuit requires an implementation of `configure` that doesn't take +// any circuit parameters, and the PiCircuit defines gates that use rotations +// that depend on MAX_TXS and MAX_CALLDATA, so these two values are required +// during the configuration. +/// Test Circuit for PiCircuit +#[cfg(any(feature = "test", test))] +#[derive(Default, Clone)] +pub struct TaikoPiTestCircuit(pub TaikoPiCircuit); + +#[cfg(any(feature = "test", test))] +impl Circuit for TaikoPiTestCircuit { + type Config = (TaikoPiCircuitConfig, Challenges); + type FloorPlanner = SimpleFloorPlanner; + type Params = (); + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let block_table = BlockTable::construct(meta); + let keccak_table = KeccakTable::construct(meta); + let byte_table = ByteTable::construct(meta); + let challenges = Challenges::construct(meta); + let challenge_exprs = challenges.exprs(meta); + ( + TaikoPiCircuitConfig::new( + meta, + TaikoPiCircuitConfigArgs { + block_table, + keccak_table, + byte_table, + challenges: challenge_exprs, + }, + ), + challenges, + ) + } + + fn synthesize( + &self, + (config, challenges): Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let challenges = challenges.values(&mut layouter); + let public_data = &self.0.public_data; + // assign block table + let randomness = challenges.evm_word(); + config + .block_table + .load(&mut layouter, &public_data.block_context, randomness)?; + // assign keccak table + config + .keccak_table + .dev_load(&mut layouter, vec![&public_data.rpi_bytes()], &challenges)?; + config.byte_table.load(&mut layouter)?; + + self.0.synthesize_sub(&config, &challenges, &mut layouter) + } +} + +#[cfg(test)] +mod taiko_pi_circuit_test { + + use super::*; + + use eth_types::ToScalar; + use halo2_proofs::{ + dev::{MockProver, VerifyFailure}, + halo2curves::bn256::Fr, + }; + use lazy_static::lazy_static; + use pretty_assertions::assert_eq; + + lazy_static! { + static ref OMMERS_HASH: H256 = H256::from_slice( + &hex::decode("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347") + .unwrap(), + ); + } + + fn run( + k: u32, + public_data: PublicData, + pi: Option>>, + ) -> Result<(), Vec> { + let circuit = TaikoPiTestCircuit::(TaikoPiCircuit::new(public_data)); + let public_inputs = pi.unwrap_or_else(|| circuit.0.instance()); + + let prover = match MockProver::run(k, &circuit, public_inputs) { + Ok(prover) => prover, + Err(e) => panic!("{:#?}", e), + }; + prover.verify() + } + + fn mock_public_data() -> PublicData { + let mut public_data = PublicData::default::(); + public_data.meta_hash = OMMERS_HASH.to_word(); + public_data.block_hash = OMMERS_HASH.to_word(); + public_data.block_context.block_hash = OMMERS_HASH.to_word(); + public_data.block_context.history_hashes = vec![Default::default(); 256]; + public_data.block_context.number = 300.into(); + public_data + } + + #[test] + fn test_default_pi() { + let public_data = mock_public_data(); + + let k = 17; + assert_eq!(run::(k, public_data, None), Ok(())); + } + + #[test] + fn test_fail_pi_hash() { + let public_data = mock_public_data(); + + let k = 17; + match run::(k, public_data, Some(vec![vec![Fr::zero(), Fr::one()]])) { + Ok(_) => unreachable!("this case must fail"), + Err(errs) => { + assert_eq!(errs.len(), 4); + for err in errs { + match err { + VerifyFailure::Permutation { .. } => return, + _ => unreachable!("unexpected error"), + } + } + } + } + } + + #[test] + fn test_fail_pi_prover() { + let mut public_data = mock_public_data(); + let address_bytes = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + ]; + + public_data.prover = Address::from_slice(&address_bytes); + + let prover: Fr = public_data.prover.to_scalar().unwrap(); + let k = 17; + match run::( + k, + public_data, + Some(vec![vec![prover, Fr::zero(), Fr::one()]]), + ) { + Ok(_) => unreachable!("this case must fail"), + Err(errs) => { + assert_eq!(errs.len(), 4); + for err in errs { + match err { + VerifyFailure::Permutation { .. } => return, + _ => unreachable!("unexpected error"), + } + } + } + } + } + + #[test] + fn test_simple_pi() { + let mut public_data = mock_public_data(); + let chain_id = 1337u64; + public_data.chain_id = Word::from(chain_id); + + let k = 17; + assert_eq!(run::(k, public_data, None), Ok(())); + } + + #[test] + fn test_verify() { + let mut block = witness::Block::::default(); + + block.eth_block.parent_hash = *OMMERS_HASH; + block.eth_block.hash = Some(*OMMERS_HASH); + block.protocol_instance.block_hash = *OMMERS_HASH; + block.protocol_instance.parent_hash = *OMMERS_HASH; + block.context.history_hashes = vec![OMMERS_HASH.to_word()]; + block.context.block_hash = OMMERS_HASH.to_word(); + block.context.number = 300.into(); + + let public_data = PublicData::new(&block); + + let k = 17; + + assert_eq!(run::(k, public_data, None), Ok(())); + } +} From a7e74870f5a30228b347a67ba1f932d1016dd407 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Sat, 19 Aug 2023 11:07:01 +0800 Subject: [PATCH 12/43] query all cells ver --- .../src/circuit_tools/cell_manager.rs | 1 + .../src/circuit_tools/constraint_builder.rs | 6 +- zkevm-circuits/src/taiko_pi_circuit__.rs | 245 ++++++++++-------- 3 files changed, 147 insertions(+), 105 deletions(-) diff --git a/zkevm-circuits/src/circuit_tools/cell_manager.rs b/zkevm-circuits/src/circuit_tools/cell_manager.rs index a307ef0421..86fecf36a3 100644 --- a/zkevm-circuits/src/circuit_tools/cell_manager.rs +++ b/zkevm-circuits/src/circuit_tools/cell_manager.rs @@ -133,6 +133,7 @@ impl CellConfig { }; columns.push(tmp); } + println!("cm init {:?} column: {:?}", self.cell_type, self.num_columns); if self.is_permute { let _ = columns .iter() diff --git a/zkevm-circuits/src/circuit_tools/constraint_builder.rs b/zkevm-circuits/src/circuit_tools/constraint_builder.rs index 48dae34476..45cf140a36 100644 --- a/zkevm-circuits/src/circuit_tools/constraint_builder.rs +++ b/zkevm-circuits/src/circuit_tools/constraint_builder.rs @@ -351,7 +351,11 @@ impl ConstraintBuilder { } pub(crate) fn query_one(&mut self, cell_type: C) -> Cell { - self.query_cells_dyn(cell_type, 1).first().unwrap().clone() + let res = self.query_cells_dyn(cell_type, 1).first().unwrap().clone(); + if res.column().index() == 45 { + println!("\n found 45 {:?}", cell_type); + } + res } pub(crate) fn query_bytes(&mut self) -> [Cell; N] { diff --git a/zkevm-circuits/src/taiko_pi_circuit__.rs b/zkevm-circuits/src/taiko_pi_circuit__.rs index d8fbd339bc..2f33d6a8f4 100644 --- a/zkevm-circuits/src/taiko_pi_circuit__.rs +++ b/zkevm-circuits/src/taiko_pi_circuit__.rs @@ -11,7 +11,7 @@ use crate::{ cell_manager::{CellManager, CellType, Cell}, gadgets::{IsEqualGadget, IsZeroGadget}, cached_region::{CachedRegion, self}, }, - witness::{self, BlockContext}, circuit, + witness::{self, BlockContext}, circuit, assignf, }; use gadgets::util::Scalar; use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256}; @@ -33,6 +33,7 @@ const N: usize = 32; const H: usize = 12; const RPI_BYTES_LEN: usize = 32 * 10; const USED_ROWS: usize = RPI_BYTES_LEN + 64; + const L1SIGNAL_IDX: usize = 0; const PARENT_HASH: usize = 4; const BLOCK_HASH: usize = 5; @@ -86,52 +87,49 @@ pub struct PublicData { } impl PublicData { - fn assignments(&self) -> [(&'static str, Option, [u8; 32]); 10] { + fn assignments(&self) -> [(&'static str, Option, Vec); 10] { let res = [ ( "l1_signal_service", None, - self.l1_signal_service.to_be_bytes(), + self.l1_signal_service.to_be_bytes().to_vec(), ), ( "l2_signal_service", None, - self.l2_signal_service.to_be_bytes(), + self.l2_signal_service.to_be_bytes().to_vec(), ), - ("l2_contract", None, self.l2_contract.to_be_bytes()), - ("meta_hash", None, self.meta_hash.to_be_bytes()), + ("l2_contract", None, self.l2_contract.to_be_bytes().to_vec()), + ("meta_hash", None, self.meta_hash.to_be_bytes().to_vec()), ( "parent_hash", Some(self.block_context.number - 1), - self.parent_hash.to_be_bytes(), + self.parent_hash.to_be_bytes().to_vec(), ), ( "block_hash", Some(self.block_context.number), - self.block_hash.to_be_bytes(), + self.block_hash.to_be_bytes().to_vec(), ), - ("signal_root", None, self.signal_root.to_be_bytes()), - ("graffiti", None, self.graffiti.to_be_bytes()), + ("signal_root", None, self.signal_root.to_be_bytes().to_vec()), + ("graffiti", None, self.graffiti.to_be_bytes().to_vec()), ( "prover+parentGasUsed+gasUsed", None, - self.field9.to_be_bytes(), + self.field9.to_be_bytes().to_vec(), ), ( "blockMaxGasLimit+maxTransactionsPerBlock+maxBytesPerTxList", None, - self.field10.to_be_bytes(), + self.field10.to_be_bytes().to_vec(), ), ]; - res.iter().for_each(|field|{ - println!("{:?}, len {:?}", field.0, field.2.len()); - }); res } /// get rpi bytes pub fn rpi_bytes(&self) -> Vec { - self.assignments().iter().flat_map(|v| v.2).collect() + self.assignments().iter().flat_map(|v| v.2.clone()).collect() } fn default() -> Self { @@ -175,6 +173,16 @@ impl PublicData { let rpi_keccak = keccak256(rpi_bytes); H256(rpi_keccak) } + + fn get_pi_hi_low(&self) -> [(&'static str, Option, Vec); 2] { + let pi_bytes = self.get_pi().to_fixed_bytes(); + let res = [ + ("high_16_bytes_of_keccak_rpi", None, pi_bytes[..16].to_vec()), + ("low_16_bytes_of_keccak_rpi", None, pi_bytes[16..].to_vec()), + ]; + println!("-----res {:?}", res); + res + } } /// Config for PiCircuit #[derive(Clone, Debug)] @@ -266,48 +274,49 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { let q_state = meta.advice_column(); let block_acc = meta.advice_column_in(SecondPhase); let mut field_gadget = FieldBytesGadget::config(&mut cb, challenges.clone()); + meta.enable_equality(public_input); meta.create_gate( "PI acc constraints", |meta| { circuit!([meta, cb], { for idx in 0..H { match idx { - L1SIGNAL_IDX => { - require!(a!(block_acc, idx) => field_gadget.evm_word_field(idx)); - }, L1SIGNAL_IDX..=FIELD9_IDX => { - require!(a!(block_acc, idx + 1) => field_gadget.block_input_acc(a!(block_acc), idx + 1)); + require!(a!(block_acc, idx + 1) => field_gadget.block_input_acc(a!(block_acc, idx), idx + 1)); + if idx == L1SIGNAL_IDX { + require!(a!(block_acc, idx) => field_gadget.keccak_field(idx)); + } if idx == PARENT_HASH || idx == BLOCK_HASH { let block_number = field_gadget.set_block_number(&mut cb); - // require!( - // ( - // BlockContextFieldTag::BlockHash.expr(), - // block_number.expr(), - // field_gadget.evm_word_field(idx) - // ) => @PiCellType::Lookup(Table::Block), (TO_FIX) - // ); + require!( + ( + BlockContextFieldTag::BlockHash.expr(), + block_number.expr(), + field_gadget.evm_word_field(idx) + ) => @PiCellType::Lookup(Table::Block), (TO_FIX) + ); } } FIELD10_IDX => { field_gadget.set_keccak_input(&mut cb, block_acc.clone()); - require!(a!(block_acc, idx + 1) => field_gadget.keccak_field(KECCAK_HI)); }, KECCAK_HI => { - require!(a!(block_acc, idx + 1) => field_gadget.keccak_output_acc(a!(block_acc), idx + 1)); - require!(x!(public_input) => a!(block_acc, idx)); + require!(a!(block_acc, idx) => field_gadget.hi_low_field(KECCAK_HI)); + field_gadget.set_keccak_output(&mut cb, idx); + require!(a!(block_acc, idx + 1) => field_gadget.keccak_output_acc(a!(block_acc, idx), idx + 1)); }, KECCAK_LOW => { + field_gadget.set_keccak_output(&mut cb, idx); // require!( // ( // 1.expr(), // field_gadget.keccak_input.clone().unwrap().expr(), // RPI_BYTES_LEN.expr(), - // a!(block_acc) + // a!(block_acc, idx) // ) // => @PiCellType::Lookup(Table::Keccak), (TO_FIX) // ); - require!(x!(public_input) => a!(block_acc, idx)); - } + }, _ => unimplemented!() } } @@ -340,72 +349,83 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { } impl TaikoPiCircuitConfig { - pub(crate) fn assign( &self, layouter: &mut impl Layouter, public_data: &PublicData, challenges: &Challenges>, ) -> Result<(), Error> { - layouter.assign_region( - || "Pi", - |mut region| { - self.q_enable.enable(&mut region, 0); - let mut cached_region = CachedRegion::new(&mut region); + let pi_cells = layouter.assign_region( + || "Pi", + |mut region| { + + // region.name_column("", self.field_gadget.keccak_output_hi_low); + + + self.q_enable.enable(&mut region, 0)?; + let mut region = CachedRegion::new(&mut region); + + let mut cell = None; let mut block_acc = F::ZERO; - let mut r = F::ZERO; + let mut evm_word_r = F::ZERO; + let mut keccak_r = F::ZERO; + let hi_low_r = F::from(BYTE_POW_BASE); + challenges.evm_word().map(|v| evm_word_r = v); + challenges.keccak_input().map(|v| keccak_r = v); + + let mut assignments = public_data.assignments().to_vec(); + assignments.append(&mut public_data.get_pi_hi_low().to_vec()); let mut offset = 0; - println!("\n==================="); - for (i, (annotation, block_number, bytes)) in public_data - .assignments() - .iter() - .enumerate() { - println!("{:?}, len {:?}, offset {:?}", annotation, bytes.len(), offset); - match i { - i if i <= FIELD10_IDX => { - challenges.keccak_input().map(|v| r = v); - block_acc += self.field_gadget.assign_bytes(&mut cached_region, r, offset, i, bytes.as_ref())?; - let block_acc_cell = assign!(cached_region, (self.block_acc, offset) => block_acc)?; - if let Some(block_number) = block_number { - println!(" block_number {:?}", block_number); - self.field_gadget.assign_block_number( - &mut cached_region, - offset, - block_number.as_u64().scalar() - )?; - } - - if i == FIELD10_IDX { - println!(" PiState::Field10 {:?}", block_acc); - self.field_gadget.assign_keccak_acc( - &mut cached_region, - offset, - block_acc_cell - )?; - block_acc = F::ZERO; - } - }, - i if i == KECCAK_HI => { - println!(" PiState::Keccak {:?}", block_acc); - challenges.evm_word().map(|v| r = v); - block_acc += self.field_gadget.assign_bytes(&mut cached_region, r, offset, i, bytes.as_ref())?; - assign!(cached_region, (self.block_acc, offset) => block_acc)?; - - }, - i if i == KECCAK_LOW => { - println!(" PiState::Keccak {:?}", block_acc); - challenges.evm_word().map(|v| r = v); - block_acc += self.field_gadget.assign_bytes(&mut cached_region, r, offset, i, bytes.as_ref())?; - assign!(cached_region, (self.block_acc, offset) => block_acc)?; - - }, - _ => unreachable!(), + let mut pi_cells = Vec::new(); + for (annotation, block_number, bytes) in assignments { + println!("{:?} {:?}, len {:?}", offset, annotation, bytes.len()); + match offset { + L1SIGNAL_IDX..=FIELD10_IDX => { + let field = self.field_gadget.assign_bytes(&mut region, keccak_r, offset, &bytes)?; + (block_acc, cell) = self.assign_acc(&mut region, keccak_r, block_acc, field, offset)?; + if let Some(block_number) = block_number { + println!(" block_number {:?}", block_number); + self.field_gadget.assign_block_number(&mut region, offset, block_number.as_u64().scalar())?; + } + if offset == FIELD10_IDX { + println!(" PiState::Field10 {:?}", block_acc); + self.field_gadget.assign_keccak_input(&mut region, offset,cell.unwrap())?; + block_acc = F::ZERO; + } } - offset += 1; + KECCAK_HI..=KECCAK_LOW => { + let mut bytes = bytes.clone(); + bytes.reverse(); + let field = self.field_gadget.assign_bytes(&mut region, hi_low_r, offset, &bytes)?; + pi_cells.push(self.field_gadget.assign_keccak_output(&mut region, offset,field)?); + println!("self.field_gadget.assign_bytes {:?}, {:?}", bytes, field); + (block_acc, cell) = self.assign_acc(&mut region, evm_word_r, block_acc, field, offset)?; + }, + _ => unimplemented!() } - Ok(()) - }, - ) + offset += 1; + } + Ok(pi_cells) + } + )?; + for (i, cell) in pi_cells.iter().enumerate() { + layouter.constrain_instance(cell.cell(), self.public_input, i)?; + println!("pi_cell {:?}", cell); + } + Ok(()) + } + + pub(crate) fn assign_acc( + &self, + region: &mut CachedRegion<'_, '_, F>, + acc_r: F, + prev: F, + other: F, + offset: usize + ) -> Result<(F, Option>), Error> { + let next = prev * acc_r + other; + let cell = assign!(region, (self.block_acc, offset) => next)?; + Ok((next, Some(cell))) } } @@ -415,6 +435,7 @@ struct FieldBytesGadget { bytes: [[Cell; N]; H], block_number: Option>, keccak_input: Option>, + keccak_output_hi_low: [Option>;2], keccak_exp: Expression, evm_exp: Expression @@ -424,14 +445,14 @@ impl FieldBytesGadget { fn config(cb: &mut ConstraintBuilder, challenges: Challenges>) -> Self { let mut bytes = Vec::new(); - for idx in 0..H { + for _ in 0..H { bytes.push(cb.query_bytes()); } - let keccak_exp = (0..N) + let keccak_exp = (0..1) .fold(1.expr(), |acc: Expression, _|{ acc * challenges.keccak_input() }); - let evm_exp = (0..N) + let evm_exp = (0..1) .fold(1.expr(), |acc: Expression, _|{ acc * challenges.evm_word() }); @@ -440,6 +461,7 @@ impl FieldBytesGadget { bytes: bytes.try_into().unwrap(), block_number: None, keccak_input: None, + keccak_output_hi_low: [None, None], keccak_exp, evm_exp, } @@ -450,7 +472,7 @@ impl FieldBytesGadget { } /// RLC of bytes of a field with evm_word 1<<8 - fn hi_low_fields_(&self, idx: usize) -> Expression { + fn hi_low_field(&self, idx: usize) -> Expression { self.bytes_expr(idx).rlc(&BYTE_POW_BASE.expr()) } @@ -470,13 +492,13 @@ impl FieldBytesGadget { /// Next value of block field bytes RLC accumulator for keccak input, per row without offset fn block_input_acc(&self, prev_field: Expression, idx: usize) -> Expression { - let r = self.challenges.clone().unwrap().keccak_input().expr(); prev_field * self.keccak_exp.expr() + self.keccak_field(idx) } + /// Next value of keccak output hi low bytes accumulator, per row without offset fn keccak_output_acc(&self, hi: Expression, idx: usize) -> Expression { - let low = self.hi_low_fields_(idx); + let low = self.hi_low_field(idx); hi * self.evm_exp.expr() + low } @@ -489,7 +511,7 @@ impl FieldBytesGadget { cb: &mut ConstraintBuilder, block_acc: Column, ) -> Expression { - let keccak_input = cb.query_cell_with_type(PiCellType::Storage2); + let keccak_input = cb.query_one(PiCellType::Storage2); cb.enable_equality(keccak_input.column()); cb.enable_equality(block_acc); self.keccak_input = Some(keccak_input.clone()); @@ -503,23 +525,38 @@ impl FieldBytesGadget { block_number.expr() } + fn set_keccak_output(&mut self, cb: &mut ConstraintBuilder, idx: usize) -> Expression { + let output = cb.query_one(PiCellType::Storage2); + cb.enable_equality(output.column()); + cb.require_equal("Keccak ouput {:?}", self.hi_low_field(idx), output.expr()); + self.keccak_output_hi_low[idx-KECCAK_HI] = Some(output.clone()); + output.expr() + } + // ------------------ Assign ------------------ /// Returns the rlc of given bytes pub(crate) fn assign_bytes( &self, region: &mut CachedRegion<'_, '_, F>, - r: F, + field_r: F, offset: usize, - idx: usize, bytes: &[u8], ) -> Result { // Assign the bytes - for (byte, column) in bytes.iter().zip(self.bytes[idx].iter()) { - assign!(region, (column.column(), offset) => byte.scalar())?; + for (byte, cell) in bytes.iter().zip(self.bytes[offset].iter()) { + assign!(region, cell, 0 => byte.scalar())?; } - let mut rlc = bytes.rlc_value(r); - Ok(rlc) + Ok(bytes.rlc_value(field_r)) + } + + pub(crate) fn assign_keccak_output( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + keccak_output: F, + ) -> Result, Error> { + assign!(region, self.keccak_output_hi_low[offset-KECCAK_HI].as_ref().unwrap(), 0 => keccak_output) } pub(crate) fn assign_block_number( @@ -536,7 +573,7 @@ impl FieldBytesGadget { } } - pub(crate) fn assign_keccak_acc( + pub(crate) fn assign_keccak_input( &self, region: &mut CachedRegion<'_, '_, F>, offset: usize, @@ -719,7 +756,7 @@ mod taiko_pi_circuit_test { ) -> Result<(), Vec> { let circuit = TaikoPiTestCircuit::(TaikoPiCircuit::new(public_data)); let public_inputs = pi.unwrap_or_else(|| circuit.0.instance()); - + println!("{:?}", public_inputs); let prover = match MockProver::run(k, &circuit, public_inputs) { Ok(prover) => prover, Err(e) => panic!("{:#?}", e), From 8e845aa259f9187bce43ee44297ab864c4556338 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Sat, 19 Aug 2023 17:07:33 +0800 Subject: [PATCH 13/43] build with sel --- .../src/circuit_tools/cached_region.rs | 12 +++++++- .../src/circuit_tools/cell_manager.rs | 4 +-- .../src/circuit_tools/constraint_builder.rs | 29 +++++++++++++------ 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/zkevm-circuits/src/circuit_tools/cached_region.rs b/zkevm-circuits/src/circuit_tools/cached_region.rs index b4339a3558..92bef7ef9e 100644 --- a/zkevm-circuits/src/circuit_tools/cached_region.rs +++ b/zkevm-circuits/src/circuit_tools/cached_region.rs @@ -10,7 +10,7 @@ use std::{ hash::{Hash, Hasher}, }; -use super::{cell_manager::CellType, constraint_builder::ConstraintBuilder}; +use super::{cell_manager::{CellType, CellColumn}, constraint_builder::ConstraintBuilder}; pub trait ChallengeSet { fn indexed(&self) -> Vec<&Value>; @@ -43,6 +43,16 @@ impl<'r, 'b, F: Field> CachedRegion<'r, 'b, F> { self.region } + pub(crate) fn annotate_columns(&mut self, col_configs: &[CellColumn]) { + for c in col_configs { + self.region + .name_column( + || format!("{:?} {:?}: {:?} queried", c.cell_type.clone(), c.index, c.height), + c.column + ); + } + } + pub(crate) fn push_region(&mut self, offset: usize, region_id: usize) { self.regions.push((offset, region_id)); } diff --git a/zkevm-circuits/src/circuit_tools/cell_manager.rs b/zkevm-circuits/src/circuit_tools/cell_manager.rs index 86fecf36a3..b7e7a256e9 100644 --- a/zkevm-circuits/src/circuit_tools/cell_manager.rs +++ b/zkevm-circuits/src/circuit_tools/cell_manager.rs @@ -204,8 +204,8 @@ pub(crate) struct CellColumn { pub(crate) cell_type: C, pub(crate) cells: Vec>, pub(crate) expr: Expression, - height: usize, - index: usize, + pub(crate) height: usize, + pub(crate) index: usize, } impl PartialEq for CellColumn { diff --git a/zkevm-circuits/src/circuit_tools/constraint_builder.rs b/zkevm-circuits/src/circuit_tools/constraint_builder.rs index 45cf140a36..53c0552a68 100644 --- a/zkevm-circuits/src/circuit_tools/constraint_builder.rs +++ b/zkevm-circuits/src/circuit_tools/constraint_builder.rs @@ -9,7 +9,7 @@ use crate::{evm_circuit::util::rlc, table::LookupTable, util::{Expr, query_expre use eth_types::Field; use gadgets::util::{and, sum, Scalar}; use halo2_proofs::{ - plonk::{ConstraintSystem, Expression, Column, Advice}, + plonk::{ConstraintSystem, Expression, Column, Advice, Selector}, }; use itertools::Itertools; @@ -417,7 +417,8 @@ impl ConstraintBuilder { &mut self, meta: &mut ConstraintSystem, cell_managers: &[CellManager], - tag:&(C, C) + tag:&(C, C), + selector: Option, ){ let (data_tag, table_tag) = tag; let challenge = self.lookup_challenge.clone().unwrap(); @@ -425,8 +426,12 @@ impl ConstraintBuilder { let table_expr = rlc::expr(&table, challenge.expr()); for cm in cell_managers { for col in cm.get_typed_columns(*data_tag) { - meta.lookup_any(format!("{:?}", data_tag), |_meta| { - vec![(col.expr(), table_expr.clone())] + meta.lookup_any(format!("{:?}", data_tag), |meta| { + let s = selector.map_or_else( + || 1.expr(), + |s| meta.query_selector(s) + ); + vec![(col.expr() * s, table_expr.clone())] }); } } @@ -436,7 +441,8 @@ impl ConstraintBuilder { pub(crate) fn build_dynamic_path( &mut self, meta: &mut ConstraintSystem, - tag: &(C, C) + tag: &(C, C), + selector: Option, ){ let (data_tag, table_tag) = tag; if let Some(lookups) = self.lookups.clone().get(data_tag) { @@ -479,11 +485,15 @@ impl ConstraintBuilder { while values.len() < table.len() { values.push(0.expr()); } - meta.lookup_any(description, |_meta| { + meta.lookup_any(description, |meta| { + let s = selector.map_or_else( + || 1.expr(), + |s| meta.query_selector(s) + ); values .iter() .zip(table.iter()) - .map(|(v, t)| (v.expr(), t.expr())) + .map(|(v, t)| (v.expr() * s.clone(), t.expr())) .collect() }); } @@ -496,11 +506,12 @@ impl ConstraintBuilder { meta: &mut ConstraintSystem, cell_managers: &[CellManager], tags: &[(C, C)], + selector: Option, ) { let _challenge = self.lookup_challenge.clone().unwrap(); for tag in tags { - self.build_fixed_path(meta, cell_managers, tag); - self.build_dynamic_path(meta, tag); + self.build_fixed_path(meta, cell_managers, tag, selector); + self.build_dynamic_path(meta, tag, selector); } } From 9ce2b2afce00bc6af63a1f592f34f35763398a19 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Sat, 19 Aug 2023 17:07:47 +0800 Subject: [PATCH 14/43] update --- zkevm-circuits/src/lib.rs | 2 + zkevm-circuits/src/taiko_pi_circuit.rs | 3 +- zkevm-circuits/src/taiko_pi_circuit_.rs | 809 +++++++++++++++++++++++ zkevm-circuits/src/taiko_pi_circuit__.rs | 5 +- 4 files changed, 816 insertions(+), 3 deletions(-) create mode 100644 zkevm-circuits/src/taiko_pi_circuit_.rs diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index b07c812575..84a2ee23fb 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -30,6 +30,8 @@ pub mod table; pub mod taiko_pi_circuit; #[macro_use] pub mod taiko_pi_circuit__; +#[macro_use] +pub mod taiko_pi_circuit_; pub mod taiko_super_circuit; #[cfg(any(feature = "test", test))] diff --git a/zkevm-circuits/src/taiko_pi_circuit.rs b/zkevm-circuits/src/taiko_pi_circuit.rs index 04f7340583..a90fe4817e 100644 --- a/zkevm-circuits/src/taiko_pi_circuit.rs +++ b/zkevm-circuits/src/taiko_pi_circuit.rs @@ -349,6 +349,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { (PiCellType::Lookup(Table::Block),PiCellType::Lookup(Table::Block)), (PiCellType::Lookup(Table::Bytecode), PiCellType::Lookup(Table::Bytecode)), ], + None ); Self { @@ -459,7 +460,7 @@ impl TaikoPiCircuitConfig { cells[RPI_CELL_IDX] = Some(rpi_cell); cells[RPI_RLC_ACC_CELL_IDX] = Some(rpi_rlc_acc_cell); if let Some(block_number) = block_number { - println!("---self.q_block_table.enable---"); + println!("---self.q_block_table.enable--- {:?} {:?}", block_number, field_bytes); self.q_block_table.enable(region, row_offset)?; region.assign_advice( || "block_index", diff --git a/zkevm-circuits/src/taiko_pi_circuit_.rs b/zkevm-circuits/src/taiko_pi_circuit_.rs new file mode 100644 index 0000000000..3f8123a1a2 --- /dev/null +++ b/zkevm-circuits/src/taiko_pi_circuit_.rs @@ -0,0 +1,809 @@ +//! Use the hash value as public input. + +use crate::{ + assign, + evm_circuit::table::Table::*, + evm_circuit::{util::{constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, rlc}, table::Table}, + table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}, + util::{Challenges, SubCircuitConfig, SubCircuit}, + circuit_tools::{ + constraint_builder::{ConstraintBuilder, RLCable, RLCChainable, TO_FIX, COMPRESS, REDUCE, RLCableValue}, + cell_manager::{CellManager, CellType, Cell, CellConfig, CellColumn}, gadgets::{IsEqualGadget, IsZeroGadget}, cached_region::{CachedRegion, self}, + }, + + witness::{self, BlockContext}, circuit, assignf, +}; +use gadgets::util::Scalar; +use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256}; +use ethers_core::utils::keccak256; +use gadgets::{util::{or, select, Expr, and}, impl_expr}; +use halo2_proofs::{ + circuit::{AssignedCell, Layouter, Region, SimpleFloorPlanner, Value}, + plonk::{ + Advice, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, Instance, SecondPhase, + Selector, VirtualCells, + }, + poly::{Rotation}, +}; +use rand_chacha::rand_core::block; +use std::{marker::PhantomData, ops::Range, usize, default}; + +const BYTE_POW_BASE: u64 = 1 << 8; +const N: usize = 32; +const H: usize = 12; +const RPI_BYTES_LEN: usize = 32 * 10; +const USED_ROWS: usize = RPI_BYTES_LEN + 64; + +const L1SIGNAL_IDX: usize = 0; +const PARENT_HASH: usize = 4; +const BLOCK_HASH: usize = 5; +const FIELD9_IDX: usize = 8; +const FIELD10_IDX: usize = 9; +const KECCAK_HI: usize = 10; +const KECCAK_LOW: usize = 11; + + +/// PublicData contains all the values that the PiCircuit receives as input +#[derive(Debug, Clone, Default)] +pub struct PublicData { + /// l1 signal service address + pub l1_signal_service: Word, + /// l2 signal service address + pub l2_signal_service: Word, + /// l2 contract address + pub l2_contract: Word, + /// meta hash + pub meta_hash: Word, + /// block hash value + pub block_hash: Word, + /// the parent block hash + pub parent_hash: Word, + /// signal root + pub signal_root: Word, + /// extra message + pub graffiti: Word, + /// union field + pub field9: Word, // prover[96:256]+parentGasUsed[64:96]+gasUsed[32:64] + /// union field + pub field10: Word, /* blockMaxGasLimit[192:256]+maxTransactionsPerBlock[128: + * 192]+maxBytesPerTxList[64:128] */ + + // privates + // Prover address + prover: Address, + // parent block gas used + parent_gas_used: u32, + // block gas used + gas_used: u32, + // blockMaxGasLimit + block_max_gas_limit: u64, + // maxTransactionsPerBlock: u64, + max_transactions_per_block: u64, + // maxBytesPerTxList: u64, + max_bytes_per_tx_list: u64, + + block_context: BlockContext, + chain_id: Word, +} + +impl PublicData { + fn assignments(&self) -> [(&'static str, Option, Vec); 10] { + let res = [ + ( + "l1_signal_service", + None, + self.l1_signal_service.to_be_bytes().to_vec(), + ), + ( + "l2_signal_service", + None, + self.l2_signal_service.to_be_bytes().to_vec(), + ), + ("l2_contract", None, self.l2_contract.to_be_bytes().to_vec()), + ("meta_hash", None, self.meta_hash.to_be_bytes().to_vec()), + ( + "parent_hash", + Some(self.block_context.number - 1), + self.parent_hash.to_be_bytes().to_vec(), + ), + ( + "block_hash", + Some(self.block_context.number), + self.block_hash.to_be_bytes().to_vec(), + ), + ("signal_root", None, self.signal_root.to_be_bytes().to_vec()), + ("graffiti", None, self.graffiti.to_be_bytes().to_vec()), + ( + "prover+parentGasUsed+gasUsed", + None, + self.field9.to_be_bytes().to_vec(), + ), + ( + "blockMaxGasLimit+maxTransactionsPerBlock+maxBytesPerTxList", + None, + self.field10.to_be_bytes().to_vec(), + ), + ]; + res + } + + /// get rpi bytes + pub fn rpi_bytes(&self) -> Vec { + self.assignments().iter().flat_map(|v| v.2.clone()).collect() + } + + fn default() -> Self { + Self::new::(&witness::Block::default()) + } + + /// create PublicData from block and taiko + pub fn new(block: &witness::Block) -> Self { + use witness::left_shift; + let field9 = left_shift(block.protocol_instance.prover, 96) + + left_shift(block.protocol_instance.parent_gas_used as u64, 64) + + left_shift(block.protocol_instance.gas_used as u64, 32); + + let field10 = left_shift(block.protocol_instance.block_max_gas_limit, 192) + + left_shift(block.protocol_instance.max_transactions_per_block, 128) + + left_shift(block.protocol_instance.max_bytes_per_tx_list, 64); + PublicData { + l1_signal_service: block.protocol_instance.l1_signal_service.to_word(), + l2_signal_service: block.protocol_instance.l2_signal_service.to_word(), + l2_contract: block.protocol_instance.l2_contract.to_word(), + meta_hash: block.protocol_instance.meta_hash.hash().to_word(), + block_hash: block.protocol_instance.block_hash.to_word(), + parent_hash: block.protocol_instance.parent_hash.to_word(), + signal_root: block.protocol_instance.signal_root.to_word(), + graffiti: block.protocol_instance.graffiti.to_word(), + prover: block.protocol_instance.prover, + parent_gas_used: block.protocol_instance.parent_gas_used, + gas_used: block.protocol_instance.gas_used, + block_max_gas_limit: block.protocol_instance.block_max_gas_limit, + max_transactions_per_block: block.protocol_instance.max_transactions_per_block, + max_bytes_per_tx_list: block.protocol_instance.max_bytes_per_tx_list, + field9, + field10, + block_context: block.context.clone(), + chain_id: block.context.chain_id, + } + } + + fn get_pi(&self) -> H256 { + let rpi_bytes = self.rpi_bytes(); + let rpi_keccak = keccak256(rpi_bytes); + H256(rpi_keccak) + } + + fn get_pi_hi_low(&self) -> [(&'static str, Option, Vec); 2] { + let pi_bytes = self.get_pi().to_fixed_bytes(); + let res = [ + ("high_16_bytes_of_keccak_rpi", None, pi_bytes[..16].to_vec()), + ("low_16_bytes_of_keccak_rpi", None, pi_bytes[16..].to_vec()), + ]; + println!("-----res {:?}", res); + res + } +} +/// Config for PiCircuit +#[derive(Clone, Debug)] +pub struct TaikoPiCircuitConfig { + q_enable: Selector, + public_input: Column, // equality + block_acc: Column, + field_gadget: FieldBytesGadget, + + block_number: [Cell;2], // Phase1 + keccak_output:[Cell;2], // Phase2, equality + + block_table: BlockTable, + keccak_table: KeccakTable, + byte_table: ByteTable, + + // To annotate columns at assignment for debug purpose + col_configs: Vec>, +} + +/// Circuit configuration arguments +pub struct TaikoPiCircuitConfigArgs { + /// BlockTable + pub block_table: BlockTable, + /// KeccakTable + pub keccak_table: KeccakTable, + /// ByteTable + pub byte_table: ByteTable, + /// Challenges + pub challenges: Challenges>, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum PiCellType { + Storage1, + Storage2, + Byte, + Lookup(Table) +} +impl CellType for PiCellType { + fn byte_type() -> Option { + Some(Self::Byte) + } + fn storage_for_phase(phase: u8) -> Self { + match phase { + 1 => PiCellType::Storage1, + 2 => PiCellType::Storage2, + _ => unimplemented!() + } + } +} +impl Default for PiCellType { + fn default() -> Self { + Self::Storage1 + } +} + + +impl SubCircuitConfig for TaikoPiCircuitConfig { + type ConfigArgs = TaikoPiCircuitConfigArgs; + + /// Return a new TaikoPiCircuitConfig + fn new( + meta: &mut ConstraintSystem, + Self::ConfigArgs { + block_table, + keccak_table, + byte_table, + challenges, + }: Self::ConfigArgs, + ) -> Self { + let field_cm = CellManager::new( + meta, + vec![ + (PiCellType::Byte, 32, 1, false), + (PiCellType::Storage1, 3, 1, false) + ], + 0, + 1, + ); + let state_cm = CellManager::new( + meta, + vec![ + (PiCellType::Storage1, 1, 1, false), + (PiCellType::Storage2, 1, 2, true), + ], + 0, + 12, + ); + let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(field_cm.clone()), Some(challenges.evm_word())); + cb.preload_tables(meta, + &[ + (PiCellType::Lookup(Keccak), &keccak_table), + (PiCellType::Lookup(Bytecode), &byte_table), + (PiCellType::Lookup(Block), &block_table) + ] + ); + let q_enable = meta.complex_selector(); + let public_input = meta.instance_column(); + let block_acc = meta.advice_column_in(SecondPhase); + let pi_field = FieldBytesGadget::config(&mut cb, &challenges); + cb.set_cell_manager(state_cm.clone()); + let block_number = [();2].map(|_| cb.query_one(PiCellType::Storage1)); + let keccak_output = [();2].map(|_| cb.query_one(PiCellType::Storage2)); + meta.enable_equality(public_input); + meta.create_gate( + "PI acc constraints", + |meta| { + circuit!([meta, cb], { + // Field of PublicData + // start with field bytes rlc with keccak_r and increment with keccak_r + ifx!(pi_field.is_field.expr() => { + ifx!(pi_field.is_first.expr() => { + require!(a!(block_acc) => pi_field.keccak_field(meta, 0)); + }); + ifx!(not!(pi_field.is_last.expr()) => { + require!( + a!(block_acc, 1) => a!(block_acc) * challenges.keccak_input() + pi_field.keccak_field(meta, 0) + ); + }) + } + // Row with Keccak Hi/Low + // start with field bytes rlc with 1 << 8 and increment with evm_word + elsex { + ifx!(pi_field.is_first.expr() => { + require!(a!(block_acc) => pi_field.hi_low_field(meta, 0)); + }); + ifx!(not!(pi_field.is_last.expr()) => { + require!( + a!(block_acc, 1) => a!(block_acc) * challenges.evm_word() + pi_field.hi_low_field(meta, 0) + ); + }) + }); + let mut offset = PARENT_HASH; + for bn in &block_number { + require!( + ( + BlockContextFieldTag::BlockHash.expr(), + bn.expr(), + pi_field.evm_word_field(meta, offset) + ) => @PiCellType::Lookup(Table::Block), (TO_FIX) + ); + offset += 1; + } + // require!(keccak_output[0].expr() => pi_field.hi_low_field(meta, KECCAK_HI)); + // require!(keccak_output[1].expr() => pi_field.hi_low_field(meta, KECCAK_LOW)); + // require!( + // ( + // 1.expr(), + // a!(block_acc, FIELD10_IDX), + // RPI_BYTES_LEN.expr(), + // a!(block_acc, KECCAK_LOW) + // ) + // => @PiCellType::Lookup(Table::Keccak), (TO_FIX) + // ); + }); + cb.build_constraints(Some(meta.query_selector(q_enable))) + } + ); + cb.build_equalities(meta); + cb.build_lookups( + meta, + &[field_cm.clone()], + &[ + (PiCellType::Byte, PiCellType::Lookup(Bytecode)), + (PiCellType::Lookup(Table::Keccak), PiCellType::Lookup(Table::Keccak)), + (PiCellType::Lookup(Table::Block), PiCellType::Lookup(Table::Block)), + ], + Some(q_enable) + ); + let mut col_configs = field_cm.columns().to_vec(); + col_configs.append(&mut state_cm.columns().to_vec()); + Self { + q_enable, + public_input, + block_acc, + field_gadget: pi_field, + block_number, + keccak_output, + block_table, + keccak_table, + byte_table, + col_configs, + } + } + +} + +impl TaikoPiCircuitConfig { + pub(crate) fn assign( + &self, + layouter: &mut impl Layouter, + public_data: &PublicData, + challenges: &Challenges>, + ) -> Result<(), Error> { + let pi_cells = layouter.assign_region( + || "Pi", + |mut region| { + + self.q_enable.enable(&mut region, 0)?; + let mut region = CachedRegion::new(&mut region); + region.annotate_columns(&self.col_configs); + + let mut block_acc = F::ZERO; + let mut evm_word_r = F::ZERO; + let mut keccak_r = F::ZERO; + let hi_low_r = F::from(BYTE_POW_BASE); + challenges.evm_word().map(|v| evm_word_r = v); + challenges.keccak_input().map(|v| keccak_r = v); + + let mut assignments = public_data.assignments().to_vec(); + assignments.append(&mut public_data.get_pi_hi_low().to_vec()); + let mut i = 0; + let mut pi_cells = Vec::new(); + for (annotation, block_number, bytes) in assignments { + println!("{:?} {:?}, len {:?}", i, annotation, bytes.len()); + match i { + L1SIGNAL_IDX..=FIELD10_IDX => { + let field = self.field_gadget.assign(&mut region, i, keccak_r, &bytes)?; + block_acc = self.assign_acc(&mut region, i, keccak_r, block_acc, field)?; + if let Some(block_number) = block_number { + println!(" block_number {:?} => {:?} {:?}", block_number, self.block_number[i - PARENT_HASH].expr().identifier(), field); + println!(" {:?}", bytes); + assign!(region, self.block_number[i - PARENT_HASH], 0 => block_number.as_u64().scalar())?; + } + } + KECCAK_HI..=KECCAK_LOW => { + let mut bytes = bytes.clone(); + bytes.reverse(); + let field = self.field_gadget.assign(&mut region, i, hi_low_r, &bytes)?; + block_acc = self.assign_acc(&mut region, i, evm_word_r, block_acc, field)?; + pi_cells.push(assign!(region, self.keccak_output[i - KECCAK_HI], 0 => field)?); + println!("self.field_gadget.assign_bytes {:?}, {:?}", bytes, field); + }, + _ => unimplemented!() + } + i += 1; + } + Ok(pi_cells) + } + )?; + for (i, cell) in pi_cells.iter().enumerate() { + layouter.constrain_instance(cell.cell(), self.public_input, i)?; + println!("pi_cell {:?}", cell); + } + Ok(()) + } + + pub(crate) fn assign_acc( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + acc_r: F, + prev: F, + other: F, + ) -> Result { + let next = prev * acc_r + other; + assign!(region, (self.block_acc, offset) => next)?; + Ok(next) + } +} + + +#[derive(Clone, Debug)] +struct FieldBytesGadget { + bytes: [Cell; N], + is_field: Cell, + is_first: Cell, + is_last: Cell, + word_r: Expression, + keccak_r: Expression, +} + +impl FieldBytesGadget { + + fn config(cb: &mut ConstraintBuilder, challenges: &Challenges>) -> Self { + Self { + bytes: cb.query_bytes(), + is_field: cb.query_bool(), + is_first: cb.query_bool(), + is_last: cb.query_bool(), + word_r: challenges.evm_word().expr(), + keccak_r: challenges.keccak_input().expr(), + } + } + + fn bytes_expr(&self, meta: &mut VirtualCells, idx: usize) -> Vec> { + self.bytes.iter().map(|b| b.rot(meta, idx)).collect() + } + + /// RLC of bytes of a field with evm_word 1<<8 + fn hi_low_field(&self, meta: &mut VirtualCells, idx: usize) -> Expression { + self.bytes_expr(meta, idx).rlc(&BYTE_POW_BASE.expr()) + } + + /// RLC of bytes of a field with evm_word + fn evm_word_field(&self, meta: &mut VirtualCells, idx: usize) -> Expression { + self.bytes_expr(meta, idx).rlc_rev(&self.word_r) + } + + /// RLC of bytes of a field with keccak_input + fn keccak_field(&self, meta: &mut VirtualCells, idx: usize) -> Expression { + self.bytes_expr(meta, idx).rlc(&self.keccak_r) + } + + // ------------------ Assign ------------------ + + /// Returns the rlc of given bytes + pub(crate) fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + r: F, + bytes: &[u8], + ) -> Result { + // Assign the bytes + for (byte, cell) in bytes.iter().zip(self.bytes.iter()) { + assign!(region, cell, offset => byte.scalar())?; + } + self.assign_flags(region, offset)?; + Ok(bytes.rlc_value(r)) + } + + pub(crate) fn assign_flags( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + ) -> Result<(), Error> { + match offset { + L1SIGNAL_IDX..=FIELD10_IDX => { + assign!(region, self.is_field, offset => true.scalar())?; + if offset == L1SIGNAL_IDX { + assign!(region, self.is_first, offset => true.scalar())?; + } else { + assign!(region, self.is_first, offset => false.scalar())?; + } + if offset == FIELD10_IDX { + assign!(region, self.is_last, offset => true.scalar())?; + } else { + assign!(region, self.is_last, offset => false.scalar())?; + } + }, + KECCAK_HI => { + assign!(region, self.is_first, offset => true.scalar())?; + assign!(region, self.is_field, offset => false.scalar())?; + assign!(region, self.is_last, offset => false.scalar())?; + }, + KECCAK_LOW => { + assign!(region, self.is_first, offset => false.scalar())?; + assign!(region, self.is_field, offset => false.scalar())?; + assign!(region, self.is_last, offset => true.scalar())?; + }, + _ => unreachable!(), + }; + Ok(()) + } +} + + +/// Public Inputs Circuit +#[derive(Clone, Default, Debug)] +pub struct TaikoPiCircuit { + /// PublicInputs data known by the verifier + pub public_data: PublicData, + _marker: PhantomData, +} + +impl TaikoPiCircuit { + /// Creates a new TaikoPiCircuit + pub fn new(public_data: PublicData) -> Self { + Self { + public_data, + _marker: PhantomData, + } + } +} + + +impl SubCircuit for TaikoPiCircuit { + type Config = TaikoPiCircuitConfig; + + fn unusable_rows() -> usize { + // No column queried at more than 3 distinct rotations, so returns 6 as + // minimum unusable rows. + 6 + } + + fn min_num_rows_block(_block: &witness::Block) -> (usize, usize) { + (USED_ROWS, USED_ROWS) + } + + fn new_from_block(block: &witness::Block) -> Self { + TaikoPiCircuit::new(PublicData::new(block)) + } + + /// Compute the public inputs for this circuit. + fn instance(&self) -> Vec> { + let keccak_rpi = self.public_data.get_pi(); + let keccak_hi = keccak_rpi + .to_fixed_bytes() + .iter() + .take(16) + .fold(F::ZERO, |acc, byte| { + acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) + }); + + let keccak_lo = keccak_rpi + .to_fixed_bytes() + .iter() + .skip(16) + .fold(F::ZERO, |acc, byte| { + acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) + }); + + let public_inputs = vec![keccak_hi, keccak_lo]; + vec![public_inputs] + } + + /// Make the assignments to the PiCircuit + fn synthesize_sub( + &self, + config: &Self::Config, + challenges: &Challenges>, + layouter: &mut impl Layouter, + ) -> Result<(), Error> { + config.byte_table.load(layouter)?; + config.assign(layouter, &self.public_data, challenges) + } +} + + +// We define the PiTestCircuit as a wrapper over PiCircuit extended to take the +// generic const parameters MAX_TXS and MAX_CALLDATA. This is necessary because +// the trait Circuit requires an implementation of `configure` that doesn't take +// any circuit parameters, and the PiCircuit defines gates that use rotations +// that depend on MAX_TXS and MAX_CALLDATA, so these two values are required +// during the configuration. +/// Test Circuit for PiCircuit +#[cfg(any(feature = "test", test))] +#[derive(Default, Clone)] +pub struct TaikoPiTestCircuit(pub TaikoPiCircuit); + +#[cfg(any(feature = "test", test))] +impl Circuit for TaikoPiTestCircuit { + type Config = (TaikoPiCircuitConfig, Challenges); + type FloorPlanner = SimpleFloorPlanner; + type Params = (); + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let block_table = BlockTable::construct(meta); + let keccak_table = KeccakTable::construct(meta); + let byte_table = ByteTable::construct(meta); + let challenges = Challenges::construct(meta); + let challenge_exprs = challenges.exprs(meta); + ( + TaikoPiCircuitConfig::new( + meta, + TaikoPiCircuitConfigArgs { + block_table, + keccak_table, + byte_table, + challenges: challenge_exprs, + }, + ), + challenges, + ) + } + + fn synthesize( + &self, + (config, challenges): Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let challenges = challenges.values(&mut layouter); + let public_data = &self.0.public_data; + // assign block table + let randomness = challenges.evm_word(); + config + .block_table + .load(&mut layouter, &public_data.block_context, randomness)?; + // assign keccak table + config + .keccak_table + .dev_load(&mut layouter, vec![&public_data.rpi_bytes()], &challenges)?; + config.byte_table.load(&mut layouter)?; + + self.0.synthesize_sub(&config, &challenges, &mut layouter) + } +} + +#[cfg(test)] +mod taiko_pi_circuit_test { + + use super::*; + + use eth_types::ToScalar; + use halo2_proofs::{ + dev::{MockProver, VerifyFailure}, + halo2curves::bn256::Fr, + }; + use lazy_static::lazy_static; + use pretty_assertions::assert_eq; + + lazy_static! { + static ref OMMERS_HASH: H256 = H256::from_slice( + &hex::decode("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347") + .unwrap(), + ); + } + + fn run( + k: u32, + public_data: PublicData, + pi: Option>>, + ) -> Result<(), Vec> { + let circuit = TaikoPiTestCircuit::(TaikoPiCircuit::new(public_data)); + let public_inputs = pi.unwrap_or_else(|| circuit.0.instance()); + println!("{:?}", public_inputs); + let prover = match MockProver::run(k, &circuit, public_inputs) { + Ok(prover) => prover, + Err(e) => panic!("{:#?}", e), + }; + prover.verify() + } + + fn mock_public_data() -> PublicData { + let mut public_data = PublicData::default::(); + public_data.meta_hash = OMMERS_HASH.to_word(); + public_data.block_hash = OMMERS_HASH.to_word(); + public_data.block_context.block_hash = OMMERS_HASH.to_word(); + public_data.block_context.history_hashes = vec![Default::default(); 256]; + public_data.block_context.number = 300.into(); + public_data + } + + #[test] + fn test_default_pi() { + let public_data = mock_public_data(); + + let k = 17; + assert_eq!(run::(k, public_data, None), Ok(())); + } + + #[test] + fn test_fail_pi_hash() { + let public_data = mock_public_data(); + + let k = 17; + match run::(k, public_data, Some(vec![vec![Fr::zero(), Fr::one()]])) { + Ok(_) => unreachable!("this case must fail"), + Err(errs) => { + assert_eq!(errs.len(), 4); + for err in errs { + match err { + VerifyFailure::Permutation { .. } => return, + _ => unreachable!("unexpected error"), + } + } + } + } + } + + #[test] + fn test_fail_pi_prover() { + let mut public_data = mock_public_data(); + let address_bytes = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + ]; + + public_data.prover = Address::from_slice(&address_bytes); + + let prover: Fr = public_data.prover.to_scalar().unwrap(); + let k = 17; + match run::( + k, + public_data, + Some(vec![vec![prover, Fr::zero(), Fr::one()]]), + ) { + Ok(_) => unreachable!("this case must fail"), + Err(errs) => { + assert_eq!(errs.len(), 4); + for err in errs { + match err { + VerifyFailure::Permutation { .. } => return, + _ => unreachable!("unexpected error"), + } + } + } + } + } + + #[test] + fn test_simple_pi() { + let mut public_data = mock_public_data(); + let chain_id = 1337u64; + public_data.chain_id = Word::from(chain_id); + + let k = 17; + assert_eq!(run::(k, public_data, None), Ok(())); + } + + #[test] + fn test_verify() { + let mut block = witness::Block::::default(); + + block.eth_block.parent_hash = *OMMERS_HASH; + block.eth_block.hash = Some(*OMMERS_HASH); + block.protocol_instance.block_hash = *OMMERS_HASH; + block.protocol_instance.parent_hash = *OMMERS_HASH; + block.context.history_hashes = vec![OMMERS_HASH.to_word()]; + block.context.block_hash = OMMERS_HASH.to_word(); + block.context.number = 300.into(); + + let public_data = PublicData::new(&block); + + let k = 17; + + assert_eq!(run::(k, public_data, None), Ok(())); + } +} \ No newline at end of file diff --git a/zkevm-circuits/src/taiko_pi_circuit__.rs b/zkevm-circuits/src/taiko_pi_circuit__.rs index 2f33d6a8f4..8fa529911c 100644 --- a/zkevm-circuits/src/taiko_pi_circuit__.rs +++ b/zkevm-circuits/src/taiko_pi_circuit__.rs @@ -269,7 +269,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { ] ); - let q_enable = meta.selector(); + let q_enable = meta.complex_selector(); let public_input = meta.instance_column(); let q_state = meta.advice_column(); let block_acc = meta.advice_column_in(SecondPhase); @@ -332,7 +332,8 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { (PiCellType::Byte, PiCellType::Lookup(Bytecode)), (PiCellType::Lookup(Table::Keccak), PiCellType::Lookup(Table::Keccak)), (PiCellType::Lookup(Table::Block), PiCellType::Lookup(Table::Block)), - ] + ], + Some(q_enable) ); Self { q_enable, From 6ee13a87c8a95ba9166438cf4bc7cf76721fca31 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Sun, 20 Aug 2023 00:28:46 +0800 Subject: [PATCH 15/43] keccak input output is wired --- zkevm-circuits/src/lib.rs | 2 - zkevm-circuits/src/table/keccak_table.rs | 3 + zkevm-circuits/src/taiko_pi_circuit_.rs | 73 +- zkevm-circuits/src/taiko_pi_circuit__.rs | 862 ----------------------- 4 files changed, 51 insertions(+), 889 deletions(-) delete mode 100644 zkevm-circuits/src/taiko_pi_circuit__.rs diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index 84a2ee23fb..c4fbf00fb6 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -29,8 +29,6 @@ pub mod super_circuit; pub mod table; pub mod taiko_pi_circuit; #[macro_use] -pub mod taiko_pi_circuit__; -#[macro_use] pub mod taiko_pi_circuit_; pub mod taiko_super_circuit; diff --git a/zkevm-circuits/src/table/keccak_table.rs b/zkevm-circuits/src/table/keccak_table.rs index 5eaff3f30d..78c5162096 100644 --- a/zkevm-circuits/src/table/keccak_table.rs +++ b/zkevm-circuits/src/table/keccak_table.rs @@ -52,6 +52,7 @@ impl KeccakTable { let input_rlc = challenges .keccak_input() .map(|challenge| rlc::value(input.iter().rev(), challenge)); + let input_len = F::from(input.len() as u64); let mut keccak = Keccak::default(); keccak.update(input); @@ -62,6 +63,7 @@ impl KeccakTable { challenge, ) }); + println!("assignments input {:?} - keccak_input {:?}| output {:?} - evm_word {:?}", input.len(), input_rlc, output.len(), output_rlc); vec![[ Value::known(F::ONE), @@ -111,6 +113,7 @@ impl KeccakTable { let keccak_table_columns = >::advice_columns(self); for input in inputs.clone() { + println!("dev_load offset={:?} input: {:?}", offset, input); for row in Self::assignments(input, challenges) { // let mut column_index = 0; for (&column, value) in keccak_table_columns.iter().zip_eq(row) { diff --git a/zkevm-circuits/src/taiko_pi_circuit_.rs b/zkevm-circuits/src/taiko_pi_circuit_.rs index 3f8123a1a2..4923b4690d 100644 --- a/zkevm-circuits/src/taiko_pi_circuit_.rs +++ b/zkevm-circuits/src/taiko_pi_circuit_.rs @@ -13,6 +13,7 @@ use crate::{ witness::{self, BlockContext}, circuit, assignf, }; +use bus_mapping::evm; use gadgets::util::Scalar; use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256}; use ethers_core::utils::keccak256; @@ -176,12 +177,10 @@ impl PublicData { fn get_pi_hi_low(&self) -> [(&'static str, Option, Vec); 2] { let pi_bytes = self.get_pi().to_fixed_bytes(); - let res = [ + [ ("high_16_bytes_of_keccak_rpi", None, pi_bytes[..16].to_vec()), ("low_16_bytes_of_keccak_rpi", None, pi_bytes[16..].to_vec()), - ]; - println!("-----res {:?}", res); - res + ] } } /// Config for PiCircuit @@ -299,8 +298,9 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { require!(a!(block_acc) => pi_field.keccak_field(meta, 0)); }); ifx!(not!(pi_field.is_last.expr()) => { + let keccak_mult = (0..N).fold(1.expr(), |acc, _| acc * challenges.keccak_input()); require!( - a!(block_acc, 1) => a!(block_acc) * challenges.keccak_input() + pi_field.keccak_field(meta, 0) + a!(block_acc, 1) => a!(block_acc) * keccak_mult + pi_field.keccak_field(meta, 0) ); }) } @@ -308,11 +308,12 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { // start with field bytes rlc with 1 << 8 and increment with evm_word elsex { ifx!(pi_field.is_first.expr() => { - require!(a!(block_acc) => pi_field.hi_low_field(meta, 0)); + require!(a!(block_acc) => pi_field.evm_word_field(meta, 0)); }); ifx!(not!(pi_field.is_last.expr()) => { + let evm_mult = (0..N/2).fold(1.expr(), |acc, _| acc * challenges.evm_word()); require!( - a!(block_acc, 1) => a!(block_acc) * challenges.evm_word() + pi_field.hi_low_field(meta, 0) + a!(block_acc, 1) => a!(block_acc) * evm_mult + pi_field.evm_word_field(meta, 0) ); }) }); @@ -327,17 +328,17 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { ); offset += 1; } - // require!(keccak_output[0].expr() => pi_field.hi_low_field(meta, KECCAK_HI)); - // require!(keccak_output[1].expr() => pi_field.hi_low_field(meta, KECCAK_LOW)); - // require!( - // ( - // 1.expr(), - // a!(block_acc, FIELD10_IDX), - // RPI_BYTES_LEN.expr(), - // a!(block_acc, KECCAK_LOW) - // ) - // => @PiCellType::Lookup(Table::Keccak), (TO_FIX) - // ); + require!(keccak_output[0].expr() => pi_field.hi_low_field(meta, KECCAK_HI)); + require!(keccak_output[1].expr() => pi_field.hi_low_field(meta, KECCAK_LOW)); + require!( + ( + 1.expr(), + a!(block_acc, FIELD10_IDX), + RPI_BYTES_LEN.expr(), + a!(block_acc, KECCAK_LOW) + ) + => @PiCellType::Lookup(Table::Keccak), (TO_FIX) + ); }); cb.build_constraints(Some(meta.query_selector(q_enable))) } @@ -392,17 +393,27 @@ impl TaikoPiCircuitConfig { let hi_low_r = F::from(BYTE_POW_BASE); challenges.evm_word().map(|v| evm_word_r = v); challenges.keccak_input().map(|v| keccak_r = v); + let mut k_cnt = 0; + let mut e_cnt = 0; + let keccak_mult = (0..N).fold(1.scalar(), |acc: F, _| {k_cnt += 1; acc * keccak_r}); + let evm_mult = (0..N/2).fold(1.scalar(), |acc: F, _| {e_cnt += 1; acc * evm_word_r}); let mut assignments = public_data.assignments().to_vec(); assignments.append(&mut public_data.get_pi_hi_low().to_vec()); let mut i = 0; let mut pi_cells = Vec::new(); for (annotation, block_number, bytes) in assignments { - println!("{:?} {:?}, len {:?}", i, annotation, bytes.len()); + println!("{:?} {:?}, len {:?} \n {:?}", i, annotation, bytes.len(), bytes); match i { L1SIGNAL_IDX..=FIELD10_IDX => { let field = self.field_gadget.assign(&mut region, i, keccak_r, &bytes)?; - block_acc = self.assign_acc(&mut region, i, keccak_r, block_acc, field)?; + block_acc = self.assign_acc(&mut region, i, keccak_mult, block_acc, field)?; + + // // k_cnt += N; + // if i == FIELD10_IDX { + // println!("FIELD10_IDX {:?}", block_acc); + // } + if let Some(block_number) = block_number { println!(" block_number {:?} => {:?} {:?}", block_number, self.block_number[i - PARENT_HASH].expr().identifier(), field); println!(" {:?}", bytes); @@ -411,14 +422,22 @@ impl TaikoPiCircuitConfig { } KECCAK_HI..=KECCAK_LOW => { let mut bytes = bytes.clone(); - bytes.reverse(); - let field = self.field_gadget.assign(&mut region, i, hi_low_r, &bytes)?; - block_acc = self.assign_acc(&mut region, i, evm_word_r, block_acc, field)?; - pi_cells.push(assign!(region, self.keccak_output[i - KECCAK_HI], 0 => field)?); - println!("self.field_gadget.assign_bytes {:?}, {:?}", bytes, field); + bytes.reverse(); + let field = self.field_gadget.assign(&mut region, i, evm_word_r, &bytes)?; + block_acc = self.assign_acc(&mut region, i, evm_mult, block_acc, field)?; + + // // e_cnt += N/2; + // if i == KECCAK_LOW { + // println!("KECCAK_LOW {:?}", block_acc); + // } + + pi_cells.push(assign!( + region, self.keccak_output[i - KECCAK_HI], 0 => rlc::value(&bytes, hi_low_r) + )?); }, _ => unimplemented!() } + println!(" block_acc {:?}", block_acc); i += 1; } Ok(pi_cells) @@ -499,6 +518,10 @@ impl FieldBytesGadget { bytes: &[u8], ) -> Result { // Assign the bytes + let mut bytes = bytes.to_vec(); + while bytes.len() < N { + bytes.push(0); + } for (byte, cell) in bytes.iter().zip(self.bytes.iter()) { assign!(region, cell, offset => byte.scalar())?; } diff --git a/zkevm-circuits/src/taiko_pi_circuit__.rs b/zkevm-circuits/src/taiko_pi_circuit__.rs deleted file mode 100644 index 8fa529911c..0000000000 --- a/zkevm-circuits/src/taiko_pi_circuit__.rs +++ /dev/null @@ -1,862 +0,0 @@ -//! Use the hash value as public input. - -use crate::{ - assign, - evm_circuit::table::Table::*, - evm_circuit::{util::{constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, rlc}, table::Table}, - table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}, - util::{Challenges, SubCircuitConfig, SubCircuit}, - circuit_tools::{ - constraint_builder::{ConstraintBuilder, RLCable, RLCChainable, TO_FIX, COMPRESS, REDUCE, RLCableValue}, - cell_manager::{CellManager, CellType, Cell}, gadgets::{IsEqualGadget, IsZeroGadget}, cached_region::{CachedRegion, self}, - }, - - witness::{self, BlockContext}, circuit, assignf, -}; -use gadgets::util::Scalar; -use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256}; -use ethers_core::utils::keccak256; -use gadgets::{util::{or, select, Expr, and}, impl_expr}; -use halo2_proofs::{ - circuit::{AssignedCell, Layouter, Region, SimpleFloorPlanner, Value}, - plonk::{ - Advice, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, Instance, SecondPhase, - Selector, VirtualCells, - }, - poly::{Rotation}, -}; -use rand_chacha::rand_core::block; -use std::{marker::PhantomData, ops::Range, usize, default}; - -const BYTE_POW_BASE: u64 = 1 << 8; -const N: usize = 32; -const H: usize = 12; -const RPI_BYTES_LEN: usize = 32 * 10; -const USED_ROWS: usize = RPI_BYTES_LEN + 64; - -const L1SIGNAL_IDX: usize = 0; -const PARENT_HASH: usize = 4; -const BLOCK_HASH: usize = 5; -const FIELD9_IDX: usize = 8; -const FIELD10_IDX: usize = 9; -const KECCAK_HI: usize = 10; -const KECCAK_LOW: usize = 11; - - -/// PublicData contains all the values that the PiCircuit receives as input -#[derive(Debug, Clone, Default)] -pub struct PublicData { - /// l1 signal service address - pub l1_signal_service: Word, - /// l2 signal service address - pub l2_signal_service: Word, - /// l2 contract address - pub l2_contract: Word, - /// meta hash - pub meta_hash: Word, - /// block hash value - pub block_hash: Word, - /// the parent block hash - pub parent_hash: Word, - /// signal root - pub signal_root: Word, - /// extra message - pub graffiti: Word, - /// union field - pub field9: Word, // prover[96:256]+parentGasUsed[64:96]+gasUsed[32:64] - /// union field - pub field10: Word, /* blockMaxGasLimit[192:256]+maxTransactionsPerBlock[128: - * 192]+maxBytesPerTxList[64:128] */ - - // privates - // Prover address - prover: Address, - // parent block gas used - parent_gas_used: u32, - // block gas used - gas_used: u32, - // blockMaxGasLimit - block_max_gas_limit: u64, - // maxTransactionsPerBlock: u64, - max_transactions_per_block: u64, - // maxBytesPerTxList: u64, - max_bytes_per_tx_list: u64, - - block_context: BlockContext, - chain_id: Word, -} - -impl PublicData { - fn assignments(&self) -> [(&'static str, Option, Vec); 10] { - let res = [ - ( - "l1_signal_service", - None, - self.l1_signal_service.to_be_bytes().to_vec(), - ), - ( - "l2_signal_service", - None, - self.l2_signal_service.to_be_bytes().to_vec(), - ), - ("l2_contract", None, self.l2_contract.to_be_bytes().to_vec()), - ("meta_hash", None, self.meta_hash.to_be_bytes().to_vec()), - ( - "parent_hash", - Some(self.block_context.number - 1), - self.parent_hash.to_be_bytes().to_vec(), - ), - ( - "block_hash", - Some(self.block_context.number), - self.block_hash.to_be_bytes().to_vec(), - ), - ("signal_root", None, self.signal_root.to_be_bytes().to_vec()), - ("graffiti", None, self.graffiti.to_be_bytes().to_vec()), - ( - "prover+parentGasUsed+gasUsed", - None, - self.field9.to_be_bytes().to_vec(), - ), - ( - "blockMaxGasLimit+maxTransactionsPerBlock+maxBytesPerTxList", - None, - self.field10.to_be_bytes().to_vec(), - ), - ]; - res - } - - /// get rpi bytes - pub fn rpi_bytes(&self) -> Vec { - self.assignments().iter().flat_map(|v| v.2.clone()).collect() - } - - fn default() -> Self { - Self::new::(&witness::Block::default()) - } - - /// create PublicData from block and taiko - pub fn new(block: &witness::Block) -> Self { - use witness::left_shift; - let field9 = left_shift(block.protocol_instance.prover, 96) - + left_shift(block.protocol_instance.parent_gas_used as u64, 64) - + left_shift(block.protocol_instance.gas_used as u64, 32); - - let field10 = left_shift(block.protocol_instance.block_max_gas_limit, 192) - + left_shift(block.protocol_instance.max_transactions_per_block, 128) - + left_shift(block.protocol_instance.max_bytes_per_tx_list, 64); - PublicData { - l1_signal_service: block.protocol_instance.l1_signal_service.to_word(), - l2_signal_service: block.protocol_instance.l2_signal_service.to_word(), - l2_contract: block.protocol_instance.l2_contract.to_word(), - meta_hash: block.protocol_instance.meta_hash.hash().to_word(), - block_hash: block.protocol_instance.block_hash.to_word(), - parent_hash: block.protocol_instance.parent_hash.to_word(), - signal_root: block.protocol_instance.signal_root.to_word(), - graffiti: block.protocol_instance.graffiti.to_word(), - prover: block.protocol_instance.prover, - parent_gas_used: block.protocol_instance.parent_gas_used, - gas_used: block.protocol_instance.gas_used, - block_max_gas_limit: block.protocol_instance.block_max_gas_limit, - max_transactions_per_block: block.protocol_instance.max_transactions_per_block, - max_bytes_per_tx_list: block.protocol_instance.max_bytes_per_tx_list, - field9, - field10, - block_context: block.context.clone(), - chain_id: block.context.chain_id, - } - } - - fn get_pi(&self) -> H256 { - let rpi_bytes = self.rpi_bytes(); - let rpi_keccak = keccak256(rpi_bytes); - H256(rpi_keccak) - } - - fn get_pi_hi_low(&self) -> [(&'static str, Option, Vec); 2] { - let pi_bytes = self.get_pi().to_fixed_bytes(); - let res = [ - ("high_16_bytes_of_keccak_rpi", None, pi_bytes[..16].to_vec()), - ("low_16_bytes_of_keccak_rpi", None, pi_bytes[16..].to_vec()), - ]; - println!("-----res {:?}", res); - res - } -} -/// Config for PiCircuit -#[derive(Clone, Debug)] -pub struct TaikoPiCircuitConfig { - q_enable: Selector, - public_input: Column, - q_state: Column, - block_acc: Column, - field_gadget: FieldBytesGadget, - - - block_table: BlockTable, - keccak_table: KeccakTable, - byte_table: ByteTable, -} - -/// Circuit configuration arguments -pub struct TaikoPiCircuitConfigArgs { - /// BlockTable - pub block_table: BlockTable, - /// KeccakTable - pub keccak_table: KeccakTable, - /// ByteTable - pub byte_table: ByteTable, - /// Challenges - pub challenges: Challenges>, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -enum PiCellType { - Storage1, - Storage2, - Byte, - Lookup(Table) -} -impl CellType for PiCellType { - fn byte_type() -> Option { - Some(Self::Byte) - } - fn storage_for_phase(phase: u8) -> Self { - match phase { - 1 => PiCellType::Storage1, - 2 => PiCellType::Storage2, - _ => unimplemented!() - } - } -} -impl Default for PiCellType { - fn default() -> Self { - Self::Storage1 - } -} - - -impl SubCircuitConfig for TaikoPiCircuitConfig { - type ConfigArgs = TaikoPiCircuitConfigArgs; - - /// Return a new TaikoPiCircuitConfig - fn new( - meta: &mut ConstraintSystem, - Self::ConfigArgs { - block_table, - keccak_table, - byte_table, - challenges, - }: Self::ConfigArgs, - ) -> Self { - let cm = CellManager::new( - meta, - vec![ - (PiCellType::Byte, 32, 1, false), - (PiCellType::Storage1, 2, 1, false), - (PiCellType::Storage2, 2, 2, false), - ], - 0, - H, - ); - let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(cm.clone()), Some(challenges.evm_word())); - cb.preload_tables(meta, - &[ - (PiCellType::Lookup(Keccak), &keccak_table), - (PiCellType::Lookup(Bytecode), &byte_table), - (PiCellType::Lookup(Block), &block_table) - ] - ); - - let q_enable = meta.complex_selector(); - let public_input = meta.instance_column(); - let q_state = meta.advice_column(); - let block_acc = meta.advice_column_in(SecondPhase); - let mut field_gadget = FieldBytesGadget::config(&mut cb, challenges.clone()); - meta.enable_equality(public_input); - meta.create_gate( - "PI acc constraints", - |meta| { - circuit!([meta, cb], { - for idx in 0..H { - match idx { - L1SIGNAL_IDX..=FIELD9_IDX => { - require!(a!(block_acc, idx + 1) => field_gadget.block_input_acc(a!(block_acc, idx), idx + 1)); - if idx == L1SIGNAL_IDX { - require!(a!(block_acc, idx) => field_gadget.keccak_field(idx)); - } - if idx == PARENT_HASH || idx == BLOCK_HASH { - let block_number = field_gadget.set_block_number(&mut cb); - require!( - ( - BlockContextFieldTag::BlockHash.expr(), - block_number.expr(), - field_gadget.evm_word_field(idx) - ) => @PiCellType::Lookup(Table::Block), (TO_FIX) - ); - } - } - FIELD10_IDX => { - field_gadget.set_keccak_input(&mut cb, block_acc.clone()); - }, - KECCAK_HI => { - require!(a!(block_acc, idx) => field_gadget.hi_low_field(KECCAK_HI)); - field_gadget.set_keccak_output(&mut cb, idx); - require!(a!(block_acc, idx + 1) => field_gadget.keccak_output_acc(a!(block_acc, idx), idx + 1)); - }, - KECCAK_LOW => { - field_gadget.set_keccak_output(&mut cb, idx); - // require!( - // ( - // 1.expr(), - // field_gadget.keccak_input.clone().unwrap().expr(), - // RPI_BYTES_LEN.expr(), - // a!(block_acc, idx) - // ) - // => @PiCellType::Lookup(Table::Keccak), (TO_FIX) - // ); - }, - _ => unimplemented!() - } - } - }); - cb.build_constraints(Some(meta.query_selector(q_enable))) - } - ); - cb.build_equalities(meta); - cb.build_lookups( - meta, - &[cm], - &[ - (PiCellType::Byte, PiCellType::Lookup(Bytecode)), - (PiCellType::Lookup(Table::Keccak), PiCellType::Lookup(Table::Keccak)), - (PiCellType::Lookup(Table::Block), PiCellType::Lookup(Table::Block)), - ], - Some(q_enable) - ); - Self { - q_enable, - public_input, - q_state, - block_acc, - field_gadget, - block_table, - keccak_table, - byte_table, - } - } - -} - -impl TaikoPiCircuitConfig { - pub(crate) fn assign( - &self, - layouter: &mut impl Layouter, - public_data: &PublicData, - challenges: &Challenges>, - ) -> Result<(), Error> { - let pi_cells = layouter.assign_region( - || "Pi", - |mut region| { - - // region.name_column("", self.field_gadget.keccak_output_hi_low); - - - self.q_enable.enable(&mut region, 0)?; - let mut region = CachedRegion::new(&mut region); - - let mut cell = None; - let mut block_acc = F::ZERO; - let mut evm_word_r = F::ZERO; - let mut keccak_r = F::ZERO; - let hi_low_r = F::from(BYTE_POW_BASE); - challenges.evm_word().map(|v| evm_word_r = v); - challenges.keccak_input().map(|v| keccak_r = v); - - let mut assignments = public_data.assignments().to_vec(); - assignments.append(&mut public_data.get_pi_hi_low().to_vec()); - let mut offset = 0; - let mut pi_cells = Vec::new(); - for (annotation, block_number, bytes) in assignments { - println!("{:?} {:?}, len {:?}", offset, annotation, bytes.len()); - match offset { - L1SIGNAL_IDX..=FIELD10_IDX => { - let field = self.field_gadget.assign_bytes(&mut region, keccak_r, offset, &bytes)?; - (block_acc, cell) = self.assign_acc(&mut region, keccak_r, block_acc, field, offset)?; - if let Some(block_number) = block_number { - println!(" block_number {:?}", block_number); - self.field_gadget.assign_block_number(&mut region, offset, block_number.as_u64().scalar())?; - } - if offset == FIELD10_IDX { - println!(" PiState::Field10 {:?}", block_acc); - self.field_gadget.assign_keccak_input(&mut region, offset,cell.unwrap())?; - block_acc = F::ZERO; - } - } - KECCAK_HI..=KECCAK_LOW => { - let mut bytes = bytes.clone(); - bytes.reverse(); - let field = self.field_gadget.assign_bytes(&mut region, hi_low_r, offset, &bytes)?; - pi_cells.push(self.field_gadget.assign_keccak_output(&mut region, offset,field)?); - println!("self.field_gadget.assign_bytes {:?}, {:?}", bytes, field); - (block_acc, cell) = self.assign_acc(&mut region, evm_word_r, block_acc, field, offset)?; - }, - _ => unimplemented!() - } - offset += 1; - } - Ok(pi_cells) - } - )?; - for (i, cell) in pi_cells.iter().enumerate() { - layouter.constrain_instance(cell.cell(), self.public_input, i)?; - println!("pi_cell {:?}", cell); - } - Ok(()) - } - - pub(crate) fn assign_acc( - &self, - region: &mut CachedRegion<'_, '_, F>, - acc_r: F, - prev: F, - other: F, - offset: usize - ) -> Result<(F, Option>), Error> { - let next = prev * acc_r + other; - let cell = assign!(region, (self.block_acc, offset) => next)?; - Ok((next, Some(cell))) - } -} - -#[derive(Clone, Debug)] -struct FieldBytesGadget { - challenges: Option>>, - bytes: [[Cell; N]; H], - block_number: Option>, - keccak_input: Option>, - keccak_output_hi_low: [Option>;2], - - keccak_exp: Expression, - evm_exp: Expression -} - -impl FieldBytesGadget { - - fn config(cb: &mut ConstraintBuilder, challenges: Challenges>) -> Self { - let mut bytes = Vec::new(); - for _ in 0..H { - bytes.push(cb.query_bytes()); - } - let keccak_exp = (0..1) - .fold(1.expr(), |acc: Expression, _|{ - acc * challenges.keccak_input() - }); - let evm_exp = (0..1) - .fold(1.expr(), |acc: Expression, _|{ - acc * challenges.evm_word() - }); - Self { - challenges: Some(challenges), - bytes: bytes.try_into().unwrap(), - block_number: None, - keccak_input: None, - keccak_output_hi_low: [None, None], - keccak_exp, - evm_exp, - } - } - - fn bytes_expr(&self, idx: usize) -> Vec> { - self.bytes[idx].iter().map(|b| b.expr()).collect() - } - - /// RLC of bytes of a field with evm_word 1<<8 - fn hi_low_field(&self, idx: usize) -> Expression { - self.bytes_expr(idx).rlc(&BYTE_POW_BASE.expr()) - } - - /// RLC of bytes of a field with evm_word - fn evm_word_field(&self, idx: usize) -> Expression { - let r = self.challenges.clone().unwrap().evm_word().expr(); - self.bytes_expr(idx).rlc(&r) - } - - /// RLC of bytes of a field with keccak_input - fn keccak_field(&self, idx: usize) -> Expression { - let r = self.challenges.clone().unwrap().keccak_input().expr(); - self.bytes_expr(idx).rlc(&r) - } - - // ------------------ Acc ------------------ - - /// Next value of block field bytes RLC accumulator for keccak input, per row without offset - fn block_input_acc(&self, prev_field: Expression, idx: usize) -> Expression { - prev_field * self.keccak_exp.expr() + self.keccak_field(idx) - } - - - /// Next value of keccak output hi low bytes accumulator, per row without offset - fn keccak_output_acc(&self, hi: Expression, idx: usize) -> Expression { - let low = self.hi_low_field(idx); - hi * self.evm_exp.expr() + low - } - - // ------------------ Set Cell ------------------ - - - /// Init a cell for keccak input at the last row of all block fields - fn set_keccak_input( - &mut self, - cb: &mut ConstraintBuilder, - block_acc: Column, - ) -> Expression { - let keccak_input = cb.query_one(PiCellType::Storage2); - cb.enable_equality(keccak_input.column()); - cb.enable_equality(block_acc); - self.keccak_input = Some(keccak_input.clone()); - keccak_input.expr() - } - - /// Init a cell for block idx when we need to lookup block table - fn set_block_number(&mut self, cb: &mut ConstraintBuilder) -> Expression { - let block_number = cb.query_default(); - self.block_number = Some(block_number.clone()); - block_number.expr() - } - - fn set_keccak_output(&mut self, cb: &mut ConstraintBuilder, idx: usize) -> Expression { - let output = cb.query_one(PiCellType::Storage2); - cb.enable_equality(output.column()); - cb.require_equal("Keccak ouput {:?}", self.hi_low_field(idx), output.expr()); - self.keccak_output_hi_low[idx-KECCAK_HI] = Some(output.clone()); - output.expr() - } - - // ------------------ Assign ------------------ - - /// Returns the rlc of given bytes - pub(crate) fn assign_bytes( - &self, - region: &mut CachedRegion<'_, '_, F>, - field_r: F, - offset: usize, - bytes: &[u8], - ) -> Result { - // Assign the bytes - for (byte, cell) in bytes.iter().zip(self.bytes[offset].iter()) { - assign!(region, cell, 0 => byte.scalar())?; - } - Ok(bytes.rlc_value(field_r)) - } - - pub(crate) fn assign_keccak_output( - &self, - region: &mut CachedRegion<'_, '_, F>, - offset: usize, - keccak_output: F, - ) -> Result, Error> { - assign!(region, self.keccak_output_hi_low[offset-KECCAK_HI].as_ref().unwrap(), 0 => keccak_output) - } - - pub(crate) fn assign_block_number( - &self, - region: &mut CachedRegion<'_, '_, F>, - offset: usize, - block_number: F, - ) -> Result<(), Error> { - if let Some(c) = &self.block_number { - c.assign(region, offset, block_number)?; - Ok(()) - } else { - Err(Error::Synthesis) - } - } - - pub(crate) fn assign_keccak_input( - &self, - region: &mut CachedRegion<'_, '_, F>, - offset: usize, - block_acc_cell: AssignedCell, - ) -> Result<(), Error> { - if let Some(c) = &self.keccak_input { - block_acc_cell.copy_advice( - || "Copy block acc into cell for keccak input", - region.inner(), - c.column(), - offset - )?; - Ok(()) - } else { - Err(Error::Synthesis) - } - } -} - - -/// Public Inputs Circuit -#[derive(Clone, Default, Debug)] -pub struct TaikoPiCircuit { - /// PublicInputs data known by the verifier - pub public_data: PublicData, - _marker: PhantomData, -} - -impl TaikoPiCircuit { - /// Creates a new TaikoPiCircuit - pub fn new(public_data: PublicData) -> Self { - Self { - public_data, - _marker: PhantomData, - } - } -} - - -impl SubCircuit for TaikoPiCircuit { - type Config = TaikoPiCircuitConfig; - - fn unusable_rows() -> usize { - // No column queried at more than 3 distinct rotations, so returns 6 as - // minimum unusable rows. - 6 - } - - fn min_num_rows_block(_block: &witness::Block) -> (usize, usize) { - (USED_ROWS, USED_ROWS) - } - - fn new_from_block(block: &witness::Block) -> Self { - TaikoPiCircuit::new(PublicData::new(block)) - } - - /// Compute the public inputs for this circuit. - fn instance(&self) -> Vec> { - let keccak_rpi = self.public_data.get_pi(); - let keccak_hi = keccak_rpi - .to_fixed_bytes() - .iter() - .take(16) - .fold(F::ZERO, |acc, byte| { - acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) - }); - - let keccak_lo = keccak_rpi - .to_fixed_bytes() - .iter() - .skip(16) - .fold(F::ZERO, |acc, byte| { - acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) - }); - - let public_inputs = vec![keccak_hi, keccak_lo]; - vec![public_inputs] - } - - /// Make the assignments to the PiCircuit - fn synthesize_sub( - &self, - config: &Self::Config, - challenges: &Challenges>, - layouter: &mut impl Layouter, - ) -> Result<(), Error> { - config.byte_table.load(layouter)?; - config.assign(layouter, &self.public_data, challenges) - } -} - - -// We define the PiTestCircuit as a wrapper over PiCircuit extended to take the -// generic const parameters MAX_TXS and MAX_CALLDATA. This is necessary because -// the trait Circuit requires an implementation of `configure` that doesn't take -// any circuit parameters, and the PiCircuit defines gates that use rotations -// that depend on MAX_TXS and MAX_CALLDATA, so these two values are required -// during the configuration. -/// Test Circuit for PiCircuit -#[cfg(any(feature = "test", test))] -#[derive(Default, Clone)] -pub struct TaikoPiTestCircuit(pub TaikoPiCircuit); - -#[cfg(any(feature = "test", test))] -impl Circuit for TaikoPiTestCircuit { - type Config = (TaikoPiCircuitConfig, Challenges); - type FloorPlanner = SimpleFloorPlanner; - type Params = (); - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let block_table = BlockTable::construct(meta); - let keccak_table = KeccakTable::construct(meta); - let byte_table = ByteTable::construct(meta); - let challenges = Challenges::construct(meta); - let challenge_exprs = challenges.exprs(meta); - ( - TaikoPiCircuitConfig::new( - meta, - TaikoPiCircuitConfigArgs { - block_table, - keccak_table, - byte_table, - challenges: challenge_exprs, - }, - ), - challenges, - ) - } - - fn synthesize( - &self, - (config, challenges): Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - let challenges = challenges.values(&mut layouter); - let public_data = &self.0.public_data; - // assign block table - let randomness = challenges.evm_word(); - config - .block_table - .load(&mut layouter, &public_data.block_context, randomness)?; - // assign keccak table - config - .keccak_table - .dev_load(&mut layouter, vec![&public_data.rpi_bytes()], &challenges)?; - config.byte_table.load(&mut layouter)?; - - self.0.synthesize_sub(&config, &challenges, &mut layouter) - } -} - -#[cfg(test)] -mod taiko_pi_circuit_test { - - use super::*; - - use eth_types::ToScalar; - use halo2_proofs::{ - dev::{MockProver, VerifyFailure}, - halo2curves::bn256::Fr, - }; - use lazy_static::lazy_static; - use pretty_assertions::assert_eq; - - lazy_static! { - static ref OMMERS_HASH: H256 = H256::from_slice( - &hex::decode("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347") - .unwrap(), - ); - } - - fn run( - k: u32, - public_data: PublicData, - pi: Option>>, - ) -> Result<(), Vec> { - let circuit = TaikoPiTestCircuit::(TaikoPiCircuit::new(public_data)); - let public_inputs = pi.unwrap_or_else(|| circuit.0.instance()); - println!("{:?}", public_inputs); - let prover = match MockProver::run(k, &circuit, public_inputs) { - Ok(prover) => prover, - Err(e) => panic!("{:#?}", e), - }; - prover.verify() - } - - fn mock_public_data() -> PublicData { - let mut public_data = PublicData::default::(); - public_data.meta_hash = OMMERS_HASH.to_word(); - public_data.block_hash = OMMERS_HASH.to_word(); - public_data.block_context.block_hash = OMMERS_HASH.to_word(); - public_data.block_context.history_hashes = vec![Default::default(); 256]; - public_data.block_context.number = 300.into(); - public_data - } - - #[test] - fn test_default_pi() { - let public_data = mock_public_data(); - - let k = 17; - assert_eq!(run::(k, public_data, None), Ok(())); - } - - #[test] - fn test_fail_pi_hash() { - let public_data = mock_public_data(); - - let k = 17; - match run::(k, public_data, Some(vec![vec![Fr::zero(), Fr::one()]])) { - Ok(_) => unreachable!("this case must fail"), - Err(errs) => { - assert_eq!(errs.len(), 4); - for err in errs { - match err { - VerifyFailure::Permutation { .. } => return, - _ => unreachable!("unexpected error"), - } - } - } - } - } - - #[test] - fn test_fail_pi_prover() { - let mut public_data = mock_public_data(); - let address_bytes = [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - ]; - - public_data.prover = Address::from_slice(&address_bytes); - - let prover: Fr = public_data.prover.to_scalar().unwrap(); - let k = 17; - match run::( - k, - public_data, - Some(vec![vec![prover, Fr::zero(), Fr::one()]]), - ) { - Ok(_) => unreachable!("this case must fail"), - Err(errs) => { - assert_eq!(errs.len(), 4); - for err in errs { - match err { - VerifyFailure::Permutation { .. } => return, - _ => unreachable!("unexpected error"), - } - } - } - } - } - - #[test] - fn test_simple_pi() { - let mut public_data = mock_public_data(); - let chain_id = 1337u64; - public_data.chain_id = Word::from(chain_id); - - let k = 17; - assert_eq!(run::(k, public_data, None), Ok(())); - } - - #[test] - fn test_verify() { - let mut block = witness::Block::::default(); - - block.eth_block.parent_hash = *OMMERS_HASH; - block.eth_block.hash = Some(*OMMERS_HASH); - block.protocol_instance.block_hash = *OMMERS_HASH; - block.protocol_instance.parent_hash = *OMMERS_HASH; - block.context.history_hashes = vec![OMMERS_HASH.to_word()]; - block.context.block_hash = OMMERS_HASH.to_word(); - block.context.number = 300.into(); - - let public_data = PublicData::new(&block); - - let k = 17; - - assert_eq!(run::(k, public_data, None), Ok(())); - } -} From 7abe67ede2033e3bf9fa679c9c3884b9f347d6c4 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Tue, 22 Aug 2023 19:23:53 +0800 Subject: [PATCH 16/43] debug tmp --- zkevm-circuits/src/taiko_pi_circuit_.rs | 45 ++++++++++++++++++------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/zkevm-circuits/src/taiko_pi_circuit_.rs b/zkevm-circuits/src/taiko_pi_circuit_.rs index 4923b4690d..c00be4dc8c 100644 --- a/zkevm-circuits/src/taiko_pi_circuit_.rs +++ b/zkevm-circuits/src/taiko_pi_circuit_.rs @@ -300,7 +300,12 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { ifx!(not!(pi_field.is_last.expr()) => { let keccak_mult = (0..N).fold(1.expr(), |acc, _| acc * challenges.keccak_input()); require!( + // [a0 .. a31] [b0..b31][c0..c31] + // a0 * r^319 + ... + c31 * r^0 + a!(block_acc, 1) => a!(block_acc) * keccak_mult + pi_field.keccak_field(meta, 0) + //pi_field.keccak_field = ( b0 * r^31 + ... + b31 * r^0) + // (a0 * r31 + ... + a31 * r^0) * r^(31*2) + (b...) * r31 + (c..) * r^31*0 ); }) } @@ -311,9 +316,8 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { require!(a!(block_acc) => pi_field.evm_word_field(meta, 0)); }); ifx!(not!(pi_field.is_last.expr()) => { - let evm_mult = (0..N/2).fold(1.expr(), |acc, _| acc * challenges.evm_word()); require!( - a!(block_acc, 1) => a!(block_acc) * evm_mult + pi_field.evm_word_field(meta, 0) + a!(block_acc, 1) => a!(block_acc) + pi_field.evm_word_field(meta, 0) ); }) }); @@ -328,8 +332,8 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { ); offset += 1; } - require!(keccak_output[0].expr() => pi_field.hi_low_field(meta, KECCAK_HI)); - require!(keccak_output[1].expr() => pi_field.hi_low_field(meta, KECCAK_LOW)); + // require!(keccak_output[0].expr() => pi_field.hi_low_field(meta, KECCAK_HI)); + // require!(keccak_output[1].expr() => pi_field.hi_low_field(meta, KECCAK_LOW)); require!( ( 1.expr(), @@ -396,7 +400,7 @@ impl TaikoPiCircuitConfig { let mut k_cnt = 0; let mut e_cnt = 0; let keccak_mult = (0..N).fold(1.scalar(), |acc: F, _| {k_cnt += 1; acc * keccak_r}); - let evm_mult = (0..N/2).fold(1.scalar(), |acc: F, _| {e_cnt += 1; acc * evm_word_r}); + // let evm_mult = (0..N/2).fold(1.scalar(), |acc: F, _| {e_cnt += 1; acc * evm_word_r}); let mut assignments = public_data.assignments().to_vec(); assignments.append(&mut public_data.get_pi_hi_low().to_vec()); @@ -406,6 +410,10 @@ impl TaikoPiCircuitConfig { println!("{:?} {:?}, len {:?} \n {:?}", i, annotation, bytes.len(), bytes); match i { L1SIGNAL_IDX..=FIELD10_IDX => { + let mut bytes = bytes.clone(); + if block_number.is_none() { + bytes.reverse(); + } let field = self.field_gadget.assign(&mut region, i, keccak_r, &bytes)?; block_acc = self.assign_acc(&mut region, i, keccak_mult, block_acc, field)?; @@ -422,15 +430,15 @@ impl TaikoPiCircuitConfig { } KECCAK_HI..=KECCAK_LOW => { let mut bytes = bytes.clone(); - bytes.reverse(); + let field = self.field_gadget.assign(&mut region, i, evm_word_r, &bytes)?; - block_acc = self.assign_acc(&mut region, i, evm_mult, block_acc, field)?; + block_acc = self.assign_acc(&mut region, i, F::ONE, block_acc, field)?; // // e_cnt += N/2; // if i == KECCAK_LOW { // println!("KECCAK_LOW {:?}", block_acc); // } - + bytes.reverse(); pi_cells.push(assign!( region, self.keccak_output[i - KECCAK_HI], 0 => rlc::value(&bytes, hi_low_r) )?); @@ -438,6 +446,7 @@ impl TaikoPiCircuitConfig { _ => unimplemented!() } println!(" block_acc {:?}", block_acc); + i += 1; } Ok(pi_cells) @@ -474,10 +483,14 @@ struct FieldBytesGadget { word_r: Expression, keccak_r: Expression, } - impl FieldBytesGadget { + fn config( + cb: &mut ConstraintBuilder, + challenges: &Challenges> + ) -> Self { + + let bytes: [Cell; 32] = cb.query_bytes(); - fn config(cb: &mut ConstraintBuilder, challenges: &Challenges>) -> Self { Self { bytes: cb.query_bytes(), is_field: cb.query_bool(), @@ -494,7 +507,7 @@ impl FieldBytesGadget { /// RLC of bytes of a field with evm_word 1<<8 fn hi_low_field(&self, meta: &mut VirtualCells, idx: usize) -> Expression { - self.bytes_expr(meta, idx).rlc(&BYTE_POW_BASE.expr()) + self.bytes_expr(meta, idx).rlc_rev(&BYTE_POW_BASE.expr()) } /// RLC of bytes of a field with evm_word @@ -502,9 +515,17 @@ impl FieldBytesGadget { self.bytes_expr(meta, idx).rlc_rev(&self.word_r) } + fn evm_word_field_tmp(&self, meta: &mut VirtualCells, idx: usize) -> Expression { + let tmpp = self.bytes_expr(meta, idx); + let tmp: Vec<_> = tmpp.iter().take(16).collect(); + tmp.rlc_rev(&self.word_r) + } + /// RLC of bytes of a field with keccak_input fn keccak_field(&self, meta: &mut VirtualCells, idx: usize) -> Expression { - self.bytes_expr(meta, idx).rlc(&self.keccak_r) + self.bytes_expr(meta, idx).rlc_rev(&self.keccak_r) // OK! + // = b0 * r^31 + ... + b31 * r^0 + } // ------------------ Assign ------------------ From c9f78dc1839a907cf13ba8e6cf6ffe4566d05b5c Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Tue, 22 Aug 2023 19:24:32 +0800 Subject: [PATCH 17/43] debug tmp --- .../src/circuit_tools/constraint_builder.rs | 10 ++++++++++ zkevm-circuits/src/table/keccak_table.rs | 13 +++++++++++++ 2 files changed, 23 insertions(+) diff --git a/zkevm-circuits/src/circuit_tools/constraint_builder.rs b/zkevm-circuits/src/circuit_tools/constraint_builder.rs index 53c0552a68..422530d43d 100644 --- a/zkevm-circuits/src/circuit_tools/constraint_builder.rs +++ b/zkevm-circuits/src/circuit_tools/constraint_builder.rs @@ -1016,18 +1016,28 @@ impl RLCChainable2 for Expression { pub trait RLCableValue { /// Returns the RLC of itself fn rlc_value(&self, r: F) -> F; + /// Returns the RLC of itself + fn rlc_rev_value(&self, r: F) -> F; } impl RLCableValue for Vec { fn rlc_value(&self, r: F) -> F { rlc::value(self, r) } + + fn rlc_rev_value(&self, r: F) -> F { + rlc::value(self.iter().rev(), r) + } } impl RLCableValue for [u8] { fn rlc_value(&self, r: F) -> F { rlc::value(self, r) } + + fn rlc_rev_value(&self, r: F) -> F { + rlc::value(self.iter().rev(), r) + } } /// Trait around RLC diff --git a/zkevm-circuits/src/table/keccak_table.rs b/zkevm-circuits/src/table/keccak_table.rs index 78c5162096..ff890e9b21 100644 --- a/zkevm-circuits/src/table/keccak_table.rs +++ b/zkevm-circuits/src/table/keccak_table.rs @@ -52,6 +52,8 @@ impl KeccakTable { let input_rlc = challenges .keccak_input() .map(|challenge| rlc::value(input.iter().rev(), challenge)); + // r = keccak_input + // = input[0] * r^319 + input[1] * r^318 + ... + input[319] * r^0 let input_len = F::from(input.len() as u64); let mut keccak = Keccak::default(); @@ -63,6 +65,17 @@ impl KeccakTable { challenge, ) }); + // r = evm_word + // = output[0] * r^31 + ... + output[31] * r^0 + + + // [0, 15 31] + // a0 ... a15 00000 + // 0000 a16 .. a31 + + + // a0 ... a15 0 .. 0 -> a0 * r31 + ... + a15 * r16 + // 0 .. 0 a16 .. a31 -> a16 * r15 + ... + a31 * r0 println!("assignments input {:?} - keccak_input {:?}| output {:?} - evm_word {:?}", input.len(), input_rlc, output.len(), output_rlc); vec![[ From 3bb26bb8720b476d264f15a3d30974330e1b3ac0 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Wed, 23 Aug 2023 20:00:10 +0800 Subject: [PATCH 18/43] yeah --- zkevm-circuits/src/table/keccak_table.rs | 10 +- zkevm-circuits/src/taiko_pi_circuit.rs | 4 + zkevm-circuits/src/taiko_pi_circuit_.rs | 418 ++++++++++------------- zkevm-circuits/src/witness/block.rs | 1 + 4 files changed, 201 insertions(+), 232 deletions(-) diff --git a/zkevm-circuits/src/table/keccak_table.rs b/zkevm-circuits/src/table/keccak_table.rs index ff890e9b21..2f398e3fab 100644 --- a/zkevm-circuits/src/table/keccak_table.rs +++ b/zkevm-circuits/src/table/keccak_table.rs @@ -1,3 +1,5 @@ +use eth_types::ToBigEndian; + use super::*; /// Keccak Table, used to verify keccak hashing from RLC'ed input. @@ -58,12 +60,16 @@ impl KeccakTable { let input_len = F::from(input.len() as u64); let mut keccak = Keccak::default(); keccak.update(input); + // BE let output = keccak.digest(); + let tmp = Word::from_big_endian(output.as_slice()).to_be_bytes(); + println!("keccak.digest {:?} => {:?}", output, tmp); let output_rlc = challenges.evm_word().map(|challenge| { rlc::value( - &Word::from_big_endian(output.as_slice()).to_le_bytes(), + &Word::from_big_endian(output.as_slice()).to_le_bytes(), // rev to LE challenge, - ) + ) // rev to BE + }); // r = evm_word // = output[0] * r^31 + ... + output[31] * r^0 diff --git a/zkevm-circuits/src/taiko_pi_circuit.rs b/zkevm-circuits/src/taiko_pi_circuit.rs index a90fe4817e..0484add75b 100644 --- a/zkevm-circuits/src/taiko_pi_circuit.rs +++ b/zkevm-circuits/src/taiko_pi_circuit.rs @@ -352,6 +352,9 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { None ); + meta.pinned().print_layout_states(); + meta.pinned().print_config_states(); + Self { rpi_field_bytes, rpi_field_bytes_acc, @@ -377,6 +380,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { } } + // input -> kecceck, output -> evm impl TaikoPiCircuitConfig { #[allow(clippy::too_many_arguments)] diff --git a/zkevm-circuits/src/taiko_pi_circuit_.rs b/zkevm-circuits/src/taiko_pi_circuit_.rs index c00be4dc8c..355291bcfa 100644 --- a/zkevm-circuits/src/taiko_pi_circuit_.rs +++ b/zkevm-circuits/src/taiko_pi_circuit_.rs @@ -13,8 +13,8 @@ use crate::{ witness::{self, BlockContext}, circuit, assignf, }; -use bus_mapping::evm; -use gadgets::util::Scalar; +use bus_mapping::{evm, operation::Op}; +use gadgets::util::{Scalar, not}; use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256}; use ethers_core::utils::keccak256; use gadgets::{util::{or, select, Expr, and}, impl_expr}; @@ -27,7 +27,7 @@ use halo2_proofs::{ poly::{Rotation}, }; use rand_chacha::rand_core::block; -use std::{marker::PhantomData, ops::Range, usize, default}; +use std::{marker::PhantomData, ops::Range, usize, default, vec}; const BYTE_POW_BASE: u64 = 1 << 8; const N: usize = 32; @@ -40,8 +40,7 @@ const PARENT_HASH: usize = 4; const BLOCK_HASH: usize = 5; const FIELD9_IDX: usize = 8; const FIELD10_IDX: usize = 9; -const KECCAK_HI: usize = 10; -const KECCAK_LOW: usize = 11; +const KECCAK_OUTPUT: usize = 10; /// PublicData contains all the values that the PiCircuit receives as input @@ -172,27 +171,39 @@ impl PublicData { fn get_pi(&self) -> H256 { let rpi_bytes = self.rpi_bytes(); let rpi_keccak = keccak256(rpi_bytes); + println!("get_pi {:?}", rpi_keccak); H256(rpi_keccak) - } + } - fn get_pi_hi_low(&self) -> [(&'static str, Option, Vec); 2] { - let pi_bytes = self.get_pi().to_fixed_bytes(); - [ - ("high_16_bytes_of_keccak_rpi", None, pi_bytes[..16].to_vec()), - ("low_16_bytes_of_keccak_rpi", None, pi_bytes[16..].to_vec()), - ] - } + fn get_pi_hi_low(&self) -> (F, F) { + let keccak_rpi = self.get_pi().to_fixed_bytes(); + ( + keccak_rpi + .iter() + .take(16) + .fold(F::ZERO, |acc, byte| { + acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) + }), + keccak_rpi + .iter() + .skip(16) + .fold(F::ZERO, |acc, byte| { + acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) + }) + ) + } } /// Config for PiCircuit #[derive(Clone, Debug)] pub struct TaikoPiCircuitConfig { q_enable: Selector, public_input: Column, // equality - block_acc: Column, - field_gadget: FieldBytesGadget, - - block_number: [Cell;2], // Phase1 - keccak_output:[Cell;2], // Phase2, equality + field: FieldBytesGadget, + state: PiState, + block_acc: Cell, // Phase2 + block_number: Cell, // Phase1 + keccak_input: Cell, // Phase2 + keccak_output:[Cell;2], // Phase2 block_table: BlockTable, keccak_table: KeccakTable, @@ -253,96 +264,74 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { challenges, }: Self::ConfigArgs, ) -> Self { - let field_cm = CellManager::new( - meta, - vec![ - (PiCellType::Byte, 32, 1, false), - (PiCellType::Storage1, 3, 1, false) - ], - 0, - 1, - ); - let state_cm = CellManager::new( + let cm = CellManager::new( meta, vec![ + (PiCellType::Byte, 1, 1, false), (PiCellType::Storage1, 1, 1, false), (PiCellType::Storage2, 1, 2, true), ], 0, - 12, + 32, ); - let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(field_cm.clone()), Some(challenges.evm_word())); + let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(cm.clone()), Some(challenges.evm_word())); cb.preload_tables(meta, &[ - (PiCellType::Lookup(Keccak), &keccak_table), - (PiCellType::Lookup(Bytecode), &byte_table), - (PiCellType::Lookup(Block), &block_table) + (PiCellType::Lookup(Keccak), &keccak_table), + (PiCellType::Lookup(Bytecode), &byte_table), + (PiCellType::Lookup(Block), &block_table) ] ); let q_enable = meta.complex_selector(); let public_input = meta.instance_column(); - let block_acc = meta.advice_column_in(SecondPhase); - let pi_field = FieldBytesGadget::config(&mut cb, &challenges); - cb.set_cell_manager(state_cm.clone()); - let block_number = [();2].map(|_| cb.query_one(PiCellType::Storage1)); + let field = FieldBytesGadget::config(&mut cb, &challenges); + let state = PiState::config(&mut cb); + let block_acc = cb.query_one(PiCellType::Storage2); + let block_number = cb.query_one(PiCellType::Storage1); + let keccak_input = cb.query_one(PiCellType::Storage2); let keccak_output = [();2].map(|_| cb.query_one(PiCellType::Storage2)); meta.enable_equality(public_input); meta.create_gate( "PI acc constraints", |meta| { + let keccak_mult = (0..N).fold(1.expr(), |acc, _| acc * challenges.keccak_input()); + // let evm_mult = (0..N).fold(1.expr(), |acc, _| acc * challenges.evm_word()); circuit!([meta, cb], { - // Field of PublicData - // start with field bytes rlc with keccak_r and increment with keccak_r - ifx!(pi_field.is_field.expr() => { - ifx!(pi_field.is_first.expr() => { - require!(a!(block_acc) => pi_field.keccak_field(meta, 0)); - }); - ifx!(not!(pi_field.is_last.expr()) => { - let keccak_mult = (0..N).fold(1.expr(), |acc, _| acc * challenges.keccak_input()); + ifx!(state.increment_step() => { + require!(block_acc.rot(meta, 32) => block_acc.expr() * keccak_mult + field.keccak_field()); + }); + matchx!(( + state.is_l1_signal.expr() => { + require!(block_acc.expr() => field.keccak_field()); + }, + state.lookup_blockhash() => { require!( - // [a0 .. a31] [b0..b31][c0..c31] - // a0 * r^319 + ... + c31 * r^0 - - a!(block_acc, 1) => a!(block_acc) * keccak_mult + pi_field.keccak_field(meta, 0) - //pi_field.keccak_field = ( b0 * r^31 + ... + b31 * r^0) - // (a0 * r31 + ... + a31 * r^0) * r^(31*2) + (b...) * r31 + (c..) * r^31*0 + ( + BlockContextFieldTag::BlockHash.expr(), + block_number.expr(), + field.evm_word_field() + ) => @PiCellType::Lookup(Table::Block), (TO_FIX) ); - }) - } - // Row with Keccak Hi/Low - // start with field bytes rlc with 1 << 8 and increment with evm_word - elsex { - ifx!(pi_field.is_first.expr() => { - require!(a!(block_acc) => pi_field.evm_word_field(meta, 0)); - }); - ifx!(not!(pi_field.is_last.expr()) => { + }, + state.is_field_10.expr() => { + require!(keccak_input.expr() => block_acc.expr()); + }, + state.is_keccak_output.expr() => { require!( - a!(block_acc, 1) => a!(block_acc) + pi_field.evm_word_field(meta, 0) + ( + 1.expr(), + keccak_input.expr(), + RPI_BYTES_LEN.expr(), + field.evm_word_field() + ) + => @PiCellType::Lookup(Table::Keccak), (TO_FIX) ); - }) - }); - let mut offset = PARENT_HASH; - for bn in &block_number { - require!( - ( - BlockContextFieldTag::BlockHash.expr(), - bn.expr(), - pi_field.evm_word_field(meta, offset) - ) => @PiCellType::Lookup(Table::Block), (TO_FIX) - ); - offset += 1; - } - // require!(keccak_output[0].expr() => pi_field.hi_low_field(meta, KECCAK_HI)); - // require!(keccak_output[1].expr() => pi_field.hi_low_field(meta, KECCAK_LOW)); - require!( - ( - 1.expr(), - a!(block_acc, FIELD10_IDX), - RPI_BYTES_LEN.expr(), - a!(block_acc, KECCAK_LOW) - ) - => @PiCellType::Lookup(Table::Keccak), (TO_FIX) - ); + let (hi_expr, low_expr) = field.hi_low_field(); + require!(keccak_output[0].expr() => hi_expr); + require!(keccak_output[1].expr() => low_expr); + keccak_output.iter().for_each(|c| cb.enable_equality(c.column())); + } + )); }); cb.build_constraints(Some(meta.query_selector(q_enable))) } @@ -350,7 +339,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { cb.build_equalities(meta); cb.build_lookups( meta, - &[field_cm.clone()], + &[cm.clone()], &[ (PiCellType::Byte, PiCellType::Lookup(Bytecode)), (PiCellType::Lookup(Table::Keccak), PiCellType::Lookup(Table::Keccak)), @@ -358,14 +347,18 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { ], Some(q_enable) ); - let mut col_configs = field_cm.columns().to_vec(); - col_configs.append(&mut state_cm.columns().to_vec()); + // meta.pinned().print_config_states(); + // meta.pinned().print_layout_states(); + + let mut col_configs = cm.columns().to_vec(); Self { q_enable, public_input, + field, + state, block_acc, - field_gadget: pi_field, block_number, + keccak_input, keccak_output, block_table, keccak_table, @@ -392,62 +385,45 @@ impl TaikoPiCircuitConfig { region.annotate_columns(&self.col_configs); let mut block_acc = F::ZERO; - let mut evm_word_r = F::ZERO; - let mut keccak_r = F::ZERO; - let hi_low_r = F::from(BYTE_POW_BASE); - challenges.evm_word().map(|v| evm_word_r = v); - challenges.keccak_input().map(|v| keccak_r = v); - let mut k_cnt = 0; - let mut e_cnt = 0; - let keccak_mult = (0..N).fold(1.scalar(), |acc: F, _| {k_cnt += 1; acc * keccak_r}); - // let evm_mult = (0..N/2).fold(1.scalar(), |acc: F, _| {e_cnt += 1; acc * evm_word_r}); + let (keccak_r, keccak_mult, evm_word_r, evm_word_mult) = self.randomness(challenges); let mut assignments = public_data.assignments().to_vec(); - assignments.append(&mut public_data.get_pi_hi_low().to_vec()); - let mut i = 0; + assignments.append(&mut vec![ + ("keccak_output", None, public_data.get_pi().to_fixed_bytes().to_vec()) + ]); + let mut offset = 0; + let mut state = 0; let mut pi_cells = Vec::new(); for (annotation, block_number, bytes) in assignments { - println!("{:?} {:?}, len {:?} \n {:?}", i, annotation, bytes.len(), bytes); - match i { - L1SIGNAL_IDX..=FIELD10_IDX => { - let mut bytes = bytes.clone(); - if block_number.is_none() { - bytes.reverse(); - } - let field = self.field_gadget.assign(&mut region, i, keccak_r, &bytes)?; - block_acc = self.assign_acc(&mut region, i, keccak_mult, block_acc, field)?; - - // // k_cnt += N; - // if i == FIELD10_IDX { - // println!("FIELD10_IDX {:?}", block_acc); - // } - - if let Some(block_number) = block_number { - println!(" block_number {:?} => {:?} {:?}", block_number, self.block_number[i - PARENT_HASH].expr().identifier(), field); - println!(" {:?}", bytes); - assign!(region, self.block_number[i - PARENT_HASH], 0 => block_number.as_u64().scalar())?; - } - } - KECCAK_HI..=KECCAK_LOW => { - let mut bytes = bytes.clone(); - - let field = self.field_gadget.assign(&mut region, i, evm_word_r, &bytes)?; - block_acc = self.assign_acc(&mut region, i, F::ONE, block_acc, field)?; - - // // e_cnt += N/2; - // if i == KECCAK_LOW { - // println!("KECCAK_LOW {:?}", block_acc); - // } - bytes.reverse(); - pi_cells.push(assign!( - region, self.keccak_output[i - KECCAK_HI], 0 => rlc::value(&bytes, hi_low_r) - )?); + + println!("{:?} {:?}, len {:?} \n {:?}", offset, annotation, bytes.len(), bytes); + + self.state.assign(&mut region, offset, state)?; + if state != KECCAK_OUTPUT { + let next = block_acc * keccak_mult + + self.field.assign(&mut region, offset, &bytes, keccak_r)?; + assign!(region, self.block_acc, offset => next)?; + block_acc = next; + } + match state { + PARENT_HASH | BLOCK_HASH => { + let block_number = block_number.expect(&format!("block_number missing at {:?}th row", offset)); + assign!(region, self.block_number, offset => block_number.as_u64().scalar())?; + }, + FIELD10_IDX => { + assign!(region, self.keccak_input, offset => block_acc)?; }, - _ => unimplemented!() + KECCAK_OUTPUT => { + let (hi, low) = public_data.get_pi_hi_low::(); + pi_cells.push(assign!(region, self.keccak_output[0], offset => hi)?); + pi_cells.push(assign!(region, self.keccak_output[1], offset => low)?); + }, + _ => () } println!(" block_acc {:?}", block_acc); - i += 1; + offset += N; + state += 1 } Ok(pi_cells) } @@ -459,73 +435,107 @@ impl TaikoPiCircuitConfig { Ok(()) } - pub(crate) fn assign_acc( + fn randomness(&self, challenges: &Challenges>) -> (F, F, F, F) { + let mut evm_word_r = F::ZERO; + let mut keccak_r = F::ZERO; + let hi_low_r = F::from(BYTE_POW_BASE); + challenges.evm_word().map(|v| evm_word_r = v); + challenges.keccak_input().map(|v| keccak_r = v); + ( + keccak_r, + (0..N).fold(1.scalar(), |acc: F, _| acc * keccak_r), + evm_word_r, + (0..N).fold(1.scalar(), |acc: F, _| acc * evm_word_r), + ) + } +} + +#[derive(Clone, Debug)] +struct PiState { + pub(crate) state: Cell, + is_l1_signal: IsEqualGadget, + is_parent_hash: IsEqualGadget, + is_block_hash: IsEqualGadget, + is_field_10: IsEqualGadget, + is_keccak_output: IsEqualGadget, +} +impl PiState { + pub(crate) fn config(cb: &mut ConstraintBuilder) -> Self { + let state = cb.query_default(); + Self { + state: state.clone(), + is_l1_signal: IsEqualGadget::construct(cb, state.expr(), L1SIGNAL_IDX.expr()), + is_parent_hash: IsEqualGadget::construct(cb, state.expr(), PARENT_HASH.expr()), + is_block_hash: IsEqualGadget::construct(cb, state.expr(), BLOCK_HASH.expr()), + is_field_10: IsEqualGadget::construct(cb, state.expr(), FIELD10_IDX.expr()), + is_keccak_output: IsEqualGadget::construct(cb, state.expr(), KECCAK_OUTPUT.expr()), + } + } + + pub(crate) fn increment_step(&self) -> Expression { + not::expr(self.is_field_10.expr()) + not::expr(self.is_keccak_output.expr()) + } + + pub(crate) fn lookup_blockhash(&self) -> Expression { + self.is_block_hash.expr() + } + + pub(crate) fn assign( &self, region: &mut CachedRegion<'_, '_, F>, offset: usize, - acc_r: F, - prev: F, - other: F, - ) -> Result { - let next = prev * acc_r + other; - assign!(region, (self.block_acc, offset) => next)?; - Ok(next) + state: usize + ) -> Result<(), Error> { + assign!(region, self.state, offset => state.scalar()); + self.is_l1_signal.assign(region, offset, state.scalar(), L1SIGNAL_IDX.scalar())?; + self.is_parent_hash.assign(region, offset, state.scalar(), PARENT_HASH.scalar())?; + self.is_block_hash.assign(region, offset, state.scalar(), BLOCK_HASH.scalar())?; + self.is_field_10.assign(region, offset, state.scalar(), FIELD10_IDX.scalar())?; + self.is_keccak_output.assign(region, offset, state.scalar(), KECCAK_OUTPUT.scalar())?; + Ok(()) } + } #[derive(Clone, Debug)] struct FieldBytesGadget { bytes: [Cell; N], - is_field: Cell, - is_first: Cell, - is_last: Cell, word_r: Expression, keccak_r: Expression, } impl FieldBytesGadget { - fn config( + pub(crate) fn config( cb: &mut ConstraintBuilder, challenges: &Challenges> ) -> Self { - - let bytes: [Cell; 32] = cb.query_bytes(); - Self { bytes: cb.query_bytes(), - is_field: cb.query_bool(), - is_first: cb.query_bool(), - is_last: cb.query_bool(), word_r: challenges.evm_word().expr(), keccak_r: challenges.keccak_input().expr(), } } - fn bytes_expr(&self, meta: &mut VirtualCells, idx: usize) -> Vec> { - self.bytes.iter().map(|b| b.rot(meta, idx)).collect() + pub(crate) fn bytes_expr(&self) -> Vec> { + self.bytes.iter().map(|b| b.expr()).collect() } /// RLC of bytes of a field with evm_word 1<<8 - fn hi_low_field(&self, meta: &mut VirtualCells, idx: usize) -> Expression { - self.bytes_expr(meta, idx).rlc_rev(&BYTE_POW_BASE.expr()) + pub(crate) fn hi_low_field(&self) -> (Expression, Expression) { + let hi = self.bytes_expr()[..16].to_vec(); + let low = self.bytes_expr()[16..].to_vec(); + (hi.rlc_rev(&BYTE_POW_BASE.expr()), low.rlc_rev(&BYTE_POW_BASE.expr())) } /// RLC of bytes of a field with evm_word - fn evm_word_field(&self, meta: &mut VirtualCells, idx: usize) -> Expression { - self.bytes_expr(meta, idx).rlc_rev(&self.word_r) - } - - fn evm_word_field_tmp(&self, meta: &mut VirtualCells, idx: usize) -> Expression { - let tmpp = self.bytes_expr(meta, idx); - let tmp: Vec<_> = tmpp.iter().take(16).collect(); - tmp.rlc_rev(&self.word_r) + pub(crate) fn evm_word_field(&self) -> Expression { + self.bytes_expr().rlc_rev(&self.word_r) } /// RLC of bytes of a field with keccak_input - fn keccak_field(&self, meta: &mut VirtualCells, idx: usize) -> Expression { - self.bytes_expr(meta, idx).rlc_rev(&self.keccak_r) // OK! + pub(crate) fn keccak_field(&self) -> Expression { + self.bytes_expr().rlc_rev(&self.keccak_r) // OK! // = b0 * r^31 + ... + b31 * r^0 - } // ------------------ Assign ------------------ @@ -535,53 +545,14 @@ impl FieldBytesGadget { &self, region: &mut CachedRegion<'_, '_, F>, offset: usize, - r: F, bytes: &[u8], + r: F, ) -> Result { // Assign the bytes - let mut bytes = bytes.to_vec(); - while bytes.len() < N { - bytes.push(0); - } for (byte, cell) in bytes.iter().zip(self.bytes.iter()) { assign!(region, cell, offset => byte.scalar())?; } - self.assign_flags(region, offset)?; - Ok(bytes.rlc_value(r)) - } - - pub(crate) fn assign_flags( - &self, - region: &mut CachedRegion<'_, '_, F>, - offset: usize, - ) -> Result<(), Error> { - match offset { - L1SIGNAL_IDX..=FIELD10_IDX => { - assign!(region, self.is_field, offset => true.scalar())?; - if offset == L1SIGNAL_IDX { - assign!(region, self.is_first, offset => true.scalar())?; - } else { - assign!(region, self.is_first, offset => false.scalar())?; - } - if offset == FIELD10_IDX { - assign!(region, self.is_last, offset => true.scalar())?; - } else { - assign!(region, self.is_last, offset => false.scalar())?; - } - }, - KECCAK_HI => { - assign!(region, self.is_first, offset => true.scalar())?; - assign!(region, self.is_field, offset => false.scalar())?; - assign!(region, self.is_last, offset => false.scalar())?; - }, - KECCAK_LOW => { - assign!(region, self.is_first, offset => false.scalar())?; - assign!(region, self.is_field, offset => false.scalar())?; - assign!(region, self.is_last, offset => true.scalar())?; - }, - _ => unreachable!(), - }; - Ok(()) + Ok(bytes.rlc_rev_value(r)) } } @@ -624,25 +595,8 @@ impl SubCircuit for TaikoPiCircuit { /// Compute the public inputs for this circuit. fn instance(&self) -> Vec> { - let keccak_rpi = self.public_data.get_pi(); - let keccak_hi = keccak_rpi - .to_fixed_bytes() - .iter() - .take(16) - .fold(F::ZERO, |acc, byte| { - acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) - }); - - let keccak_lo = keccak_rpi - .to_fixed_bytes() - .iter() - .skip(16) - .fold(F::ZERO, |acc, byte| { - acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) - }); - - let public_inputs = vec![keccak_hi, keccak_lo]; - vec![public_inputs] + let (hi, low) = self.public_data.get_pi_hi_low::(); + vec![vec![hi, low]] } /// Make the assignments to the PiCircuit @@ -711,6 +665,8 @@ impl Circuit for TaikoPiTestCircuit { config .block_table .load(&mut layouter, &public_data.block_context, randomness)?; + // [Tag, 0 (b0*r^31 + ... + b31*r^0)] + // assign keccak table config .keccak_table @@ -747,6 +703,7 @@ mod taiko_pi_circuit_test { pi: Option>>, ) -> Result<(), Vec> { let circuit = TaikoPiTestCircuit::(TaikoPiCircuit::new(public_data)); + let public_inputs = pi.unwrap_or_else(|| circuit.0.instance()); println!("{:?}", public_inputs); let prover = match MockProver::run(k, &circuit, public_inputs) { @@ -772,6 +729,7 @@ mod taiko_pi_circuit_test { let k = 17; assert_eq!(run::(k, public_data, None), Ok(())); + // run::(k, public_data, None); } #[test] diff --git a/zkevm-circuits/src/witness/block.rs b/zkevm-circuits/src/witness/block.rs index 73c4e99c57..60c03d6fcc 100644 --- a/zkevm-circuits/src/witness/block.rs +++ b/zkevm-circuits/src/witness/block.rs @@ -188,6 +188,7 @@ impl BlockContext { Value::known(self.number.to_scalar().unwrap()), randomness .map(|randomness| rlc::value(&self.block_hash.to_le_bytes(), randomness)), + // b0 * r^31 + ... + b31 * r^0 ], ], { From b3df73ef7ed975316124d62250cdc334df75dbc7 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Wed, 23 Aug 2023 21:40:41 +0800 Subject: [PATCH 19/43] cleanup --- .../src/circuit_tools/cached_region.rs | 1 - .../src/circuit_tools/cell_manager.rs | 1 - .../src/circuit_tools/constraint_builder.rs | 4 - zkevm-circuits/src/lib.rs | 3 +- zkevm-circuits/src/table/keccak_table.rs | 24 +- zkevm-circuits/src/taiko_pi_circuit.rs | 762 ++++++++-------- zkevm-circuits/src/taiko_pi_circuit_.rs | 811 ------------------ 7 files changed, 352 insertions(+), 1254 deletions(-) delete mode 100644 zkevm-circuits/src/taiko_pi_circuit_.rs diff --git a/zkevm-circuits/src/circuit_tools/cached_region.rs b/zkevm-circuits/src/circuit_tools/cached_region.rs index 92bef7ef9e..07bc9f992a 100644 --- a/zkevm-circuits/src/circuit_tools/cached_region.rs +++ b/zkevm-circuits/src/circuit_tools/cached_region.rs @@ -68,7 +68,6 @@ impl<'r, 'b, F: Field> CachedRegion<'r, 'b, F> { ) -> Result<(), Error> { for (offset, region_id) in self.regions.clone() { for stored_expression in cb.get_stored_expressions(region_id).iter() { - // println!("stored expression: {}", stored_expression.name); stored_expression.assign(self, challenges, offset)?; } } diff --git a/zkevm-circuits/src/circuit_tools/cell_manager.rs b/zkevm-circuits/src/circuit_tools/cell_manager.rs index b7e7a256e9..395964c93c 100644 --- a/zkevm-circuits/src/circuit_tools/cell_manager.rs +++ b/zkevm-circuits/src/circuit_tools/cell_manager.rs @@ -133,7 +133,6 @@ impl CellConfig { }; columns.push(tmp); } - println!("cm init {:?} column: {:?}", self.cell_type, self.num_columns); if self.is_permute { let _ = columns .iter() diff --git a/zkevm-circuits/src/circuit_tools/constraint_builder.rs b/zkevm-circuits/src/circuit_tools/constraint_builder.rs index 422530d43d..8345015e2c 100644 --- a/zkevm-circuits/src/circuit_tools/constraint_builder.rs +++ b/zkevm-circuits/src/circuit_tools/constraint_builder.rs @@ -352,9 +352,6 @@ impl ConstraintBuilder { pub(crate) fn query_one(&mut self, cell_type: C) -> Cell { let res = self.query_cells_dyn(cell_type, 1).first().unwrap().clone(); - if res.column().index() == 45 { - println!("\n found 45 {:?}", cell_type); - } res } @@ -409,7 +406,6 @@ impl ConstraintBuilder { self.equalities .iter() .for_each(|c| { - println!("Enable equality for {:?}", c.index()); meta.enable_equality(c.clone())}); } diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index c4fbf00fb6..cf92f3b944 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -27,9 +27,8 @@ pub mod root_circuit; pub mod state_circuit; pub mod super_circuit; pub mod table; -pub mod taiko_pi_circuit; #[macro_use] -pub mod taiko_pi_circuit_; +pub mod taiko_pi_circuit; pub mod taiko_super_circuit; #[cfg(any(feature = "test", test))] diff --git a/zkevm-circuits/src/table/keccak_table.rs b/zkevm-circuits/src/table/keccak_table.rs index 2f398e3fab..aa2cb94d47 100644 --- a/zkevm-circuits/src/table/keccak_table.rs +++ b/zkevm-circuits/src/table/keccak_table.rs @@ -54,36 +54,17 @@ impl KeccakTable { let input_rlc = challenges .keccak_input() .map(|challenge| rlc::value(input.iter().rev(), challenge)); - // r = keccak_input - // = input[0] * r^319 + input[1] * r^318 + ... + input[319] * r^0 let input_len = F::from(input.len() as u64); let mut keccak = Keccak::default(); keccak.update(input); - // BE let output = keccak.digest(); - let tmp = Word::from_big_endian(output.as_slice()).to_be_bytes(); - println!("keccak.digest {:?} => {:?}", output, tmp); let output_rlc = challenges.evm_word().map(|challenge| { rlc::value( - &Word::from_big_endian(output.as_slice()).to_le_bytes(), // rev to LE + &Word::from_big_endian(output.as_slice()).to_le_bytes(), challenge, - ) // rev to BE - + ) }); - // r = evm_word - // = output[0] * r^31 + ... + output[31] * r^0 - - - // [0, 15 31] - // a0 ... a15 00000 - // 0000 a16 .. a31 - - - // a0 ... a15 0 .. 0 -> a0 * r31 + ... + a15 * r16 - // 0 .. 0 a16 .. a31 -> a16 * r15 + ... + a31 * r0 - println!("assignments input {:?} - keccak_input {:?}| output {:?} - evm_word {:?}", input.len(), input_rlc, output.len(), output_rlc); - vec![[ Value::known(F::ONE), input_rlc, @@ -132,7 +113,6 @@ impl KeccakTable { let keccak_table_columns = >::advice_columns(self); for input in inputs.clone() { - println!("dev_load offset={:?} input: {:?}", offset, input); for row in Self::assignments(input, challenges) { // let mut column_index = 0; for (&column, value) in keccak_table_columns.iter().zip_eq(row) { diff --git a/zkevm-circuits/src/taiko_pi_circuit.rs b/zkevm-circuits/src/taiko_pi_circuit.rs index 0484add75b..f8f688dead 100644 --- a/zkevm-circuits/src/taiko_pi_circuit.rs +++ b/zkevm-circuits/src/taiko_pi_circuit.rs @@ -1,38 +1,48 @@ //! Use the hash value as public input. use crate::{ + assign, evm_circuit::table::Table::*, - evm_circuit::{util::constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, table::Table}, + evm_circuit::{util::{constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, rlc}, table::Table}, table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}, - util::{random_linear_combine_word as rlc, Challenges, SubCircuit, SubCircuitConfig}, + util::{Challenges, SubCircuitConfig, SubCircuit}, circuit_tools::{ - constraint_builder::{ConstraintBuilder, TO_FIX}, - cell_manager::{CellManager, CellType}, + constraint_builder::{ConstraintBuilder, RLCable, RLCChainable, TO_FIX, COMPRESS, REDUCE, RLCableValue}, + cell_manager::{CellManager, CellType, Cell, CellConfig, CellColumn}, gadgets::{IsEqualGadget, IsZeroGadget}, cached_region::{CachedRegion, self}, }, - witness::{self, BlockContext}, circuit, + witness::{self, BlockContext}, circuit, assignf, }; +use bus_mapping::{evm, operation::Op}; +use gadgets::util::{Scalar, not}; use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256}; use ethers_core::utils::keccak256; -use gadgets::util::{or, select, Expr, and}; +use gadgets::{util::{or, select, Expr, and}, impl_expr}; use halo2_proofs::{ circuit::{AssignedCell, Layouter, Region, SimpleFloorPlanner, Value}, plonk::{ Advice, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, Instance, SecondPhase, - Selector, + Selector, VirtualCells, }, - poly::Rotation, + poly::{Rotation}, }; -use std::marker::PhantomData; +use rand_chacha::rand_core::block; +use std::{marker::PhantomData, ops::Range, usize, default, vec}; -const MAX_DEGREE: usize = 9; -const RPI_CELL_IDX: usize = 0; -const RPI_RLC_ACC_CELL_IDX: usize = 1; const BYTE_POW_BASE: u64 = 1 << 8; +const N: usize = 32; +const H: usize = 12; const RPI_BYTES_LEN: usize = 32 * 10; -// 10 fields * 32B + lo(16B) + hi(16B) + keccak(32B) const USED_ROWS: usize = RPI_BYTES_LEN + 64; +const L1SIGNAL_IDX: usize = 0; +const PARENT_HASH: usize = 4; +const BLOCK_HASH: usize = 5; +const FIELD9_IDX: usize = 8; +const FIELD10_IDX: usize = 9; +const KECCAK_OUTPUT: usize = 10; + + /// PublicData contains all the values that the PiCircuit receives as input #[derive(Debug, Clone, Default)] pub struct PublicData { @@ -77,48 +87,48 @@ pub struct PublicData { } impl PublicData { - fn assignments(&self) -> [(&'static str, Option, [u8; 32]); 10] { + fn assignments(&self) -> [(&'static str, Option, Vec); 10] { [ ( "l1_signal_service", None, - self.l1_signal_service.to_be_bytes(), + self.l1_signal_service.to_be_bytes().to_vec(), ), ( "l2_signal_service", None, - self.l2_signal_service.to_be_bytes(), + self.l2_signal_service.to_be_bytes().to_vec(), ), - ("l2_contract", None, self.l2_contract.to_be_bytes()), - ("meta_hash", None, self.meta_hash.to_be_bytes()), + ("l2_contract", None, self.l2_contract.to_be_bytes().to_vec()), + ("meta_hash", None, self.meta_hash.to_be_bytes().to_vec()), ( "parent_hash", Some(self.block_context.number - 1), - self.parent_hash.to_be_bytes(), + self.parent_hash.to_be_bytes().to_vec(), ), ( "block_hash", Some(self.block_context.number), - self.block_hash.to_be_bytes(), + self.block_hash.to_be_bytes().to_vec(), ), - ("signal_root", None, self.signal_root.to_be_bytes()), - ("graffiti", None, self.graffiti.to_be_bytes()), + ("signal_root", None, self.signal_root.to_be_bytes().to_vec()), + ("graffiti", None, self.graffiti.to_be_bytes().to_vec()), ( "prover+parentGasUsed+gasUsed", None, - self.field9.to_be_bytes(), + self.field9.to_be_bytes().to_vec(), ), ( "blockMaxGasLimit+maxTransactionsPerBlock+maxBytesPerTxList", None, - self.field10.to_be_bytes(), + self.field10.to_be_bytes().to_vec(), ), ] } /// get rpi bytes pub fn rpi_bytes(&self) -> Vec { - self.assignments().iter().flat_map(|v| v.2).collect() + self.assignments().iter().flat_map(|v| v.2.clone()).collect() } fn default() -> Self { @@ -161,33 +171,44 @@ impl PublicData { let rpi_bytes = self.rpi_bytes(); let rpi_keccak = keccak256(rpi_bytes); H256(rpi_keccak) + } + + fn get_pi_hi_low(&self) -> (F, F) { + let keccak_rpi = self.get_pi().to_fixed_bytes(); + ( + keccak_rpi + .iter() + .take(16) + .fold(F::ZERO, |acc, byte| { + acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) + }), + keccak_rpi + .iter() + .skip(16) + .fold(F::ZERO, |acc, byte| { + acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) + }) + ) } } - /// Config for PiCircuit #[derive(Clone, Debug)] pub struct TaikoPiCircuitConfig { - rpi_field_bytes: Column, - rpi_field_bytes_acc: Column, - rpi_rlc_acc: Column, - q_field_start: Selector, - q_field_step: Selector, - q_field_end: Selector, - is_field_rlc: Column, + q_enable: Selector, + public_input: Column, // equality + field: FieldBytesGadget, + state: PiState, + block_acc: Cell, // Phase2 + block_number: Cell, // Phase1 + keccak_input: Cell, // Phase2 + keccak_output:[Cell;2], // Phase2 - byte_table: ByteTable, - - pi: Column, // keccak_hi, keccak_lo - - q_keccak: Selector, - keccak_table: KeccakTable, - - // External tables - q_block_table: Selector, - block_index: Column, block_table: BlockTable, + keccak_table: KeccakTable, + byte_table: ByteTable, - _marker: PhantomData, + // To annotate columns at assignment for debug purpose + col_configs: Vec>, } /// Circuit configuration arguments @@ -202,392 +223,320 @@ pub struct TaikoPiCircuitConfigArgs { pub challenges: Challenges>, } -/// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum PiCellType { - /// - Storage, - /// +enum PiCellType { + Storage1, + Storage2, + Byte, Lookup(Table) } impl CellType for PiCellType { fn byte_type() -> Option { - unimplemented!() + Some(Self::Byte) } fn storage_for_phase(phase: u8) -> Self { - unimplemented!() + match phase { + 1 => PiCellType::Storage1, + 2 => PiCellType::Storage2, + _ => unimplemented!() + } } } impl Default for PiCellType { fn default() -> Self { - Self::Storage + Self::Storage1 } } - impl SubCircuitConfig for TaikoPiCircuitConfig { type ConfigArgs = TaikoPiCircuitConfigArgs; - /// Return a new TaikoPiCircuitConfig - fn new( - meta: &mut ConstraintSystem, - Self::ConfigArgs { - block_table, - keccak_table, - byte_table, - challenges, - }: Self::ConfigArgs, - ) -> Self { - let rpi_field_bytes = meta.advice_column(); - let rpi_field_bytes_acc = meta.advice_column_in(SecondPhase); - let rpi_rlc_acc = meta.advice_column_in(SecondPhase); - let q_field_start = meta.complex_selector(); - let q_field_step = meta.complex_selector(); - let q_field_end = meta.complex_selector(); - let is_field_rlc = meta.fixed_column(); - - let pi = meta.instance_column(); - - let q_keccak = meta.complex_selector(); - let q_block_table = meta.complex_selector(); - let block_index = meta.advice_column(); - - meta.enable_equality(rpi_field_bytes); - meta.enable_equality(rpi_field_bytes_acc); - meta.enable_equality(rpi_rlc_acc); - meta.enable_equality(block_table.value); - meta.enable_equality(pi); - - let cm = CellManager::new( - meta, - vec![ - (PiCellType::Storage, 3, 1, false), - ], - 0, - 1, - ); - let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(cm), Some(challenges.evm_word())); - cb.preload_tables(meta, - &[ - (PiCellType::Lookup(Keccak), &keccak_table), - (PiCellType::Lookup(Bytecode), &byte_table), - (PiCellType::Lookup(Block), &block_table) - ] - ); - - // field bytes - meta.create_gate( - "PI acc constraints", - |meta| { - circuit!([meta, cb], { - // rpi_field_bytes_acc[i+1] = rpi_field_bytes_acc[i] * t + rpi_bytes[i+1] - ifx!(q!(q_field_step) => { - let t = ifx!(f!(is_field_rlc) => { - challenges.evm_word() - } elsex { - BYTE_POW_BASE.expr() + /// Return a new TaikoPiCircuitConfig + fn new( + meta: &mut ConstraintSystem, + Self::ConfigArgs { + block_table, + keccak_table, + byte_table, + challenges, + }: Self::ConfigArgs, + ) -> Self { + let cm = CellManager::new( + meta, + vec![ + (PiCellType::Byte, 1, 1, false), + (PiCellType::Storage1, 1, 1, false), + (PiCellType::Storage2, 1, 2, true), + ], + 0, + 32, + ); + let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(cm.clone()), Some(challenges.evm_word())); + cb.preload_tables(meta, + &[ + (PiCellType::Lookup(Keccak), &keccak_table), + (PiCellType::Lookup(Bytecode), &byte_table), + (PiCellType::Lookup(Block), &block_table) + ] + ); + let q_enable = meta.complex_selector(); + let public_input = meta.instance_column(); + let field = FieldBytesGadget::config(&mut cb, &challenges); + let state = PiState::config(&mut cb); + let block_acc = cb.query_one(PiCellType::Storage2); + let block_number = cb.query_one(PiCellType::Storage1); + let keccak_input = cb.query_one(PiCellType::Storage2); + let keccak_output = [();2].map(|_| cb.query_one(PiCellType::Storage2)); + meta.enable_equality(public_input); + meta.create_gate( + "PI acc constraints", + |meta| { + let keccak_mult = (0..N).fold(1.expr(), |acc, _| acc * challenges.keccak_input()); + // let evm_mult = (0..N).fold(1.expr(), |acc, _| acc * challenges.evm_word()); + circuit!([meta, cb], { + ifx!(state.increment_step() => { + require!(block_acc.rot(meta, 32) => block_acc.expr() * keccak_mult + field.keccak_field()); + }); + matchx!(( + state.is_l1_signal.expr() => { + require!(block_acc.expr() => field.keccak_field()); + }, + state.lookup_blockhash() => { + require!( + ( + BlockContextFieldTag::BlockHash.expr(), + block_number.expr(), + field.evm_word_field() + ) => @PiCellType::Lookup(Table::Block), (TO_FIX) + ); + }, + state.is_field_10.expr() => { + require!(keccak_input.expr() => block_acc.expr()); + }, + state.is_keccak_output.expr() => { + require!( + ( + 1.expr(), + keccak_input.expr(), + RPI_BYTES_LEN.expr(), + field.evm_word_field() + ) + => @PiCellType::Lookup(Table::Keccak), (TO_FIX) + ); + let (hi_expr, low_expr) = field.hi_low_field(); + require!(keccak_output[0].expr() => hi_expr); + require!(keccak_output[1].expr() => low_expr); + keccak_output.iter().for_each(|c| cb.enable_equality(c.column())); + } + )); }); - require!( - a!(rpi_field_bytes_acc, 1) => a!(rpi_field_bytes_acc) * t + a!(rpi_field_bytes, 1) - ); - }); - // rpi_field_bytes_acc[0] = rpi_field_bytes[0] - ifx!(q!(q_field_start) => { - require!(a!(rpi_field_bytes_acc) => a!(rpi_field_bytes)); - }); - }); - cb.build_constraints(None) - }); - - meta.create_gate("keccak(rpi)", |meta| { - cb.restart(); - circuit!([meta, cb], { - ifx!(q!(q_keccak) => { - // - require!( - (1.expr(), a!(rpi_field_bytes_acc), RPI_BYTES_LEN.expr(), a!(rpi_rlc_acc)) - => @PiCellType::Lookup(Table::Keccak), (TO_FIX) - ); - }); - }); - cb.build_constraints(None) - }); - - meta.create_gate("in block table", |meta| { - circuit!([meta, cb], { - ifx!(q!(q_block_table) => { - // (BlockHashTag, block#, f0*c^0+f1*c^1+...), field rlc with challenge - require!( - ( - BlockContextFieldTag::BlockHash.expr(), - a!(block_index), - a!(rpi_field_bytes_acc) // 此时等于 block hash - ) => @PiCellType::Lookup(Table::Block), (TO_FIX) - ); - }); - }); - cb.build_constraints(None) - }); - - meta.create_gate("is_byte", |meta| { - circuit!([meta, cb], { - ifx!(or::expr([q!(q_field_start), q!(q_field_end)]) => { - require!( - (a!(rpi_field_bytes)) => @PiCellType::Lookup(Table::Bytecode), (TO_FIX) - ); - }); - }); - cb.build_constraints(None) - }); - - cb.build_lookups( - meta, - &[cb.cell_manager.clone().unwrap()], - &[ - (PiCellType::Lookup(Table::Keccak), PiCellType::Lookup(Table::Keccak)), - (PiCellType::Lookup(Table::Block),PiCellType::Lookup(Table::Block)), - (PiCellType::Lookup(Table::Bytecode), PiCellType::Lookup(Table::Bytecode)), - ], - None - ); - - meta.pinned().print_layout_states(); - meta.pinned().print_config_states(); - - Self { - rpi_field_bytes, - rpi_field_bytes_acc, - rpi_rlc_acc, - q_field_start, - q_field_step, - q_field_end, - - byte_table, - is_field_rlc, - - pi, // keccak_hi, keccak_lo - - q_keccak, - keccak_table, - - q_block_table, - block_index, - block_table, - - _marker: PhantomData, + cb.build_constraints(Some(meta.query_selector(q_enable))) + } + ); + cb.build_equalities(meta); + cb.build_lookups( + meta, + &[cm.clone()], + &[ + (PiCellType::Byte, PiCellType::Lookup(Bytecode)), + (PiCellType::Lookup(Table::Keccak), PiCellType::Lookup(Table::Keccak)), + (PiCellType::Lookup(Table::Block), PiCellType::Lookup(Table::Block)), + ], + Some(q_enable) + ); + // meta.pinned().print_config_states(); + // meta.pinned().print_layout_states(); + + let mut col_configs = cm.columns().to_vec(); + Self { + q_enable, + public_input, + field, + state, + block_acc, + block_number, + keccak_input, + keccak_output, + block_table, + keccak_table, + byte_table, + col_configs, + } } - } -} +} -// input -> kecceck, output -> evm impl TaikoPiCircuitConfig { - #[allow(clippy::too_many_arguments)] - fn assign_pi_field( + pub(crate) fn assign( &self, - region: &mut Region<'_, F>, - offset: &mut usize, - _annotation: &'static str, - field_bytes: &[u8], - rpi_rlc_acc: &mut Value, // 总的 + layouter: &mut impl Layouter, + public_data: &PublicData, challenges: &Challenges>, - keccak_hi_lo: bool, - block_number: Option, - ) -> Result>, Error> { - let len = field_bytes.len(); - let mut field_rlc_acc = Value::known(F::ZERO); // field 的 - let (use_rlc, t, t_) = if len * 8 > F::CAPACITY as usize { - // 正常字段 - (F::ONE, challenges.evm_word(), "evm_work") - } else { - println!("use_rlc {:?}, t={:?}", len, BYTE_POW_BASE); - // lo hi - (F::ZERO, Value::known(F::from(BYTE_POW_BASE)), "BYTE_POW_BASE") - }; - - let (randomness, r_) = if keccak_hi_lo { - // input - (challenges.evm_word(), "evm_word") - } else { - // keccak input - (challenges.keccak_input(), "keccak_input") - }; - println!(" t={:?}, r={:?}", t_, r_); - let mut cells = vec![None; field_bytes.len() + 2]; - for (i, byte) in field_bytes.iter().enumerate() { - let row_offset = *offset + i; - - region.assign_fixed( - || "is_field_rlc", - self.is_field_rlc, - row_offset, - || Value::known(use_rlc), - )?; - - // 直接写 Byte - let field_byte_cell = region.assign_advice( - || "field bytes", - self.rpi_field_bytes, - row_offset, - || Value::known(F::from(*byte as u64)), - )?; - - // 字段 - field_rlc_acc = field_rlc_acc * t + Value::known(F::from(*byte as u64)); - let rpi_cell = region.assign_advice( - || "field bytes acc", - self.rpi_field_bytes_acc, - row_offset, - || field_rlc_acc, - )?; - - // 总体 - // 到了 keccak hi & low 那俩行,用 evm_word - // 否则用 keccak_input - *rpi_rlc_acc = *rpi_rlc_acc * randomness + Value::known(F::from(*byte as u64)); - let rpi_rlc_acc_cell = region.assign_advice( - || "rpi_rlc_acc", - self.rpi_rlc_acc, - row_offset, - || *rpi_rlc_acc, - )?; - - - // setup selector - if i == 0 { - self.q_field_start.enable(region, row_offset)?; - } - // the last offset of field - if i == field_bytes.len() - 1 { - self.q_field_end.enable(region, row_offset)?; - cells[RPI_CELL_IDX] = Some(rpi_cell); - cells[RPI_RLC_ACC_CELL_IDX] = Some(rpi_rlc_acc_cell); - if let Some(block_number) = block_number { - println!("---self.q_block_table.enable--- {:?} {:?}", block_number, field_bytes); - self.q_block_table.enable(region, row_offset)?; - region.assign_advice( - || "block_index", - self.block_index, - row_offset, - || Value::known(F::from(block_number.as_u64())), - )?; + ) -> Result<(), Error> { + let pi_cells = layouter.assign_region( + || "Pi", + |mut region| { + + self.q_enable.enable(&mut region, 0)?; + let mut region = CachedRegion::new(&mut region); + region.annotate_columns(&self.col_configs); + + let mut block_acc = F::ZERO; + let mut keccak_r = F::ZERO; + challenges.keccak_input().map(|v| keccak_r = v); + let keccak_mult = (0..N).fold(1.scalar(), |acc: F, _| acc * keccak_r); + + let mut assignments = public_data.assignments().to_vec(); + assignments.append(&mut vec![ + ("keccak_output", None, public_data.get_pi().to_fixed_bytes().to_vec()) + ]); + let mut offset = 0; + let mut state = 0; + let mut pi_cells = Vec::new(); + for (annotation, block_number, bytes) in assignments { + self.state.assign(&mut region, offset, state)?; + if state != KECCAK_OUTPUT { + let next = block_acc * keccak_mult + + self.field.assign(&mut region, offset, &bytes, keccak_r)?; + assign!(region, self.block_acc, offset => next)?; + block_acc = next; + } + match state { + PARENT_HASH | BLOCK_HASH => { + let block_number = block_number.expect(&format!("block_number missing at {:?}th row", offset)); + assign!(region, self.block_number, offset => block_number.as_u64().scalar())?; + }, + FIELD10_IDX => { + assign!(region, self.keccak_input, offset => block_acc)?; + }, + KECCAK_OUTPUT => { + let (hi, low) = public_data.get_pi_hi_low::(); + pi_cells.push(assign!(region, self.keccak_output[0], offset => hi)?); + pi_cells.push(assign!(region, self.keccak_output[1], offset => low)?); + }, + _ => () + } + offset += N; + state += 1 } - } else { - self.q_field_step.enable(region, row_offset)?; + Ok(pi_cells) } - cells[2 + i] = Some(field_byte_cell); + )?; + for (i, cell) in pi_cells.iter().enumerate() { + layouter.constrain_instance(cell.cell(), self.public_input, i)?; } - *offset += field_bytes.len(); - Ok(cells.into_iter().map(|cell| cell.unwrap()).collect()) + Ok(()) + } +} + +#[derive(Clone, Debug)] +struct PiState { + pub(crate) state: Cell, + is_l1_signal: IsEqualGadget, + is_parent_hash: IsEqualGadget, + is_block_hash: IsEqualGadget, + is_field_10: IsEqualGadget, + is_keccak_output: IsEqualGadget, +} +impl PiState { + pub(crate) fn config(cb: &mut ConstraintBuilder) -> Self { + let state = cb.query_default(); + Self { + state: state.clone(), + is_l1_signal: IsEqualGadget::construct(cb, state.expr(), L1SIGNAL_IDX.expr()), + is_parent_hash: IsEqualGadget::construct(cb, state.expr(), PARENT_HASH.expr()), + is_block_hash: IsEqualGadget::construct(cb, state.expr(), BLOCK_HASH.expr()), + is_field_10: IsEqualGadget::construct(cb, state.expr(), FIELD10_IDX.expr()), + is_keccak_output: IsEqualGadget::construct(cb, state.expr(), KECCAK_OUTPUT.expr()), + } + } + + pub(crate) fn increment_step(&self) -> Expression { + not::expr(self.is_field_10.expr()) + not::expr(self.is_keccak_output.expr()) } - fn assign( + pub(crate) fn lookup_blockhash(&self) -> Expression { + self.is_block_hash.expr() + } + + pub(crate) fn assign( &self, - layouter: &mut impl Layouter, - public_data: &PublicData, - challenges: &Challenges>, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + state: usize ) -> Result<(), Error> { - let pi = layouter.assign_region( - || "region 0", - |ref mut region| { - let mut rpi_rlc_acc = Value::known(F::ZERO); - let mut offset = 0; - let mut rpi_rlc_acc_cell = None; - println!("\n==================="); - for (annotation, block_number, field_bytes) in public_data.assignments() { - println!("{:?}, len {:?}, offset {:?}", annotation, field_bytes.len(), offset); - let cells = self.assign_pi_field( - region, - &mut offset, - annotation, - &field_bytes, - &mut rpi_rlc_acc, - challenges, - false, - block_number, - )?; - rpi_rlc_acc_cell = Some(cells[RPI_RLC_ACC_CELL_IDX].clone()); - } + assign!(region, self.state, offset => state.scalar()); + self.is_l1_signal.assign(region, offset, state.scalar(), L1SIGNAL_IDX.scalar())?; + self.is_parent_hash.assign(region, offset, state.scalar(), PARENT_HASH.scalar())?; + self.is_block_hash.assign(region, offset, state.scalar(), BLOCK_HASH.scalar())?; + self.is_field_10.assign(region, offset, state.scalar(), FIELD10_IDX.scalar())?; + self.is_keccak_output.assign(region, offset, state.scalar(), KECCAK_OUTPUT.scalar())?; + Ok(()) + } - // input_rlc in self.rpi_field_bytes_acc - // input_len in self.rpi_len_acc - // output_rlc in self.rpi_rlc_acc - let keccak_row = offset; - let rpi_rlc_acc_cell = rpi_rlc_acc_cell.unwrap(); - rpi_rlc_acc_cell.copy_advice( - || "keccak(rpi)_input", - region, - self.rpi_field_bytes_acc, - keccak_row, - )?; - let keccak = public_data.get_pi(); - let mut keccak_output = keccak.to_fixed_bytes(); - keccak_output.reverse(); - let keccak_output_rlc = challenges - .evm_word() - .map(|randomness| rlc(keccak_output, randomness)); - let keccak_output_cell = region.assign_advice( - || "keccak(rpi)_output", - self.rpi_rlc_acc, - keccak_row, - || keccak_output_rlc, - )?; - println!("---self.q_keccak.enable---"); - self.q_keccak.enable(region, keccak_row)?; - - // 设成零 !!!!! - - rpi_rlc_acc = Value::known(F::ZERO); - offset += 1; - let mut pi = Vec::with_capacity(2); - - // kck_output = rlc( - // r = evm_word(), - // keccak(concat([l1_signal, l2_signal, metadata, ...])) - // ) - // kck_output -> keccak_output_cell - - for (idx, (annotation, field_bytes)) in [ - ( - "high_16_bytes_of_keccak_rpi", - &keccak.to_fixed_bytes()[..16], - ), - ("low_16_bytes_of_keccak_rpi", &keccak.to_fixed_bytes()[16..]), - ] - .into_iter() - .enumerate() - { - println!("high_16_bytes_of_keccak_rpi"); - let cells = self.assign_pi_field( - region, - &mut offset, - annotation, - field_bytes, - &mut rpi_rlc_acc, - challenges, - true, - None, - )?; - pi.push(cells[RPI_CELL_IDX].clone()); - if idx == 1 { - region.constrain_equal( - keccak_output_cell.cell(), - cells[RPI_RLC_ACC_CELL_IDX].cell(), - )?; - } - } +} - Ok(pi) - }, - )?; - for (idx, cell) in pi.into_iter().enumerate() { - layouter.constrain_instance(cell.cell(), self.pi, idx)?; + +#[derive(Clone, Debug)] +struct FieldBytesGadget { + bytes: [Cell; N], + word_r: Expression, + keccak_r: Expression, +} +impl FieldBytesGadget { + pub(crate) fn config( + cb: &mut ConstraintBuilder, + challenges: &Challenges> + ) -> Self { + Self { + bytes: cb.query_bytes(), + word_r: challenges.evm_word().expr(), + keccak_r: challenges.keccak_input().expr(), } - Ok(()) + } + + pub(crate) fn bytes_expr(&self) -> Vec> { + self.bytes.iter().map(|b| b.expr()).collect() + } + + /// RLC of bytes of a field with evm_word 1<<8 + pub(crate) fn hi_low_field(&self) -> (Expression, Expression) { + let hi = self.bytes_expr()[..16].to_vec(); + let low = self.bytes_expr()[16..].to_vec(); + (hi.rlc_rev(&BYTE_POW_BASE.expr()), low.rlc_rev(&BYTE_POW_BASE.expr())) + } + + /// RLC of bytes of a field with evm_word + pub(crate) fn evm_word_field(&self) -> Expression { + self.bytes_expr().rlc_rev(&self.word_r) + } + + /// RLC of bytes of a field with keccak_input + pub(crate) fn keccak_field(&self) -> Expression { + self.bytes_expr().rlc_rev(&self.keccak_r) // OK! + // = b0 * r^31 + ... + b31 * r^0 + } + + // ------------------ Assign ------------------ + + /// Returns the rlc of given bytes + pub(crate) fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + bytes: &[u8], + r: F, + ) -> Result { + // Assign the bytes + for (byte, cell) in bytes.iter().zip(self.bytes.iter()) { + assign!(region, cell, offset => byte.scalar())?; + } + Ok(bytes.rlc_rev_value(r)) } } + /// Public Inputs Circuit #[derive(Clone, Default, Debug)] pub struct TaikoPiCircuit { @@ -606,6 +555,7 @@ impl TaikoPiCircuit { } } + impl SubCircuit for TaikoPiCircuit { type Config = TaikoPiCircuitConfig; @@ -625,25 +575,8 @@ impl SubCircuit for TaikoPiCircuit { /// Compute the public inputs for this circuit. fn instance(&self) -> Vec> { - let keccak_rpi = self.public_data.get_pi(); - let keccak_hi = keccak_rpi - .to_fixed_bytes() - .iter() - .take(16) - .fold(F::ZERO, |acc, byte| { - acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) - }); - - let keccak_lo = keccak_rpi - .to_fixed_bytes() - .iter() - .skip(16) - .fold(F::ZERO, |acc, byte| { - acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) - }); - - let public_inputs = vec![keccak_hi, keccak_lo]; - vec![public_inputs] + let (hi, low) = self.public_data.get_pi_hi_low::(); + vec![vec![hi, low]] } /// Make the assignments to the PiCircuit @@ -658,6 +591,7 @@ impl SubCircuit for TaikoPiCircuit { } } + // We define the PiTestCircuit as a wrapper over PiCircuit extended to take the // generic const parameters MAX_TXS and MAX_CALLDATA. This is necessary because // the trait Circuit requires an implementation of `configure` that doesn't take @@ -711,6 +645,8 @@ impl Circuit for TaikoPiTestCircuit { config .block_table .load(&mut layouter, &public_data.block_context, randomness)?; + // [Tag, 0 (b0*r^31 + ... + b31*r^0)] + // assign keccak table config .keccak_table @@ -747,8 +683,8 @@ mod taiko_pi_circuit_test { pi: Option>>, ) -> Result<(), Vec> { let circuit = TaikoPiTestCircuit::(TaikoPiCircuit::new(public_data)); + let public_inputs = pi.unwrap_or_else(|| circuit.0.instance()); - let prover = match MockProver::run(k, &circuit, public_inputs) { Ok(prover) => prover, Err(e) => panic!("{:#?}", e), @@ -850,4 +786,4 @@ mod taiko_pi_circuit_test { assert_eq!(run::(k, public_data, None), Ok(())); } -} +} \ No newline at end of file diff --git a/zkevm-circuits/src/taiko_pi_circuit_.rs b/zkevm-circuits/src/taiko_pi_circuit_.rs deleted file mode 100644 index 355291bcfa..0000000000 --- a/zkevm-circuits/src/taiko_pi_circuit_.rs +++ /dev/null @@ -1,811 +0,0 @@ -//! Use the hash value as public input. - -use crate::{ - assign, - evm_circuit::table::Table::*, - evm_circuit::{util::{constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, rlc}, table::Table}, - table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}, - util::{Challenges, SubCircuitConfig, SubCircuit}, - circuit_tools::{ - constraint_builder::{ConstraintBuilder, RLCable, RLCChainable, TO_FIX, COMPRESS, REDUCE, RLCableValue}, - cell_manager::{CellManager, CellType, Cell, CellConfig, CellColumn}, gadgets::{IsEqualGadget, IsZeroGadget}, cached_region::{CachedRegion, self}, - }, - - witness::{self, BlockContext}, circuit, assignf, -}; -use bus_mapping::{evm, operation::Op}; -use gadgets::util::{Scalar, not}; -use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256}; -use ethers_core::utils::keccak256; -use gadgets::{util::{or, select, Expr, and}, impl_expr}; -use halo2_proofs::{ - circuit::{AssignedCell, Layouter, Region, SimpleFloorPlanner, Value}, - plonk::{ - Advice, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, Instance, SecondPhase, - Selector, VirtualCells, - }, - poly::{Rotation}, -}; -use rand_chacha::rand_core::block; -use std::{marker::PhantomData, ops::Range, usize, default, vec}; - -const BYTE_POW_BASE: u64 = 1 << 8; -const N: usize = 32; -const H: usize = 12; -const RPI_BYTES_LEN: usize = 32 * 10; -const USED_ROWS: usize = RPI_BYTES_LEN + 64; - -const L1SIGNAL_IDX: usize = 0; -const PARENT_HASH: usize = 4; -const BLOCK_HASH: usize = 5; -const FIELD9_IDX: usize = 8; -const FIELD10_IDX: usize = 9; -const KECCAK_OUTPUT: usize = 10; - - -/// PublicData contains all the values that the PiCircuit receives as input -#[derive(Debug, Clone, Default)] -pub struct PublicData { - /// l1 signal service address - pub l1_signal_service: Word, - /// l2 signal service address - pub l2_signal_service: Word, - /// l2 contract address - pub l2_contract: Word, - /// meta hash - pub meta_hash: Word, - /// block hash value - pub block_hash: Word, - /// the parent block hash - pub parent_hash: Word, - /// signal root - pub signal_root: Word, - /// extra message - pub graffiti: Word, - /// union field - pub field9: Word, // prover[96:256]+parentGasUsed[64:96]+gasUsed[32:64] - /// union field - pub field10: Word, /* blockMaxGasLimit[192:256]+maxTransactionsPerBlock[128: - * 192]+maxBytesPerTxList[64:128] */ - - // privates - // Prover address - prover: Address, - // parent block gas used - parent_gas_used: u32, - // block gas used - gas_used: u32, - // blockMaxGasLimit - block_max_gas_limit: u64, - // maxTransactionsPerBlock: u64, - max_transactions_per_block: u64, - // maxBytesPerTxList: u64, - max_bytes_per_tx_list: u64, - - block_context: BlockContext, - chain_id: Word, -} - -impl PublicData { - fn assignments(&self) -> [(&'static str, Option, Vec); 10] { - let res = [ - ( - "l1_signal_service", - None, - self.l1_signal_service.to_be_bytes().to_vec(), - ), - ( - "l2_signal_service", - None, - self.l2_signal_service.to_be_bytes().to_vec(), - ), - ("l2_contract", None, self.l2_contract.to_be_bytes().to_vec()), - ("meta_hash", None, self.meta_hash.to_be_bytes().to_vec()), - ( - "parent_hash", - Some(self.block_context.number - 1), - self.parent_hash.to_be_bytes().to_vec(), - ), - ( - "block_hash", - Some(self.block_context.number), - self.block_hash.to_be_bytes().to_vec(), - ), - ("signal_root", None, self.signal_root.to_be_bytes().to_vec()), - ("graffiti", None, self.graffiti.to_be_bytes().to_vec()), - ( - "prover+parentGasUsed+gasUsed", - None, - self.field9.to_be_bytes().to_vec(), - ), - ( - "blockMaxGasLimit+maxTransactionsPerBlock+maxBytesPerTxList", - None, - self.field10.to_be_bytes().to_vec(), - ), - ]; - res - } - - /// get rpi bytes - pub fn rpi_bytes(&self) -> Vec { - self.assignments().iter().flat_map(|v| v.2.clone()).collect() - } - - fn default() -> Self { - Self::new::(&witness::Block::default()) - } - - /// create PublicData from block and taiko - pub fn new(block: &witness::Block) -> Self { - use witness::left_shift; - let field9 = left_shift(block.protocol_instance.prover, 96) - + left_shift(block.protocol_instance.parent_gas_used as u64, 64) - + left_shift(block.protocol_instance.gas_used as u64, 32); - - let field10 = left_shift(block.protocol_instance.block_max_gas_limit, 192) - + left_shift(block.protocol_instance.max_transactions_per_block, 128) - + left_shift(block.protocol_instance.max_bytes_per_tx_list, 64); - PublicData { - l1_signal_service: block.protocol_instance.l1_signal_service.to_word(), - l2_signal_service: block.protocol_instance.l2_signal_service.to_word(), - l2_contract: block.protocol_instance.l2_contract.to_word(), - meta_hash: block.protocol_instance.meta_hash.hash().to_word(), - block_hash: block.protocol_instance.block_hash.to_word(), - parent_hash: block.protocol_instance.parent_hash.to_word(), - signal_root: block.protocol_instance.signal_root.to_word(), - graffiti: block.protocol_instance.graffiti.to_word(), - prover: block.protocol_instance.prover, - parent_gas_used: block.protocol_instance.parent_gas_used, - gas_used: block.protocol_instance.gas_used, - block_max_gas_limit: block.protocol_instance.block_max_gas_limit, - max_transactions_per_block: block.protocol_instance.max_transactions_per_block, - max_bytes_per_tx_list: block.protocol_instance.max_bytes_per_tx_list, - field9, - field10, - block_context: block.context.clone(), - chain_id: block.context.chain_id, - } - } - - fn get_pi(&self) -> H256 { - let rpi_bytes = self.rpi_bytes(); - let rpi_keccak = keccak256(rpi_bytes); - println!("get_pi {:?}", rpi_keccak); - H256(rpi_keccak) - } - - fn get_pi_hi_low(&self) -> (F, F) { - let keccak_rpi = self.get_pi().to_fixed_bytes(); - ( - keccak_rpi - .iter() - .take(16) - .fold(F::ZERO, |acc, byte| { - acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) - }), - keccak_rpi - .iter() - .skip(16) - .fold(F::ZERO, |acc, byte| { - acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) - }) - ) - } -} -/// Config for PiCircuit -#[derive(Clone, Debug)] -pub struct TaikoPiCircuitConfig { - q_enable: Selector, - public_input: Column, // equality - field: FieldBytesGadget, - state: PiState, - block_acc: Cell, // Phase2 - block_number: Cell, // Phase1 - keccak_input: Cell, // Phase2 - keccak_output:[Cell;2], // Phase2 - - block_table: BlockTable, - keccak_table: KeccakTable, - byte_table: ByteTable, - - // To annotate columns at assignment for debug purpose - col_configs: Vec>, -} - -/// Circuit configuration arguments -pub struct TaikoPiCircuitConfigArgs { - /// BlockTable - pub block_table: BlockTable, - /// KeccakTable - pub keccak_table: KeccakTable, - /// ByteTable - pub byte_table: ByteTable, - /// Challenges - pub challenges: Challenges>, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -enum PiCellType { - Storage1, - Storage2, - Byte, - Lookup(Table) -} -impl CellType for PiCellType { - fn byte_type() -> Option { - Some(Self::Byte) - } - fn storage_for_phase(phase: u8) -> Self { - match phase { - 1 => PiCellType::Storage1, - 2 => PiCellType::Storage2, - _ => unimplemented!() - } - } -} -impl Default for PiCellType { - fn default() -> Self { - Self::Storage1 - } -} - - -impl SubCircuitConfig for TaikoPiCircuitConfig { - type ConfigArgs = TaikoPiCircuitConfigArgs; - - /// Return a new TaikoPiCircuitConfig - fn new( - meta: &mut ConstraintSystem, - Self::ConfigArgs { - block_table, - keccak_table, - byte_table, - challenges, - }: Self::ConfigArgs, - ) -> Self { - let cm = CellManager::new( - meta, - vec![ - (PiCellType::Byte, 1, 1, false), - (PiCellType::Storage1, 1, 1, false), - (PiCellType::Storage2, 1, 2, true), - ], - 0, - 32, - ); - let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(cm.clone()), Some(challenges.evm_word())); - cb.preload_tables(meta, - &[ - (PiCellType::Lookup(Keccak), &keccak_table), - (PiCellType::Lookup(Bytecode), &byte_table), - (PiCellType::Lookup(Block), &block_table) - ] - ); - let q_enable = meta.complex_selector(); - let public_input = meta.instance_column(); - let field = FieldBytesGadget::config(&mut cb, &challenges); - let state = PiState::config(&mut cb); - let block_acc = cb.query_one(PiCellType::Storage2); - let block_number = cb.query_one(PiCellType::Storage1); - let keccak_input = cb.query_one(PiCellType::Storage2); - let keccak_output = [();2].map(|_| cb.query_one(PiCellType::Storage2)); - meta.enable_equality(public_input); - meta.create_gate( - "PI acc constraints", - |meta| { - let keccak_mult = (0..N).fold(1.expr(), |acc, _| acc * challenges.keccak_input()); - // let evm_mult = (0..N).fold(1.expr(), |acc, _| acc * challenges.evm_word()); - circuit!([meta, cb], { - ifx!(state.increment_step() => { - require!(block_acc.rot(meta, 32) => block_acc.expr() * keccak_mult + field.keccak_field()); - }); - matchx!(( - state.is_l1_signal.expr() => { - require!(block_acc.expr() => field.keccak_field()); - }, - state.lookup_blockhash() => { - require!( - ( - BlockContextFieldTag::BlockHash.expr(), - block_number.expr(), - field.evm_word_field() - ) => @PiCellType::Lookup(Table::Block), (TO_FIX) - ); - }, - state.is_field_10.expr() => { - require!(keccak_input.expr() => block_acc.expr()); - }, - state.is_keccak_output.expr() => { - require!( - ( - 1.expr(), - keccak_input.expr(), - RPI_BYTES_LEN.expr(), - field.evm_word_field() - ) - => @PiCellType::Lookup(Table::Keccak), (TO_FIX) - ); - let (hi_expr, low_expr) = field.hi_low_field(); - require!(keccak_output[0].expr() => hi_expr); - require!(keccak_output[1].expr() => low_expr); - keccak_output.iter().for_each(|c| cb.enable_equality(c.column())); - } - )); - }); - cb.build_constraints(Some(meta.query_selector(q_enable))) - } - ); - cb.build_equalities(meta); - cb.build_lookups( - meta, - &[cm.clone()], - &[ - (PiCellType::Byte, PiCellType::Lookup(Bytecode)), - (PiCellType::Lookup(Table::Keccak), PiCellType::Lookup(Table::Keccak)), - (PiCellType::Lookup(Table::Block), PiCellType::Lookup(Table::Block)), - ], - Some(q_enable) - ); - // meta.pinned().print_config_states(); - // meta.pinned().print_layout_states(); - - let mut col_configs = cm.columns().to_vec(); - Self { - q_enable, - public_input, - field, - state, - block_acc, - block_number, - keccak_input, - keccak_output, - block_table, - keccak_table, - byte_table, - col_configs, - } - } - -} - -impl TaikoPiCircuitConfig { - pub(crate) fn assign( - &self, - layouter: &mut impl Layouter, - public_data: &PublicData, - challenges: &Challenges>, - ) -> Result<(), Error> { - let pi_cells = layouter.assign_region( - || "Pi", - |mut region| { - - self.q_enable.enable(&mut region, 0)?; - let mut region = CachedRegion::new(&mut region); - region.annotate_columns(&self.col_configs); - - let mut block_acc = F::ZERO; - let (keccak_r, keccak_mult, evm_word_r, evm_word_mult) = self.randomness(challenges); - - let mut assignments = public_data.assignments().to_vec(); - assignments.append(&mut vec![ - ("keccak_output", None, public_data.get_pi().to_fixed_bytes().to_vec()) - ]); - let mut offset = 0; - let mut state = 0; - let mut pi_cells = Vec::new(); - for (annotation, block_number, bytes) in assignments { - - println!("{:?} {:?}, len {:?} \n {:?}", offset, annotation, bytes.len(), bytes); - - self.state.assign(&mut region, offset, state)?; - if state != KECCAK_OUTPUT { - let next = block_acc * keccak_mult - + self.field.assign(&mut region, offset, &bytes, keccak_r)?; - assign!(region, self.block_acc, offset => next)?; - block_acc = next; - } - match state { - PARENT_HASH | BLOCK_HASH => { - let block_number = block_number.expect(&format!("block_number missing at {:?}th row", offset)); - assign!(region, self.block_number, offset => block_number.as_u64().scalar())?; - }, - FIELD10_IDX => { - assign!(region, self.keccak_input, offset => block_acc)?; - }, - KECCAK_OUTPUT => { - let (hi, low) = public_data.get_pi_hi_low::(); - pi_cells.push(assign!(region, self.keccak_output[0], offset => hi)?); - pi_cells.push(assign!(region, self.keccak_output[1], offset => low)?); - }, - _ => () - } - println!(" block_acc {:?}", block_acc); - - offset += N; - state += 1 - } - Ok(pi_cells) - } - )?; - for (i, cell) in pi_cells.iter().enumerate() { - layouter.constrain_instance(cell.cell(), self.public_input, i)?; - println!("pi_cell {:?}", cell); - } - Ok(()) - } - - fn randomness(&self, challenges: &Challenges>) -> (F, F, F, F) { - let mut evm_word_r = F::ZERO; - let mut keccak_r = F::ZERO; - let hi_low_r = F::from(BYTE_POW_BASE); - challenges.evm_word().map(|v| evm_word_r = v); - challenges.keccak_input().map(|v| keccak_r = v); - ( - keccak_r, - (0..N).fold(1.scalar(), |acc: F, _| acc * keccak_r), - evm_word_r, - (0..N).fold(1.scalar(), |acc: F, _| acc * evm_word_r), - ) - } -} - -#[derive(Clone, Debug)] -struct PiState { - pub(crate) state: Cell, - is_l1_signal: IsEqualGadget, - is_parent_hash: IsEqualGadget, - is_block_hash: IsEqualGadget, - is_field_10: IsEqualGadget, - is_keccak_output: IsEqualGadget, -} -impl PiState { - pub(crate) fn config(cb: &mut ConstraintBuilder) -> Self { - let state = cb.query_default(); - Self { - state: state.clone(), - is_l1_signal: IsEqualGadget::construct(cb, state.expr(), L1SIGNAL_IDX.expr()), - is_parent_hash: IsEqualGadget::construct(cb, state.expr(), PARENT_HASH.expr()), - is_block_hash: IsEqualGadget::construct(cb, state.expr(), BLOCK_HASH.expr()), - is_field_10: IsEqualGadget::construct(cb, state.expr(), FIELD10_IDX.expr()), - is_keccak_output: IsEqualGadget::construct(cb, state.expr(), KECCAK_OUTPUT.expr()), - } - } - - pub(crate) fn increment_step(&self) -> Expression { - not::expr(self.is_field_10.expr()) + not::expr(self.is_keccak_output.expr()) - } - - pub(crate) fn lookup_blockhash(&self) -> Expression { - self.is_block_hash.expr() - } - - pub(crate) fn assign( - &self, - region: &mut CachedRegion<'_, '_, F>, - offset: usize, - state: usize - ) -> Result<(), Error> { - assign!(region, self.state, offset => state.scalar()); - self.is_l1_signal.assign(region, offset, state.scalar(), L1SIGNAL_IDX.scalar())?; - self.is_parent_hash.assign(region, offset, state.scalar(), PARENT_HASH.scalar())?; - self.is_block_hash.assign(region, offset, state.scalar(), BLOCK_HASH.scalar())?; - self.is_field_10.assign(region, offset, state.scalar(), FIELD10_IDX.scalar())?; - self.is_keccak_output.assign(region, offset, state.scalar(), KECCAK_OUTPUT.scalar())?; - Ok(()) - } - -} - - -#[derive(Clone, Debug)] -struct FieldBytesGadget { - bytes: [Cell; N], - word_r: Expression, - keccak_r: Expression, -} -impl FieldBytesGadget { - pub(crate) fn config( - cb: &mut ConstraintBuilder, - challenges: &Challenges> - ) -> Self { - Self { - bytes: cb.query_bytes(), - word_r: challenges.evm_word().expr(), - keccak_r: challenges.keccak_input().expr(), - } - } - - pub(crate) fn bytes_expr(&self) -> Vec> { - self.bytes.iter().map(|b| b.expr()).collect() - } - - /// RLC of bytes of a field with evm_word 1<<8 - pub(crate) fn hi_low_field(&self) -> (Expression, Expression) { - let hi = self.bytes_expr()[..16].to_vec(); - let low = self.bytes_expr()[16..].to_vec(); - (hi.rlc_rev(&BYTE_POW_BASE.expr()), low.rlc_rev(&BYTE_POW_BASE.expr())) - } - - /// RLC of bytes of a field with evm_word - pub(crate) fn evm_word_field(&self) -> Expression { - self.bytes_expr().rlc_rev(&self.word_r) - } - - /// RLC of bytes of a field with keccak_input - pub(crate) fn keccak_field(&self) -> Expression { - self.bytes_expr().rlc_rev(&self.keccak_r) // OK! - // = b0 * r^31 + ... + b31 * r^0 - } - - // ------------------ Assign ------------------ - - /// Returns the rlc of given bytes - pub(crate) fn assign( - &self, - region: &mut CachedRegion<'_, '_, F>, - offset: usize, - bytes: &[u8], - r: F, - ) -> Result { - // Assign the bytes - for (byte, cell) in bytes.iter().zip(self.bytes.iter()) { - assign!(region, cell, offset => byte.scalar())?; - } - Ok(bytes.rlc_rev_value(r)) - } -} - - -/// Public Inputs Circuit -#[derive(Clone, Default, Debug)] -pub struct TaikoPiCircuit { - /// PublicInputs data known by the verifier - pub public_data: PublicData, - _marker: PhantomData, -} - -impl TaikoPiCircuit { - /// Creates a new TaikoPiCircuit - pub fn new(public_data: PublicData) -> Self { - Self { - public_data, - _marker: PhantomData, - } - } -} - - -impl SubCircuit for TaikoPiCircuit { - type Config = TaikoPiCircuitConfig; - - fn unusable_rows() -> usize { - // No column queried at more than 3 distinct rotations, so returns 6 as - // minimum unusable rows. - 6 - } - - fn min_num_rows_block(_block: &witness::Block) -> (usize, usize) { - (USED_ROWS, USED_ROWS) - } - - fn new_from_block(block: &witness::Block) -> Self { - TaikoPiCircuit::new(PublicData::new(block)) - } - - /// Compute the public inputs for this circuit. - fn instance(&self) -> Vec> { - let (hi, low) = self.public_data.get_pi_hi_low::(); - vec![vec![hi, low]] - } - - /// Make the assignments to the PiCircuit - fn synthesize_sub( - &self, - config: &Self::Config, - challenges: &Challenges>, - layouter: &mut impl Layouter, - ) -> Result<(), Error> { - config.byte_table.load(layouter)?; - config.assign(layouter, &self.public_data, challenges) - } -} - - -// We define the PiTestCircuit as a wrapper over PiCircuit extended to take the -// generic const parameters MAX_TXS and MAX_CALLDATA. This is necessary because -// the trait Circuit requires an implementation of `configure` that doesn't take -// any circuit parameters, and the PiCircuit defines gates that use rotations -// that depend on MAX_TXS and MAX_CALLDATA, so these two values are required -// during the configuration. -/// Test Circuit for PiCircuit -#[cfg(any(feature = "test", test))] -#[derive(Default, Clone)] -pub struct TaikoPiTestCircuit(pub TaikoPiCircuit); - -#[cfg(any(feature = "test", test))] -impl Circuit for TaikoPiTestCircuit { - type Config = (TaikoPiCircuitConfig, Challenges); - type FloorPlanner = SimpleFloorPlanner; - type Params = (); - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let block_table = BlockTable::construct(meta); - let keccak_table = KeccakTable::construct(meta); - let byte_table = ByteTable::construct(meta); - let challenges = Challenges::construct(meta); - let challenge_exprs = challenges.exprs(meta); - ( - TaikoPiCircuitConfig::new( - meta, - TaikoPiCircuitConfigArgs { - block_table, - keccak_table, - byte_table, - challenges: challenge_exprs, - }, - ), - challenges, - ) - } - - fn synthesize( - &self, - (config, challenges): Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - let challenges = challenges.values(&mut layouter); - let public_data = &self.0.public_data; - // assign block table - let randomness = challenges.evm_word(); - config - .block_table - .load(&mut layouter, &public_data.block_context, randomness)?; - // [Tag, 0 (b0*r^31 + ... + b31*r^0)] - - // assign keccak table - config - .keccak_table - .dev_load(&mut layouter, vec![&public_data.rpi_bytes()], &challenges)?; - config.byte_table.load(&mut layouter)?; - - self.0.synthesize_sub(&config, &challenges, &mut layouter) - } -} - -#[cfg(test)] -mod taiko_pi_circuit_test { - - use super::*; - - use eth_types::ToScalar; - use halo2_proofs::{ - dev::{MockProver, VerifyFailure}, - halo2curves::bn256::Fr, - }; - use lazy_static::lazy_static; - use pretty_assertions::assert_eq; - - lazy_static! { - static ref OMMERS_HASH: H256 = H256::from_slice( - &hex::decode("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347") - .unwrap(), - ); - } - - fn run( - k: u32, - public_data: PublicData, - pi: Option>>, - ) -> Result<(), Vec> { - let circuit = TaikoPiTestCircuit::(TaikoPiCircuit::new(public_data)); - - let public_inputs = pi.unwrap_or_else(|| circuit.0.instance()); - println!("{:?}", public_inputs); - let prover = match MockProver::run(k, &circuit, public_inputs) { - Ok(prover) => prover, - Err(e) => panic!("{:#?}", e), - }; - prover.verify() - } - - fn mock_public_data() -> PublicData { - let mut public_data = PublicData::default::(); - public_data.meta_hash = OMMERS_HASH.to_word(); - public_data.block_hash = OMMERS_HASH.to_word(); - public_data.block_context.block_hash = OMMERS_HASH.to_word(); - public_data.block_context.history_hashes = vec![Default::default(); 256]; - public_data.block_context.number = 300.into(); - public_data - } - - #[test] - fn test_default_pi() { - let public_data = mock_public_data(); - - let k = 17; - assert_eq!(run::(k, public_data, None), Ok(())); - // run::(k, public_data, None); - } - - #[test] - fn test_fail_pi_hash() { - let public_data = mock_public_data(); - - let k = 17; - match run::(k, public_data, Some(vec![vec![Fr::zero(), Fr::one()]])) { - Ok(_) => unreachable!("this case must fail"), - Err(errs) => { - assert_eq!(errs.len(), 4); - for err in errs { - match err { - VerifyFailure::Permutation { .. } => return, - _ => unreachable!("unexpected error"), - } - } - } - } - } - - #[test] - fn test_fail_pi_prover() { - let mut public_data = mock_public_data(); - let address_bytes = [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - ]; - - public_data.prover = Address::from_slice(&address_bytes); - - let prover: Fr = public_data.prover.to_scalar().unwrap(); - let k = 17; - match run::( - k, - public_data, - Some(vec![vec![prover, Fr::zero(), Fr::one()]]), - ) { - Ok(_) => unreachable!("this case must fail"), - Err(errs) => { - assert_eq!(errs.len(), 4); - for err in errs { - match err { - VerifyFailure::Permutation { .. } => return, - _ => unreachable!("unexpected error"), - } - } - } - } - } - - #[test] - fn test_simple_pi() { - let mut public_data = mock_public_data(); - let chain_id = 1337u64; - public_data.chain_id = Word::from(chain_id); - - let k = 17; - assert_eq!(run::(k, public_data, None), Ok(())); - } - - #[test] - fn test_verify() { - let mut block = witness::Block::::default(); - - block.eth_block.parent_hash = *OMMERS_HASH; - block.eth_block.hash = Some(*OMMERS_HASH); - block.protocol_instance.block_hash = *OMMERS_HASH; - block.protocol_instance.parent_hash = *OMMERS_HASH; - block.context.history_hashes = vec![OMMERS_HASH.to_word()]; - block.context.block_hash = OMMERS_HASH.to_word(); - block.context.number = 300.into(); - - let public_data = PublicData::new(&block); - - let k = 17; - - assert_eq!(run::(k, public_data, None), Ok(())); - } -} \ No newline at end of file From e723a841f28b9e4bda36347aca73217f4ceca0e0 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Wed, 23 Aug 2023 21:41:56 +0800 Subject: [PATCH 20/43] cargo fix --- .../src/circuit_tools/cell_manager.rs | 2 +- zkevm-circuits/src/table/keccak_table.rs | 2 +- zkevm-circuits/src/taiko_pi_circuit.rs | 27 +++++++++---------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/zkevm-circuits/src/circuit_tools/cell_manager.rs b/zkevm-circuits/src/circuit_tools/cell_manager.rs index 395964c93c..7408577a69 100644 --- a/zkevm-circuits/src/circuit_tools/cell_manager.rs +++ b/zkevm-circuits/src/circuit_tools/cell_manager.rs @@ -1,7 +1,7 @@ //! Cell manager use crate::{ circuit_tools::cached_region::CachedRegion, - util::{query_expression, Expr}, evm_circuit::util::rlc, + util::{query_expression, Expr}, }; use crate::table::LookupTable; diff --git a/zkevm-circuits/src/table/keccak_table.rs b/zkevm-circuits/src/table/keccak_table.rs index aa2cb94d47..14b33d6ecf 100644 --- a/zkevm-circuits/src/table/keccak_table.rs +++ b/zkevm-circuits/src/table/keccak_table.rs @@ -1,4 +1,4 @@ -use eth_types::ToBigEndian; + use super::*; diff --git a/zkevm-circuits/src/taiko_pi_circuit.rs b/zkevm-circuits/src/taiko_pi_circuit.rs index f8f688dead..930c349030 100644 --- a/zkevm-circuits/src/taiko_pi_circuit.rs +++ b/zkevm-circuits/src/taiko_pi_circuit.rs @@ -3,31 +3,30 @@ use crate::{ assign, evm_circuit::table::Table::*, - evm_circuit::{util::{constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, rlc}, table::Table}, + evm_circuit::{util::{constraint_builder::{ConstrainBuilderCommon}}, table::Table}, table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}, util::{Challenges, SubCircuitConfig, SubCircuit}, circuit_tools::{ - constraint_builder::{ConstraintBuilder, RLCable, RLCChainable, TO_FIX, COMPRESS, REDUCE, RLCableValue}, - cell_manager::{CellManager, CellType, Cell, CellConfig, CellColumn}, gadgets::{IsEqualGadget, IsZeroGadget}, cached_region::{CachedRegion, self}, + constraint_builder::{ConstraintBuilder, RLCable, TO_FIX, RLCableValue}, + cell_manager::{CellManager, CellType, Cell, CellColumn}, gadgets::{IsEqualGadget}, cached_region::{CachedRegion}, }, - witness::{self, BlockContext}, circuit, assignf, + witness::{self, BlockContext}, circuit, }; -use bus_mapping::{evm, operation::Op}; + use gadgets::util::{Scalar, not}; use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256}; use ethers_core::utils::keccak256; -use gadgets::{util::{or, select, Expr, and}, impl_expr}; +use gadgets::{util::{Expr}}; use halo2_proofs::{ - circuit::{AssignedCell, Layouter, Region, SimpleFloorPlanner, Value}, + circuit::{Layouter, SimpleFloorPlanner, Value}, plonk::{ - Advice, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, Instance, SecondPhase, - Selector, VirtualCells, + Circuit, Column, ConstraintSystem, Error, Expression, Instance, + Selector, }, - poly::{Rotation}, }; -use rand_chacha::rand_core::block; -use std::{marker::PhantomData, ops::Range, usize, default, vec}; + +use std::{marker::PhantomData, usize, vec}; const BYTE_POW_BASE: u64 = 1 << 8; const N: usize = 32; @@ -348,7 +347,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { // meta.pinned().print_config_states(); // meta.pinned().print_layout_states(); - let mut col_configs = cm.columns().to_vec(); + let col_configs = cm.columns().to_vec(); Self { q_enable, public_input, @@ -394,7 +393,7 @@ impl TaikoPiCircuitConfig { let mut offset = 0; let mut state = 0; let mut pi_cells = Vec::new(); - for (annotation, block_number, bytes) in assignments { + for (_annotation, block_number, bytes) in assignments { self.state.assign(&mut region, offset, state)?; if state != KECCAK_OUTPUT { let next = block_acc * keccak_mult From 4f8d2c05822e1a2fdf0d3101c961ec130ee8f5d0 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Fri, 1 Sep 2023 00:26:32 +0800 Subject: [PATCH 21/43] rewrite for new A5 testnest --- zkevm-circuits/src/lib.rs | 2 + zkevm-circuits/src/taiko_pi_circuit.rs | 14 +- zkevm-circuits/src/taiko_pi_circuit_.rs | 393 ++++++++++++++++++++++++ 3 files changed, 408 insertions(+), 1 deletion(-) create mode 100644 zkevm-circuits/src/taiko_pi_circuit_.rs diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index cf92f3b944..1f76bf8f33 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -29,6 +29,8 @@ pub mod super_circuit; pub mod table; #[macro_use] pub mod taiko_pi_circuit; +#[macro_use] +pub mod taiko_pi_circuit_; pub mod taiko_super_circuit; #[cfg(any(feature = "test", test))] diff --git a/zkevm-circuits/src/taiko_pi_circuit.rs b/zkevm-circuits/src/taiko_pi_circuit.rs index 930c349030..c1bf652092 100644 --- a/zkevm-circuits/src/taiko_pi_circuit.rs +++ b/zkevm-circuits/src/taiko_pi_circuit.rs @@ -51,6 +51,8 @@ pub struct PublicData { pub l2_signal_service: Word, /// l2 contract address pub l2_contract: Word, + + /// meta hash pub meta_hash: Word, /// block hash value @@ -99,6 +101,7 @@ impl PublicData { self.l2_signal_service.to_be_bytes().to_vec(), ), ("l2_contract", None, self.l2_contract.to_be_bytes().to_vec()), + ("meta_hash", None, self.meta_hash.to_be_bytes().to_vec()), ( "parent_hash", @@ -125,6 +128,10 @@ impl PublicData { ] } + // pub fn abi_encode(&self) -> Vec { + + // } + /// get rpi bytes pub fn rpi_bytes(&self) -> Vec { self.assignments().iter().flat_map(|v| v.2.clone()).collect() @@ -778,11 +785,16 @@ mod taiko_pi_circuit_test { block.context.history_hashes = vec![OMMERS_HASH.to_word()]; block.context.block_hash = OMMERS_HASH.to_word(); block.context.number = 300.into(); + + println!("{:?}\n{:?}", + block.protocol_instance.meta_hash.hash(), + block.protocol_instance.meta_hash.hash().to_word()); let public_data = PublicData::new(&block); + println!("public_data: {:?}\n{:?}", public_data.meta_hash, public_data.meta_hash.to_be_bytes()); let k = 17; - assert_eq!(run::(k, public_data, None), Ok(())); + // assert_eq!(run::(k, public_data, None), Ok(())); } } \ No newline at end of file diff --git a/zkevm-circuits/src/taiko_pi_circuit_.rs b/zkevm-circuits/src/taiko_pi_circuit_.rs new file mode 100644 index 0000000000..88093d6d9c --- /dev/null +++ b/zkevm-circuits/src/taiko_pi_circuit_.rs @@ -0,0 +1,393 @@ +// function getInstance(TaikoData.BlockEvidence memory evidence) + // internal + // pure + // returns (bytes32 instance) +// { + // uint256[6] memory inputs; + // inputs[0] = uint256(evidence.metaHash); + // inputs[1] = uint256(evidence.parentHash); + // inputs[2] = uint256(evidence.blockHash); + // inputs[3] = uint256(evidence.signalRoot); + // inputs[4] = uint256(evidence.graffiti); + // inputs[5] = (uint256(uint160(evidence.prover)) << 96) + // | (uint256(evidence.parentGasUsed) << 64) + // | (uint256(evidence.gasUsed) << 32); + + + + // @dev Struct representing block evidence. + // struct BlockEvidence { + // bytes32 metaHash; + // bytes32 parentHash; + // bytes32 blockHash; + // bytes32 signalRoot; + // bytes32 graffiti; + // address prover; + // bytes proofs; + // } + + // if (evidence.prover != address(1)) return 0; + // else return keccak256(abi.encode(evidence)); + + // evidence.proofs = bytes.concat( + // bytes2(verifierId), + // bytes16(0), + // bytes16(instance), + // bytes16(0), + // bytes16(uint128(uint256(instance))), + // new bytes(100) + // ); +// } + + +use eth_types::{ToWord, ToBigEndian, Field}; +use ethers_core::abi::*; +use ethers_core::abi::FixedBytes; +use std::convert::TryInto; + +const PADDING_LEN: usize = 32; +const PROOF_LEN: usize = 102; +const BYTE_POW_BASE: u64 = 1 << 8; + +#[derive(Debug, Clone)] +pub struct BlockEvidence { + meta_hash: Token, + parent_hash: Token, + block_hash: Token, + signal_root: Token, + graffiti: Token, + prover: Token, + proofs: Token, +} + +impl BlockEvidence { + fn new(block: &witness::Block) -> Self { + let meta_hash = Token::FixedBytes(block.protocol_instance.meta_hash.hash().to_word().to_be_bytes().to_vec()); + let parent_hash = Token::FixedBytes(block.protocol_instance.parent_hash.to_word().to_be_bytes().to_vec()); + let block_hash = Token::FixedBytes(block.protocol_instance.block_hash.to_word().to_be_bytes().to_vec()); + let signal_root = Token::FixedBytes(block.protocol_instance.signal_root.to_word().to_be_bytes().to_vec()); + let graffiti = Token::FixedBytes(block.protocol_instance.graffiti.to_word().to_be_bytes().to_vec()); + let prover = Token::Address(block.protocol_instance.prover); + let proofs = Token::Bytes(vec![0;PROOF_LEN]); + + Self { + meta_hash, + parent_hash, + block_hash, + signal_root, + graffiti, + prover, + proofs, + } + } + + fn default() -> Self { + Self::new::(&witness::Block::default()) + } + + fn encode_raw(&self) -> Vec { + // Initial padding: 20 means there's one dyn data, 40 means two, ... + // 0000000000000000000000000000000000000000000000000000000000000020 + // Fixed bytes are directly concat + // 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef + // 0000000000000000000000000000000000000000000000000000000000000000 + // 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef + // 0000000000000000000000000000000000000000000000000000000000000000 + // 0000000000000000000000000000000000000000000000000000000000000000 + // 0000000000000000000000002222222222222222222222222222222222222222 + // Second padding happens right before dyn bytes + // 00000000000000000000000000000000000000000000000000000000000000e0 + // Proofs: new bytes(102), 0x66 = 102 + // 0000000000000000000000000000000000000000000000000000000000000066 + // 0000000000000000000000000000000000000000000000000000000000000000 + // 0000000000000000000000000000000000000000000000000000000000000000 + // 0000000000000000000000000000000000000000000000000000000000000000 + // 0000000000000000000000000000000000000000000000000000000000000000 + encode(&[ + Token::FixedArray(vec![ + self.meta_hash.clone(), + self.parent_hash.clone(), + self.block_hash.clone(), + self.signal_root.clone(), + self.graffiti.clone(), + self.prover.clone(), + self.proofs.clone(), + ]) + ]) + } + + fn max_height(&self) -> usize { + self.encode_raw().len() + } + +} + +trait AbiLength { + fn len(&self) -> usize; +} +impl AbiLength for Token { + fn len(&self) -> usize { + // TODO(Cecilia): handle padding for dyn data + match self { + // Solidity fixed type: bytes8, bytes32, bytes64, bytes1024,... + Token::FixedBytes(bytes) => ((bytes.len() + 31) / 32) * 32, + // Solidity dyn type: bytes, encoded with one more word representing the length + Token::Bytes(bytes) => (((bytes.len() + 31) / 32) + 1) * 32, + Token::Int(_) | Token::Uint(_) | Token::Bool(_) | Token::Address(_) => 32, + _ => unimplemented!() + } + } +} + +#[derive(Clone, Debug)] +pub struct TaikoPiCircuitConfig { + q_enable: Selector, + public_input: Column, // equality + keccak_output: Vec>, // hi, lo + block_numbers: Vec>, + public_input_bytes: FieldGadget, + + padding1: FieldGadget, + meta_hash: FieldGadget, + parent_hash: FieldGadget, + block_hash: FieldGadget, + signal_root: FieldGadget, + graffiti: FieldGadget, + prover: FieldGadget, + padding2: FieldGadget, + proofs: FieldGadget, + + annotation_configs: Vec>, +} + +pub struct TaikoPiCircuitConfigArgs { + /// + pub evidence: BlockEvidence, + /// BlockTable + pub block_table: BlockTable, + /// KeccakTable + pub keccak_table: KeccakTable, + /// ByteTable + pub byte_table: ByteTable, + /// Challenges + pub challenges: Challenges>, +} + + +impl SubCircuitConfig for TaikoPiCircuitConfig { + type ConfigArgs = TaikoPiCircuitConfigArgs; + /// Return a new TaikoPiCircuitConfig + fn new( + meta: &mut ConstraintSystem, + Self::ConfigArgs { + evidence, + block_table, + keccak_table, + byte_table, + challenges, + }: Self::ConfigArgs, + ) -> Self { + let keccak_r = challenges.keccak_input(); + let evm_word = challenges.evm_word(); + let cm = CellManager::new( + meta, + vec![ + (PiCellType::Byte, 1, 1, false), + (PiCellType::Storage2, 1, 2, true), + ], + 0, + 32, + ); + let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(cm.clone()), Some(evm_word.expr())); + cb.preload_tables(meta, + &[ + (PiCellType::Lookup(Table::Keccak), &keccak_table), + (PiCellType::Lookup(Table::Bytecode), &byte_table), + (PiCellType::Lookup(Table::Block), &block_table) + ] + ); + let q_enable = meta.complex_selector(); + let public_input = meta.instance_column(); + + let keccak_output = [(); 2].iter().map(|_| cb.query_one(PiCellType::Storage2)).collect::>(); + let block_numbers = [(); 2].iter().map(|_| cb.query_one(PiCellType::Storage1)).collect::>(); + let public_input_bytes = FieldGadget::config(&mut cb, PADDING_LEN); + + let padding1 = FieldGadget::config(&mut cb, PADDING_LEN); + let meta_hash = FieldGadget::config(&mut cb, evidence.meta_hash.len()); + let parent_hash = FieldGadget::config(&mut cb, evidence.parent_hash.len()); + let block_hash = FieldGadget::config(&mut cb, evidence.block_hash.len()); + let signal_root = FieldGadget::config(&mut cb, evidence.signal_root.len()); + let graffiti = FieldGadget::config(&mut cb, evidence.graffiti.len()); + let prover = FieldGadget::config(&mut cb, evidence.prover.len()); + let padding2 = FieldGadget::config(&mut cb, PADDING_LEN); + let proofs = FieldGadget::config(&mut cb, evidence.proofs.len()); + + meta.create_gate( + "PI acc constraints", + |meta| { + circuit!([meta, cb], { + for (b, n) in [parent_hash.clone(), block_hash.clone()].iter().zip(block_numbers.clone().iter()) { + require!( + ( + BlockContextFieldTag::BlockHash.expr(), + n.expr(), + b.acc(evm_word.expr()) + ) => @PiCellType::Lookup(Table::Block), (TO_FIX) + ); + } + let keccak_input = [ + padding1.clone(), + meta_hash.clone(), + parent_hash.clone(), + block_hash.clone(), + signal_root.clone(), + graffiti.clone(), + prover.clone(), + padding2.clone(), + proofs.clone() + ].iter().fold(0.expr(), |acc, gadget| { + let mult = (0..gadget.len).fold(1.expr(), |acc, _| acc * keccak_r.expr()); + acc * mult + gadget.acc(keccak_r.expr()) + }); + require!( + ( + 1.expr(), + keccak_input, + evidence.max_height().expr(), + public_input_bytes.acc(evm_word.expr()) + ) + => @PiCellType::Lookup(Table::Keccak), (TO_FIX) + ); + let hi_lo = public_input_bytes.hi_low_field(); + keccak_output.iter().zip(hi_lo.iter()).for_each(|(output, epxr)| { + require!(output.expr() => epxr); + cb.enable_equality(output.column()); + }); + }); + cb.build_constraints(Some(meta.query_selector(q_enable))) + } + ); + cb.build_lookups( + meta, + &[cm.clone()], + &[ + (PiCellType::Byte, PiCellType::Lookup(Table::Bytecode)), + (PiCellType::Lookup(Table::Keccak), PiCellType::Lookup(Table::Keccak)), + (PiCellType::Lookup(Table::Block), PiCellType::Lookup(Table::Block)), + ], + Some(q_enable) + ); + let annotation_configs = cm.columns().to_vec(); + Self { + q_enable, + public_input, + keccak_output, + block_numbers, + public_input_bytes, + padding1, + meta_hash, + parent_hash, + block_hash, + signal_root, + graffiti, + prover, + padding2, + proofs, + annotation_configs + } + } +} + +use gadgets::util::Expr; +use halo2_proofs::plonk::{Expression, ConstraintSystem, Selector, Instance, Column}; +use keccak256::keccak_arith::Keccak; +use snark_verifier::loader::evm; + +use crate::circuit_tools::cached_region::CachedRegion; +use crate::circuit_tools::cell_manager::{Cell, CellType, CellManager, CellColumn}; +use crate::circuit_tools::constraint_builder::{ConstraintBuilder, TO_FIX, RLCable}; +use crate::evm_circuit::table::Table; +use crate::util::{Challenges, SubCircuitConfig}; +use crate::witness::{self, Bytecode}; +use crate::{circuit, assign}; +use crate::table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}; + +#[test] +fn test(){ + println!("test"); +} + +/// +#[derive(Debug, Clone, Default)] +pub struct FieldGadget { + field: Vec>, + len: usize, +} + +impl FieldGadget { + fn config(cb: &mut ConstraintBuilder, len: usize) -> Self { + Self { + field: cb.query_cells_dyn(PiCellType::Byte, len), + len + } + } + + fn bytes_expr(&self) -> Vec> { + self.field.iter().map(|f| f.expr()).collect() + } + + fn acc(&self, r: Expression) -> Expression { + self.bytes_expr().rlc_rev(&r) + } + + pub(crate) fn hi_low_field(&self) -> [Expression; 2] { + assert!(self.len == 32); + let hi = self.bytes_expr()[..16].to_vec(); + let low = self.bytes_expr()[16..].to_vec(); + [hi.rlc_rev(&BYTE_POW_BASE.expr()), low.rlc_rev(&BYTE_POW_BASE.expr())] + } + + fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + bytes: &[F], + ) -> core::result::Result<(), Error> { + assert!(bytes.len() == self.len); + self.field + .iter() + .zip(bytes.iter()) + .for_each(|(cell, byte)| { + assign!(region, cell, offset => *byte); + }); + Ok(()) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum PiCellType { + Storage1, + Storage2, + Byte, + LookupPi, + Lookup(Table), + +} +impl CellType for PiCellType { + fn byte_type() -> Option { + Some(Self::Byte) + } + fn storage_for_phase(phase: u8) -> Self { + match phase { + 1 => PiCellType::Storage1, + 2 => PiCellType::Storage2, + _ => unimplemented!() + } + } +} +impl Default for PiCellType { + fn default() -> Self { + Self::Storage1 + } +} \ No newline at end of file From a0c95dbce6878391e3abb49822a6e52cf850567a Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Sun, 3 Sep 2023 02:07:44 +0800 Subject: [PATCH 22/43] update --- zkevm-circuits/src/lib.rs | 1 + zkevm-circuits/src/taiko_pi_circuit_.rs | 157 +++++++++++++++++------- 2 files changed, 115 insertions(+), 43 deletions(-) diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index 1f76bf8f33..f01bcce753 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -30,6 +30,7 @@ pub mod table; #[macro_use] pub mod taiko_pi_circuit; #[macro_use] +#[allow(missing_docs)] pub mod taiko_pi_circuit_; pub mod taiko_super_circuit; diff --git a/zkevm-circuits/src/taiko_pi_circuit_.rs b/zkevm-circuits/src/taiko_pi_circuit_.rs index 88093d6d9c..8dacadc923 100644 --- a/zkevm-circuits/src/taiko_pi_circuit_.rs +++ b/zkevm-circuits/src/taiko_pi_circuit_.rs @@ -39,28 +39,42 @@ // ); // } - use eth_types::{ToWord, ToBigEndian, Field}; use ethers_core::abi::*; use ethers_core::abi::FixedBytes; +use ethers_core::utils::keccak256; +use halo2_proofs::circuit::{Value, Layouter}; use std::convert::TryInto; +use std::marker::PhantomData; +use gadgets::util::{Expr, Scalar}; +use halo2_proofs::plonk::{Expression, ConstraintSystem, Selector, Instance, Column}; +use keccak256::keccak_arith::Keccak; +use halo2_proofs::plonk::Error; +use core::result::Result; +use crate::circuit_tools::cached_region::CachedRegion; +use crate::circuit_tools::cell_manager::{Cell, CellType, CellManager, CellColumn}; +use crate::circuit_tools::constraint_builder::{ConstraintBuilder, TO_FIX, RLCable}; +use crate::evm_circuit::table::Table; +use crate::util::{Challenges, SubCircuitConfig, SubCircuit}; +use crate::witness::{self, Bytecode}; +use crate::{circuit, assign}; +use crate::table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}; const PADDING_LEN: usize = 32; const PROOF_LEN: usize = 102; const BYTE_POW_BASE: u64 = 1 << 8; #[derive(Debug, Clone)] -pub struct BlockEvidence { +pub struct PublicData { meta_hash: Token, parent_hash: Token, block_hash: Token, signal_root: Token, graffiti: Token, prover: Token, - proofs: Token, } -impl BlockEvidence { +impl PublicData { fn new(block: &witness::Block) -> Self { let meta_hash = Token::FixedBytes(block.protocol_instance.meta_hash.hash().to_word().to_be_bytes().to_vec()); let parent_hash = Token::FixedBytes(block.protocol_instance.parent_hash.to_word().to_be_bytes().to_vec()); @@ -68,7 +82,6 @@ impl BlockEvidence { let signal_root = Token::FixedBytes(block.protocol_instance.signal_root.to_word().to_be_bytes().to_vec()); let graffiti = Token::FixedBytes(block.protocol_instance.graffiti.to_word().to_be_bytes().to_vec()); let prover = Token::Address(block.protocol_instance.prover); - let proofs = Token::Bytes(vec![0;PROOF_LEN]); Self { meta_hash, @@ -77,7 +90,6 @@ impl BlockEvidence { signal_root, graffiti, prover, - proofs, } } @@ -86,8 +98,6 @@ impl BlockEvidence { } fn encode_raw(&self) -> Vec { - // Initial padding: 20 means there's one dyn data, 40 means two, ... - // 0000000000000000000000000000000000000000000000000000000000000020 // Fixed bytes are directly concat // 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef // 0000000000000000000000000000000000000000000000000000000000000000 @@ -111,11 +121,29 @@ impl BlockEvidence { self.signal_root.clone(), self.graffiti.clone(), self.prover.clone(), - self.proofs.clone(), ]) ]) } + + fn get_pi_hi_low(&self) -> (u64, u64) { + let keccaked_pi = keccak256(self.encode_raw()); + ( + keccaked_pi + .iter() + .take(16) + .fold(0u64, |acc: u64, byte| { + acc * BYTE_POW_BASE + *byte as u64 + }), + keccaked_pi + .iter() + .skip(16) + .fold(0u64, |acc: u64, byte| { + acc * BYTE_POW_BASE + *byte as u64 + }) + ) + } + fn max_height(&self) -> usize { self.encode_raw().len() } @@ -147,22 +175,23 @@ pub struct TaikoPiCircuitConfig { block_numbers: Vec>, public_input_bytes: FieldGadget, - padding1: FieldGadget, meta_hash: FieldGadget, parent_hash: FieldGadget, block_hash: FieldGadget, signal_root: FieldGadget, graffiti: FieldGadget, prover: FieldGadget, - padding2: FieldGadget, - proofs: FieldGadget, + + block_table: BlockTable, + keccak_table: KeccakTable, + byte_table: ByteTable, annotation_configs: Vec>, } pub struct TaikoPiCircuitConfigArgs { /// - pub evidence: BlockEvidence, + pub evidence: PublicData, /// BlockTable pub block_table: BlockTable, /// KeccakTable @@ -213,15 +242,12 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { let block_numbers = [(); 2].iter().map(|_| cb.query_one(PiCellType::Storage1)).collect::>(); let public_input_bytes = FieldGadget::config(&mut cb, PADDING_LEN); - let padding1 = FieldGadget::config(&mut cb, PADDING_LEN); let meta_hash = FieldGadget::config(&mut cb, evidence.meta_hash.len()); let parent_hash = FieldGadget::config(&mut cb, evidence.parent_hash.len()); let block_hash = FieldGadget::config(&mut cb, evidence.block_hash.len()); let signal_root = FieldGadget::config(&mut cb, evidence.signal_root.len()); let graffiti = FieldGadget::config(&mut cb, evidence.graffiti.len()); let prover = FieldGadget::config(&mut cb, evidence.prover.len()); - let padding2 = FieldGadget::config(&mut cb, PADDING_LEN); - let proofs = FieldGadget::config(&mut cb, evidence.proofs.len()); meta.create_gate( "PI acc constraints", @@ -237,15 +263,12 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { ); } let keccak_input = [ - padding1.clone(), meta_hash.clone(), parent_hash.clone(), block_hash.clone(), signal_root.clone(), graffiti.clone(), prover.clone(), - padding2.clone(), - proofs.clone() ].iter().fold(0.expr(), |acc, gadget| { let mult = (0..gadget.len).fold(1.expr(), |acc, _| acc * keccak_r.expr()); acc * mult + gadget.acc(keccak_r.expr()) @@ -285,39 +308,20 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { keccak_output, block_numbers, public_input_bytes, - padding1, meta_hash, parent_hash, block_hash, signal_root, graffiti, prover, - padding2, - proofs, + block_table, + keccak_table, + byte_table, annotation_configs } } } -use gadgets::util::Expr; -use halo2_proofs::plonk::{Expression, ConstraintSystem, Selector, Instance, Column}; -use keccak256::keccak_arith::Keccak; -use snark_verifier::loader::evm; - -use crate::circuit_tools::cached_region::CachedRegion; -use crate::circuit_tools::cell_manager::{Cell, CellType, CellManager, CellColumn}; -use crate::circuit_tools::constraint_builder::{ConstraintBuilder, TO_FIX, RLCable}; -use crate::evm_circuit::table::Table; -use crate::util::{Challenges, SubCircuitConfig}; -use crate::witness::{self, Bytecode}; -use crate::{circuit, assign}; -use crate::table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}; - -#[test] -fn test(){ - println!("test"); -} - /// #[derive(Debug, Clone, Default)] pub struct FieldGadget { @@ -353,7 +357,7 @@ impl FieldGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, bytes: &[F], - ) -> core::result::Result<(), Error> { + ) -> Result<(), Error> { assert!(bytes.len() == self.len); self.field .iter() @@ -390,4 +394,71 @@ impl Default for PiCellType { fn default() -> Self { Self::Storage1 } -} \ No newline at end of file +} + + +/// Public Inputs Circuit +#[derive(Clone, Debug)] +pub struct TaikoPiCircuit { + /// PublicInputs data known by the verifier + pub evidence: PublicData, + _marker: PhantomData, +} + +impl Default for TaikoPiCircuit { + fn default() -> Self { + Self::new(PublicData::default::()) + } +} + +impl TaikoPiCircuit { + /// Creates a new TaikoPiCircuit + pub fn new(evidence: PublicData) -> Self { + Self { + evidence: evidence, + _marker: PhantomData, + } + } +} + +impl SubCircuit for TaikoPiCircuit { + type Config = TaikoPiCircuitConfig; + + fn unusable_rows() -> usize { + // No column queried at more than 3 distinct rotations, so returns 6 as + // minimum unusable rows. + 6 + } + + fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { + // TODO(Cecilia): what is the first field? + (0, PublicData::new(block).max_height()) + } + + fn new_from_block(block: &witness::Block) -> Self { + TaikoPiCircuit::new(PublicData::new(block)) + } + + /// Compute the public inputs for this circuit. + fn instance(&self) -> Vec> { + let (hi, low) = self.evidence.get_pi_hi_low(); + vec![vec![hi.scalar(), low.scalar()]] + } + + /// Make the assignments to the PiCircuit + fn synthesize_sub( + &self, + config: &Self::Config, + challenges: &Challenges>, + layouter: &mut impl Layouter, + ) -> Result<(), Error> { + config.byte_table.load(layouter)?; + // config.assign(layouter, &self.evidence, challenges) + Ok(()) + } +} + +#[test] +fn test(){ + println!("test"); +} From e12af58718148a85692899b1472e80643942a262 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Sun, 3 Sep 2023 23:30:03 +0800 Subject: [PATCH 23/43] fixing --- .../src/circuit_tools/constraint_builder.rs | 2 +- zkevm-circuits/src/table/keccak_table.rs | 1 + zkevm-circuits/src/taiko_pi_circuit_.rs | 625 ++++++++++++------ zkevm-circuits/src/witness/block.rs | 6 +- 4 files changed, 428 insertions(+), 206 deletions(-) diff --git a/zkevm-circuits/src/circuit_tools/constraint_builder.rs b/zkevm-circuits/src/circuit_tools/constraint_builder.rs index 8345015e2c..ba0aa15d14 100644 --- a/zkevm-circuits/src/circuit_tools/constraint_builder.rs +++ b/zkevm-circuits/src/circuit_tools/constraint_builder.rs @@ -9,7 +9,7 @@ use crate::{evm_circuit::util::rlc, table::LookupTable, util::{Expr, query_expre use eth_types::Field; use gadgets::util::{and, sum, Scalar}; use halo2_proofs::{ - plonk::{ConstraintSystem, Expression, Column, Advice, Selector}, + plonk::{ConstraintSystem, Expression, Column, Advice, Selector}, circuit::AssignedCell, }; use itertools::Itertools; diff --git a/zkevm-circuits/src/table/keccak_table.rs b/zkevm-circuits/src/table/keccak_table.rs index 14b33d6ecf..a70dae78f4 100644 --- a/zkevm-circuits/src/table/keccak_table.rs +++ b/zkevm-circuits/src/table/keccak_table.rs @@ -65,6 +65,7 @@ impl KeccakTable { challenge, ) }); + println!("keccak table input rlc {:?} len {:?} output rlc {:?}", input, input_len, output); vec![[ Value::known(F::ONE), input_rlc, diff --git a/zkevm-circuits/src/taiko_pi_circuit_.rs b/zkevm-circuits/src/taiko_pi_circuit_.rs index 8dacadc923..5dc6a5df4b 100644 --- a/zkevm-circuits/src/taiko_pi_circuit_.rs +++ b/zkevm-circuits/src/taiko_pi_circuit_.rs @@ -39,15 +39,17 @@ // ); // } -use eth_types::{ToWord, ToBigEndian, Field}; +use bus_mapping::evm; +use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256, H160}; use ethers_core::abi::*; use ethers_core::abi::FixedBytes; use ethers_core::utils::keccak256; -use halo2_proofs::circuit::{Value, Layouter}; +use halo2_proofs::circuit::{Value, Layouter, SimpleFloorPlanner, AssignedCell}; +use itertools::Itertools; use std::convert::TryInto; use std::marker::PhantomData; use gadgets::util::{Expr, Scalar}; -use halo2_proofs::plonk::{Expression, ConstraintSystem, Selector, Instance, Column}; +use halo2_proofs::plonk::{Expression, ConstraintSystem, Selector, Instance, Column, Circuit}; use keccak256::keccak_arith::Keccak; use halo2_proofs::plonk::Error; use core::result::Result; @@ -55,129 +57,224 @@ use crate::circuit_tools::cached_region::CachedRegion; use crate::circuit_tools::cell_manager::{Cell, CellType, CellManager, CellColumn}; use crate::circuit_tools::constraint_builder::{ConstraintBuilder, TO_FIX, RLCable}; use crate::evm_circuit::table::Table; +use crate::evm_circuit::util::rlc; use crate::util::{Challenges, SubCircuitConfig, SubCircuit}; -use crate::witness::{self, Bytecode}; +use crate::witness::{self, Bytecode, BlockContext}; use crate::{circuit, assign}; use crate::table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}; -const PADDING_LEN: usize = 32; -const PROOF_LEN: usize = 102; const BYTE_POW_BASE: u64 = 1 << 8; +const PADDING_LEN: usize = 32; + + +/// +#[derive(Debug, Clone, Default)] +pub struct FieldGadget { + field: Vec>, + len: usize, +} + +impl FieldGadget { + fn config(cb: &mut ConstraintBuilder, len: usize) -> Self { + Self { + field: cb.query_cells_dyn(PiCellType::Byte, len), + len + } + } + + fn bytes_expr(&self) -> Vec> { + self.field.iter().map(|f| f.expr()).collect() + } + + fn acc(&self, r: Expression) -> Expression { + self.bytes_expr().rlc(&r) + } + + pub(crate) fn hi_low_field(&self) -> [Expression; 2] { + assert!(self.len == 32); + let hi = self.bytes_expr()[..16].to_vec(); + let low = self.bytes_expr()[16..].to_vec(); + [hi.rlc_rev(&BYTE_POW_BASE.expr()), low.rlc_rev(&BYTE_POW_BASE.expr())] + } + + fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + bytes: &[F], + ) -> Result>, Error> { + assert!(bytes.len() == self.len); + let cells = self.field.iter().zip(bytes.iter()).map( + |(cell, byte)| { + assign!(region, cell, offset => *byte).unwrap() + } + ).collect(); + Ok(cells) + } + + fn assign_acc( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + bytes: &[F], + r: F + ) -> Result { + assert!(bytes.len() == self.len); + let mut acc = F::ZERO; + self.field.iter().zip(bytes.iter()).for_each( + |(cell, byte)| { + assign!(region, cell, offset => *byte).unwrap(); + acc = acc * r + *byte; + } + ); + Ok(acc) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum PiCellType { + Storage1, + Storage2, + Byte, + LookupPi, + Lookup(Table), + +} +impl CellType for PiCellType { + fn byte_type() -> Option { + Some(Self::Byte) + } + fn storage_for_phase(phase: u8) -> Self { + match phase { + 1 => PiCellType::Storage1, + 2 => PiCellType::Storage2, + _ => unimplemented!() + } + } +} +impl Default for PiCellType { + fn default() -> Self { + Self::Storage1 + } +} + + #[derive(Debug, Clone)] -pub struct PublicData { - meta_hash: Token, - parent_hash: Token, - block_hash: Token, - signal_root: Token, - graffiti: Token, - prover: Token, +pub struct PublicData { + evidence: Token, + block_context: BlockContext, + _phantom: PhantomData, } -impl PublicData { - fn new(block: &witness::Block) -> Self { +impl Default for PublicData { + fn default() -> Self { + Self::new(&witness::Block::default()) + } +} + +impl PublicData { + fn new(block: &witness::Block) -> Self { let meta_hash = Token::FixedBytes(block.protocol_instance.meta_hash.hash().to_word().to_be_bytes().to_vec()); let parent_hash = Token::FixedBytes(block.protocol_instance.parent_hash.to_word().to_be_bytes().to_vec()); let block_hash = Token::FixedBytes(block.protocol_instance.block_hash.to_word().to_be_bytes().to_vec()); let signal_root = Token::FixedBytes(block.protocol_instance.signal_root.to_word().to_be_bytes().to_vec()); let graffiti = Token::FixedBytes(block.protocol_instance.graffiti.to_word().to_be_bytes().to_vec()); let prover = Token::Address(block.protocol_instance.prover); - - Self { - meta_hash, - parent_hash, - block_hash, - signal_root, - graffiti, - prover, + Self { + evidence: Token::FixedArray(vec![ + meta_hash, + parent_hash, + block_hash, + signal_root, + graffiti, + prover, + ]), + block_context: block.context.clone(), + _phantom: PhantomData } } - fn default() -> Self { - Self::new::(&witness::Block::default()) + fn set_field(&mut self, idx: usize, bytes: Vec) { + match self.evidence { + Token::FixedArray(ref mut tokens) => { + tokens[idx] = match tokens[idx].clone() { + Token::Bytes(_) => Token::Bytes(bytes), + Token::FixedBytes(_) => Token::FixedBytes(bytes), + Token::Address(_) => Token::Address( + H160::from(&bytes.try_into().expect("Wrong number of bytes for address") + )), + _ => unreachable!(), + }; + } + _ => unreachable!(), + } } fn encode_raw(&self) -> Vec { - // Fixed bytes are directly concat - // 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - // 0000000000000000000000000000000000000000000000000000000000000000 - // 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - // 0000000000000000000000000000000000000000000000000000000000000000 - // 0000000000000000000000000000000000000000000000000000000000000000 - // 0000000000000000000000002222222222222222222222222222222222222222 - // Second padding happens right before dyn bytes - // 00000000000000000000000000000000000000000000000000000000000000e0 - // Proofs: new bytes(102), 0x66 = 102 - // 0000000000000000000000000000000000000000000000000000000000000066 - // 0000000000000000000000000000000000000000000000000000000000000000 - // 0000000000000000000000000000000000000000000000000000000000000000 - // 0000000000000000000000000000000000000000000000000000000000000000 - // 0000000000000000000000000000000000000000000000000000000000000000 - encode(&[ - Token::FixedArray(vec![ - self.meta_hash.clone(), - self.parent_hash.clone(), - self.block_hash.clone(), - self.signal_root.clone(), - self.graffiti.clone(), - self.prover.clone(), - ]) - ]) - } - - - fn get_pi_hi_low(&self) -> (u64, u64) { + encode(&[self.evidence.clone()]) + } + + fn encode_field(&self, idx: usize) -> Vec { + let field = match self.evidence { + Token::FixedArray(ref tokens) => tokens[idx].clone(), + _ => unreachable!(), + }; + let res = encode(&[field]); + res.into_iter().rev().collect() + } + + fn assignment(&self, idx: usize) -> Vec { + self.encode_field(idx) + .iter() + .map(|b| F::from(*b as u64)) + .collect() + } + + fn keccak_hi_low(&self) -> [F; 2] { let keccaked_pi = keccak256(self.encode_raw()); - ( + [ keccaked_pi .iter() .take(16) - .fold(0u64, |acc: u64, byte| { - acc * BYTE_POW_BASE + *byte as u64 + .fold(F::ZERO, |acc: F, byte| { + acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) }), keccaked_pi .iter() .skip(16) - .fold(0u64, |acc: u64, byte| { - acc * BYTE_POW_BASE + *byte as u64 + .fold(F::ZERO, |acc: F, byte| { + acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) }) - ) + ] } - fn max_height(&self) -> usize { - self.encode_raw().len() + fn keccak(&self) -> Vec { + keccak256(self.encode_raw()).to_vec() } -} + fn total_len(&self) -> usize { + self.encode_raw().len() + } -trait AbiLength { - fn len(&self) -> usize; -} -impl AbiLength for Token { - fn len(&self) -> usize { - // TODO(Cecilia): handle padding for dyn data - match self { - // Solidity fixed type: bytes8, bytes32, bytes64, bytes1024,... - Token::FixedBytes(bytes) => ((bytes.len() + 31) / 32) * 32, - // Solidity dyn type: bytes, encoded with one more word representing the length - Token::Bytes(bytes) => (((bytes.len() + 31) / 32) + 1) * 32, - Token::Int(_) | Token::Uint(_) | Token::Bool(_) | Token::Address(_) => 32, - _ => unimplemented!() - } + fn field_len(&self, idx: usize) -> usize { + self.encode_field(idx).len() } + + } #[derive(Clone, Debug)] pub struct TaikoPiCircuitConfig { q_enable: Selector, - public_input: Column, // equality - keccak_output: Vec>, // hi, lo - block_numbers: Vec>, - public_input_bytes: FieldGadget, + keccak_instance: Column, // equality + keccak_hi_lo: Vec>, // hi, lo + keccak_bytes: FieldGadget, meta_hash: FieldGadget, - parent_hash: FieldGadget, - block_hash: FieldGadget, + // block number, blockhash + parent_hash: (Cell, FieldGadget), + block_hash: (Cell, FieldGadget), signal_root: FieldGadget, graffiti: FieldGadget, prover: FieldGadget, @@ -191,7 +288,7 @@ pub struct TaikoPiCircuitConfig { pub struct TaikoPiCircuitConfigArgs { /// - pub evidence: PublicData, + pub evidence: PublicData, /// BlockTable pub block_table: BlockTable, /// KeccakTable @@ -222,10 +319,11 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { meta, vec![ (PiCellType::Byte, 1, 1, false), - (PiCellType::Storage2, 1, 2, true), + (PiCellType::Storage1, 1, 1, true), + (PiCellType::Storage2, 1, 1, true), ], 0, - 32, + evidence.total_len() + PADDING_LEN, ); let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(cm.clone()), Some(evm_word.expr())); cb.preload_tables(meta, @@ -239,21 +337,28 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { let public_input = meta.instance_column(); let keccak_output = [(); 2].iter().map(|_| cb.query_one(PiCellType::Storage2)).collect::>(); - let block_numbers = [(); 2].iter().map(|_| cb.query_one(PiCellType::Storage1)).collect::>(); - let public_input_bytes = FieldGadget::config(&mut cb, PADDING_LEN); + let keccak_bytes = FieldGadget::config(&mut cb, PADDING_LEN); - let meta_hash = FieldGadget::config(&mut cb, evidence.meta_hash.len()); - let parent_hash = FieldGadget::config(&mut cb, evidence.parent_hash.len()); - let block_hash = FieldGadget::config(&mut cb, evidence.block_hash.len()); - let signal_root = FieldGadget::config(&mut cb, evidence.signal_root.len()); - let graffiti = FieldGadget::config(&mut cb, evidence.graffiti.len()); - let prover = FieldGadget::config(&mut cb, evidence.prover.len()); + let meta_hash = FieldGadget::config(&mut cb, evidence.field_len(0)); + let parent_hash = ( + cb.query_one(PiCellType::Storage1), + FieldGadget::config(&mut cb, evidence.field_len(1)), + // cb.query_one(PiCellType::Storage2), + ); + let block_hash =( + cb.query_one(PiCellType::Storage1), + FieldGadget::config(&mut cb, evidence.field_len(2)), + // cb.query_one(PiCellType::Storage2), + ); + let signal_root = FieldGadget::config(&mut cb, evidence.field_len(3)); + let graffiti = FieldGadget::config(&mut cb, evidence.field_len(4)); + let prover = FieldGadget::config(&mut cb, evidence.field_len(5)); meta.create_gate( "PI acc constraints", |meta| { circuit!([meta, cb], { - for (b, n) in [parent_hash.clone(), block_hash.clone()].iter().zip(block_numbers.clone().iter()) { + for (n, b) in [parent_hash.clone() , block_hash.clone()] { require!( ( BlockContextFieldTag::BlockHash.expr(), @@ -261,11 +366,17 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { b.acc(evm_word.expr()) ) => @PiCellType::Lookup(Table::Block), (TO_FIX) ); + println!( + "require ({:?}, {:?}, {:?})", + BlockContextFieldTag::BlockHash, + n.expr().identifier(), + b.acc(1.expr()).identifier() + ); } let keccak_input = [ meta_hash.clone(), - parent_hash.clone(), - block_hash.clone(), + parent_hash.1.clone(), + block_hash.1.clone(), signal_root.clone(), graffiti.clone(), prover.clone(), @@ -273,16 +384,17 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { let mult = (0..gadget.len).fold(1.expr(), |acc, _| acc * keccak_r.expr()); acc * mult + gadget.acc(keccak_r.expr()) }); - require!( - ( - 1.expr(), - keccak_input, - evidence.max_height().expr(), - public_input_bytes.acc(evm_word.expr()) - ) - => @PiCellType::Lookup(Table::Keccak), (TO_FIX) - ); - let hi_lo = public_input_bytes.hi_low_field(); + println!("config keccak len {:?}", evidence.total_len()); + // require!( + // ( + // 1.expr(), + // keccak_input, + // evidence.total_len().expr(), + // keccak_bytes.acc(evm_word.expr()) + // ) + // => @PiCellType::Lookup(Table::Keccak), (TO_FIX) + // ); + let hi_lo = keccak_bytes.hi_low_field(); keccak_output.iter().zip(hi_lo.iter()).for_each(|(output, epxr)| { require!(output.expr() => epxr); cb.enable_equality(output.column()); @@ -304,10 +416,9 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { let annotation_configs = cm.columns().to_vec(); Self { q_enable, - public_input, - keccak_output, - block_numbers, - public_input_bytes, + keccak_instance: public_input, + keccak_hi_lo: keccak_output, + keccak_bytes, meta_hash, parent_hash, block_hash, @@ -322,101 +433,69 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { } } -/// -#[derive(Debug, Clone, Default)] -pub struct FieldGadget { - field: Vec>, - len: usize, -} - -impl FieldGadget { - fn config(cb: &mut ConstraintBuilder, len: usize) -> Self { - Self { - field: cb.query_cells_dyn(PiCellType::Byte, len), - len - } - } - - fn bytes_expr(&self) -> Vec> { - self.field.iter().map(|f| f.expr()).collect() - } - - fn acc(&self, r: Expression) -> Expression { - self.bytes_expr().rlc_rev(&r) - } - - pub(crate) fn hi_low_field(&self) -> [Expression; 2] { - assert!(self.len == 32); - let hi = self.bytes_expr()[..16].to_vec(); - let low = self.bytes_expr()[16..].to_vec(); - [hi.rlc_rev(&BYTE_POW_BASE.expr()), low.rlc_rev(&BYTE_POW_BASE.expr())] - } - - fn assign( - &self, - region: &mut CachedRegion<'_, '_, F>, - offset: usize, - bytes: &[F], +impl TaikoPiCircuitConfig { + pub(crate) fn assign( + &self, + layouter: &mut impl Layouter, + // challenge: &Challenges>, + evidence: &PublicData, ) -> Result<(), Error> { - assert!(bytes.len() == self.len); - self.field - .iter() - .zip(bytes.iter()) - .for_each(|(cell, byte)| { - assign!(region, cell, offset => *byte); - }); - Ok(()) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -enum PiCellType { - Storage1, - Storage2, - Byte, - LookupPi, - Lookup(Table), + let pi_cells = layouter.assign_region( + || "Pi", + |mut region| { + self.q_enable.enable(&mut region, 0)?; + let mut region = CachedRegion::new(&mut region); + region.annotate_columns(&self.annotation_configs); + + let mut acc = F::ZERO; + let mut offset = 0; + let mut idx = 0; + [ + &self.meta_hash, + &self.parent_hash.1, + &self.block_hash.1, + &self.signal_root, + &self.graffiti, + &self.prover, + ].iter().for_each(|gadget| { + println!("assignment {:?}: {:?}, {:?}", idx, offset, evidence.encode_field(idx)); + gadget.assign(&mut region, offset, &evidence.assignment(idx)) + .expect(&format!("FieldGadget assignment failed at {:?}", idx)); + + offset += evidence.field_len(idx); + idx += 1; + }); -} -impl CellType for PiCellType { - fn byte_type() -> Option { - Some(Self::Byte) - } - fn storage_for_phase(phase: u8) -> Self { - match phase { - 1 => PiCellType::Storage1, - 2 => PiCellType::Storage2, - _ => unimplemented!() - } - } -} -impl Default for PiCellType { - fn default() -> Self { - Self::Storage1 + println!("evidence.block_context.number: {:?}\n", evidence.block_context.number); + assign!(region, self.parent_hash.0, 0 => (evidence.block_context.number - 1).as_u64().scalar()); + assign!(region, self.block_hash.0, 0 => (evidence.block_context.number).as_u64().scalar()); + + // self.keccak_bytes.assign(&mut region, offset, &bytes_to_fields(evidence.keccak())); + // println!("assing keccak bytes: {:?}", evidence.keccak()); + // self.keccak_hi_lo + // .iter() + // .zip(evidence.keccak_hi_low().iter()) + // .for_each(|(cell, val)| { + // assign!(region, cell, offset => *val); + // }); + + Ok(()) + }); + Ok(()) } } - - /// Public Inputs Circuit -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct TaikoPiCircuit { /// PublicInputs data known by the verifier - pub evidence: PublicData, - _marker: PhantomData, -} - -impl Default for TaikoPiCircuit { - fn default() -> Self { - Self::new(PublicData::default::()) - } + pub evidence: PublicData, } impl TaikoPiCircuit { /// Creates a new TaikoPiCircuit - pub fn new(evidence: PublicData) -> Self { + pub fn new(evidence: PublicData) -> Self { Self { evidence: evidence, - _marker: PhantomData, } } } @@ -427,12 +506,12 @@ impl SubCircuit for TaikoPiCircuit { fn unusable_rows() -> usize { // No column queried at more than 3 distinct rotations, so returns 6 as // minimum unusable rows. - 6 + PublicData::::default().total_len() + 3 } fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { // TODO(Cecilia): what is the first field? - (0, PublicData::new(block).max_height()) + (0, PublicData::new(block).total_len()) } fn new_from_block(block: &witness::Block) -> Self { @@ -441,24 +520,164 @@ impl SubCircuit for TaikoPiCircuit { /// Compute the public inputs for this circuit. fn instance(&self) -> Vec> { - let (hi, low) = self.evidence.get_pi_hi_low(); - vec![vec![hi.scalar(), low.scalar()]] + vec![ self.evidence.keccak_hi_low().to_vec()] } /// Make the assignments to the PiCircuit fn synthesize_sub( &self, config: &Self::Config, - challenges: &Challenges>, + _challenges: &Challenges>, layouter: &mut impl Layouter, ) -> Result<(), Error> { config.byte_table.load(layouter)?; - // config.assign(layouter, &self.evidence, challenges) - Ok(()) + config.assign(layouter, &self.evidence) + } +} + +#[cfg(any(feature = "test", test))] +impl Circuit for TaikoPiCircuit { + type Config = (TaikoPiCircuitConfig, Challenges); + type FloorPlanner = SimpleFloorPlanner; + type Params = PublicData; + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn params(&self) -> Self::Params { + self.evidence.clone() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + Self::configure_with_params(meta, PublicData::default()) } + + fn configure_with_params( + meta: &mut ConstraintSystem, + params: Self::Params, + ) -> Self::Config { + let block_table = BlockTable::construct(meta); + let keccak_table = KeccakTable::construct(meta); + let byte_table = ByteTable::construct(meta); + let challenges = Challenges::construct(meta); + let challenge_exprs = challenges.exprs(meta); + ( + TaikoPiCircuitConfig::new( + meta, + TaikoPiCircuitConfigArgs { + evidence: params, + block_table, + keccak_table, + byte_table, + challenges: challenge_exprs, + } + ), + challenges + ) + } + + fn synthesize( + &self, + (config, challenges): Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let challenges = challenges.values(&mut layouter); + let evidance = self.params(); + let randomness = challenges.evm_word(); + // assign block table + config + .block_table + .load(&mut layouter, &evidance.block_context, randomness)?; + // assign keccak table + config + .keccak_table + .dev_load(&mut layouter, vec![&evidance.encode_raw()], &challenges)?; + config.byte_table.load(&mut layouter)?; + + self.synthesize_sub(&config, &challenges, &mut layouter) + } + } -#[test] -fn test(){ - println!("test"); +#[cfg(test)] +mod taiko_pi_circuit_test { + + use std::vec; + + use super::*; + + use eth_types::ToScalar; + use halo2_proofs::{ + dev::{MockProver, VerifyFailure}, + halo2curves::bn256::Fr, + }; + use lazy_static::lazy_static; + use pretty_assertions::assert_eq; + + lazy_static! { + static ref OMMERS_HASH: H256 = H256::from_slice( + &hex::decode("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347") + .unwrap(), + ); + } + + fn run( + k: u32, + evidence: PublicData, + pi: Option>>, + ) -> Result<(), Vec> { + let circuit = TaikoPiCircuit::new(evidence); + let public_inputs = pi.unwrap_or_else(|| circuit.instance()); + let prover = match MockProver::run(k, &circuit, public_inputs) { + Ok(prover) => prover, + Err(e) => panic!("{:#?}", e), + }; + prover.verify() + } + + fn mock_public_data() -> PublicData { + let A = OMMERS_HASH.clone()/* H256::default() */; + let mut evidence = PublicData::default(); + // meta_hash + evidence.set_field(0, A.to_fixed_bytes().to_vec()); + // block_hash + evidence.set_field(2, A.to_fixed_bytes().to_vec()); + evidence.block_context.block_hash = A.to_word(); + evidence.block_context.history_hashes = vec![Default::default(); 256]; + evidence.block_context.number = 300.into(); + evidence + } + + #[test] + fn test_default_pi() { + let evidence = mock_public_data(); + + let k = 17; + assert_eq!(run::(k, evidence, None), Ok(())); + } + + #[test] + fn test(){ + println!("test"); + let mut evidence = PublicData::::default(); + let data = hex::decode("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef").unwrap(); + evidence.set_field(0, data.clone()); + evidence.set_field(1, vec![0u8; 32]); + evidence.set_field(2, data.clone()); + evidence.set_field(3, vec![0u8; 32]); + evidence.set_field(4, vec![0u8; 32]); + evidence.set_field(5, vec![0u8; 20]); + + // evidence.parent_hash = Token::FixedBytes(vec![0u8; 32]); + // evidence.block_hash = Token::FixedBytes(data); + // evidence.signal_root = Token::FixedBytes(vec![0u8; 32]); + // evidence.graffiti = Token::FixedBytes(vec![0u8; 32]); + // evidence.prover = Token::Address([0x22u8; 20].into()); + let encode_raw = evidence.encode_raw(); + println!("abi.encode {:?}\nkeccak {:?}\nhi-lo {:?}", encode_raw.clone(), keccak256(encode_raw), evidence.keccak_hi_low()); + } + } + + diff --git a/zkevm-circuits/src/witness/block.rs b/zkevm-circuits/src/witness/block.rs index 60c03d6fcc..0df8c41d82 100644 --- a/zkevm-circuits/src/witness/block.rs +++ b/zkevm-circuits/src/witness/block.rs @@ -10,7 +10,8 @@ use bus_mapping::{ circuit_input_builder::{self, CircuitsParams, CopyEvent, ExpEvent}, Error, }; -use eth_types::{Address, Field, ToLittleEndian, ToScalar, ToWord, Word}; +use eth_types::{Address, Field, ToLittleEndian, ToScalar, ToWord, Word, ToBigEndian}; +use gadgets::util::Scalar; use halo2_proofs::circuit::Value; use super::{tx::tx_convert, Bytecode, ExecStep, ProtocolInstance, Rw, RwMap, Transaction}; @@ -143,6 +144,8 @@ pub struct BlockContext { impl BlockContext { /// Assignments for block table pub fn table_assignments(&self, randomness: Value) -> Vec<[Value; 3]> { + // let randomness = Value::known(F::from(2u64)); + println!("block table {:?} (le_bytes) {:?} evm_word = {:?}", self.number, self.block_hash.to_le_bytes(), randomness); [ vec![ [ @@ -188,7 +191,6 @@ impl BlockContext { Value::known(self.number.to_scalar().unwrap()), randomness .map(|randomness| rlc::value(&self.block_hash.to_le_bytes(), randomness)), - // b0 * r^31 + ... + b31 * r^0 ], ], { From 9de1a9afa2c7363a5c524b8d4547b4079fb68849 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Mon, 4 Sep 2023 02:11:24 +0800 Subject: [PATCH 24/43] block_hash work --- zkevm-circuits/src/lib.rs | 4 +- zkevm-circuits/src/taiko_pi_circuit_.rs | 6 +- zkevm-circuits/src/taiko_pi_circuit__.rs | 540 +++++++++++++++++++++++ zkevm-circuits/src/witness/block.rs | 3 +- 4 files changed, 548 insertions(+), 5 deletions(-) create mode 100644 zkevm-circuits/src/taiko_pi_circuit__.rs diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index f01bcce753..40a248ca7c 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -33,7 +33,9 @@ pub mod taiko_pi_circuit; #[allow(missing_docs)] pub mod taiko_pi_circuit_; pub mod taiko_super_circuit; - +#[macro_use] +#[allow(missing_docs)] +pub mod taiko_pi_circuit__; #[cfg(any(feature = "test", test))] pub mod test_util; pub mod circuit_tools; diff --git a/zkevm-circuits/src/taiko_pi_circuit_.rs b/zkevm-circuits/src/taiko_pi_circuit_.rs index 5dc6a5df4b..206b29338f 100644 --- a/zkevm-circuits/src/taiko_pi_circuit_.rs +++ b/zkevm-circuits/src/taiko_pi_circuit_.rs @@ -87,6 +87,7 @@ impl FieldGadget { } fn acc(&self, r: Expression) -> Expression { + //0.expr() self.bytes_expr().rlc(&r) } @@ -227,7 +228,7 @@ impl PublicData { fn assignment(&self, idx: usize) -> Vec { self.encode_field(idx) .iter() - .map(|b| F::from(*b as u64)) + .map(|b| F::from(0 as u64)) .collect() } @@ -363,7 +364,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { ( BlockContextFieldTag::BlockHash.expr(), n.expr(), - b.acc(evm_word.expr()) + /* 0.expr()/ */ b.acc(1.expr()) ) => @PiCellType::Lookup(Table::Block), (TO_FIX) ); println!( @@ -461,7 +462,6 @@ impl TaikoPiCircuitConfig { println!("assignment {:?}: {:?}, {:?}", idx, offset, evidence.encode_field(idx)); gadget.assign(&mut region, offset, &evidence.assignment(idx)) .expect(&format!("FieldGadget assignment failed at {:?}", idx)); - offset += evidence.field_len(idx); idx += 1; }); diff --git a/zkevm-circuits/src/taiko_pi_circuit__.rs b/zkevm-circuits/src/taiko_pi_circuit__.rs new file mode 100644 index 0000000000..8b6306f8f7 --- /dev/null +++ b/zkevm-circuits/src/taiko_pi_circuit__.rs @@ -0,0 +1,540 @@ + + +use bus_mapping::evm; +use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256, H160}; +use ethers_core::abi::*; +use ethers_core::abi::FixedBytes; +use ethers_core::utils::keccak256; +use halo2_proofs::circuit::{Value, Layouter, SimpleFloorPlanner, AssignedCell}; +use itertools::Itertools; +use std::convert::TryInto; +use std::marker::PhantomData; +use gadgets::util::{Expr, Scalar}; +use halo2_proofs::plonk::{Expression, ConstraintSystem, Selector, Instance, Column, Circuit}; +use keccak256::keccak_arith::Keccak; +use halo2_proofs::plonk::Error; +use core::result::Result; +use crate::circuit_tools::cached_region::CachedRegion; +use crate::circuit_tools::cell_manager::{Cell, CellType, CellManager, CellColumn}; +use crate::circuit_tools::constraint_builder::{ConstraintBuilder, TO_FIX, RLCable}; +use crate::evm_circuit::table::Table; +use crate::evm_circuit::util::rlc; +use crate::util::{Challenges, SubCircuitConfig, SubCircuit}; +use crate::witness::{self, Bytecode, BlockContext}; +use crate::{circuit, assign}; +use crate::table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}; + +const BYTE_POW_BASE: u64 = 1 << 8; +const PADDING_LEN: usize = 32; + + +/// +#[derive(Debug, Clone, Default)] +pub struct FieldGadget { + field: Vec>, + len: usize, +} + +impl FieldGadget { + fn config(cb: &mut ConstraintBuilder, len: usize) -> Self { + Self { + field: cb.query_cells_dyn(PiCellType::Byte, len), + len + } + } + + fn bytes_expr(&self) -> Vec> { + self.field.iter().map(|f| f.expr()).collect() + } + + fn acc(&self, r: Expression) -> Expression { + //0.expr() + self.bytes_expr().rlc_rev(&r) + } + + pub(crate) fn hi_low_field(&self) -> [Expression; 2] { + assert!(self.len == 32); + let hi = self.bytes_expr()[..16].to_vec(); + let low = self.bytes_expr()[16..].to_vec(); + [hi.rlc_rev(&BYTE_POW_BASE.expr()), low.rlc_rev(&BYTE_POW_BASE.expr())] + } + + fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + bytes: &[F], + ) -> Result>, Error> { + assert!(bytes.len() == self.len); + let cells = self.field.iter().zip(bytes.iter()).map( + |(cell, byte)| { + assign!(region, cell, offset => *byte).unwrap() + } + ).collect(); + Ok(cells) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum PiCellType { + Storage1, + Storage2, + Byte, + LookupPi, + Lookup(Table), + +} +impl CellType for PiCellType { + fn byte_type() -> Option { + Some(Self::Byte) + } + fn storage_for_phase(phase: u8) -> Self { + match phase { + 1 => PiCellType::Storage1, + 2 => PiCellType::Storage2, + _ => unimplemented!() + } + } +} +impl Default for PiCellType { + fn default() -> Self { + Self::Storage1 + } +} + + + +#[derive(Debug, Clone)] +pub struct PublicData { + evidence: Token, + block_context: BlockContext, + _phantom: PhantomData, +} + +impl Default for PublicData { + fn default() -> Self { + Self::new(&witness::Block::default()) + } +} + +impl PublicData { + fn new(block: &witness::Block) -> Self { + let block_hash = Token::FixedBytes(block.protocol_instance.block_hash.to_word().to_be_bytes().to_vec()); + Self { + evidence: Token::FixedArray(vec![ + block_hash, + ]), + block_context: block.context.clone(), + _phantom: PhantomData + } + } + + fn set_field(&mut self, idx: usize, bytes: Vec) { + match self.evidence { + Token::FixedArray(ref mut tokens) => { + tokens[idx] = match tokens[idx].clone() { + Token::Bytes(_) => Token::Bytes(bytes), + Token::FixedBytes(_) => Token::FixedBytes(bytes), + Token::Address(_) => Token::Address( + H160::from(&bytes.try_into().expect("Wrong number of bytes for address") + )), + _ => unreachable!(), + }; + } + _ => unreachable!(), + } + } + + fn encode_raw(&self) -> Vec { + encode(&[self.evidence.clone()]) + } + + fn encode_field(&self, idx: usize) -> Vec { + let field = match self.evidence { + Token::FixedArray(ref tokens) => tokens[idx].clone(), + _ => unreachable!(), + }; + encode(&[field]) + } + + fn assignment(&self, idx: usize) -> Vec { + self.encode_field(idx) + .iter() + .map(|b| F::from(*b as u64)) + .collect() + } + + fn keccak_hi_low(&self) -> [F; 2] { + let keccaked_pi = keccak256(self.encode_raw()); + [ + keccaked_pi + .iter() + .take(16) + .fold(F::ZERO, |acc: F, byte| { + acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) + }), + keccaked_pi + .iter() + .skip(16) + .fold(F::ZERO, |acc: F, byte| { + acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) + }) + ] + } + + fn keccak(&self) -> Vec { + keccak256(self.encode_raw()).to_vec() + } + + fn total_len(&self) -> usize { + self.encode_raw().len() + } + + fn field_len(&self, idx: usize) -> usize { + self.encode_field(idx).len() + } + + +} + +#[derive(Clone, Debug)] +pub struct TaikoPiCircuitConfig { + q_enable: Selector, + public_input: Column, // equality + block_hash: (Cell, FieldGadget), + + block_table: BlockTable, + keccak_table: KeccakTable, + byte_table: ByteTable, + + annotation_configs: Vec>, +} + +pub struct TaikoPiCircuitConfigArgs { + /// + pub evidence: PublicData, + /// BlockTable + pub block_table: BlockTable, + /// KeccakTable + pub keccak_table: KeccakTable, + /// ByteTable + pub byte_table: ByteTable, + /// Challenges + pub challenges: Challenges>, +} + + +impl SubCircuitConfig for TaikoPiCircuitConfig { + type ConfigArgs = TaikoPiCircuitConfigArgs; + /// Return a new TaikoPiCircuitConfig + fn new( + meta: &mut ConstraintSystem, + Self::ConfigArgs { + evidence, + block_table, + keccak_table, + byte_table, + challenges, + }: Self::ConfigArgs, + ) -> Self { + let keccak_r = challenges.keccak_input(); + let evm_word = challenges.evm_word(); + let cm = CellManager::new( + meta, + vec![ + (PiCellType::Byte, 1, 1, false), + (PiCellType::Storage1, 1, 1, true), + (PiCellType::Storage2, 1, 1, true), + ], + 0, + evidence.total_len() + PADDING_LEN, + ); + let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(cm.clone()), Some(evm_word.expr())); + cb.preload_tables(meta, + &[ + (PiCellType::Lookup(Table::Keccak), &keccak_table), + (PiCellType::Lookup(Table::Bytecode), &byte_table), + (PiCellType::Lookup(Table::Block), &block_table) + ] + ); + let q_enable = meta.complex_selector(); + let public_input = meta.instance_column(); + let block_hash =( + cb.query_one(PiCellType::Storage1), + FieldGadget::config(&mut cb, evidence.field_len(0)), + ); + + meta.create_gate( + "PI acc constraints", + |meta| { + circuit!([meta, cb], { + for (n, b) in [/* parent_hash.clone() , */ block_hash.clone()] { + require!( + ( + BlockContextFieldTag::BlockHash.expr(), + n.expr(), + b.acc(evm_word.expr()) + ) => @PiCellType::Lookup(Table::Block), (TO_FIX) + ); + println!( + "require ({:?}, {:?}, {:?})", + BlockContextFieldTag::BlockHash, + n.expr().identifier(), + b.acc(evm_word.expr()).identifier() + ); + } + }); + cb.build_constraints(Some(meta.query_selector(q_enable))) + } + ); + cb.build_lookups( + meta, + &[cm.clone()], + &[ + (PiCellType::Byte, PiCellType::Lookup(Table::Bytecode)), + (PiCellType::Lookup(Table::Keccak), PiCellType::Lookup(Table::Keccak)), + (PiCellType::Lookup(Table::Block), PiCellType::Lookup(Table::Block)), + ], + Some(q_enable) + ); + let annotation_configs = cm.columns().to_vec(); + Self { + q_enable, + public_input, + block_hash, + block_table, + keccak_table, + byte_table, + annotation_configs + } + } +} + +impl TaikoPiCircuitConfig { + pub(crate) fn assign( + &self, + layouter: &mut impl Layouter, + // challenge: &Challenges>, + evidence: &PublicData, + ) -> Result<(), Error> { + let pi_cells = layouter.assign_region( + || "Pi", + |mut region| { + self.q_enable.enable(&mut region, 0)?; + let mut region = CachedRegion::new(&mut region); + region.annotate_columns(&self.annotation_configs); + + let mut acc = F::ZERO; + let mut offset = 0; + let mut idx = 0; + [ + &self.block_hash.1, + ].iter().for_each(|gadget| { + println!("assignment {:?}: {:?}, {:?}", idx, offset, evidence.encode_field(idx)); + gadget.assign(&mut region, offset, &evidence.assignment(idx)) + .expect(&format!("FieldGadget assignment failed at {:?}", idx)); + offset += evidence.field_len(idx); + idx += 1; + }); + + println!("evidence.block_context.number: {:?}\n", evidence.block_context.number); + assign!(region, self.block_hash.0, 0 => (evidence.block_context.number).as_u64().scalar()); + + Ok(()) + }); + Ok(()) + } +} +/// Public Inputs Circuit +#[derive(Clone, Debug, Default)] +pub struct TaikoPiCircuit { + /// PublicInputs data known by the verifier + pub evidence: PublicData, +} + +impl TaikoPiCircuit { + /// Creates a new TaikoPiCircuit + pub fn new(evidence: PublicData) -> Self { + Self { + evidence: evidence, + } + } +} + +impl SubCircuit for TaikoPiCircuit { + type Config = TaikoPiCircuitConfig; + + fn unusable_rows() -> usize { + // No column queried at more than 3 distinct rotations, so returns 6 as + // minimum unusable rows. + PublicData::::default().total_len() + 3 + } + + fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { + // TODO(Cecilia): what is the first field? + (0, PublicData::new(block).total_len()) + } + + fn new_from_block(block: &witness::Block) -> Self { + TaikoPiCircuit::new(PublicData::new(block)) + } + + /// Compute the public inputs for this circuit. + fn instance(&self) -> Vec> { + vec![ self.evidence.keccak_hi_low().to_vec()] + } + + /// Make the assignments to the PiCircuit + fn synthesize_sub( + &self, + config: &Self::Config, + _challenges: &Challenges>, + layouter: &mut impl Layouter, + ) -> Result<(), Error> { + config.byte_table.load(layouter)?; + config.assign(layouter, &self.evidence) + } +} + +#[cfg(any(feature = "test", test))] +impl Circuit for TaikoPiCircuit { + type Config = (TaikoPiCircuitConfig, Challenges); + type FloorPlanner = SimpleFloorPlanner; + type Params = PublicData; + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn params(&self) -> Self::Params { + self.evidence.clone() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + Self::configure_with_params(meta, PublicData::default()) + } + + fn configure_with_params( + meta: &mut ConstraintSystem, + params: Self::Params, + ) -> Self::Config { + let block_table = BlockTable::construct(meta); + let keccak_table = KeccakTable::construct(meta); + let byte_table = ByteTable::construct(meta); + let challenges = Challenges::construct(meta); + let challenge_exprs = challenges.exprs(meta); + ( + TaikoPiCircuitConfig::new( + meta, + TaikoPiCircuitConfigArgs { + evidence: params, + block_table, + keccak_table, + byte_table, + challenges: challenge_exprs, + } + ), + challenges + ) + } + + fn synthesize( + &self, + (config, challenges): Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let challenges = challenges.values(&mut layouter); + let evidance = self.params(); + let randomness = challenges.evm_word(); + // assign block table + config + .block_table + .load(&mut layouter, &evidance.block_context, randomness)?; + // assign keccak table + config + .keccak_table + .dev_load(&mut layouter, vec![&evidance.encode_raw()], &challenges)?; + config.byte_table.load(&mut layouter)?; + + self.synthesize_sub(&config, &challenges, &mut layouter) + } + +} + +#[cfg(test)] +mod taiko_pi_circuit_test { + + use std::vec; + + use super::*; + + use eth_types::ToScalar; + use halo2_proofs::{ + dev::{MockProver, VerifyFailure}, + halo2curves::bn256::Fr, + }; + use lazy_static::lazy_static; + use pretty_assertions::assert_eq; + + lazy_static! { + static ref OMMERS_HASH: H256 = H256::from_slice( + &hex::decode("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347") + .unwrap(), + ); + } + + fn run( + k: u32, + evidence: PublicData, + pi: Option>>, + ) -> Result<(), Vec> { + let circuit = TaikoPiCircuit::new(evidence); + let public_inputs = pi.unwrap_or_else(|| circuit.instance()); + let prover = match MockProver::run(k, &circuit, public_inputs) { + Ok(prover) => prover, + Err(e) => panic!("{:#?}", e), + }; + prover.verify() + } + + fn mock_public_data() -> PublicData { + let A = OMMERS_HASH.clone()/* H256::default() */; + let mut evidence = PublicData::default(); + evidence.set_field(0, A.to_fixed_bytes().to_vec()); + evidence.block_context.number = 300.into(); + evidence.block_context.block_hash = A.to_word(); + evidence + } + + #[test] + fn test_default_pi() { + let evidence = mock_public_data(); + + let k = 17; + assert_eq!(run::(k, evidence, None), Ok(())); + } + + #[test] + fn test(){ + println!("test"); + let mut evidence = PublicData::::default(); + let data = hex::decode("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef").unwrap(); + evidence.set_field(0, data.clone()); + // evidence.set_field(1, vec![0u8; 32]); + // evidence.set_field(2, data.clone()); + // evidence.set_field(3, vec![0u8; 32]); + // evidence.set_field(4, vec![0u8; 32]); + // evidence.set_field(5, vec![0u8; 20]); + + // evidence.parent_hash = Token::FixedBytes(vec![0u8; 32]); + // evidence.block_hash = Token::FixedBytes(data); + // evidence.signal_root = Token::FixedBytes(vec![0u8; 32]); + // evidence.graffiti = Token::FixedBytes(vec![0u8; 32]); + // evidence.prover = Token::Address([0x22u8; 20].into()); + let encode_raw = evidence.encode_raw(); + println!("abi.encode {:?}\nkeccak {:?}\nhi-lo {:?}", encode_raw.clone(), keccak256(encode_raw), evidence.keccak_hi_low()); + } + +} + + diff --git a/zkevm-circuits/src/witness/block.rs b/zkevm-circuits/src/witness/block.rs index 0df8c41d82..af1548d1fd 100644 --- a/zkevm-circuits/src/witness/block.rs +++ b/zkevm-circuits/src/witness/block.rs @@ -144,7 +144,7 @@ pub struct BlockContext { impl BlockContext { /// Assignments for block table pub fn table_assignments(&self, randomness: Value) -> Vec<[Value; 3]> { - // let randomness = Value::known(F::from(2u64)); + // let randomness: Value = Value::known(F::from(0u64)); println!("block table {:?} (le_bytes) {:?} evm_word = {:?}", self.number, self.block_hash.to_le_bytes(), randomness); [ vec![ @@ -189,6 +189,7 @@ impl BlockContext { [ Value::known(F::from(BlockContextFieldTag::BlockHash as u64)), Value::known(self.number.to_scalar().unwrap()), + // Value::known(F::ZERO), randomness .map(|randomness| rlc::value(&self.block_hash.to_le_bytes(), randomness)), ], From 4ac0a3f5daa87f6c4ab95ff1f1fb29230414eb16 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Mon, 4 Sep 2023 02:33:18 +0800 Subject: [PATCH 25/43] block acc cell --- zkevm-circuits/src/taiko_pi_circuit__.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/zkevm-circuits/src/taiko_pi_circuit__.rs b/zkevm-circuits/src/taiko_pi_circuit__.rs index 8b6306f8f7..5ea32dfd02 100644 --- a/zkevm-circuits/src/taiko_pi_circuit__.rs +++ b/zkevm-circuits/src/taiko_pi_circuit__.rs @@ -16,7 +16,7 @@ use halo2_proofs::plonk::Error; use core::result::Result; use crate::circuit_tools::cached_region::CachedRegion; use crate::circuit_tools::cell_manager::{Cell, CellType, CellManager, CellColumn}; -use crate::circuit_tools::constraint_builder::{ConstraintBuilder, TO_FIX, RLCable}; +use crate::circuit_tools::constraint_builder::{ConstraintBuilder, TO_FIX, RLCable, ExprVec}; use crate::evm_circuit::table::Table; use crate::evm_circuit::util::rlc; use crate::util::{Challenges, SubCircuitConfig, SubCircuit}; @@ -164,6 +164,12 @@ impl PublicData { .collect() } + fn assignment_acc(&self, idx: usize, r: Value) -> F { + let mut rand = F::ZERO; + r.map(|r| rand = r); + rlc::value(self.encode_field(idx).iter().rev(), rand) + } + fn keccak_hi_low(&self) -> [F; 2] { let keccaked_pi = keccak256(self.encode_raw()); [ @@ -201,7 +207,7 @@ impl PublicData { pub struct TaikoPiCircuitConfig { q_enable: Selector, public_input: Column, // equality - block_hash: (Cell, FieldGadget), + block_hash: (Cell, FieldGadget, Cell), block_table: BlockTable, keccak_table: KeccakTable, @@ -262,13 +268,15 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { let block_hash =( cb.query_one(PiCellType::Storage1), FieldGadget::config(&mut cb, evidence.field_len(0)), + cb.query_one(PiCellType::Storage2) ); meta.create_gate( "PI acc constraints", |meta| { circuit!([meta, cb], { - for (n, b) in [/* parent_hash.clone() , */ block_hash.clone()] { + for (n, b, acc) in [/* parent_hash.clone() , */ block_hash.clone()] { + require!(acc.expr() => b.acc(evm_word.expr())); require!( ( BlockContextFieldTag::BlockHash.expr(), @@ -314,9 +322,11 @@ impl TaikoPiCircuitConfig { pub(crate) fn assign( &self, layouter: &mut impl Layouter, - // challenge: &Challenges>, + challenge: &Challenges>, evidence: &PublicData, ) -> Result<(), Error> { + let evm_word = challenge.evm_word(); + let keccak_r = challenge.keccak_input(); let pi_cells = layouter.assign_region( || "Pi", |mut region| { @@ -339,6 +349,7 @@ impl TaikoPiCircuitConfig { println!("evidence.block_context.number: {:?}\n", evidence.block_context.number); assign!(region, self.block_hash.0, 0 => (evidence.block_context.number).as_u64().scalar()); + assign!(region, self.block_hash.2, 0 => evidence.assignment_acc(0, evm_word)); Ok(()) }); @@ -388,11 +399,11 @@ impl SubCircuit for TaikoPiCircuit { fn synthesize_sub( &self, config: &Self::Config, - _challenges: &Challenges>, + challenges: &Challenges>, layouter: &mut impl Layouter, ) -> Result<(), Error> { config.byte_table.load(layouter)?; - config.assign(layouter, &self.evidence) + config.assign(layouter, challenges, &self.evidence) } } From bbfd3004d7d0e1edbd47e9e5f846ecde49b5ad37 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Mon, 4 Sep 2023 03:01:14 +0800 Subject: [PATCH 26/43] acc --- zkevm-circuits/src/taiko_pi_circuit__.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zkevm-circuits/src/taiko_pi_circuit__.rs b/zkevm-circuits/src/taiko_pi_circuit__.rs index 5ea32dfd02..0e1d9174ac 100644 --- a/zkevm-circuits/src/taiko_pi_circuit__.rs +++ b/zkevm-circuits/src/taiko_pi_circuit__.rs @@ -281,7 +281,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { ( BlockContextFieldTag::BlockHash.expr(), n.expr(), - b.acc(evm_word.expr()) + acc.expr() ) => @PiCellType::Lookup(Table::Block), (TO_FIX) ); println!( From 530a5533e7a8ce57246f5a679144767a43ad8948 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Mon, 4 Sep 2023 03:24:30 +0800 Subject: [PATCH 27/43] siganl root --- zkevm-circuits/src/taiko_pi_circuit__.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/zkevm-circuits/src/taiko_pi_circuit__.rs b/zkevm-circuits/src/taiko_pi_circuit__.rs index 0e1d9174ac..4624096d23 100644 --- a/zkevm-circuits/src/taiko_pi_circuit__.rs +++ b/zkevm-circuits/src/taiko_pi_circuit__.rs @@ -120,9 +120,11 @@ impl Default for PublicData { impl PublicData { fn new(block: &witness::Block) -> Self { let block_hash = Token::FixedBytes(block.protocol_instance.block_hash.to_word().to_be_bytes().to_vec()); + let signal_root = Token::FixedBytes(block.protocol_instance.signal_root.to_word().to_be_bytes().to_vec()); Self { evidence: Token::FixedArray(vec![ block_hash, + signal_root, ]), block_context: block.context.clone(), _phantom: PhantomData @@ -208,7 +210,7 @@ pub struct TaikoPiCircuitConfig { q_enable: Selector, public_input: Column, // equality block_hash: (Cell, FieldGadget, Cell), - + signal_root: FieldGadget, block_table: BlockTable, keccak_table: KeccakTable, byte_table: ByteTable, @@ -270,6 +272,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { FieldGadget::config(&mut cb, evidence.field_len(0)), cb.query_one(PiCellType::Storage2) ); + let signal_root = FieldGadget::config(&mut cb, evidence.field_len(1)); meta.create_gate( "PI acc constraints", @@ -310,6 +313,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { q_enable, public_input, block_hash, + signal_root, block_table, keccak_table, byte_table, @@ -339,6 +343,7 @@ impl TaikoPiCircuitConfig { let mut idx = 0; [ &self.block_hash.1, + &self.signal_root, ].iter().for_each(|gadget| { println!("assignment {:?}: {:?}, {:?}", idx, offset, evidence.encode_field(idx)); gadget.assign(&mut region, offset, &evidence.assignment(idx)) From 857bcf68046d020bc0de5397405ec63291d34eca Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Mon, 4 Sep 2023 03:58:27 +0800 Subject: [PATCH 28/43] fuckin offset is dumb --- zkevm-circuits/src/table/keccak_table.rs | 1 - zkevm-circuits/src/taiko_pi_circuit__.rs | 20 ++++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/zkevm-circuits/src/table/keccak_table.rs b/zkevm-circuits/src/table/keccak_table.rs index a70dae78f4..14b33d6ecf 100644 --- a/zkevm-circuits/src/table/keccak_table.rs +++ b/zkevm-circuits/src/table/keccak_table.rs @@ -65,7 +65,6 @@ impl KeccakTable { challenge, ) }); - println!("keccak table input rlc {:?} len {:?} output rlc {:?}", input, input_len, output); vec![[ Value::known(F::ONE), input_rlc, diff --git a/zkevm-circuits/src/taiko_pi_circuit__.rs b/zkevm-circuits/src/taiko_pi_circuit__.rs index 4624096d23..9348262b0b 100644 --- a/zkevm-circuits/src/taiko_pi_circuit__.rs +++ b/zkevm-circuits/src/taiko_pi_circuit__.rs @@ -119,10 +119,12 @@ impl Default for PublicData { impl PublicData { fn new(block: &witness::Block) -> Self { + let meta_hash = Token::FixedBytes(block.protocol_instance.meta_hash.hash().to_word().to_be_bytes().to_vec()); let block_hash = Token::FixedBytes(block.protocol_instance.block_hash.to_word().to_be_bytes().to_vec()); let signal_root = Token::FixedBytes(block.protocol_instance.signal_root.to_word().to_be_bytes().to_vec()); Self { evidence: Token::FixedArray(vec![ + meta_hash, block_hash, signal_root, ]), @@ -209,6 +211,7 @@ impl PublicData { pub struct TaikoPiCircuitConfig { q_enable: Selector, public_input: Column, // equality + meta_hash: FieldGadget, block_hash: (Cell, FieldGadget, Cell), signal_root: FieldGadget, block_table: BlockTable, @@ -267,12 +270,13 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { ); let q_enable = meta.complex_selector(); let public_input = meta.instance_column(); + let meta_hash = FieldGadget::config(&mut cb, evidence.field_len(0)); let block_hash =( cb.query_one(PiCellType::Storage1), - FieldGadget::config(&mut cb, evidence.field_len(0)), + FieldGadget::config(&mut cb, evidence.field_len(1)), cb.query_one(PiCellType::Storage2) ); - let signal_root = FieldGadget::config(&mut cb, evidence.field_len(1)); + let signal_root = FieldGadget::config(&mut cb, evidence.field_len(2)); meta.create_gate( "PI acc constraints", @@ -312,6 +316,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { Self { q_enable, public_input, + meta_hash, block_hash, signal_root, block_table, @@ -339,22 +344,21 @@ impl TaikoPiCircuitConfig { region.annotate_columns(&self.annotation_configs); let mut acc = F::ZERO; - let mut offset = 0; let mut idx = 0; [ + &self.meta_hash, &self.block_hash.1, &self.signal_root, ].iter().for_each(|gadget| { - println!("assignment {:?}: {:?}, {:?}", idx, offset, evidence.encode_field(idx)); - gadget.assign(&mut region, offset, &evidence.assignment(idx)) + println!("assignment {:?}:\n{:?}", idx, evidence.encode_field(idx)); + gadget.assign(&mut region, 0, &evidence.assignment(idx)) .expect(&format!("FieldGadget assignment failed at {:?}", idx)); - offset += evidence.field_len(idx); idx += 1; }); println!("evidence.block_context.number: {:?}\n", evidence.block_context.number); assign!(region, self.block_hash.0, 0 => (evidence.block_context.number).as_u64().scalar()); - assign!(region, self.block_hash.2, 0 => evidence.assignment_acc(0, evm_word)); + assign!(region, self.block_hash.2, 0 => evidence.assignment_acc(1, evm_word)); Ok(()) }); @@ -516,7 +520,7 @@ mod taiko_pi_circuit_test { fn mock_public_data() -> PublicData { let A = OMMERS_HASH.clone()/* H256::default() */; let mut evidence = PublicData::default(); - evidence.set_field(0, A.to_fixed_bytes().to_vec()); + evidence.set_field(1, A.to_fixed_bytes().to_vec()); evidence.block_context.number = 300.into(); evidence.block_context.block_hash = A.to_word(); evidence From 791ecfffbcd7e8668e821a5ff64387f21fd639b4 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Mon, 4 Sep 2023 04:14:38 +0800 Subject: [PATCH 29/43] parent_hash --- zkevm-circuits/src/taiko_pi_circuit__.rs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/zkevm-circuits/src/taiko_pi_circuit__.rs b/zkevm-circuits/src/taiko_pi_circuit__.rs index 9348262b0b..3ac23506f5 100644 --- a/zkevm-circuits/src/taiko_pi_circuit__.rs +++ b/zkevm-circuits/src/taiko_pi_circuit__.rs @@ -120,11 +120,13 @@ impl Default for PublicData { impl PublicData { fn new(block: &witness::Block) -> Self { let meta_hash = Token::FixedBytes(block.protocol_instance.meta_hash.hash().to_word().to_be_bytes().to_vec()); + let parent_hash = Token::FixedBytes(block.protocol_instance.parent_hash.to_word().to_be_bytes().to_vec()); let block_hash = Token::FixedBytes(block.protocol_instance.block_hash.to_word().to_be_bytes().to_vec()); let signal_root = Token::FixedBytes(block.protocol_instance.signal_root.to_word().to_be_bytes().to_vec()); Self { evidence: Token::FixedArray(vec![ meta_hash, + parent_hash, block_hash, signal_root, ]), @@ -212,6 +214,7 @@ pub struct TaikoPiCircuitConfig { q_enable: Selector, public_input: Column, // equality meta_hash: FieldGadget, + parent_hash: (Cell, FieldGadget, Cell), block_hash: (Cell, FieldGadget, Cell), signal_root: FieldGadget, block_table: BlockTable, @@ -271,18 +274,23 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { let q_enable = meta.complex_selector(); let public_input = meta.instance_column(); let meta_hash = FieldGadget::config(&mut cb, evidence.field_len(0)); - let block_hash =( + let parent_hash =( cb.query_one(PiCellType::Storage1), FieldGadget::config(&mut cb, evidence.field_len(1)), cb.query_one(PiCellType::Storage2) ); - let signal_root = FieldGadget::config(&mut cb, evidence.field_len(2)); + let block_hash =( + cb.query_one(PiCellType::Storage1), + FieldGadget::config(&mut cb, evidence.field_len(2)), + cb.query_one(PiCellType::Storage2) + ); + let signal_root = FieldGadget::config(&mut cb, evidence.field_len(3)); meta.create_gate( "PI acc constraints", |meta| { circuit!([meta, cb], { - for (n, b, acc) in [/* parent_hash.clone() , */ block_hash.clone()] { + for (n, b, acc) in [parent_hash.clone() , block_hash.clone()] { require!(acc.expr() => b.acc(evm_word.expr())); require!( ( @@ -317,6 +325,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { q_enable, public_input, meta_hash, + parent_hash, block_hash, signal_root, block_table, @@ -347,6 +356,7 @@ impl TaikoPiCircuitConfig { let mut idx = 0; [ &self.meta_hash, + &self.parent_hash.1, &self.block_hash.1, &self.signal_root, ].iter().for_each(|gadget| { @@ -357,8 +367,10 @@ impl TaikoPiCircuitConfig { }); println!("evidence.block_context.number: {:?}\n", evidence.block_context.number); + assign!(region, self.parent_hash.0, 0 => (evidence.block_context.number - 1).as_u64().scalar()); + assign!(region, self.parent_hash.2, 0 => evidence.assignment_acc(1, evm_word)); assign!(region, self.block_hash.0, 0 => (evidence.block_context.number).as_u64().scalar()); - assign!(region, self.block_hash.2, 0 => evidence.assignment_acc(1, evm_word)); + assign!(region, self.block_hash.2, 0 => evidence.assignment_acc(2, evm_word)); Ok(()) }); @@ -521,8 +533,10 @@ mod taiko_pi_circuit_test { let A = OMMERS_HASH.clone()/* H256::default() */; let mut evidence = PublicData::default(); evidence.set_field(1, A.to_fixed_bytes().to_vec()); + evidence.set_field(2, A.to_fixed_bytes().to_vec()); evidence.block_context.number = 300.into(); evidence.block_context.block_hash = A.to_word(); + evidence.block_context.history_hashes = vec![A.to_word()]; evidence } From 934773be4f5164f556bd638b774ec5b06f1841c8 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Mon, 4 Sep 2023 04:32:47 +0800 Subject: [PATCH 30/43] last two --- zkevm-circuits/src/taiko_pi_circuit__.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/zkevm-circuits/src/taiko_pi_circuit__.rs b/zkevm-circuits/src/taiko_pi_circuit__.rs index 3ac23506f5..8c2ea1f4f8 100644 --- a/zkevm-circuits/src/taiko_pi_circuit__.rs +++ b/zkevm-circuits/src/taiko_pi_circuit__.rs @@ -123,12 +123,16 @@ impl PublicData { let parent_hash = Token::FixedBytes(block.protocol_instance.parent_hash.to_word().to_be_bytes().to_vec()); let block_hash = Token::FixedBytes(block.protocol_instance.block_hash.to_word().to_be_bytes().to_vec()); let signal_root = Token::FixedBytes(block.protocol_instance.signal_root.to_word().to_be_bytes().to_vec()); + let graffiti = Token::FixedBytes(block.protocol_instance.graffiti.to_word().to_be_bytes().to_vec()); + let prover = Token::Address(block.protocol_instance.prover); Self { evidence: Token::FixedArray(vec![ meta_hash, parent_hash, block_hash, signal_root, + graffiti, + prover, ]), block_context: block.context.clone(), _phantom: PhantomData @@ -217,6 +221,8 @@ pub struct TaikoPiCircuitConfig { parent_hash: (Cell, FieldGadget, Cell), block_hash: (Cell, FieldGadget, Cell), signal_root: FieldGadget, + graffiti: FieldGadget, + prover: FieldGadget, block_table: BlockTable, keccak_table: KeccakTable, byte_table: ByteTable, @@ -285,6 +291,8 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { cb.query_one(PiCellType::Storage2) ); let signal_root = FieldGadget::config(&mut cb, evidence.field_len(3)); + let graffiti = FieldGadget::config(&mut cb, evidence.field_len(4)); + let prover = FieldGadget::config(&mut cb, evidence.field_len(5)); meta.create_gate( "PI acc constraints", @@ -328,6 +336,8 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { parent_hash, block_hash, signal_root, + graffiti, + prover, block_table, keccak_table, byte_table, @@ -359,6 +369,8 @@ impl TaikoPiCircuitConfig { &self.parent_hash.1, &self.block_hash.1, &self.signal_root, + &self.graffiti, + &self.prover, ].iter().for_each(|gadget| { println!("assignment {:?}:\n{:?}", idx, evidence.encode_field(idx)); gadget.assign(&mut region, 0, &evidence.assignment(idx)) From 01ff54be81657205fd9212d323711b0085b5dea6 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Mon, 4 Sep 2023 05:15:56 +0800 Subject: [PATCH 31/43] hi lo no instance --- zkevm-circuits/src/taiko_pi_circuit__.rs | 61 +++++++++++++++++++----- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/zkevm-circuits/src/taiko_pi_circuit__.rs b/zkevm-circuits/src/taiko_pi_circuit__.rs index 8c2ea1f4f8..18a5b9094f 100644 --- a/zkevm-circuits/src/taiko_pi_circuit__.rs +++ b/zkevm-circuits/src/taiko_pi_circuit__.rs @@ -6,6 +6,7 @@ use ethers_core::abi::*; use ethers_core::abi::FixedBytes; use ethers_core::utils::keccak256; use halo2_proofs::circuit::{Value, Layouter, SimpleFloorPlanner, AssignedCell}; +use halo2_proofs::poly::Rotation; use itertools::Itertools; use std::convert::TryInto; use std::marker::PhantomData; @@ -202,6 +203,13 @@ impl PublicData { keccak256(self.encode_raw()).to_vec() } + fn keccak_assignment(&self) -> Vec { + self.keccak() + .iter() + .map(|b| F::from(*b as u64)) + .collect() + } + fn total_len(&self) -> usize { self.encode_raw().len() } @@ -216,16 +224,20 @@ impl PublicData { #[derive(Clone, Debug)] pub struct TaikoPiCircuitConfig { q_enable: Selector, - public_input: Column, // equality + keccak_instance: Column, // equality + meta_hash: FieldGadget, parent_hash: (Cell, FieldGadget, Cell), block_hash: (Cell, FieldGadget, Cell), signal_root: FieldGadget, graffiti: FieldGadget, prover: FieldGadget, + keccak_bytes: FieldGadget, + keccak_hi_lo: [Cell; 2], + block_table: BlockTable, keccak_table: KeccakTable, - byte_table: ByteTable, + byte_table: ByteTable, annotation_configs: Vec>, } @@ -278,7 +290,9 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { ] ); let q_enable = meta.complex_selector(); - let public_input = meta.instance_column(); + let keccak_instance = meta.instance_column(); + meta.enable_equality(keccak_instance); + let meta_hash = FieldGadget::config(&mut cb, evidence.field_len(0)); let parent_hash =( cb.query_one(PiCellType::Storage1), @@ -293,7 +307,8 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { let signal_root = FieldGadget::config(&mut cb, evidence.field_len(3)); let graffiti = FieldGadget::config(&mut cb, evidence.field_len(4)); let prover = FieldGadget::config(&mut cb, evidence.field_len(5)); - + let keccak_bytes = FieldGadget::config(&mut cb, PADDING_LEN); + let keccak_hi_lo = [cb.query_one(PiCellType::Storage1), cb.query_one(PiCellType::Storage1)]; meta.create_gate( "PI acc constraints", |meta| { @@ -314,6 +329,12 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { b.acc(evm_word.expr()).identifier() ); } + let hi_lo = keccak_bytes.hi_low_field(); + keccak_hi_lo.iter().zip(hi_lo.iter()).for_each(|(cell, epxr)| { + require!(cell.expr() => epxr); + cb.enable_equality(cell.column()); + }); + }); cb.build_constraints(Some(meta.query_selector(q_enable))) } @@ -331,13 +352,15 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { let annotation_configs = cm.columns().to_vec(); Self { q_enable, - public_input, + keccak_instance, meta_hash, parent_hash, block_hash, signal_root, graffiti, prover, + keccak_bytes, + keccak_hi_lo, block_table, keccak_table, byte_table, @@ -355,13 +378,20 @@ impl TaikoPiCircuitConfig { ) -> Result<(), Error> { let evm_word = challenge.evm_word(); let keccak_r = challenge.keccak_input(); - let pi_cells = layouter.assign_region( + let mut hi_lo_cells = Vec::new(); + layouter.assign_region( || "Pi", |mut region| { self.q_enable.enable(&mut region, 0)?; let mut region = CachedRegion::new(&mut region); region.annotate_columns(&self.annotation_configs); + println!("evidence.block_context.number: {:?}\n", evidence.block_context.number); + assign!(region, self.parent_hash.0, 0 => (evidence.block_context.number - 1).as_u64().scalar()); + assign!(region, self.parent_hash.2, 0 => evidence.assignment_acc(1, evm_word)); + assign!(region, self.block_hash.0, 0 => (evidence.block_context.number).as_u64().scalar()); + assign!(region, self.block_hash.2, 0 => evidence.assignment_acc(2, evm_word)); + let mut acc = F::ZERO; let mut idx = 0; [ @@ -377,15 +407,20 @@ impl TaikoPiCircuitConfig { .expect(&format!("FieldGadget assignment failed at {:?}", idx)); idx += 1; }); - - println!("evidence.block_context.number: {:?}\n", evidence.block_context.number); - assign!(region, self.parent_hash.0, 0 => (evidence.block_context.number - 1).as_u64().scalar()); - assign!(region, self.parent_hash.2, 0 => evidence.assignment_acc(1, evm_word)); - assign!(region, self.block_hash.0, 0 => (evidence.block_context.number).as_u64().scalar()); - assign!(region, self.block_hash.2, 0 => evidence.assignment_acc(2, evm_word)); + self.keccak_bytes.assign(&mut region, 0, &evidence.keccak_assignment()) + .expect("Keccak bytes assignment failed"); + self.keccak_hi_lo + .iter() + .zip(evidence.keccak_hi_low().iter()) + .for_each(|(cell, val)| { + hi_lo_cells.push(assign!(region, cell, 0 => *val).unwrap()); + }); Ok(()) - }); + })?; + // for (i, cell) in hi_lo_cells.iter().enumerate() { + // layouter.constrain_instance(cell.cell(), self.keccak_instance, i)?; + // } Ok(()) } } From 925a263e127d8530b486a1ae7dc7a2f5f1e13106 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Mon, 4 Sep 2023 05:30:30 +0800 Subject: [PATCH 32/43] hi lo instance constraint --- zkevm-circuits/src/taiko_pi_circuit__.rs | 26 +++++++++++------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/zkevm-circuits/src/taiko_pi_circuit__.rs b/zkevm-circuits/src/taiko_pi_circuit__.rs index 18a5b9094f..ddcdfca658 100644 --- a/zkevm-circuits/src/taiko_pi_circuit__.rs +++ b/zkevm-circuits/src/taiko_pi_circuit__.rs @@ -378,8 +378,7 @@ impl TaikoPiCircuitConfig { ) -> Result<(), Error> { let evm_word = challenge.evm_word(); let keccak_r = challenge.keccak_input(); - let mut hi_lo_cells = Vec::new(); - layouter.assign_region( + let mut hi_lo_cells = layouter.assign_region( || "Pi", |mut region| { self.q_enable.enable(&mut region, 0)?; @@ -409,18 +408,16 @@ impl TaikoPiCircuitConfig { }); self.keccak_bytes.assign(&mut region, 0, &evidence.keccak_assignment()) .expect("Keccak bytes assignment failed"); - self.keccak_hi_lo - .iter() - .zip(evidence.keccak_hi_low().iter()) - .for_each(|(cell, val)| { - hi_lo_cells.push(assign!(region, cell, 0 => *val).unwrap()); - }); + let hi_low_assignment = evidence.keccak_hi_low(); + let hi = assign!(region, self.keccak_hi_lo[0], 0 => hi_low_assignment[0])?; + let lo = assign!(region, self.keccak_hi_lo[1], 0 => hi_low_assignment[1])?; - Ok(()) + Ok([hi, lo]) })?; - // for (i, cell) in hi_lo_cells.iter().enumerate() { - // layouter.constrain_instance(cell.cell(), self.keccak_instance, i)?; - // } + for (i, cell) in hi_lo_cells.iter().enumerate() { + layouter.constrain_instance(cell.cell(), self.keccak_instance, i)?; + println!("cell: {:?}, keccak_instance: {:?}, i: {:?}", cell.cell(), self.keccak_instance, i); + } Ok(()) } } @@ -568,8 +565,9 @@ mod taiko_pi_circuit_test { pi: Option>>, ) -> Result<(), Vec> { let circuit = TaikoPiCircuit::new(evidence); - let public_inputs = pi.unwrap_or_else(|| circuit.instance()); - let prover = match MockProver::run(k, &circuit, public_inputs) { + let keccak_instance = pi.unwrap_or_else(|| circuit.instance()); + println!("run instance: {:?}", keccak_instance); + let prover = match MockProver::run(k, &circuit, keccak_instance) { Ok(prover) => prover, Err(e) => panic!("{:#?}", e), }; From 760f64251ff4727400ddf403399efe2ae85715c6 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Mon, 4 Sep 2023 05:41:40 +0800 Subject: [PATCH 33/43] doen --- zkevm-circuits/src/taiko_pi_circuit__.rs | 38 +++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/zkevm-circuits/src/taiko_pi_circuit__.rs b/zkevm-circuits/src/taiko_pi_circuit__.rs index ddcdfca658..1802c5d8e6 100644 --- a/zkevm-circuits/src/taiko_pi_circuit__.rs +++ b/zkevm-circuits/src/taiko_pi_circuit__.rs @@ -168,6 +168,16 @@ impl PublicData { encode(&[field]) } + fn total_acc(&self, r: Value) -> F { + let mut rand = F::ZERO; + r.map(|r| rand = r); + self.encode_raw() + .iter() + .fold(F::ZERO, |acc: F, byte| { + acc * rand + F::from(*byte as u64) + }) + } + fn assignment(&self, idx: usize) -> Vec { self.encode_field(idx) .iter() @@ -232,6 +242,8 @@ pub struct TaikoPiCircuitConfig { signal_root: FieldGadget, graffiti: FieldGadget, prover: FieldGadget, + + total_acc: Cell, keccak_bytes: FieldGadget, keccak_hi_lo: [Cell; 2], @@ -307,6 +319,8 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { let signal_root = FieldGadget::config(&mut cb, evidence.field_len(3)); let graffiti = FieldGadget::config(&mut cb, evidence.field_len(4)); let prover = FieldGadget::config(&mut cb, evidence.field_len(5)); + + let total_acc = cb.query_one(PiCellType::Storage2); let keccak_bytes = FieldGadget::config(&mut cb, PADDING_LEN); let keccak_hi_lo = [cb.query_one(PiCellType::Storage1), cb.query_one(PiCellType::Storage1)]; meta.create_gate( @@ -329,12 +343,32 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { b.acc(evm_word.expr()).identifier() ); } + let acc_val = [ + meta_hash.clone(), + parent_hash.1.clone(), + block_hash.1.clone(), + signal_root.clone(), + graffiti.clone(), + prover.clone(), + ].iter().fold(0.expr(), |acc, gadget| { + let mult = (0..gadget.len).fold(1.expr(), |acc, _| acc * keccak_r.expr()); + acc * mult + gadget.acc(keccak_r.expr()) + }); + require!(total_acc.expr() => acc_val); + require!( + ( + 1.expr(), + total_acc.expr(), + evidence.total_len().expr(), + keccak_bytes.acc(evm_word.expr()) + ) + => @PiCellType::Lookup(Table::Keccak), (TO_FIX) + ); let hi_lo = keccak_bytes.hi_low_field(); keccak_hi_lo.iter().zip(hi_lo.iter()).for_each(|(cell, epxr)| { require!(cell.expr() => epxr); cb.enable_equality(cell.column()); }); - }); cb.build_constraints(Some(meta.query_selector(q_enable))) } @@ -359,6 +393,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { signal_root, graffiti, prover, + total_acc, keccak_bytes, keccak_hi_lo, block_table, @@ -408,6 +443,7 @@ impl TaikoPiCircuitConfig { }); self.keccak_bytes.assign(&mut region, 0, &evidence.keccak_assignment()) .expect("Keccak bytes assignment failed"); + assign!(region, self.total_acc, 0 => evidence.total_acc(keccak_r))?; let hi_low_assignment = evidence.keccak_hi_low(); let hi = assign!(region, self.keccak_hi_lo[0], 0 => hi_low_assignment[0])?; let lo = assign!(region, self.keccak_hi_lo[1], 0 => hi_low_assignment[1])?; From caae2eaa0456f5f09640e72bb96e24da0639b5ce Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Mon, 4 Sep 2023 05:50:07 +0800 Subject: [PATCH 34/43] const --- zkevm-circuits/src/taiko_pi_circuit__.rs | 48 +++++++++++++----------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/zkevm-circuits/src/taiko_pi_circuit__.rs b/zkevm-circuits/src/taiko_pi_circuit__.rs index 1802c5d8e6..211af102eb 100644 --- a/zkevm-circuits/src/taiko_pi_circuit__.rs +++ b/zkevm-circuits/src/taiko_pi_circuit__.rs @@ -27,8 +27,14 @@ use crate::table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, Kecc const BYTE_POW_BASE: u64 = 1 << 8; const PADDING_LEN: usize = 32; - - +const META_HASH: usize = 0; +const PARENT_HASH: usize = 1; +const BLOCK_HASH: usize = 2; +const SIGNAL_ROOT: usize = 3; +const GRAFFITI: usize = 4; +const PROVER: usize = 5; +const S1: PiCellType = PiCellType::Storage1; +const S2: PiCellType = PiCellType::Storage2; /// #[derive(Debug, Clone, Default)] pub struct FieldGadget { @@ -105,6 +111,7 @@ impl Default for PiCellType { + #[derive(Debug, Clone)] pub struct PublicData { evidence: Token, @@ -305,24 +312,24 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { let keccak_instance = meta.instance_column(); meta.enable_equality(keccak_instance); - let meta_hash = FieldGadget::config(&mut cb, evidence.field_len(0)); + let meta_hash = FieldGadget::config(&mut cb, evidence.field_len(META_HASH)); let parent_hash =( - cb.query_one(PiCellType::Storage1), - FieldGadget::config(&mut cb, evidence.field_len(1)), - cb.query_one(PiCellType::Storage2) + cb.query_one(S1), + FieldGadget::config(&mut cb, evidence.field_len(PARENT_HASH)), + cb.query_one(S2) ); let block_hash =( - cb.query_one(PiCellType::Storage1), - FieldGadget::config(&mut cb, evidence.field_len(2)), - cb.query_one(PiCellType::Storage2) + cb.query_one(S1), + FieldGadget::config(&mut cb, evidence.field_len(BLOCK_HASH)), + cb.query_one(S2) ); - let signal_root = FieldGadget::config(&mut cb, evidence.field_len(3)); - let graffiti = FieldGadget::config(&mut cb, evidence.field_len(4)); - let prover = FieldGadget::config(&mut cb, evidence.field_len(5)); + let signal_root = FieldGadget::config(&mut cb, evidence.field_len(SIGNAL_ROOT)); + let graffiti = FieldGadget::config(&mut cb, evidence.field_len(GRAFFITI)); + let prover = FieldGadget::config(&mut cb, evidence.field_len(PROVER)); - let total_acc = cb.query_one(PiCellType::Storage2); + let total_acc = cb.query_one(S2); let keccak_bytes = FieldGadget::config(&mut cb, PADDING_LEN); - let keccak_hi_lo = [cb.query_one(PiCellType::Storage1), cb.query_one(PiCellType::Storage1)]; + let keccak_hi_lo = [cb.query_one(S1), cb.query_one(S1)]; meta.create_gate( "PI acc constraints", |meta| { @@ -422,9 +429,9 @@ impl TaikoPiCircuitConfig { println!("evidence.block_context.number: {:?}\n", evidence.block_context.number); assign!(region, self.parent_hash.0, 0 => (evidence.block_context.number - 1).as_u64().scalar()); - assign!(region, self.parent_hash.2, 0 => evidence.assignment_acc(1, evm_word)); + assign!(region, self.parent_hash.2, 0 => evidence.assignment_acc(PARENT_HASH, evm_word)); assign!(region, self.block_hash.0, 0 => (evidence.block_context.number).as_u64().scalar()); - assign!(region, self.block_hash.2, 0 => evidence.assignment_acc(2, evm_word)); + assign!(region, self.block_hash.2, 0 => evidence.assignment_acc(BLOCK_HASH, evm_word)); let mut acc = F::ZERO; let mut idx = 0; @@ -611,13 +618,12 @@ mod taiko_pi_circuit_test { } fn mock_public_data() -> PublicData { - let A = OMMERS_HASH.clone()/* H256::default() */; let mut evidence = PublicData::default(); - evidence.set_field(1, A.to_fixed_bytes().to_vec()); - evidence.set_field(2, A.to_fixed_bytes().to_vec()); + evidence.set_field(PARENT_HASH, OMMERS_HASH.to_fixed_bytes().to_vec()); + evidence.set_field(BLOCK_HASH, OMMERS_HASH.to_fixed_bytes().to_vec()); evidence.block_context.number = 300.into(); - evidence.block_context.block_hash = A.to_word(); - evidence.block_context.history_hashes = vec![A.to_word()]; + evidence.block_context.block_hash = OMMERS_HASH.to_word(); + evidence.block_context.history_hashes = vec![OMMERS_HASH.to_word()]; evidence } From 3956c176f9ef29bf19b9c53d2eb27d9a6cfc0255 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Mon, 4 Sep 2023 06:12:27 +0800 Subject: [PATCH 35/43] more tests --- zkevm-circuits/src/taiko_pi_circuit.rs | 2 +- zkevm-circuits/src/taiko_pi_circuit__.rs | 74 +++++++++++++++++------- 2 files changed, 55 insertions(+), 21 deletions(-) diff --git a/zkevm-circuits/src/taiko_pi_circuit.rs b/zkevm-circuits/src/taiko_pi_circuit.rs index c1bf652092..f9a5ebdffd 100644 --- a/zkevm-circuits/src/taiko_pi_circuit.rs +++ b/zkevm-circuits/src/taiko_pi_circuit.rs @@ -795,6 +795,6 @@ mod taiko_pi_circuit_test { let k = 17; - // assert_eq!(run::(k, public_data, None), Ok(())); + assert_eq!(run::(k, public_data, None), Ok(())); } } \ No newline at end of file diff --git a/zkevm-circuits/src/taiko_pi_circuit__.rs b/zkevm-circuits/src/taiko_pi_circuit__.rs index 211af102eb..f211865f3f 100644 --- a/zkevm-circuits/src/taiko_pi_circuit__.rs +++ b/zkevm-circuits/src/taiko_pi_circuit__.rs @@ -1,7 +1,7 @@ use bus_mapping::evm; -use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256, H160}; +use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256, H160, U256}; use ethers_core::abi::*; use ethers_core::abi::FixedBytes; use ethers_core::utils::keccak256; @@ -121,7 +121,10 @@ pub struct PublicData { impl Default for PublicData { fn default() -> Self { - Self::new(&witness::Block::default()) + // has to have at least one history hash, block number must start with at least one + let mut ret = Self::new(&witness::Block::default()); + ret.block_context.history_hashes = vec![U256::default()]; + ret } } @@ -623,6 +626,7 @@ mod taiko_pi_circuit_test { evidence.set_field(BLOCK_HASH, OMMERS_HASH.to_fixed_bytes().to_vec()); evidence.block_context.number = 300.into(); evidence.block_context.block_hash = OMMERS_HASH.to_word(); + // has to have at least one history block evidence.block_context.history_hashes = vec![OMMERS_HASH.to_word()]; evidence } @@ -636,26 +640,56 @@ mod taiko_pi_circuit_test { } #[test] - fn test(){ - println!("test"); - let mut evidence = PublicData::::default(); - let data = hex::decode("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef").unwrap(); - evidence.set_field(0, data.clone()); - // evidence.set_field(1, vec![0u8; 32]); - // evidence.set_field(2, data.clone()); - // evidence.set_field(3, vec![0u8; 32]); - // evidence.set_field(4, vec![0u8; 32]); - // evidence.set_field(5, vec![0u8; 20]); - - // evidence.parent_hash = Token::FixedBytes(vec![0u8; 32]); - // evidence.block_hash = Token::FixedBytes(data); - // evidence.signal_root = Token::FixedBytes(vec![0u8; 32]); - // evidence.graffiti = Token::FixedBytes(vec![0u8; 32]); - // evidence.prover = Token::Address([0x22u8; 20].into()); - let encode_raw = evidence.encode_raw(); - println!("abi.encode {:?}\nkeccak {:?}\nhi-lo {:?}", encode_raw.clone(), keccak256(encode_raw), evidence.keccak_hi_low()); + fn test_fail_pi_hash() { + let evidence = mock_public_data(); + + let k = 17; + match run::(k, evidence, Some(vec![vec![Fr::zero(), Fr::one()]])) { + Ok(_) => unreachable!("this case must fail"), + Err(errs) => { + assert_eq!(errs.len(), 4); + for err in errs { + match err { + VerifyFailure::Permutation { .. } => return, + _ => unreachable!("unexpected error"), + } + } + } + } + } + + #[test] + fn test_simple_pi() { + let mut evidence = mock_public_data(); + let block_number = 1337u64; + evidence.block_context.number = block_number.into(); + evidence.block_context.history_hashes = vec![OMMERS_HASH.to_word()]; + evidence.set_field(PROVER, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19].into()); + + let k = 17; + assert_eq!(run::(k, evidence, None), Ok(())); + } + + #[test] + fn test_verify() { + let mut block = witness::Block::::default(); + + block.eth_block.parent_hash = *OMMERS_HASH; + block.eth_block.hash = Some(*OMMERS_HASH); + block.protocol_instance.block_hash = *OMMERS_HASH; + block.protocol_instance.parent_hash = *OMMERS_HASH; + block.context.history_hashes = vec![OMMERS_HASH.to_word()]; + block.context.block_hash = OMMERS_HASH.to_word(); + block.context.number = 300.into(); + + let evidence = PublicData::new(&block); + + let k = 17; + + assert_eq!(run::(k, evidence, None), Ok(())); } + } From 198adaeb2098e9c8043bab327f6152f0da6415ab Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Mon, 4 Sep 2023 06:15:44 +0800 Subject: [PATCH 36/43] remove files --- zkevm-circuits/src/lib.rs | 8 +- zkevm-circuits/src/taiko_pi_circuit.rs | 1017 ++++++++++------------ zkevm-circuits/src/taiko_pi_circuit_.rs | 683 --------------- zkevm-circuits/src/taiko_pi_circuit__.rs | 695 --------------- zkevm-circuits/src/witness/block.rs | 2 - 5 files changed, 453 insertions(+), 1952 deletions(-) delete mode 100644 zkevm-circuits/src/taiko_pi_circuit_.rs delete mode 100644 zkevm-circuits/src/taiko_pi_circuit__.rs diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index 40a248ca7c..5307f4ebd6 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -27,15 +27,11 @@ pub mod root_circuit; pub mod state_circuit; pub mod super_circuit; pub mod table; -#[macro_use] -pub mod taiko_pi_circuit; + #[macro_use] #[allow(missing_docs)] -pub mod taiko_pi_circuit_; +pub mod taiko_pi_circuit; pub mod taiko_super_circuit; -#[macro_use] -#[allow(missing_docs)] -pub mod taiko_pi_circuit__; #[cfg(any(feature = "test", test))] pub mod test_util; pub mod circuit_tools; diff --git a/zkevm-circuits/src/taiko_pi_circuit.rs b/zkevm-circuits/src/taiko_pi_circuit.rs index f9a5ebdffd..aa594e133c 100644 --- a/zkevm-circuits/src/taiko_pi_circuit.rs +++ b/zkevm-circuits/src/taiko_pi_circuit.rs @@ -1,232 +1,85 @@ -//! Use the hash value as public input. - -use crate::{ - assign, - evm_circuit::table::Table::*, - evm_circuit::{util::{constraint_builder::{ConstrainBuilderCommon}}, table::Table}, - table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}, - util::{Challenges, SubCircuitConfig, SubCircuit}, - circuit_tools::{ - constraint_builder::{ConstraintBuilder, RLCable, TO_FIX, RLCableValue}, - cell_manager::{CellManager, CellType, Cell, CellColumn}, gadgets::{IsEqualGadget}, cached_region::{CachedRegion}, - }, - - witness::{self, BlockContext}, circuit, -}; - -use gadgets::util::{Scalar, not}; -use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256}; -use ethers_core::utils::keccak256; -use gadgets::{util::{Expr}}; -use halo2_proofs::{ - circuit::{Layouter, SimpleFloorPlanner, Value}, - plonk::{ - Circuit, Column, ConstraintSystem, Error, Expression, Instance, - Selector, - }, -}; - -use std::{marker::PhantomData, usize, vec}; - -const BYTE_POW_BASE: u64 = 1 << 8; -const N: usize = 32; -const H: usize = 12; -const RPI_BYTES_LEN: usize = 32 * 10; -const USED_ROWS: usize = RPI_BYTES_LEN + 64; -const L1SIGNAL_IDX: usize = 0; -const PARENT_HASH: usize = 4; -const BLOCK_HASH: usize = 5; -const FIELD9_IDX: usize = 8; -const FIELD10_IDX: usize = 9; -const KECCAK_OUTPUT: usize = 10; +use bus_mapping::evm; +use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256, H160, U256}; +use ethers_core::abi::*; +use ethers_core::abi::FixedBytes; +use ethers_core::utils::keccak256; +use halo2_proofs::circuit::{Value, Layouter, SimpleFloorPlanner, AssignedCell}; +use halo2_proofs::poly::Rotation; +use itertools::Itertools; +use std::convert::TryInto; +use std::marker::PhantomData; +use gadgets::util::{Expr, Scalar}; +use halo2_proofs::plonk::{Expression, ConstraintSystem, Selector, Instance, Column, Circuit}; +use keccak256::keccak_arith::Keccak; +use halo2_proofs::plonk::Error; +use core::result::Result; +use crate::circuit_tools::cached_region::CachedRegion; +use crate::circuit_tools::cell_manager::{Cell, CellType, CellManager, CellColumn}; +use crate::circuit_tools::constraint_builder::{ConstraintBuilder, TO_FIX, RLCable, ExprVec}; +use crate::evm_circuit::table::Table; +use crate::evm_circuit::util::rlc; +use crate::util::{Challenges, SubCircuitConfig, SubCircuit}; +use crate::witness::{self, Bytecode, BlockContext}; +use crate::{circuit, assign}; +use crate::table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}; -/// PublicData contains all the values that the PiCircuit receives as input +const BYTE_POW_BASE: u64 = 1 << 8; +const PADDING_LEN: usize = 32; +const META_HASH: usize = 0; +const PARENT_HASH: usize = 1; +const BLOCK_HASH: usize = 2; +const SIGNAL_ROOT: usize = 3; +const GRAFFITI: usize = 4; +const PROVER: usize = 5; +const S1: PiCellType = PiCellType::Storage1; +const S2: PiCellType = PiCellType::Storage2; +/// #[derive(Debug, Clone, Default)] -pub struct PublicData { - /// l1 signal service address - pub l1_signal_service: Word, - /// l2 signal service address - pub l2_signal_service: Word, - /// l2 contract address - pub l2_contract: Word, - - - /// meta hash - pub meta_hash: Word, - /// block hash value - pub block_hash: Word, - /// the parent block hash - pub parent_hash: Word, - /// signal root - pub signal_root: Word, - /// extra message - pub graffiti: Word, - /// union field - pub field9: Word, // prover[96:256]+parentGasUsed[64:96]+gasUsed[32:64] - /// union field - pub field10: Word, /* blockMaxGasLimit[192:256]+maxTransactionsPerBlock[128: - * 192]+maxBytesPerTxList[64:128] */ - - // privates - // Prover address - prover: Address, - // parent block gas used - parent_gas_used: u32, - // block gas used - gas_used: u32, - // blockMaxGasLimit - block_max_gas_limit: u64, - // maxTransactionsPerBlock: u64, - max_transactions_per_block: u64, - // maxBytesPerTxList: u64, - max_bytes_per_tx_list: u64, - - block_context: BlockContext, - chain_id: Word, +pub struct FieldGadget { + field: Vec>, + len: usize, } -impl PublicData { - fn assignments(&self) -> [(&'static str, Option, Vec); 10] { - [ - ( - "l1_signal_service", - None, - self.l1_signal_service.to_be_bytes().to_vec(), - ), - ( - "l2_signal_service", - None, - self.l2_signal_service.to_be_bytes().to_vec(), - ), - ("l2_contract", None, self.l2_contract.to_be_bytes().to_vec()), - - ("meta_hash", None, self.meta_hash.to_be_bytes().to_vec()), - ( - "parent_hash", - Some(self.block_context.number - 1), - self.parent_hash.to_be_bytes().to_vec(), - ), - ( - "block_hash", - Some(self.block_context.number), - self.block_hash.to_be_bytes().to_vec(), - ), - ("signal_root", None, self.signal_root.to_be_bytes().to_vec()), - ("graffiti", None, self.graffiti.to_be_bytes().to_vec()), - ( - "prover+parentGasUsed+gasUsed", - None, - self.field9.to_be_bytes().to_vec(), - ), - ( - "blockMaxGasLimit+maxTransactionsPerBlock+maxBytesPerTxList", - None, - self.field10.to_be_bytes().to_vec(), - ), - ] - } - - // pub fn abi_encode(&self) -> Vec { - - // } - - /// get rpi bytes - pub fn rpi_bytes(&self) -> Vec { - self.assignments().iter().flat_map(|v| v.2.clone()).collect() - } - - fn default() -> Self { - Self::new::(&witness::Block::default()) - } - - /// create PublicData from block and taiko - pub fn new(block: &witness::Block) -> Self { - use witness::left_shift; - let field9 = left_shift(block.protocol_instance.prover, 96) - + left_shift(block.protocol_instance.parent_gas_used as u64, 64) - + left_shift(block.protocol_instance.gas_used as u64, 32); - - let field10 = left_shift(block.protocol_instance.block_max_gas_limit, 192) - + left_shift(block.protocol_instance.max_transactions_per_block, 128) - + left_shift(block.protocol_instance.max_bytes_per_tx_list, 64); - PublicData { - l1_signal_service: block.protocol_instance.l1_signal_service.to_word(), - l2_signal_service: block.protocol_instance.l2_signal_service.to_word(), - l2_contract: block.protocol_instance.l2_contract.to_word(), - meta_hash: block.protocol_instance.meta_hash.hash().to_word(), - block_hash: block.protocol_instance.block_hash.to_word(), - parent_hash: block.protocol_instance.parent_hash.to_word(), - signal_root: block.protocol_instance.signal_root.to_word(), - graffiti: block.protocol_instance.graffiti.to_word(), - prover: block.protocol_instance.prover, - parent_gas_used: block.protocol_instance.parent_gas_used, - gas_used: block.protocol_instance.gas_used, - block_max_gas_limit: block.protocol_instance.block_max_gas_limit, - max_transactions_per_block: block.protocol_instance.max_transactions_per_block, - max_bytes_per_tx_list: block.protocol_instance.max_bytes_per_tx_list, - field9, - field10, - block_context: block.context.clone(), - chain_id: block.context.chain_id, +impl FieldGadget { + fn config(cb: &mut ConstraintBuilder, len: usize) -> Self { + Self { + field: cb.query_cells_dyn(PiCellType::Byte, len), + len } } - fn get_pi(&self) -> H256 { - let rpi_bytes = self.rpi_bytes(); - let rpi_keccak = keccak256(rpi_bytes); - H256(rpi_keccak) - } - - fn get_pi_hi_low(&self) -> (F, F) { - let keccak_rpi = self.get_pi().to_fixed_bytes(); - ( - keccak_rpi - .iter() - .take(16) - .fold(F::ZERO, |acc, byte| { - acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) - }), - keccak_rpi - .iter() - .skip(16) - .fold(F::ZERO, |acc, byte| { - acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) - }) - ) + fn bytes_expr(&self) -> Vec> { + self.field.iter().map(|f| f.expr()).collect() } -} -/// Config for PiCircuit -#[derive(Clone, Debug)] -pub struct TaikoPiCircuitConfig { - q_enable: Selector, - public_input: Column, // equality - field: FieldBytesGadget, - state: PiState, - block_acc: Cell, // Phase2 - block_number: Cell, // Phase1 - keccak_input: Cell, // Phase2 - keccak_output:[Cell;2], // Phase2 - block_table: BlockTable, - keccak_table: KeccakTable, - byte_table: ByteTable, + fn acc(&self, r: Expression) -> Expression { + //0.expr() + self.bytes_expr().rlc_rev(&r) + } - // To annotate columns at assignment for debug purpose - col_configs: Vec>, -} + pub(crate) fn hi_low_field(&self) -> [Expression; 2] { + assert!(self.len == 32); + let hi = self.bytes_expr()[..16].to_vec(); + let low = self.bytes_expr()[16..].to_vec(); + [hi.rlc_rev(&BYTE_POW_BASE.expr()), low.rlc_rev(&BYTE_POW_BASE.expr())] + } -/// Circuit configuration arguments -pub struct TaikoPiCircuitConfigArgs { - /// BlockTable - pub block_table: BlockTable, - /// KeccakTable - pub keccak_table: KeccakTable, - /// ByteTable - pub byte_table: ByteTable, - /// Challenges - pub challenges: Challenges>, + fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + bytes: &[F], + ) -> Result>, Error> { + assert!(bytes.len() == self.len); + let cells = self.field.iter().zip(bytes.iter()).map( + |(cell, byte)| { + assign!(region, cell, offset => *byte).unwrap() + } + ).collect(); + Ok(cells) + } } #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -234,7 +87,9 @@ enum PiCellType { Storage1, Storage2, Byte, - Lookup(Table) + LookupPi, + Lookup(Table), + } impl CellType for PiCellType { fn byte_type() -> Option { @@ -255,324 +110,382 @@ impl Default for PiCellType { } -impl SubCircuitConfig for TaikoPiCircuitConfig { - type ConfigArgs = TaikoPiCircuitConfigArgs; - /// Return a new TaikoPiCircuitConfig - fn new( - meta: &mut ConstraintSystem, - Self::ConfigArgs { - block_table, - keccak_table, - byte_table, - challenges, - }: Self::ConfigArgs, - ) -> Self { - let cm = CellManager::new( - meta, - vec![ - (PiCellType::Byte, 1, 1, false), - (PiCellType::Storage1, 1, 1, false), - (PiCellType::Storage2, 1, 2, true), - ], - 0, - 32, - ); - let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(cm.clone()), Some(challenges.evm_word())); - cb.preload_tables(meta, - &[ - (PiCellType::Lookup(Keccak), &keccak_table), - (PiCellType::Lookup(Bytecode), &byte_table), - (PiCellType::Lookup(Block), &block_table) - ] - ); - let q_enable = meta.complex_selector(); - let public_input = meta.instance_column(); - let field = FieldBytesGadget::config(&mut cb, &challenges); - let state = PiState::config(&mut cb); - let block_acc = cb.query_one(PiCellType::Storage2); - let block_number = cb.query_one(PiCellType::Storage1); - let keccak_input = cb.query_one(PiCellType::Storage2); - let keccak_output = [();2].map(|_| cb.query_one(PiCellType::Storage2)); - meta.enable_equality(public_input); - meta.create_gate( - "PI acc constraints", - |meta| { - let keccak_mult = (0..N).fold(1.expr(), |acc, _| acc * challenges.keccak_input()); - // let evm_mult = (0..N).fold(1.expr(), |acc, _| acc * challenges.evm_word()); - circuit!([meta, cb], { - ifx!(state.increment_step() => { - require!(block_acc.rot(meta, 32) => block_acc.expr() * keccak_mult + field.keccak_field()); - }); - matchx!(( - state.is_l1_signal.expr() => { - require!(block_acc.expr() => field.keccak_field()); - }, - state.lookup_blockhash() => { - require!( - ( - BlockContextFieldTag::BlockHash.expr(), - block_number.expr(), - field.evm_word_field() - ) => @PiCellType::Lookup(Table::Block), (TO_FIX) - ); - }, - state.is_field_10.expr() => { - require!(keccak_input.expr() => block_acc.expr()); - }, - state.is_keccak_output.expr() => { - require!( - ( - 1.expr(), - keccak_input.expr(), - RPI_BYTES_LEN.expr(), - field.evm_word_field() - ) - => @PiCellType::Lookup(Table::Keccak), (TO_FIX) - ); - let (hi_expr, low_expr) = field.hi_low_field(); - require!(keccak_output[0].expr() => hi_expr); - require!(keccak_output[1].expr() => low_expr); - keccak_output.iter().for_each(|c| cb.enable_equality(c.column())); - } - )); - }); - cb.build_constraints(Some(meta.query_selector(q_enable))) - } - ); - cb.build_equalities(meta); - cb.build_lookups( - meta, - &[cm.clone()], - &[ - (PiCellType::Byte, PiCellType::Lookup(Bytecode)), - (PiCellType::Lookup(Table::Keccak), PiCellType::Lookup(Table::Keccak)), - (PiCellType::Lookup(Table::Block), PiCellType::Lookup(Table::Block)), - ], - Some(q_enable) - ); - // meta.pinned().print_config_states(); - // meta.pinned().print_layout_states(); - - let col_configs = cm.columns().to_vec(); - Self { - q_enable, - public_input, - field, - state, - block_acc, - block_number, - keccak_input, - keccak_output, - block_table, - keccak_table, - byte_table, - col_configs, - } - } +#[derive(Debug, Clone)] +pub struct PublicData { + evidence: Token, + block_context: BlockContext, + _phantom: PhantomData, } -impl TaikoPiCircuitConfig { - pub(crate) fn assign( - &self, - layouter: &mut impl Layouter, - public_data: &PublicData, - challenges: &Challenges>, - ) -> Result<(), Error> { - let pi_cells = layouter.assign_region( - || "Pi", - |mut region| { - - self.q_enable.enable(&mut region, 0)?; - let mut region = CachedRegion::new(&mut region); - region.annotate_columns(&self.col_configs); - - let mut block_acc = F::ZERO; - let mut keccak_r = F::ZERO; - challenges.keccak_input().map(|v| keccak_r = v); - let keccak_mult = (0..N).fold(1.scalar(), |acc: F, _| acc * keccak_r); - - let mut assignments = public_data.assignments().to_vec(); - assignments.append(&mut vec![ - ("keccak_output", None, public_data.get_pi().to_fixed_bytes().to_vec()) - ]); - let mut offset = 0; - let mut state = 0; - let mut pi_cells = Vec::new(); - for (_annotation, block_number, bytes) in assignments { - self.state.assign(&mut region, offset, state)?; - if state != KECCAK_OUTPUT { - let next = block_acc * keccak_mult - + self.field.assign(&mut region, offset, &bytes, keccak_r)?; - assign!(region, self.block_acc, offset => next)?; - block_acc = next; - } - match state { - PARENT_HASH | BLOCK_HASH => { - let block_number = block_number.expect(&format!("block_number missing at {:?}th row", offset)); - assign!(region, self.block_number, offset => block_number.as_u64().scalar())?; - }, - FIELD10_IDX => { - assign!(region, self.keccak_input, offset => block_acc)?; - }, - KECCAK_OUTPUT => { - let (hi, low) = public_data.get_pi_hi_low::(); - pi_cells.push(assign!(region, self.keccak_output[0], offset => hi)?); - pi_cells.push(assign!(region, self.keccak_output[1], offset => low)?); - }, - _ => () - } - offset += N; - state += 1 - } - Ok(pi_cells) - } - )?; - for (i, cell) in pi_cells.iter().enumerate() { - layouter.constrain_instance(cell.cell(), self.public_input, i)?; - } - Ok(()) +impl Default for PublicData { + fn default() -> Self { + // has to have at least one history hash, block number must start with at least one + let mut ret = Self::new(&witness::Block::default()); + ret.block_context.history_hashes = vec![U256::default()]; + ret } } -#[derive(Clone, Debug)] -struct PiState { - pub(crate) state: Cell, - is_l1_signal: IsEqualGadget, - is_parent_hash: IsEqualGadget, - is_block_hash: IsEqualGadget, - is_field_10: IsEqualGadget, - is_keccak_output: IsEqualGadget, -} -impl PiState { - pub(crate) fn config(cb: &mut ConstraintBuilder) -> Self { - let state = cb.query_default(); +impl PublicData { + fn new(block: &witness::Block) -> Self { + let meta_hash = Token::FixedBytes(block.protocol_instance.meta_hash.hash().to_word().to_be_bytes().to_vec()); + let parent_hash = Token::FixedBytes(block.protocol_instance.parent_hash.to_word().to_be_bytes().to_vec()); + let block_hash = Token::FixedBytes(block.protocol_instance.block_hash.to_word().to_be_bytes().to_vec()); + let signal_root = Token::FixedBytes(block.protocol_instance.signal_root.to_word().to_be_bytes().to_vec()); + let graffiti = Token::FixedBytes(block.protocol_instance.graffiti.to_word().to_be_bytes().to_vec()); + let prover = Token::Address(block.protocol_instance.prover); Self { - state: state.clone(), - is_l1_signal: IsEqualGadget::construct(cb, state.expr(), L1SIGNAL_IDX.expr()), - is_parent_hash: IsEqualGadget::construct(cb, state.expr(), PARENT_HASH.expr()), - is_block_hash: IsEqualGadget::construct(cb, state.expr(), BLOCK_HASH.expr()), - is_field_10: IsEqualGadget::construct(cb, state.expr(), FIELD10_IDX.expr()), - is_keccak_output: IsEqualGadget::construct(cb, state.expr(), KECCAK_OUTPUT.expr()), + evidence: Token::FixedArray(vec![ + meta_hash, + parent_hash, + block_hash, + signal_root, + graffiti, + prover, + ]), + block_context: block.context.clone(), + _phantom: PhantomData } } - pub(crate) fn increment_step(&self) -> Expression { - not::expr(self.is_field_10.expr()) + not::expr(self.is_keccak_output.expr()) + fn set_field(&mut self, idx: usize, bytes: Vec) { + match self.evidence { + Token::FixedArray(ref mut tokens) => { + tokens[idx] = match tokens[idx].clone() { + Token::Bytes(_) => Token::Bytes(bytes), + Token::FixedBytes(_) => Token::FixedBytes(bytes), + Token::Address(_) => Token::Address( + H160::from(&bytes.try_into().expect("Wrong number of bytes for address") + )), + _ => unreachable!(), + }; + } + _ => unreachable!(), + } } - pub(crate) fn lookup_blockhash(&self) -> Expression { - self.is_block_hash.expr() + fn encode_raw(&self) -> Vec { + encode(&[self.evidence.clone()]) } - pub(crate) fn assign( - &self, - region: &mut CachedRegion<'_, '_, F>, - offset: usize, - state: usize - ) -> Result<(), Error> { - assign!(region, self.state, offset => state.scalar()); - self.is_l1_signal.assign(region, offset, state.scalar(), L1SIGNAL_IDX.scalar())?; - self.is_parent_hash.assign(region, offset, state.scalar(), PARENT_HASH.scalar())?; - self.is_block_hash.assign(region, offset, state.scalar(), BLOCK_HASH.scalar())?; - self.is_field_10.assign(region, offset, state.scalar(), FIELD10_IDX.scalar())?; - self.is_keccak_output.assign(region, offset, state.scalar(), KECCAK_OUTPUT.scalar())?; - Ok(()) + fn encode_field(&self, idx: usize) -> Vec { + let field = match self.evidence { + Token::FixedArray(ref tokens) => tokens[idx].clone(), + _ => unreachable!(), + }; + encode(&[field]) } -} + fn total_acc(&self, r: Value) -> F { + let mut rand = F::ZERO; + r.map(|r| rand = r); + self.encode_raw() + .iter() + .fold(F::ZERO, |acc: F, byte| { + acc * rand + F::from(*byte as u64) + }) + } + fn assignment(&self, idx: usize) -> Vec { + self.encode_field(idx) + .iter() + .map(|b| F::from(*b as u64)) + .collect() + } -#[derive(Clone, Debug)] -struct FieldBytesGadget { - bytes: [Cell; N], - word_r: Expression, - keccak_r: Expression, -} -impl FieldBytesGadget { - pub(crate) fn config( - cb: &mut ConstraintBuilder, - challenges: &Challenges> - ) -> Self { - Self { - bytes: cb.query_bytes(), - word_r: challenges.evm_word().expr(), - keccak_r: challenges.keccak_input().expr(), - } - } + fn assignment_acc(&self, idx: usize, r: Value) -> F { + let mut rand = F::ZERO; + r.map(|r| rand = r); + rlc::value(self.encode_field(idx).iter().rev(), rand) + } + + fn keccak_hi_low(&self) -> [F; 2] { + let keccaked_pi = keccak256(self.encode_raw()); + [ + keccaked_pi + .iter() + .take(16) + .fold(F::ZERO, |acc: F, byte| { + acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) + }), + keccaked_pi + .iter() + .skip(16) + .fold(F::ZERO, |acc: F, byte| { + acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) + }) + ] + } - pub(crate) fn bytes_expr(&self) -> Vec> { - self.bytes.iter().map(|b| b.expr()).collect() + fn keccak(&self) -> Vec { + keccak256(self.encode_raw()).to_vec() } - /// RLC of bytes of a field with evm_word 1<<8 - pub(crate) fn hi_low_field(&self) -> (Expression, Expression) { - let hi = self.bytes_expr()[..16].to_vec(); - let low = self.bytes_expr()[16..].to_vec(); - (hi.rlc_rev(&BYTE_POW_BASE.expr()), low.rlc_rev(&BYTE_POW_BASE.expr())) + fn keccak_assignment(&self) -> Vec { + self.keccak() + .iter() + .map(|b| F::from(*b as u64)) + .collect() } - /// RLC of bytes of a field with evm_word - pub(crate) fn evm_word_field(&self) -> Expression { - self.bytes_expr().rlc_rev(&self.word_r) + fn total_len(&self) -> usize { + self.encode_raw().len() } - /// RLC of bytes of a field with keccak_input - pub(crate) fn keccak_field(&self) -> Expression { - self.bytes_expr().rlc_rev(&self.keccak_r) // OK! - // = b0 * r^31 + ... + b31 * r^0 + fn field_len(&self, idx: usize) -> usize { + self.encode_field(idx).len() } - // ------------------ Assign ------------------ - /// Returns the rlc of given bytes +} + +#[derive(Clone, Debug)] +pub struct TaikoPiCircuitConfig { + q_enable: Selector, + keccak_instance: Column, // equality + + meta_hash: FieldGadget, + parent_hash: (Cell, FieldGadget, Cell), + block_hash: (Cell, FieldGadget, Cell), + signal_root: FieldGadget, + graffiti: FieldGadget, + prover: FieldGadget, + + total_acc: Cell, + keccak_bytes: FieldGadget, + keccak_hi_lo: [Cell; 2], + + block_table: BlockTable, + keccak_table: KeccakTable, + byte_table: ByteTable, + + annotation_configs: Vec>, +} + +pub struct TaikoPiCircuitConfigArgs { + /// + pub evidence: PublicData, + /// BlockTable + pub block_table: BlockTable, + /// KeccakTable + pub keccak_table: KeccakTable, + /// ByteTable + pub byte_table: ByteTable, + /// Challenges + pub challenges: Challenges>, +} + + +impl SubCircuitConfig for TaikoPiCircuitConfig { + type ConfigArgs = TaikoPiCircuitConfigArgs; + /// Return a new TaikoPiCircuitConfig + fn new( + meta: &mut ConstraintSystem, + Self::ConfigArgs { + evidence, + block_table, + keccak_table, + byte_table, + challenges, + }: Self::ConfigArgs, + ) -> Self { + let keccak_r = challenges.keccak_input(); + let evm_word = challenges.evm_word(); + let cm = CellManager::new( + meta, + vec![ + (PiCellType::Byte, 1, 1, false), + (PiCellType::Storage1, 1, 1, true), + (PiCellType::Storage2, 1, 1, true), + ], + 0, + evidence.total_len() + PADDING_LEN, + ); + let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(cm.clone()), Some(evm_word.expr())); + cb.preload_tables(meta, + &[ + (PiCellType::Lookup(Table::Keccak), &keccak_table), + (PiCellType::Lookup(Table::Bytecode), &byte_table), + (PiCellType::Lookup(Table::Block), &block_table) + ] + ); + let q_enable = meta.complex_selector(); + let keccak_instance = meta.instance_column(); + meta.enable_equality(keccak_instance); + + let meta_hash = FieldGadget::config(&mut cb, evidence.field_len(META_HASH)); + let parent_hash =( + cb.query_one(S1), + FieldGadget::config(&mut cb, evidence.field_len(PARENT_HASH)), + cb.query_one(S2) + ); + let block_hash =( + cb.query_one(S1), + FieldGadget::config(&mut cb, evidence.field_len(BLOCK_HASH)), + cb.query_one(S2) + ); + let signal_root = FieldGadget::config(&mut cb, evidence.field_len(SIGNAL_ROOT)); + let graffiti = FieldGadget::config(&mut cb, evidence.field_len(GRAFFITI)); + let prover = FieldGadget::config(&mut cb, evidence.field_len(PROVER)); + + let total_acc = cb.query_one(S2); + let keccak_bytes = FieldGadget::config(&mut cb, PADDING_LEN); + let keccak_hi_lo = [cb.query_one(S1), cb.query_one(S1)]; + meta.create_gate( + "PI acc constraints", + |meta| { + circuit!([meta, cb], { + for (n, b, acc) in [parent_hash.clone() , block_hash.clone()] { + require!(acc.expr() => b.acc(evm_word.expr())); + require!( + ( + BlockContextFieldTag::BlockHash.expr(), + n.expr(), + acc.expr() + ) => @PiCellType::Lookup(Table::Block), (TO_FIX) + ); + } + let acc_val = [ + meta_hash.clone(), + parent_hash.1.clone(), + block_hash.1.clone(), + signal_root.clone(), + graffiti.clone(), + prover.clone(), + ].iter().fold(0.expr(), |acc, gadget| { + let mult = (0..gadget.len).fold(1.expr(), |acc, _| acc * keccak_r.expr()); + acc * mult + gadget.acc(keccak_r.expr()) + }); + require!(total_acc.expr() => acc_val); + require!( + ( + 1.expr(), + total_acc.expr(), + evidence.total_len().expr(), + keccak_bytes.acc(evm_word.expr()) + ) + => @PiCellType::Lookup(Table::Keccak), (TO_FIX) + ); + let hi_lo = keccak_bytes.hi_low_field(); + keccak_hi_lo.iter().zip(hi_lo.iter()).for_each(|(cell, epxr)| { + require!(cell.expr() => epxr); + cb.enable_equality(cell.column()); + }); + }); + cb.build_constraints(Some(meta.query_selector(q_enable))) + } + ); + cb.build_lookups( + meta, + &[cm.clone()], + &[ + (PiCellType::Byte, PiCellType::Lookup(Table::Bytecode)), + (PiCellType::Lookup(Table::Keccak), PiCellType::Lookup(Table::Keccak)), + (PiCellType::Lookup(Table::Block), PiCellType::Lookup(Table::Block)), + ], + Some(q_enable) + ); + let annotation_configs = cm.columns().to_vec(); + Self { + q_enable, + keccak_instance, + meta_hash, + parent_hash, + block_hash, + signal_root, + graffiti, + prover, + total_acc, + keccak_bytes, + keccak_hi_lo, + block_table, + keccak_table, + byte_table, + annotation_configs + } + } +} + +impl TaikoPiCircuitConfig { pub(crate) fn assign( &self, - region: &mut CachedRegion<'_, '_, F>, - offset: usize, - bytes: &[u8], - r: F, - ) -> Result { - // Assign the bytes - for (byte, cell) in bytes.iter().zip(self.bytes.iter()) { - assign!(region, cell, offset => byte.scalar())?; + layouter: &mut impl Layouter, + challenge: &Challenges>, + evidence: &PublicData, + ) -> Result<(), Error> { + let evm_word = challenge.evm_word(); + let keccak_r = challenge.keccak_input(); + let mut hi_lo_cells = layouter.assign_region( + || "Pi", + |mut region| { + self.q_enable.enable(&mut region, 0)?; + let mut region = CachedRegion::new(&mut region); + region.annotate_columns(&self.annotation_configs); + + assign!(region, self.parent_hash.0, 0 => (evidence.block_context.number - 1).as_u64().scalar()); + assign!(region, self.parent_hash.2, 0 => evidence.assignment_acc(PARENT_HASH, evm_word)); + assign!(region, self.block_hash.0, 0 => (evidence.block_context.number).as_u64().scalar()); + assign!(region, self.block_hash.2, 0 => evidence.assignment_acc(BLOCK_HASH, evm_word)); + + let mut acc = F::ZERO; + let mut idx = 0; + [ + &self.meta_hash, + &self.parent_hash.1, + &self.block_hash.1, + &self.signal_root, + &self.graffiti, + &self.prover, + ].iter().for_each(|gadget| { + gadget.assign(&mut region, 0, &evidence.assignment(idx)) + .expect(&format!("FieldGadget assignment failed at {:?}", idx)); + idx += 1; + }); + self.keccak_bytes.assign(&mut region, 0, &evidence.keccak_assignment()) + .expect("Keccak bytes assignment failed"); + assign!(region, self.total_acc, 0 => evidence.total_acc(keccak_r))?; + let hi_low_assignment = evidence.keccak_hi_low(); + let hi = assign!(region, self.keccak_hi_lo[0], 0 => hi_low_assignment[0])?; + let lo = assign!(region, self.keccak_hi_lo[1], 0 => hi_low_assignment[1])?; + + Ok([hi, lo]) + })?; + for (i, cell) in hi_lo_cells.iter().enumerate() { + layouter.constrain_instance(cell.cell(), self.keccak_instance, i)?; } - Ok(bytes.rlc_rev_value(r)) + Ok(()) } } - - /// Public Inputs Circuit -#[derive(Clone, Default, Debug)] +#[derive(Clone, Debug, Default)] pub struct TaikoPiCircuit { /// PublicInputs data known by the verifier - pub public_data: PublicData, - _marker: PhantomData, + pub evidence: PublicData, } impl TaikoPiCircuit { /// Creates a new TaikoPiCircuit - pub fn new(public_data: PublicData) -> Self { + pub fn new(evidence: PublicData) -> Self { Self { - public_data, - _marker: PhantomData, + evidence: evidence, } } } - impl SubCircuit for TaikoPiCircuit { type Config = TaikoPiCircuitConfig; fn unusable_rows() -> usize { // No column queried at more than 3 distinct rotations, so returns 6 as // minimum unusable rows. - 6 + PublicData::::default().total_len() + 3 } - fn min_num_rows_block(_block: &witness::Block) -> (usize, usize) { - (USED_ROWS, USED_ROWS) + fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { + // TODO(Cecilia): what is the first field? + (0, PublicData::new(block).total_len()) } fn new_from_block(block: &witness::Block) -> Self { @@ -581,8 +494,7 @@ impl SubCircuit for TaikoPiCircuit { /// Compute the public inputs for this circuit. fn instance(&self) -> Vec> { - let (hi, low) = self.public_data.get_pi_hi_low::(); - vec![vec![hi, low]] + vec![ self.evidence.keccak_hi_low().to_vec()] } /// Make the assignments to the PiCircuit @@ -593,33 +505,32 @@ impl SubCircuit for TaikoPiCircuit { layouter: &mut impl Layouter, ) -> Result<(), Error> { config.byte_table.load(layouter)?; - config.assign(layouter, &self.public_data, challenges) + config.assign(layouter, challenges, &self.evidence) } } - -// We define the PiTestCircuit as a wrapper over PiCircuit extended to take the -// generic const parameters MAX_TXS and MAX_CALLDATA. This is necessary because -// the trait Circuit requires an implementation of `configure` that doesn't take -// any circuit parameters, and the PiCircuit defines gates that use rotations -// that depend on MAX_TXS and MAX_CALLDATA, so these two values are required -// during the configuration. -/// Test Circuit for PiCircuit -#[cfg(any(feature = "test", test))] -#[derive(Default, Clone)] -pub struct TaikoPiTestCircuit(pub TaikoPiCircuit); - #[cfg(any(feature = "test", test))] -impl Circuit for TaikoPiTestCircuit { - type Config = (TaikoPiCircuitConfig, Challenges); +impl Circuit for TaikoPiCircuit { + type Config = (TaikoPiCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; - type Params = (); + type Params = PublicData; fn without_witnesses(&self) -> Self { Self::default() } + fn params(&self) -> Self::Params { + self.evidence.clone() + } + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + Self::configure_with_params(meta, PublicData::default()) + } + + fn configure_with_params( + meta: &mut ConstraintSystem, + params: Self::Params, + ) -> Self::Config { let block_table = BlockTable::construct(meta); let keccak_table = KeccakTable::construct(meta); let byte_table = ByteTable::construct(meta); @@ -629,13 +540,14 @@ impl Circuit for TaikoPiTestCircuit { TaikoPiCircuitConfig::new( meta, TaikoPiCircuitConfigArgs { + evidence: params, block_table, keccak_table, byte_table, challenges: challenge_exprs, - }, + } ), - challenges, + challenges ) } @@ -645,27 +557,28 @@ impl Circuit for TaikoPiTestCircuit { mut layouter: impl Layouter, ) -> Result<(), Error> { let challenges = challenges.values(&mut layouter); - let public_data = &self.0.public_data; - // assign block table + let evidance = self.params(); let randomness = challenges.evm_word(); + // assign block table config .block_table - .load(&mut layouter, &public_data.block_context, randomness)?; - // [Tag, 0 (b0*r^31 + ... + b31*r^0)] - + .load(&mut layouter, &evidance.block_context, randomness)?; // assign keccak table config .keccak_table - .dev_load(&mut layouter, vec![&public_data.rpi_bytes()], &challenges)?; + .dev_load(&mut layouter, vec![&evidance.encode_raw()], &challenges)?; config.byte_table.load(&mut layouter)?; - self.0.synthesize_sub(&config, &challenges, &mut layouter) + self.synthesize_sub(&config, &challenges, &mut layouter) } + } #[cfg(test)] mod taiko_pi_circuit_test { + use std::vec; + use super::*; use eth_types::ToScalar; @@ -685,72 +598,43 @@ mod taiko_pi_circuit_test { fn run( k: u32, - public_data: PublicData, + evidence: PublicData, pi: Option>>, ) -> Result<(), Vec> { - let circuit = TaikoPiTestCircuit::(TaikoPiCircuit::new(public_data)); - - let public_inputs = pi.unwrap_or_else(|| circuit.0.instance()); - let prover = match MockProver::run(k, &circuit, public_inputs) { + let circuit = TaikoPiCircuit::new(evidence); + let keccak_instance = pi.unwrap_or_else(|| circuit.instance()); + let prover = match MockProver::run(k, &circuit, keccak_instance) { Ok(prover) => prover, Err(e) => panic!("{:#?}", e), }; prover.verify() } - fn mock_public_data() -> PublicData { - let mut public_data = PublicData::default::(); - public_data.meta_hash = OMMERS_HASH.to_word(); - public_data.block_hash = OMMERS_HASH.to_word(); - public_data.block_context.block_hash = OMMERS_HASH.to_word(); - public_data.block_context.history_hashes = vec![Default::default(); 256]; - public_data.block_context.number = 300.into(); - public_data + fn mock_public_data() -> PublicData { + let mut evidence = PublicData::default(); + evidence.set_field(PARENT_HASH, OMMERS_HASH.to_fixed_bytes().to_vec()); + evidence.set_field(BLOCK_HASH, OMMERS_HASH.to_fixed_bytes().to_vec()); + evidence.block_context.number = 300.into(); + evidence.block_context.block_hash = OMMERS_HASH.to_word(); + // has to have at least one history block + evidence.block_context.history_hashes = vec![OMMERS_HASH.to_word()]; + evidence } #[test] fn test_default_pi() { - let public_data = mock_public_data(); + let evidence = mock_public_data(); let k = 17; - assert_eq!(run::(k, public_data, None), Ok(())); + assert_eq!(run::(k, evidence, None), Ok(())); } #[test] fn test_fail_pi_hash() { - let public_data = mock_public_data(); - - let k = 17; - match run::(k, public_data, Some(vec![vec![Fr::zero(), Fr::one()]])) { - Ok(_) => unreachable!("this case must fail"), - Err(errs) => { - assert_eq!(errs.len(), 4); - for err in errs { - match err { - VerifyFailure::Permutation { .. } => return, - _ => unreachable!("unexpected error"), - } - } - } - } - } - - #[test] - fn test_fail_pi_prover() { - let mut public_data = mock_public_data(); - let address_bytes = [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - ]; - - public_data.prover = Address::from_slice(&address_bytes); + let evidence = mock_public_data(); - let prover: Fr = public_data.prover.to_scalar().unwrap(); let k = 17; - match run::( - k, - public_data, - Some(vec![vec![prover, Fr::zero(), Fr::one()]]), - ) { + match run::(k, evidence, Some(vec![vec![Fr::zero(), Fr::one()]])) { Ok(_) => unreachable!("this case must fail"), Err(errs) => { assert_eq!(errs.len(), 4); @@ -766,12 +650,14 @@ mod taiko_pi_circuit_test { #[test] fn test_simple_pi() { - let mut public_data = mock_public_data(); - let chain_id = 1337u64; - public_data.chain_id = Word::from(chain_id); + let mut evidence = mock_public_data(); + let block_number = 1337u64; + evidence.block_context.number = block_number.into(); + evidence.block_context.history_hashes = vec![OMMERS_HASH.to_word()]; + evidence.set_field(PROVER, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19].into()); let k = 17; - assert_eq!(run::(k, public_data, None), Ok(())); + assert_eq!(run::(k, evidence, None), Ok(())); } #[test] @@ -785,16 +671,15 @@ mod taiko_pi_circuit_test { block.context.history_hashes = vec![OMMERS_HASH.to_word()]; block.context.block_hash = OMMERS_HASH.to_word(); block.context.number = 300.into(); - - println!("{:?}\n{:?}", - block.protocol_instance.meta_hash.hash(), - block.protocol_instance.meta_hash.hash().to_word()); - let public_data = PublicData::new(&block); - println!("public_data: {:?}\n{:?}", public_data.meta_hash, public_data.meta_hash.to_be_bytes()); + let evidence = PublicData::new(&block); let k = 17; - assert_eq!(run::(k, public_data, None), Ok(())); + assert_eq!(run::(k, evidence, None), Ok(())); } -} \ No newline at end of file + + +} + + diff --git a/zkevm-circuits/src/taiko_pi_circuit_.rs b/zkevm-circuits/src/taiko_pi_circuit_.rs deleted file mode 100644 index 206b29338f..0000000000 --- a/zkevm-circuits/src/taiko_pi_circuit_.rs +++ /dev/null @@ -1,683 +0,0 @@ -// function getInstance(TaikoData.BlockEvidence memory evidence) - // internal - // pure - // returns (bytes32 instance) -// { - // uint256[6] memory inputs; - // inputs[0] = uint256(evidence.metaHash); - // inputs[1] = uint256(evidence.parentHash); - // inputs[2] = uint256(evidence.blockHash); - // inputs[3] = uint256(evidence.signalRoot); - // inputs[4] = uint256(evidence.graffiti); - // inputs[5] = (uint256(uint160(evidence.prover)) << 96) - // | (uint256(evidence.parentGasUsed) << 64) - // | (uint256(evidence.gasUsed) << 32); - - - - // @dev Struct representing block evidence. - // struct BlockEvidence { - // bytes32 metaHash; - // bytes32 parentHash; - // bytes32 blockHash; - // bytes32 signalRoot; - // bytes32 graffiti; - // address prover; - // bytes proofs; - // } - - // if (evidence.prover != address(1)) return 0; - // else return keccak256(abi.encode(evidence)); - - // evidence.proofs = bytes.concat( - // bytes2(verifierId), - // bytes16(0), - // bytes16(instance), - // bytes16(0), - // bytes16(uint128(uint256(instance))), - // new bytes(100) - // ); -// } - -use bus_mapping::evm; -use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256, H160}; -use ethers_core::abi::*; -use ethers_core::abi::FixedBytes; -use ethers_core::utils::keccak256; -use halo2_proofs::circuit::{Value, Layouter, SimpleFloorPlanner, AssignedCell}; -use itertools::Itertools; -use std::convert::TryInto; -use std::marker::PhantomData; -use gadgets::util::{Expr, Scalar}; -use halo2_proofs::plonk::{Expression, ConstraintSystem, Selector, Instance, Column, Circuit}; -use keccak256::keccak_arith::Keccak; -use halo2_proofs::plonk::Error; -use core::result::Result; -use crate::circuit_tools::cached_region::CachedRegion; -use crate::circuit_tools::cell_manager::{Cell, CellType, CellManager, CellColumn}; -use crate::circuit_tools::constraint_builder::{ConstraintBuilder, TO_FIX, RLCable}; -use crate::evm_circuit::table::Table; -use crate::evm_circuit::util::rlc; -use crate::util::{Challenges, SubCircuitConfig, SubCircuit}; -use crate::witness::{self, Bytecode, BlockContext}; -use crate::{circuit, assign}; -use crate::table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}; - -const BYTE_POW_BASE: u64 = 1 << 8; -const PADDING_LEN: usize = 32; - - -/// -#[derive(Debug, Clone, Default)] -pub struct FieldGadget { - field: Vec>, - len: usize, -} - -impl FieldGadget { - fn config(cb: &mut ConstraintBuilder, len: usize) -> Self { - Self { - field: cb.query_cells_dyn(PiCellType::Byte, len), - len - } - } - - fn bytes_expr(&self) -> Vec> { - self.field.iter().map(|f| f.expr()).collect() - } - - fn acc(&self, r: Expression) -> Expression { - //0.expr() - self.bytes_expr().rlc(&r) - } - - pub(crate) fn hi_low_field(&self) -> [Expression; 2] { - assert!(self.len == 32); - let hi = self.bytes_expr()[..16].to_vec(); - let low = self.bytes_expr()[16..].to_vec(); - [hi.rlc_rev(&BYTE_POW_BASE.expr()), low.rlc_rev(&BYTE_POW_BASE.expr())] - } - - fn assign( - &self, - region: &mut CachedRegion<'_, '_, F>, - offset: usize, - bytes: &[F], - ) -> Result>, Error> { - assert!(bytes.len() == self.len); - let cells = self.field.iter().zip(bytes.iter()).map( - |(cell, byte)| { - assign!(region, cell, offset => *byte).unwrap() - } - ).collect(); - Ok(cells) - } - - fn assign_acc( - &self, - region: &mut CachedRegion<'_, '_, F>, - offset: usize, - bytes: &[F], - r: F - ) -> Result { - assert!(bytes.len() == self.len); - let mut acc = F::ZERO; - self.field.iter().zip(bytes.iter()).for_each( - |(cell, byte)| { - assign!(region, cell, offset => *byte).unwrap(); - acc = acc * r + *byte; - } - ); - Ok(acc) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -enum PiCellType { - Storage1, - Storage2, - Byte, - LookupPi, - Lookup(Table), - -} -impl CellType for PiCellType { - fn byte_type() -> Option { - Some(Self::Byte) - } - fn storage_for_phase(phase: u8) -> Self { - match phase { - 1 => PiCellType::Storage1, - 2 => PiCellType::Storage2, - _ => unimplemented!() - } - } -} -impl Default for PiCellType { - fn default() -> Self { - Self::Storage1 - } -} - - - -#[derive(Debug, Clone)] -pub struct PublicData { - evidence: Token, - block_context: BlockContext, - _phantom: PhantomData, -} - -impl Default for PublicData { - fn default() -> Self { - Self::new(&witness::Block::default()) - } -} - -impl PublicData { - fn new(block: &witness::Block) -> Self { - let meta_hash = Token::FixedBytes(block.protocol_instance.meta_hash.hash().to_word().to_be_bytes().to_vec()); - let parent_hash = Token::FixedBytes(block.protocol_instance.parent_hash.to_word().to_be_bytes().to_vec()); - let block_hash = Token::FixedBytes(block.protocol_instance.block_hash.to_word().to_be_bytes().to_vec()); - let signal_root = Token::FixedBytes(block.protocol_instance.signal_root.to_word().to_be_bytes().to_vec()); - let graffiti = Token::FixedBytes(block.protocol_instance.graffiti.to_word().to_be_bytes().to_vec()); - let prover = Token::Address(block.protocol_instance.prover); - Self { - evidence: Token::FixedArray(vec![ - meta_hash, - parent_hash, - block_hash, - signal_root, - graffiti, - prover, - ]), - block_context: block.context.clone(), - _phantom: PhantomData - } - } - - fn set_field(&mut self, idx: usize, bytes: Vec) { - match self.evidence { - Token::FixedArray(ref mut tokens) => { - tokens[idx] = match tokens[idx].clone() { - Token::Bytes(_) => Token::Bytes(bytes), - Token::FixedBytes(_) => Token::FixedBytes(bytes), - Token::Address(_) => Token::Address( - H160::from(&bytes.try_into().expect("Wrong number of bytes for address") - )), - _ => unreachable!(), - }; - } - _ => unreachable!(), - } - } - - fn encode_raw(&self) -> Vec { - encode(&[self.evidence.clone()]) - } - - fn encode_field(&self, idx: usize) -> Vec { - let field = match self.evidence { - Token::FixedArray(ref tokens) => tokens[idx].clone(), - _ => unreachable!(), - }; - let res = encode(&[field]); - res.into_iter().rev().collect() - } - - fn assignment(&self, idx: usize) -> Vec { - self.encode_field(idx) - .iter() - .map(|b| F::from(0 as u64)) - .collect() - } - - fn keccak_hi_low(&self) -> [F; 2] { - let keccaked_pi = keccak256(self.encode_raw()); - [ - keccaked_pi - .iter() - .take(16) - .fold(F::ZERO, |acc: F, byte| { - acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) - }), - keccaked_pi - .iter() - .skip(16) - .fold(F::ZERO, |acc: F, byte| { - acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) - }) - ] - } - - fn keccak(&self) -> Vec { - keccak256(self.encode_raw()).to_vec() - } - - fn total_len(&self) -> usize { - self.encode_raw().len() - } - - fn field_len(&self, idx: usize) -> usize { - self.encode_field(idx).len() - } - - -} - -#[derive(Clone, Debug)] -pub struct TaikoPiCircuitConfig { - q_enable: Selector, - keccak_instance: Column, // equality - keccak_hi_lo: Vec>, // hi, lo - keccak_bytes: FieldGadget, - - meta_hash: FieldGadget, - // block number, blockhash - parent_hash: (Cell, FieldGadget), - block_hash: (Cell, FieldGadget), - signal_root: FieldGadget, - graffiti: FieldGadget, - prover: FieldGadget, - - block_table: BlockTable, - keccak_table: KeccakTable, - byte_table: ByteTable, - - annotation_configs: Vec>, -} - -pub struct TaikoPiCircuitConfigArgs { - /// - pub evidence: PublicData, - /// BlockTable - pub block_table: BlockTable, - /// KeccakTable - pub keccak_table: KeccakTable, - /// ByteTable - pub byte_table: ByteTable, - /// Challenges - pub challenges: Challenges>, -} - - -impl SubCircuitConfig for TaikoPiCircuitConfig { - type ConfigArgs = TaikoPiCircuitConfigArgs; - /// Return a new TaikoPiCircuitConfig - fn new( - meta: &mut ConstraintSystem, - Self::ConfigArgs { - evidence, - block_table, - keccak_table, - byte_table, - challenges, - }: Self::ConfigArgs, - ) -> Self { - let keccak_r = challenges.keccak_input(); - let evm_word = challenges.evm_word(); - let cm = CellManager::new( - meta, - vec![ - (PiCellType::Byte, 1, 1, false), - (PiCellType::Storage1, 1, 1, true), - (PiCellType::Storage2, 1, 1, true), - ], - 0, - evidence.total_len() + PADDING_LEN, - ); - let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(cm.clone()), Some(evm_word.expr())); - cb.preload_tables(meta, - &[ - (PiCellType::Lookup(Table::Keccak), &keccak_table), - (PiCellType::Lookup(Table::Bytecode), &byte_table), - (PiCellType::Lookup(Table::Block), &block_table) - ] - ); - let q_enable = meta.complex_selector(); - let public_input = meta.instance_column(); - - let keccak_output = [(); 2].iter().map(|_| cb.query_one(PiCellType::Storage2)).collect::>(); - let keccak_bytes = FieldGadget::config(&mut cb, PADDING_LEN); - - let meta_hash = FieldGadget::config(&mut cb, evidence.field_len(0)); - let parent_hash = ( - cb.query_one(PiCellType::Storage1), - FieldGadget::config(&mut cb, evidence.field_len(1)), - // cb.query_one(PiCellType::Storage2), - ); - let block_hash =( - cb.query_one(PiCellType::Storage1), - FieldGadget::config(&mut cb, evidence.field_len(2)), - // cb.query_one(PiCellType::Storage2), - ); - let signal_root = FieldGadget::config(&mut cb, evidence.field_len(3)); - let graffiti = FieldGadget::config(&mut cb, evidence.field_len(4)); - let prover = FieldGadget::config(&mut cb, evidence.field_len(5)); - - meta.create_gate( - "PI acc constraints", - |meta| { - circuit!([meta, cb], { - for (n, b) in [parent_hash.clone() , block_hash.clone()] { - require!( - ( - BlockContextFieldTag::BlockHash.expr(), - n.expr(), - /* 0.expr()/ */ b.acc(1.expr()) - ) => @PiCellType::Lookup(Table::Block), (TO_FIX) - ); - println!( - "require ({:?}, {:?}, {:?})", - BlockContextFieldTag::BlockHash, - n.expr().identifier(), - b.acc(1.expr()).identifier() - ); - } - let keccak_input = [ - meta_hash.clone(), - parent_hash.1.clone(), - block_hash.1.clone(), - signal_root.clone(), - graffiti.clone(), - prover.clone(), - ].iter().fold(0.expr(), |acc, gadget| { - let mult = (0..gadget.len).fold(1.expr(), |acc, _| acc * keccak_r.expr()); - acc * mult + gadget.acc(keccak_r.expr()) - }); - println!("config keccak len {:?}", evidence.total_len()); - // require!( - // ( - // 1.expr(), - // keccak_input, - // evidence.total_len().expr(), - // keccak_bytes.acc(evm_word.expr()) - // ) - // => @PiCellType::Lookup(Table::Keccak), (TO_FIX) - // ); - let hi_lo = keccak_bytes.hi_low_field(); - keccak_output.iter().zip(hi_lo.iter()).for_each(|(output, epxr)| { - require!(output.expr() => epxr); - cb.enable_equality(output.column()); - }); - }); - cb.build_constraints(Some(meta.query_selector(q_enable))) - } - ); - cb.build_lookups( - meta, - &[cm.clone()], - &[ - (PiCellType::Byte, PiCellType::Lookup(Table::Bytecode)), - (PiCellType::Lookup(Table::Keccak), PiCellType::Lookup(Table::Keccak)), - (PiCellType::Lookup(Table::Block), PiCellType::Lookup(Table::Block)), - ], - Some(q_enable) - ); - let annotation_configs = cm.columns().to_vec(); - Self { - q_enable, - keccak_instance: public_input, - keccak_hi_lo: keccak_output, - keccak_bytes, - meta_hash, - parent_hash, - block_hash, - signal_root, - graffiti, - prover, - block_table, - keccak_table, - byte_table, - annotation_configs - } - } -} - -impl TaikoPiCircuitConfig { - pub(crate) fn assign( - &self, - layouter: &mut impl Layouter, - // challenge: &Challenges>, - evidence: &PublicData, - ) -> Result<(), Error> { - let pi_cells = layouter.assign_region( - || "Pi", - |mut region| { - self.q_enable.enable(&mut region, 0)?; - let mut region = CachedRegion::new(&mut region); - region.annotate_columns(&self.annotation_configs); - - let mut acc = F::ZERO; - let mut offset = 0; - let mut idx = 0; - [ - &self.meta_hash, - &self.parent_hash.1, - &self.block_hash.1, - &self.signal_root, - &self.graffiti, - &self.prover, - ].iter().for_each(|gadget| { - println!("assignment {:?}: {:?}, {:?}", idx, offset, evidence.encode_field(idx)); - gadget.assign(&mut region, offset, &evidence.assignment(idx)) - .expect(&format!("FieldGadget assignment failed at {:?}", idx)); - offset += evidence.field_len(idx); - idx += 1; - }); - - println!("evidence.block_context.number: {:?}\n", evidence.block_context.number); - assign!(region, self.parent_hash.0, 0 => (evidence.block_context.number - 1).as_u64().scalar()); - assign!(region, self.block_hash.0, 0 => (evidence.block_context.number).as_u64().scalar()); - - // self.keccak_bytes.assign(&mut region, offset, &bytes_to_fields(evidence.keccak())); - // println!("assing keccak bytes: {:?}", evidence.keccak()); - // self.keccak_hi_lo - // .iter() - // .zip(evidence.keccak_hi_low().iter()) - // .for_each(|(cell, val)| { - // assign!(region, cell, offset => *val); - // }); - - Ok(()) - }); - Ok(()) - } -} -/// Public Inputs Circuit -#[derive(Clone, Debug, Default)] -pub struct TaikoPiCircuit { - /// PublicInputs data known by the verifier - pub evidence: PublicData, -} - -impl TaikoPiCircuit { - /// Creates a new TaikoPiCircuit - pub fn new(evidence: PublicData) -> Self { - Self { - evidence: evidence, - } - } -} - -impl SubCircuit for TaikoPiCircuit { - type Config = TaikoPiCircuitConfig; - - fn unusable_rows() -> usize { - // No column queried at more than 3 distinct rotations, so returns 6 as - // minimum unusable rows. - PublicData::::default().total_len() + 3 - } - - fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { - // TODO(Cecilia): what is the first field? - (0, PublicData::new(block).total_len()) - } - - fn new_from_block(block: &witness::Block) -> Self { - TaikoPiCircuit::new(PublicData::new(block)) - } - - /// Compute the public inputs for this circuit. - fn instance(&self) -> Vec> { - vec![ self.evidence.keccak_hi_low().to_vec()] - } - - /// Make the assignments to the PiCircuit - fn synthesize_sub( - &self, - config: &Self::Config, - _challenges: &Challenges>, - layouter: &mut impl Layouter, - ) -> Result<(), Error> { - config.byte_table.load(layouter)?; - config.assign(layouter, &self.evidence) - } -} - -#[cfg(any(feature = "test", test))] -impl Circuit for TaikoPiCircuit { - type Config = (TaikoPiCircuitConfig, Challenges); - type FloorPlanner = SimpleFloorPlanner; - type Params = PublicData; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn params(&self) -> Self::Params { - self.evidence.clone() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - Self::configure_with_params(meta, PublicData::default()) - } - - fn configure_with_params( - meta: &mut ConstraintSystem, - params: Self::Params, - ) -> Self::Config { - let block_table = BlockTable::construct(meta); - let keccak_table = KeccakTable::construct(meta); - let byte_table = ByteTable::construct(meta); - let challenges = Challenges::construct(meta); - let challenge_exprs = challenges.exprs(meta); - ( - TaikoPiCircuitConfig::new( - meta, - TaikoPiCircuitConfigArgs { - evidence: params, - block_table, - keccak_table, - byte_table, - challenges: challenge_exprs, - } - ), - challenges - ) - } - - fn synthesize( - &self, - (config, challenges): Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - let challenges = challenges.values(&mut layouter); - let evidance = self.params(); - let randomness = challenges.evm_word(); - // assign block table - config - .block_table - .load(&mut layouter, &evidance.block_context, randomness)?; - // assign keccak table - config - .keccak_table - .dev_load(&mut layouter, vec![&evidance.encode_raw()], &challenges)?; - config.byte_table.load(&mut layouter)?; - - self.synthesize_sub(&config, &challenges, &mut layouter) - } - -} - -#[cfg(test)] -mod taiko_pi_circuit_test { - - use std::vec; - - use super::*; - - use eth_types::ToScalar; - use halo2_proofs::{ - dev::{MockProver, VerifyFailure}, - halo2curves::bn256::Fr, - }; - use lazy_static::lazy_static; - use pretty_assertions::assert_eq; - - lazy_static! { - static ref OMMERS_HASH: H256 = H256::from_slice( - &hex::decode("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347") - .unwrap(), - ); - } - - fn run( - k: u32, - evidence: PublicData, - pi: Option>>, - ) -> Result<(), Vec> { - let circuit = TaikoPiCircuit::new(evidence); - let public_inputs = pi.unwrap_or_else(|| circuit.instance()); - let prover = match MockProver::run(k, &circuit, public_inputs) { - Ok(prover) => prover, - Err(e) => panic!("{:#?}", e), - }; - prover.verify() - } - - fn mock_public_data() -> PublicData { - let A = OMMERS_HASH.clone()/* H256::default() */; - let mut evidence = PublicData::default(); - // meta_hash - evidence.set_field(0, A.to_fixed_bytes().to_vec()); - // block_hash - evidence.set_field(2, A.to_fixed_bytes().to_vec()); - evidence.block_context.block_hash = A.to_word(); - evidence.block_context.history_hashes = vec![Default::default(); 256]; - evidence.block_context.number = 300.into(); - evidence - } - - #[test] - fn test_default_pi() { - let evidence = mock_public_data(); - - let k = 17; - assert_eq!(run::(k, evidence, None), Ok(())); - } - - #[test] - fn test(){ - println!("test"); - let mut evidence = PublicData::::default(); - let data = hex::decode("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef").unwrap(); - evidence.set_field(0, data.clone()); - evidence.set_field(1, vec![0u8; 32]); - evidence.set_field(2, data.clone()); - evidence.set_field(3, vec![0u8; 32]); - evidence.set_field(4, vec![0u8; 32]); - evidence.set_field(5, vec![0u8; 20]); - - // evidence.parent_hash = Token::FixedBytes(vec![0u8; 32]); - // evidence.block_hash = Token::FixedBytes(data); - // evidence.signal_root = Token::FixedBytes(vec![0u8; 32]); - // evidence.graffiti = Token::FixedBytes(vec![0u8; 32]); - // evidence.prover = Token::Address([0x22u8; 20].into()); - let encode_raw = evidence.encode_raw(); - println!("abi.encode {:?}\nkeccak {:?}\nhi-lo {:?}", encode_raw.clone(), keccak256(encode_raw), evidence.keccak_hi_low()); - } - -} - - diff --git a/zkevm-circuits/src/taiko_pi_circuit__.rs b/zkevm-circuits/src/taiko_pi_circuit__.rs deleted file mode 100644 index f211865f3f..0000000000 --- a/zkevm-circuits/src/taiko_pi_circuit__.rs +++ /dev/null @@ -1,695 +0,0 @@ - - -use bus_mapping::evm; -use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256, H160, U256}; -use ethers_core::abi::*; -use ethers_core::abi::FixedBytes; -use ethers_core::utils::keccak256; -use halo2_proofs::circuit::{Value, Layouter, SimpleFloorPlanner, AssignedCell}; -use halo2_proofs::poly::Rotation; -use itertools::Itertools; -use std::convert::TryInto; -use std::marker::PhantomData; -use gadgets::util::{Expr, Scalar}; -use halo2_proofs::plonk::{Expression, ConstraintSystem, Selector, Instance, Column, Circuit}; -use keccak256::keccak_arith::Keccak; -use halo2_proofs::plonk::Error; -use core::result::Result; -use crate::circuit_tools::cached_region::CachedRegion; -use crate::circuit_tools::cell_manager::{Cell, CellType, CellManager, CellColumn}; -use crate::circuit_tools::constraint_builder::{ConstraintBuilder, TO_FIX, RLCable, ExprVec}; -use crate::evm_circuit::table::Table; -use crate::evm_circuit::util::rlc; -use crate::util::{Challenges, SubCircuitConfig, SubCircuit}; -use crate::witness::{self, Bytecode, BlockContext}; -use crate::{circuit, assign}; -use crate::table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}; - -const BYTE_POW_BASE: u64 = 1 << 8; -const PADDING_LEN: usize = 32; -const META_HASH: usize = 0; -const PARENT_HASH: usize = 1; -const BLOCK_HASH: usize = 2; -const SIGNAL_ROOT: usize = 3; -const GRAFFITI: usize = 4; -const PROVER: usize = 5; -const S1: PiCellType = PiCellType::Storage1; -const S2: PiCellType = PiCellType::Storage2; -/// -#[derive(Debug, Clone, Default)] -pub struct FieldGadget { - field: Vec>, - len: usize, -} - -impl FieldGadget { - fn config(cb: &mut ConstraintBuilder, len: usize) -> Self { - Self { - field: cb.query_cells_dyn(PiCellType::Byte, len), - len - } - } - - fn bytes_expr(&self) -> Vec> { - self.field.iter().map(|f| f.expr()).collect() - } - - fn acc(&self, r: Expression) -> Expression { - //0.expr() - self.bytes_expr().rlc_rev(&r) - } - - pub(crate) fn hi_low_field(&self) -> [Expression; 2] { - assert!(self.len == 32); - let hi = self.bytes_expr()[..16].to_vec(); - let low = self.bytes_expr()[16..].to_vec(); - [hi.rlc_rev(&BYTE_POW_BASE.expr()), low.rlc_rev(&BYTE_POW_BASE.expr())] - } - - fn assign( - &self, - region: &mut CachedRegion<'_, '_, F>, - offset: usize, - bytes: &[F], - ) -> Result>, Error> { - assert!(bytes.len() == self.len); - let cells = self.field.iter().zip(bytes.iter()).map( - |(cell, byte)| { - assign!(region, cell, offset => *byte).unwrap() - } - ).collect(); - Ok(cells) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -enum PiCellType { - Storage1, - Storage2, - Byte, - LookupPi, - Lookup(Table), - -} -impl CellType for PiCellType { - fn byte_type() -> Option { - Some(Self::Byte) - } - fn storage_for_phase(phase: u8) -> Self { - match phase { - 1 => PiCellType::Storage1, - 2 => PiCellType::Storage2, - _ => unimplemented!() - } - } -} -impl Default for PiCellType { - fn default() -> Self { - Self::Storage1 - } -} - - - - -#[derive(Debug, Clone)] -pub struct PublicData { - evidence: Token, - block_context: BlockContext, - _phantom: PhantomData, -} - -impl Default for PublicData { - fn default() -> Self { - // has to have at least one history hash, block number must start with at least one - let mut ret = Self::new(&witness::Block::default()); - ret.block_context.history_hashes = vec![U256::default()]; - ret - } -} - -impl PublicData { - fn new(block: &witness::Block) -> Self { - let meta_hash = Token::FixedBytes(block.protocol_instance.meta_hash.hash().to_word().to_be_bytes().to_vec()); - let parent_hash = Token::FixedBytes(block.protocol_instance.parent_hash.to_word().to_be_bytes().to_vec()); - let block_hash = Token::FixedBytes(block.protocol_instance.block_hash.to_word().to_be_bytes().to_vec()); - let signal_root = Token::FixedBytes(block.protocol_instance.signal_root.to_word().to_be_bytes().to_vec()); - let graffiti = Token::FixedBytes(block.protocol_instance.graffiti.to_word().to_be_bytes().to_vec()); - let prover = Token::Address(block.protocol_instance.prover); - Self { - evidence: Token::FixedArray(vec![ - meta_hash, - parent_hash, - block_hash, - signal_root, - graffiti, - prover, - ]), - block_context: block.context.clone(), - _phantom: PhantomData - } - } - - fn set_field(&mut self, idx: usize, bytes: Vec) { - match self.evidence { - Token::FixedArray(ref mut tokens) => { - tokens[idx] = match tokens[idx].clone() { - Token::Bytes(_) => Token::Bytes(bytes), - Token::FixedBytes(_) => Token::FixedBytes(bytes), - Token::Address(_) => Token::Address( - H160::from(&bytes.try_into().expect("Wrong number of bytes for address") - )), - _ => unreachable!(), - }; - } - _ => unreachable!(), - } - } - - fn encode_raw(&self) -> Vec { - encode(&[self.evidence.clone()]) - } - - fn encode_field(&self, idx: usize) -> Vec { - let field = match self.evidence { - Token::FixedArray(ref tokens) => tokens[idx].clone(), - _ => unreachable!(), - }; - encode(&[field]) - } - - fn total_acc(&self, r: Value) -> F { - let mut rand = F::ZERO; - r.map(|r| rand = r); - self.encode_raw() - .iter() - .fold(F::ZERO, |acc: F, byte| { - acc * rand + F::from(*byte as u64) - }) - } - - fn assignment(&self, idx: usize) -> Vec { - self.encode_field(idx) - .iter() - .map(|b| F::from(*b as u64)) - .collect() - } - - fn assignment_acc(&self, idx: usize, r: Value) -> F { - let mut rand = F::ZERO; - r.map(|r| rand = r); - rlc::value(self.encode_field(idx).iter().rev(), rand) - } - - fn keccak_hi_low(&self) -> [F; 2] { - let keccaked_pi = keccak256(self.encode_raw()); - [ - keccaked_pi - .iter() - .take(16) - .fold(F::ZERO, |acc: F, byte| { - acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) - }), - keccaked_pi - .iter() - .skip(16) - .fold(F::ZERO, |acc: F, byte| { - acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) - }) - ] - } - - fn keccak(&self) -> Vec { - keccak256(self.encode_raw()).to_vec() - } - - fn keccak_assignment(&self) -> Vec { - self.keccak() - .iter() - .map(|b| F::from(*b as u64)) - .collect() - } - - fn total_len(&self) -> usize { - self.encode_raw().len() - } - - fn field_len(&self, idx: usize) -> usize { - self.encode_field(idx).len() - } - - -} - -#[derive(Clone, Debug)] -pub struct TaikoPiCircuitConfig { - q_enable: Selector, - keccak_instance: Column, // equality - - meta_hash: FieldGadget, - parent_hash: (Cell, FieldGadget, Cell), - block_hash: (Cell, FieldGadget, Cell), - signal_root: FieldGadget, - graffiti: FieldGadget, - prover: FieldGadget, - - total_acc: Cell, - keccak_bytes: FieldGadget, - keccak_hi_lo: [Cell; 2], - - block_table: BlockTable, - keccak_table: KeccakTable, - byte_table: ByteTable, - - annotation_configs: Vec>, -} - -pub struct TaikoPiCircuitConfigArgs { - /// - pub evidence: PublicData, - /// BlockTable - pub block_table: BlockTable, - /// KeccakTable - pub keccak_table: KeccakTable, - /// ByteTable - pub byte_table: ByteTable, - /// Challenges - pub challenges: Challenges>, -} - - -impl SubCircuitConfig for TaikoPiCircuitConfig { - type ConfigArgs = TaikoPiCircuitConfigArgs; - /// Return a new TaikoPiCircuitConfig - fn new( - meta: &mut ConstraintSystem, - Self::ConfigArgs { - evidence, - block_table, - keccak_table, - byte_table, - challenges, - }: Self::ConfigArgs, - ) -> Self { - let keccak_r = challenges.keccak_input(); - let evm_word = challenges.evm_word(); - let cm = CellManager::new( - meta, - vec![ - (PiCellType::Byte, 1, 1, false), - (PiCellType::Storage1, 1, 1, true), - (PiCellType::Storage2, 1, 1, true), - ], - 0, - evidence.total_len() + PADDING_LEN, - ); - let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(cm.clone()), Some(evm_word.expr())); - cb.preload_tables(meta, - &[ - (PiCellType::Lookup(Table::Keccak), &keccak_table), - (PiCellType::Lookup(Table::Bytecode), &byte_table), - (PiCellType::Lookup(Table::Block), &block_table) - ] - ); - let q_enable = meta.complex_selector(); - let keccak_instance = meta.instance_column(); - meta.enable_equality(keccak_instance); - - let meta_hash = FieldGadget::config(&mut cb, evidence.field_len(META_HASH)); - let parent_hash =( - cb.query_one(S1), - FieldGadget::config(&mut cb, evidence.field_len(PARENT_HASH)), - cb.query_one(S2) - ); - let block_hash =( - cb.query_one(S1), - FieldGadget::config(&mut cb, evidence.field_len(BLOCK_HASH)), - cb.query_one(S2) - ); - let signal_root = FieldGadget::config(&mut cb, evidence.field_len(SIGNAL_ROOT)); - let graffiti = FieldGadget::config(&mut cb, evidence.field_len(GRAFFITI)); - let prover = FieldGadget::config(&mut cb, evidence.field_len(PROVER)); - - let total_acc = cb.query_one(S2); - let keccak_bytes = FieldGadget::config(&mut cb, PADDING_LEN); - let keccak_hi_lo = [cb.query_one(S1), cb.query_one(S1)]; - meta.create_gate( - "PI acc constraints", - |meta| { - circuit!([meta, cb], { - for (n, b, acc) in [parent_hash.clone() , block_hash.clone()] { - require!(acc.expr() => b.acc(evm_word.expr())); - require!( - ( - BlockContextFieldTag::BlockHash.expr(), - n.expr(), - acc.expr() - ) => @PiCellType::Lookup(Table::Block), (TO_FIX) - ); - println!( - "require ({:?}, {:?}, {:?})", - BlockContextFieldTag::BlockHash, - n.expr().identifier(), - b.acc(evm_word.expr()).identifier() - ); - } - let acc_val = [ - meta_hash.clone(), - parent_hash.1.clone(), - block_hash.1.clone(), - signal_root.clone(), - graffiti.clone(), - prover.clone(), - ].iter().fold(0.expr(), |acc, gadget| { - let mult = (0..gadget.len).fold(1.expr(), |acc, _| acc * keccak_r.expr()); - acc * mult + gadget.acc(keccak_r.expr()) - }); - require!(total_acc.expr() => acc_val); - require!( - ( - 1.expr(), - total_acc.expr(), - evidence.total_len().expr(), - keccak_bytes.acc(evm_word.expr()) - ) - => @PiCellType::Lookup(Table::Keccak), (TO_FIX) - ); - let hi_lo = keccak_bytes.hi_low_field(); - keccak_hi_lo.iter().zip(hi_lo.iter()).for_each(|(cell, epxr)| { - require!(cell.expr() => epxr); - cb.enable_equality(cell.column()); - }); - }); - cb.build_constraints(Some(meta.query_selector(q_enable))) - } - ); - cb.build_lookups( - meta, - &[cm.clone()], - &[ - (PiCellType::Byte, PiCellType::Lookup(Table::Bytecode)), - (PiCellType::Lookup(Table::Keccak), PiCellType::Lookup(Table::Keccak)), - (PiCellType::Lookup(Table::Block), PiCellType::Lookup(Table::Block)), - ], - Some(q_enable) - ); - let annotation_configs = cm.columns().to_vec(); - Self { - q_enable, - keccak_instance, - meta_hash, - parent_hash, - block_hash, - signal_root, - graffiti, - prover, - total_acc, - keccak_bytes, - keccak_hi_lo, - block_table, - keccak_table, - byte_table, - annotation_configs - } - } -} - -impl TaikoPiCircuitConfig { - pub(crate) fn assign( - &self, - layouter: &mut impl Layouter, - challenge: &Challenges>, - evidence: &PublicData, - ) -> Result<(), Error> { - let evm_word = challenge.evm_word(); - let keccak_r = challenge.keccak_input(); - let mut hi_lo_cells = layouter.assign_region( - || "Pi", - |mut region| { - self.q_enable.enable(&mut region, 0)?; - let mut region = CachedRegion::new(&mut region); - region.annotate_columns(&self.annotation_configs); - - println!("evidence.block_context.number: {:?}\n", evidence.block_context.number); - assign!(region, self.parent_hash.0, 0 => (evidence.block_context.number - 1).as_u64().scalar()); - assign!(region, self.parent_hash.2, 0 => evidence.assignment_acc(PARENT_HASH, evm_word)); - assign!(region, self.block_hash.0, 0 => (evidence.block_context.number).as_u64().scalar()); - assign!(region, self.block_hash.2, 0 => evidence.assignment_acc(BLOCK_HASH, evm_word)); - - let mut acc = F::ZERO; - let mut idx = 0; - [ - &self.meta_hash, - &self.parent_hash.1, - &self.block_hash.1, - &self.signal_root, - &self.graffiti, - &self.prover, - ].iter().for_each(|gadget| { - println!("assignment {:?}:\n{:?}", idx, evidence.encode_field(idx)); - gadget.assign(&mut region, 0, &evidence.assignment(idx)) - .expect(&format!("FieldGadget assignment failed at {:?}", idx)); - idx += 1; - }); - self.keccak_bytes.assign(&mut region, 0, &evidence.keccak_assignment()) - .expect("Keccak bytes assignment failed"); - assign!(region, self.total_acc, 0 => evidence.total_acc(keccak_r))?; - let hi_low_assignment = evidence.keccak_hi_low(); - let hi = assign!(region, self.keccak_hi_lo[0], 0 => hi_low_assignment[0])?; - let lo = assign!(region, self.keccak_hi_lo[1], 0 => hi_low_assignment[1])?; - - Ok([hi, lo]) - })?; - for (i, cell) in hi_lo_cells.iter().enumerate() { - layouter.constrain_instance(cell.cell(), self.keccak_instance, i)?; - println!("cell: {:?}, keccak_instance: {:?}, i: {:?}", cell.cell(), self.keccak_instance, i); - } - Ok(()) - } -} -/// Public Inputs Circuit -#[derive(Clone, Debug, Default)] -pub struct TaikoPiCircuit { - /// PublicInputs data known by the verifier - pub evidence: PublicData, -} - -impl TaikoPiCircuit { - /// Creates a new TaikoPiCircuit - pub fn new(evidence: PublicData) -> Self { - Self { - evidence: evidence, - } - } -} - -impl SubCircuit for TaikoPiCircuit { - type Config = TaikoPiCircuitConfig; - - fn unusable_rows() -> usize { - // No column queried at more than 3 distinct rotations, so returns 6 as - // minimum unusable rows. - PublicData::::default().total_len() + 3 - } - - fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { - // TODO(Cecilia): what is the first field? - (0, PublicData::new(block).total_len()) - } - - fn new_from_block(block: &witness::Block) -> Self { - TaikoPiCircuit::new(PublicData::new(block)) - } - - /// Compute the public inputs for this circuit. - fn instance(&self) -> Vec> { - vec![ self.evidence.keccak_hi_low().to_vec()] - } - - /// Make the assignments to the PiCircuit - fn synthesize_sub( - &self, - config: &Self::Config, - challenges: &Challenges>, - layouter: &mut impl Layouter, - ) -> Result<(), Error> { - config.byte_table.load(layouter)?; - config.assign(layouter, challenges, &self.evidence) - } -} - -#[cfg(any(feature = "test", test))] -impl Circuit for TaikoPiCircuit { - type Config = (TaikoPiCircuitConfig, Challenges); - type FloorPlanner = SimpleFloorPlanner; - type Params = PublicData; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn params(&self) -> Self::Params { - self.evidence.clone() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - Self::configure_with_params(meta, PublicData::default()) - } - - fn configure_with_params( - meta: &mut ConstraintSystem, - params: Self::Params, - ) -> Self::Config { - let block_table = BlockTable::construct(meta); - let keccak_table = KeccakTable::construct(meta); - let byte_table = ByteTable::construct(meta); - let challenges = Challenges::construct(meta); - let challenge_exprs = challenges.exprs(meta); - ( - TaikoPiCircuitConfig::new( - meta, - TaikoPiCircuitConfigArgs { - evidence: params, - block_table, - keccak_table, - byte_table, - challenges: challenge_exprs, - } - ), - challenges - ) - } - - fn synthesize( - &self, - (config, challenges): Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - let challenges = challenges.values(&mut layouter); - let evidance = self.params(); - let randomness = challenges.evm_word(); - // assign block table - config - .block_table - .load(&mut layouter, &evidance.block_context, randomness)?; - // assign keccak table - config - .keccak_table - .dev_load(&mut layouter, vec![&evidance.encode_raw()], &challenges)?; - config.byte_table.load(&mut layouter)?; - - self.synthesize_sub(&config, &challenges, &mut layouter) - } - -} - -#[cfg(test)] -mod taiko_pi_circuit_test { - - use std::vec; - - use super::*; - - use eth_types::ToScalar; - use halo2_proofs::{ - dev::{MockProver, VerifyFailure}, - halo2curves::bn256::Fr, - }; - use lazy_static::lazy_static; - use pretty_assertions::assert_eq; - - lazy_static! { - static ref OMMERS_HASH: H256 = H256::from_slice( - &hex::decode("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347") - .unwrap(), - ); - } - - fn run( - k: u32, - evidence: PublicData, - pi: Option>>, - ) -> Result<(), Vec> { - let circuit = TaikoPiCircuit::new(evidence); - let keccak_instance = pi.unwrap_or_else(|| circuit.instance()); - println!("run instance: {:?}", keccak_instance); - let prover = match MockProver::run(k, &circuit, keccak_instance) { - Ok(prover) => prover, - Err(e) => panic!("{:#?}", e), - }; - prover.verify() - } - - fn mock_public_data() -> PublicData { - let mut evidence = PublicData::default(); - evidence.set_field(PARENT_HASH, OMMERS_HASH.to_fixed_bytes().to_vec()); - evidence.set_field(BLOCK_HASH, OMMERS_HASH.to_fixed_bytes().to_vec()); - evidence.block_context.number = 300.into(); - evidence.block_context.block_hash = OMMERS_HASH.to_word(); - // has to have at least one history block - evidence.block_context.history_hashes = vec![OMMERS_HASH.to_word()]; - evidence - } - - #[test] - fn test_default_pi() { - let evidence = mock_public_data(); - - let k = 17; - assert_eq!(run::(k, evidence, None), Ok(())); - } - - #[test] - fn test_fail_pi_hash() { - let evidence = mock_public_data(); - - let k = 17; - match run::(k, evidence, Some(vec![vec![Fr::zero(), Fr::one()]])) { - Ok(_) => unreachable!("this case must fail"), - Err(errs) => { - assert_eq!(errs.len(), 4); - for err in errs { - match err { - VerifyFailure::Permutation { .. } => return, - _ => unreachable!("unexpected error"), - } - } - } - } - } - - #[test] - fn test_simple_pi() { - let mut evidence = mock_public_data(); - let block_number = 1337u64; - evidence.block_context.number = block_number.into(); - evidence.block_context.history_hashes = vec![OMMERS_HASH.to_word()]; - evidence.set_field(PROVER, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19].into()); - - let k = 17; - assert_eq!(run::(k, evidence, None), Ok(())); - } - - #[test] - fn test_verify() { - let mut block = witness::Block::::default(); - - block.eth_block.parent_hash = *OMMERS_HASH; - block.eth_block.hash = Some(*OMMERS_HASH); - block.protocol_instance.block_hash = *OMMERS_HASH; - block.protocol_instance.parent_hash = *OMMERS_HASH; - block.context.history_hashes = vec![OMMERS_HASH.to_word()]; - block.context.block_hash = OMMERS_HASH.to_word(); - block.context.number = 300.into(); - - let evidence = PublicData::new(&block); - - let k = 17; - - assert_eq!(run::(k, evidence, None), Ok(())); - } - - -} - - diff --git a/zkevm-circuits/src/witness/block.rs b/zkevm-circuits/src/witness/block.rs index af1548d1fd..5cfd1fd7cc 100644 --- a/zkevm-circuits/src/witness/block.rs +++ b/zkevm-circuits/src/witness/block.rs @@ -144,8 +144,6 @@ pub struct BlockContext { impl BlockContext { /// Assignments for block table pub fn table_assignments(&self, randomness: Value) -> Vec<[Value; 3]> { - // let randomness: Value = Value::known(F::from(0u64)); - println!("block table {:?} (le_bytes) {:?} evm_word = {:?}", self.number, self.block_hash.to_le_bytes(), randomness); [ vec![ [ From 09cb220515da88000030871c4a0f34f9c2215c61 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Mon, 4 Sep 2023 06:22:40 +0800 Subject: [PATCH 37/43] fix super --- zkevm-circuits/src/taiko_pi_circuit.rs | 2 +- zkevm-circuits/src/taiko_super_circuit.rs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/zkevm-circuits/src/taiko_pi_circuit.rs b/zkevm-circuits/src/taiko_pi_circuit.rs index aa594e133c..d0da067da3 100644 --- a/zkevm-circuits/src/taiko_pi_circuit.rs +++ b/zkevm-circuits/src/taiko_pi_circuit.rs @@ -166,7 +166,7 @@ impl PublicData { } } - fn encode_raw(&self) -> Vec { + pub fn encode_raw(&self) -> Vec { encode(&[self.evidence.clone()]) } diff --git a/zkevm-circuits/src/taiko_super_circuit.rs b/zkevm-circuits/src/taiko_super_circuit.rs index 650700be56..ca57fd4665 100644 --- a/zkevm-circuits/src/taiko_super_circuit.rs +++ b/zkevm-circuits/src/taiko_super_circuit.rs @@ -7,7 +7,7 @@ pub mod test; use crate::{ anchor_tx_circuit::{AnchorTxCircuit, AnchorTxCircuitConfig, AnchorTxCircuitConfigArgs}, table::{byte_table::ByteTable, BlockTable, KeccakTable, PiTable, TxTable}, - taiko_pi_circuit::{TaikoPiCircuit, TaikoPiCircuitConfig, TaikoPiCircuitConfigArgs}, + taiko_pi_circuit::{TaikoPiCircuit, TaikoPiCircuitConfig, TaikoPiCircuitConfigArgs, PublicData}, util::{log2_ceil, Challenges, SubCircuit, SubCircuitConfig}, witness::{block_convert, Block, ProtocolInstance}, }; @@ -59,6 +59,7 @@ impl SubCircuitConfig for SuperCircuitConfig { let pi_circuit = TaikoPiCircuitConfig::new( meta, TaikoPiCircuitConfigArgs { + evidence: PublicData::default(), block_table: block_table.clone(), keccak_table: keccak_table.clone(), byte_table: byte_table.clone(), @@ -206,7 +207,7 @@ impl Circuit for SuperCircuit { .load(&mut layouter, &self.block.context, randomness)?; config.keccak_table.dev_load( &mut layouter, - vec![&self.pi_circuit.public_data.rpi_bytes()], + vec![&self.pi_circuit.evidence.encode_raw()], &challenges, )?; config.byte_table.load(&mut layouter)?; From 54c17643376d20a3e275941244fc4008b1dedffd Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Mon, 4 Sep 2023 06:23:52 +0800 Subject: [PATCH 38/43] cargo fix --- .../src/circuit_tools/constraint_builder.rs | 2 +- zkevm-circuits/src/taiko_pi_circuit.rs | 18 +++++++++--------- zkevm-circuits/src/witness/block.rs | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/zkevm-circuits/src/circuit_tools/constraint_builder.rs b/zkevm-circuits/src/circuit_tools/constraint_builder.rs index ba0aa15d14..8345015e2c 100644 --- a/zkevm-circuits/src/circuit_tools/constraint_builder.rs +++ b/zkevm-circuits/src/circuit_tools/constraint_builder.rs @@ -9,7 +9,7 @@ use crate::{evm_circuit::util::rlc, table::LookupTable, util::{Expr, query_expre use eth_types::Field; use gadgets::util::{and, sum, Scalar}; use halo2_proofs::{ - plonk::{ConstraintSystem, Expression, Column, Advice, Selector}, circuit::AssignedCell, + plonk::{ConstraintSystem, Expression, Column, Advice, Selector}, }; use itertools::Itertools; diff --git a/zkevm-circuits/src/taiko_pi_circuit.rs b/zkevm-circuits/src/taiko_pi_circuit.rs index d0da067da3..0db6b8839f 100644 --- a/zkevm-circuits/src/taiko_pi_circuit.rs +++ b/zkevm-circuits/src/taiko_pi_circuit.rs @@ -1,18 +1,18 @@ -use bus_mapping::evm; -use eth_types::{Address, Field, ToBigEndian, ToWord, Word, H256, H160, U256}; + +use eth_types::{Field, ToBigEndian, ToWord, H256, H160, U256}; use ethers_core::abi::*; -use ethers_core::abi::FixedBytes; + use ethers_core::utils::keccak256; use halo2_proofs::circuit::{Value, Layouter, SimpleFloorPlanner, AssignedCell}; -use halo2_proofs::poly::Rotation; + use itertools::Itertools; use std::convert::TryInto; use std::marker::PhantomData; use gadgets::util::{Expr, Scalar}; use halo2_proofs::plonk::{Expression, ConstraintSystem, Selector, Instance, Column, Circuit}; -use keccak256::keccak_arith::Keccak; + use halo2_proofs::plonk::Error; use core::result::Result; use crate::circuit_tools::cached_region::CachedRegion; @@ -21,7 +21,7 @@ use crate::circuit_tools::constraint_builder::{ConstraintBuilder, TO_FIX, RLCabl use crate::evm_circuit::table::Table; use crate::evm_circuit::util::rlc; use crate::util::{Challenges, SubCircuitConfig, SubCircuit}; -use crate::witness::{self, Bytecode, BlockContext}; +use crate::witness::{self, BlockContext}; use crate::{circuit, assign}; use crate::table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}; @@ -417,7 +417,7 @@ impl TaikoPiCircuitConfig { ) -> Result<(), Error> { let evm_word = challenge.evm_word(); let keccak_r = challenge.keccak_input(); - let mut hi_lo_cells = layouter.assign_region( + let hi_lo_cells = layouter.assign_region( || "Pi", |mut region| { self.q_enable.enable(&mut region, 0)?; @@ -429,7 +429,7 @@ impl TaikoPiCircuitConfig { assign!(region, self.block_hash.0, 0 => (evidence.block_context.number).as_u64().scalar()); assign!(region, self.block_hash.2, 0 => evidence.assignment_acc(BLOCK_HASH, evm_word)); - let mut acc = F::ZERO; + let _acc = F::ZERO; let mut idx = 0; [ &self.meta_hash, @@ -581,7 +581,7 @@ mod taiko_pi_circuit_test { use super::*; - use eth_types::ToScalar; + use halo2_proofs::{ dev::{MockProver, VerifyFailure}, halo2curves::bn256::Fr, diff --git a/zkevm-circuits/src/witness/block.rs b/zkevm-circuits/src/witness/block.rs index 5cfd1fd7cc..40cc4c1d70 100644 --- a/zkevm-circuits/src/witness/block.rs +++ b/zkevm-circuits/src/witness/block.rs @@ -10,8 +10,8 @@ use bus_mapping::{ circuit_input_builder::{self, CircuitsParams, CopyEvent, ExpEvent}, Error, }; -use eth_types::{Address, Field, ToLittleEndian, ToScalar, ToWord, Word, ToBigEndian}; -use gadgets::util::Scalar; +use eth_types::{Address, Field, ToLittleEndian, ToScalar, ToWord, Word}; + use halo2_proofs::circuit::Value; use super::{tx::tx_convert, Bytecode, ExecStep, ProtocolInstance, Rw, RwMap, Transaction}; From 1fd67433a674a13ce5fbfc4121e567903a8f2920 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Mon, 25 Sep 2023 15:14:58 +0700 Subject: [PATCH 39/43] update --- zkevm-circuits/src/taiko_pi_circuit.rs | 130 +++++++++++++++---------- 1 file changed, 76 insertions(+), 54 deletions(-) diff --git a/zkevm-circuits/src/taiko_pi_circuit.rs b/zkevm-circuits/src/taiko_pi_circuit.rs index 0db6b8839f..0e76bd32e0 100644 --- a/zkevm-circuits/src/taiko_pi_circuit.rs +++ b/zkevm-circuits/src/taiko_pi_circuit.rs @@ -7,7 +7,6 @@ use ethers_core::abi::*; use ethers_core::utils::keccak256; use halo2_proofs::circuit::{Value, Layouter, SimpleFloorPlanner, AssignedCell}; -use itertools::Itertools; use std::convert::TryInto; use std::marker::PhantomData; use gadgets::util::{Expr, Scalar}; @@ -33,8 +32,8 @@ const BLOCK_HASH: usize = 2; const SIGNAL_ROOT: usize = 3; const GRAFFITI: usize = 4; const PROVER: usize = 5; -const S1: PiCellType = PiCellType::Storage1; -const S2: PiCellType = PiCellType::Storage2; +const S1: PiCellType = PiCellType::StoragePhase1; +const S2: PiCellType = PiCellType::StoragePhase2; /// #[derive(Debug, Clone, Default)] pub struct FieldGadget { @@ -54,7 +53,7 @@ impl FieldGadget { self.field.iter().map(|f| f.expr()).collect() } - fn acc(&self, r: Expression) -> Expression { + fn rlc_acc(&self, r: Expression) -> Expression { //0.expr() self.bytes_expr().rlc_rev(&r) } @@ -84,34 +83,32 @@ impl FieldGadget { #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] enum PiCellType { - Storage1, - Storage2, + StoragePhase1, + StoragePhase2, Byte, LookupPi, Lookup(Table), - } + impl CellType for PiCellType { fn byte_type() -> Option { Some(Self::Byte) } fn storage_for_phase(phase: u8) -> Self { match phase { - 1 => PiCellType::Storage1, - 2 => PiCellType::Storage2, + 1 => PiCellType::StoragePhase1, + 2 => PiCellType::StoragePhase2, _ => unimplemented!() } } } impl Default for PiCellType { fn default() -> Self { - Self::Storage1 + Self::StoragePhase1 } } - - #[derive(Debug, Clone)] pub struct PublicData { evidence: Token, @@ -181,11 +178,7 @@ impl PublicData { fn total_acc(&self, r: Value) -> F { let mut rand = F::ZERO; r.map(|r| rand = r); - self.encode_raw() - .iter() - .fold(F::ZERO, |acc: F, byte| { - acc * rand + F::from(*byte as u64) - }) + rlc::value(self.encode_raw().iter().rev(), rand) } fn assignment(&self, idx: usize) -> Vec { @@ -204,18 +197,8 @@ impl PublicData { fn keccak_hi_low(&self) -> [F; 2] { let keccaked_pi = keccak256(self.encode_raw()); [ - keccaked_pi - .iter() - .take(16) - .fold(F::ZERO, |acc: F, byte| { - acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) - }), - keccaked_pi - .iter() - .skip(16) - .fold(F::ZERO, |acc: F, byte| { - acc * F::from(BYTE_POW_BASE) + F::from(*byte as u64) - }) + rlc::value(keccaked_pi[0..16].iter().rev(), BYTE_POW_BASE.scalar()), + rlc::value(keccaked_pi[16..].iter().rev(), BYTE_POW_BASE.scalar()), ] } @@ -296,12 +279,12 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { let cm = CellManager::new( meta, vec![ - (PiCellType::Byte, 1, 1, false), - (PiCellType::Storage1, 1, 1, true), - (PiCellType::Storage2, 1, 1, true), + (PiCellType::Byte, 7, 1, false), + (PiCellType::StoragePhase1, 1, 1, true), + (PiCellType::StoragePhase2, 1, 2, true), ], 0, - evidence.total_len() + PADDING_LEN, + 32, ); let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(cm.clone()), Some(evm_word.expr())); cb.preload_tables(meta, @@ -337,13 +320,13 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { "PI acc constraints", |meta| { circuit!([meta, cb], { - for (n, b, acc) in [parent_hash.clone() , block_hash.clone()] { - require!(acc.expr() => b.acc(evm_word.expr())); + for (block_number, block_hash, block_hash_rlc) in [parent_hash.clone() , block_hash.clone()] { + require!(block_hash_rlc.expr() => block_hash.rlc_acc(evm_word.expr())); require!( ( BlockContextFieldTag::BlockHash.expr(), - n.expr(), - acc.expr() + block_number.expr(), + block_hash_rlc.expr() ) => @PiCellType::Lookup(Table::Block), (TO_FIX) ); } @@ -356,7 +339,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { prover.clone(), ].iter().fold(0.expr(), |acc, gadget| { let mult = (0..gadget.len).fold(1.expr(), |acc, _| acc * keccak_r.expr()); - acc * mult + gadget.acc(keccak_r.expr()) + acc * mult + gadget.rlc_acc(keccak_r.expr()) }); require!(total_acc.expr() => acc_val); require!( @@ -364,7 +347,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { 1.expr(), total_acc.expr(), evidence.total_len().expr(), - keccak_bytes.acc(evm_word.expr()) + keccak_bytes.rlc_acc(evm_word.expr()) ) => @PiCellType::Lookup(Table::Keccak), (TO_FIX) ); @@ -581,7 +564,7 @@ mod taiko_pi_circuit_test { use super::*; - + use eth_types::H256; use halo2_proofs::{ dev::{MockProver, VerifyFailure}, halo2curves::bn256::Fr, @@ -590,10 +573,16 @@ mod taiko_pi_circuit_test { use pretty_assertions::assert_eq; lazy_static! { - static ref OMMERS_HASH: H256 = H256::from_slice( + static ref LAST_HASH: H256 = H256::from_slice( &hex::decode("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347") .unwrap(), ); + static ref THIS_HASH: H256 = H256::from_slice( + &hex::decode("1dcc4de8dec751111b85b567b6cc12fea12451b9480000000a142fd40d493111") + .unwrap(), + ); + static ref PROVER_ADDR: H160 = + H160::from_slice(&hex::decode("8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199").unwrap(),); } fn run( @@ -612,12 +601,12 @@ mod taiko_pi_circuit_test { fn mock_public_data() -> PublicData { let mut evidence = PublicData::default(); - evidence.set_field(PARENT_HASH, OMMERS_HASH.to_fixed_bytes().to_vec()); - evidence.set_field(BLOCK_HASH, OMMERS_HASH.to_fixed_bytes().to_vec()); + evidence.set_field(PARENT_HASH, LAST_HASH.to_fixed_bytes().to_vec()); + evidence.set_field(BLOCK_HASH, THIS_HASH.to_fixed_bytes().to_vec()); evidence.block_context.number = 300.into(); - evidence.block_context.block_hash = OMMERS_HASH.to_word(); + evidence.block_context.block_hash = THIS_HASH.to_word(); // has to have at least one history block - evidence.block_context.history_hashes = vec![OMMERS_HASH.to_word()]; + evidence.block_context.history_hashes = vec![LAST_HASH.to_word()]; evidence } @@ -630,7 +619,7 @@ mod taiko_pi_circuit_test { } #[test] - fn test_fail_pi_hash() { + fn test_fail_hi_lo() { let evidence = mock_public_data(); let k = 17; @@ -648,28 +637,61 @@ mod taiko_pi_circuit_test { } } + #[test] + fn test_fail_historical_hash() { + let mut block = witness::Block::::default(); + + block.eth_block.parent_hash = *LAST_HASH; + block.eth_block.hash = Some(*THIS_HASH); + block.protocol_instance.block_hash = *THIS_HASH; + block.protocol_instance.parent_hash = *LAST_HASH; + + // parent hash doesn't exist in table! + block.context.history_hashes = vec![THIS_HASH.to_word(), THIS_HASH.to_word()]; + block.context.block_hash = THIS_HASH.to_word(); + block.context.number = 300.into(); + + let evidence = PublicData::new(&block); + + let k = 17; + match run::(k, evidence, None) { + Ok(_) => unreachable!("this case must fail"), + Err(errs) => { + assert_eq!(errs.len(), 1); + for err in errs { + match err { + VerifyFailure::Lookup { .. } => return, + _ => unreachable!("unexpected error"), + } + } + } + } + } + + #[test] fn test_simple_pi() { let mut evidence = mock_public_data(); let block_number = 1337u64; evidence.block_context.number = block_number.into(); - evidence.block_context.history_hashes = vec![OMMERS_HASH.to_word()]; - evidence.set_field(PROVER, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19].into()); + evidence.block_context.history_hashes = vec![LAST_HASH.to_word()]; + evidence.set_field(PROVER, PROVER_ADDR.to_fixed_bytes().to_vec()); let k = 17; assert_eq!(run::(k, evidence, None), Ok(())); } + #[test] fn test_verify() { let mut block = witness::Block::::default(); - block.eth_block.parent_hash = *OMMERS_HASH; - block.eth_block.hash = Some(*OMMERS_HASH); - block.protocol_instance.block_hash = *OMMERS_HASH; - block.protocol_instance.parent_hash = *OMMERS_HASH; - block.context.history_hashes = vec![OMMERS_HASH.to_word()]; - block.context.block_hash = OMMERS_HASH.to_word(); + block.eth_block.parent_hash = *LAST_HASH; + block.eth_block.hash = Some(*THIS_HASH); + block.protocol_instance.block_hash = *THIS_HASH; + block.protocol_instance.parent_hash = *LAST_HASH; + block.context.history_hashes = vec![LAST_HASH.to_word()]; + block.context.block_hash = THIS_HASH.to_word(); block.context.number = 300.into(); let evidence = PublicData::new(&block); From e3910ba128f8e355c09c2b6595c5409b9f6bd298 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Mon, 25 Sep 2023 19:32:10 +0700 Subject: [PATCH 40/43] fmt --- gadgets/src/util.rs | 1 - .../src/circuit_tools/cached_region.rs | 21 +- .../src/circuit_tools/cell_manager.rs | 1 - .../src/circuit_tools/constraint_builder.rs | 311 +++++++++--------- zkevm-circuits/src/circuit_tools/memory.rs | 152 +++++---- zkevm-circuits/src/lib.rs | 2 +- zkevm-circuits/src/table/keccak_table.rs | 2 - zkevm-circuits/src/taiko_pi_circuit.rs | 278 +++++++++------- zkevm-circuits/src/taiko_super_circuit.rs | 4 +- 9 files changed, 400 insertions(+), 372 deletions(-) diff --git a/gadgets/src/util.rs b/gadgets/src/util.rs index 8ff5e3bcec..6a315375dd 100644 --- a/gadgets/src/util.rs +++ b/gadgets/src/util.rs @@ -169,7 +169,6 @@ macro_rules! impl_scalar { }; } - /// Trait that implements functionality to get a constant expression from /// commonly used types. pub trait Expr { diff --git a/zkevm-circuits/src/circuit_tools/cached_region.rs b/zkevm-circuits/src/circuit_tools/cached_region.rs index 07bc9f992a..fe2eadbb79 100644 --- a/zkevm-circuits/src/circuit_tools/cached_region.rs +++ b/zkevm-circuits/src/circuit_tools/cached_region.rs @@ -10,7 +10,10 @@ use std::{ hash::{Hash, Hasher}, }; -use super::{cell_manager::{CellType, CellColumn}, constraint_builder::ConstraintBuilder}; +use super::{ + cell_manager::{CellColumn, CellType}, + constraint_builder::ConstraintBuilder, +}; pub trait ChallengeSet { fn indexed(&self) -> Vec<&Value>; @@ -45,11 +48,17 @@ impl<'r, 'b, F: Field> CachedRegion<'r, 'b, F> { pub(crate) fn annotate_columns(&mut self, col_configs: &[CellColumn]) { for c in col_configs { - self.region - .name_column( - || format!("{:?} {:?}: {:?} queried", c.cell_type.clone(), c.index, c.height), - c.column - ); + self.region.name_column( + || { + format!( + "{:?} {:?}: {:?} queried", + c.cell_type.clone(), + c.index, + c.height + ) + }, + c.column, + ); } } diff --git a/zkevm-circuits/src/circuit_tools/cell_manager.rs b/zkevm-circuits/src/circuit_tools/cell_manager.rs index 7408577a69..0d5c8c53c1 100644 --- a/zkevm-circuits/src/circuit_tools/cell_manager.rs +++ b/zkevm-circuits/src/circuit_tools/cell_manager.rs @@ -235,7 +235,6 @@ impl Expr for CellColumn { } } - #[derive(Clone, Debug, Default)] pub struct CellManager { configs: Vec>, diff --git a/zkevm-circuits/src/circuit_tools/constraint_builder.rs b/zkevm-circuits/src/circuit_tools/constraint_builder.rs index 8345015e2c..0a9dbfd601 100644 --- a/zkevm-circuits/src/circuit_tools/constraint_builder.rs +++ b/zkevm-circuits/src/circuit_tools/constraint_builder.rs @@ -1,16 +1,19 @@ //! Circuit utilities use std::{ collections::HashMap, + marker::PhantomData, ops::{Add, Mul}, - vec, marker::PhantomData, + vec, }; -use crate::{evm_circuit::util::rlc, table::LookupTable, util::{Expr, query_expression}}; +use crate::{ + evm_circuit::util::rlc, + table::LookupTable, + util::{query_expression, Expr}, +}; use eth_types::Field; use gadgets::util::{and, sum, Scalar}; -use halo2_proofs::{ - plonk::{ConstraintSystem, Expression, Column, Advice, Selector}, -}; +use halo2_proofs::plonk::{Advice, Column, ConstraintSystem, Expression, Selector}; use itertools::Itertools; use super::{ @@ -64,7 +67,7 @@ impl TableData { } } -struct TableMerger{ +struct TableMerger { data: Vec>, _phantom: PhantomData, } @@ -77,7 +80,7 @@ impl TableMerger { }); } - fn merge_unsafe(&self,) -> (Expression, Vec>) { + fn merge_unsafe(&self) -> (Expression, Vec>) { if self.data.is_empty() { return (0.expr(), Vec::new()); } @@ -86,16 +89,13 @@ impl TableMerger { let max_length = self.data.iter().map(|t| t.values.len()).max().unwrap(); let mut merged_values = vec![0.expr(); max_length]; let default_value = 0.expr(); - merged_values - .iter_mut() - .enumerate() - .for_each(|(idx, v)| { - *v = sum::expr( - self.data - .iter() - .map(|t| t.condition() * t.values.get(idx).unwrap_or(&default_value).expr()), - ); - }); + merged_values.iter_mut().enumerate().for_each(|(idx, v)| { + *v = sum::expr( + self.data + .iter() + .map(|t| t.condition() * t.values.get(idx).unwrap_or(&default_value).expr()), + ); + }); (selector, merged_values) } @@ -107,11 +107,8 @@ impl TableMerger { self.merge_unsafe() } - fn merge_and_select( - &self, - _cb: &mut ConstraintBuilder, - ) -> Vec> { - let (selector, v) = self.merge_unsafe(); + fn merge_and_select(&self, _cb: &mut ConstraintBuilder) -> Vec> { + let (selector, v) = self.merge_unsafe(); v.iter().map(|v| selector.expr() * v.expr()).collect() } } @@ -203,13 +200,14 @@ impl ConstraintBuilder { } pub(crate) fn preload_tables( - &mut self, + &mut self, meta: &mut ConstraintSystem, - tables: &[(C, &dyn LookupTable)] + tables: &[(C, &dyn LookupTable)], ) { query_expression(meta, |meta| { for (tag, table) in tables { - self.fixed_tables.insert(tag.clone(), table.table_exprs(meta)); + self.fixed_tables + .insert(tag.clone(), table.table_exprs(meta)); } }) } @@ -331,7 +329,7 @@ impl ConstraintBuilder { self.get_condition().unwrap_or_else(|| 1.expr()) } - pub(crate) fn enable_equality(&mut self, column: Column){ + pub(crate) fn enable_equality(&mut self, column: Column) { self.equalities.push(column); } @@ -387,35 +385,34 @@ impl ConstraintBuilder { } } - pub(crate) fn build_constraints(&self, selector: Option>) -> Vec<(&'static str, Expression)> { + pub(crate) fn build_constraints( + &self, + selector: Option>, + ) -> Vec<(&'static str, Expression)> { if self.constraints.is_empty() { return vec![("No constraints", 0.expr())]; } - selector.map_or( - self.constraints.clone(), - |s| { - self.constraints - .iter() - .map(|(name, c)| (*name, s.expr() * c.clone())) - .collect() - } - ) + selector.map_or(self.constraints.clone(), |s| { + self.constraints + .iter() + .map(|(name, c)| (*name, s.expr() * c.clone())) + .collect() + }) } pub(crate) fn build_equalities(&self, meta: &mut ConstraintSystem) { self.equalities .iter() - .for_each(|c| { - meta.enable_equality(c.clone())}); + .for_each(|c| meta.enable_equality(c.clone())); } pub(crate) fn build_fixed_path( &mut self, meta: &mut ConstraintSystem, cell_managers: &[CellManager], - tag:&(C, C), + tag: &(C, C), selector: Option, - ){ + ) { let (data_tag, table_tag) = tag; let challenge = self.lookup_challenge.clone().unwrap(); if let Some(table) = self.fixed_tables.get(table_tag) { @@ -423,10 +420,7 @@ impl ConstraintBuilder { for cm in cell_managers { for col in cm.get_typed_columns(*data_tag) { meta.lookup_any(format!("{:?}", data_tag), |meta| { - let s = selector.map_or_else( - || 1.expr(), - |s| meta.query_selector(s) - ); + let s = selector.map_or_else(|| 1.expr(), |s| meta.query_selector(s)); vec![(col.expr() * s, table_expr.clone())] }); } @@ -439,7 +433,7 @@ impl ConstraintBuilder { meta: &mut ConstraintSystem, tag: &(C, C), selector: Option, - ){ + ) { let (data_tag, table_tag) = tag; if let Some(lookups) = self.lookups.clone().get(data_tag) { for data in lookups.iter() { @@ -482,10 +476,7 @@ impl ConstraintBuilder { values.push(0.expr()); } meta.lookup_any(description, |meta| { - let s = selector.map_or_else( - || 1.expr(), - |s| meta.query_selector(s) - ); + let s = selector.map_or_else(|| 1.expr(), |s| meta.query_selector(s)); values .iter() .zip(table.iter()) @@ -493,10 +484,9 @@ impl ConstraintBuilder { .collect() }); } - } + } } - pub(crate) fn build_lookups( &mut self, meta: &mut ConstraintSystem, @@ -580,34 +570,25 @@ impl ConstraintBuilder { target_cell: Option>, compress: bool, reduce: bool, - ) -> Vec >{ - + ) -> Vec> { let local_condition = self.get_condition_expr(); let challenge = self.lookup_challenge.clone().unwrap(); - let mut local_compression = | values: &[Expression]| -> Expression { + let mut local_compression = |values: &[Expression]| -> Expression { let rlc = rlc::expr(&values, challenge.expr()) * local_condition.expr(); match reduce { true => { let reduced_rlc = self.split_expression("compression", rlc); - self.store_expression( - name, - reduced_rlc, - cell_type, - target_cell.clone() - ) - }, - false => rlc + self.store_expression(name, reduced_rlc, cell_type, target_cell.clone()) + } + false => rlc, } }; match (compress, reduce) { (true, true) => vec![local_compression(&values)], - (true, false) => vec![local_compression(&values)], - (false, true) => values - .iter() - .map(|_v| local_compression(&values)) - .collect(), + (true, false) => vec![local_compression(&values)], + (false, true) => values.iter().map(|_v| local_compression(&values)).collect(), (false, false) => values .iter() .map(|v| v.expr() * local_condition.expr()) @@ -616,11 +597,12 @@ impl ConstraintBuilder { } pub(crate) fn dynamic_table_merged(&mut self, tag: C) -> Vec> { - let data = self.dynamic_tables + let data = self + .dynamic_tables .get(&tag) .unwrap_or_else(|| panic!("Dynamic table {:?} not found", tag)) .clone(); - let table_merger = TableMerger{ + let table_merger = TableMerger { data, _phantom: PhantomData, }; @@ -1461,7 +1443,8 @@ macro_rules! circuit { use $crate::circuit_tools::constraint_builder::{ExprResult, ExprVec}; #[allow(unused_imports)] use $crate::{ - _ifx, _matchx, _require, _to_and, _to_values_vec, _to_options_vec, _unreachablex, concat_with_preamble, + _ifx, _matchx, _require, _to_and, _to_options_vec, _to_values_vec, _unreachablex, + concat_with_preamble, }; #[allow(unused_macros)] @@ -1524,104 +1507,104 @@ macro_rules! circuit { #[allow(unused_macros)] macro_rules! require { - ($lhs:expr => bool) => {{ - _require!($cb, $lhs => bool); - }}; - - ($lhs:expr => $rhs:expr) => {{ - _require!($cb, $lhs => $rhs); - }}; - - ($name:expr, $lhs:expr => $rhs:expr) => {{ - _require!($cb, $name, $lhs => $rhs); - }}; - - // Lookups build from table - // only reduce flag is allowed - ($values:tt =>> @$tag:expr, $options:tt) => {{ - let values = _to_values_vec!($values); - let options = _to_options_vec!($options); - _require!($cb, values =>> @$tag, options); - }}; - ($values:tt =>> @$tag:expr) => {{ - let values = _to_values_vec!($values); - let options = Vec::new(); - _require!($cb, values =>> @$tag, options); - }}; - ($descr:expr, $values:tt =>> @$tag:expr, $options:tt) => {{ - let values = _to_values_vec!($values); - let options = _to_options_vec!($options); - _require!($cb, $descr, values =>> @$tag, options); - }}; - ($descr:expr, $values:tt =>> @$tag:expr) => {{ - let values = _to_values_vec!($values); - let options = Vec::new(); - _require!($cb, $descr, values =>> @$tag, options); - }}; - - - - ($values:tt => @$tag:expr, $options:tt) => {{ - let values = _to_values_vec!($values); - let options = _to_options_vec!($options); - _require!($cb, values => @$tag, options); - }}; - ($values:tt => @$tag:expr) => {{ - let values = _to_values_vec!($values); - let options = Vec::new(); - _require!($cb, values => @$tag, options); - }}; - ($descr:expr, $values:tt => @$tag:expr, $options:tt) => {{ - let values = _to_values_vec!($values); - let options = _to_options_vec!($options); - _require!($cb, $descr, values => @$tag, options); - }}; - ($descr:expr, $values:tt => @$tag:expr) => {{ - let values = _to_values_vec!($values); - let options = Vec::new(); - _require!($cb, $descr, values => @$tag, options); - }}; - - - (@$tag:expr, $options:tt => $values:tt) => {{ - let values = _to_values_vec!($values); - let options = _to_options_vec!($options); - _require!($cb, @$tag, options => values); - }}; - (@$tag:expr => $values:tt) => {{ - let values = _to_values_vec!($values); - let options = Vec::new(); - _require!($cb, @$tag, options => values); - }}; - (@$tag:expr, $options:tt =>> $values:tt) => {{ - let values = _to_values_vec!($values); - let options = _to_options_vec!($options); - _require!($cb, @$tag, options =>> values); - }}; - (@$tag:expr =>> $values:tt) => {{ - let values = _to_values_vec!($values); - let options = Vec::new(); - _require!($cb, @$tag, options =>> values); - }}; + ($lhs:expr => bool) => {{ + _require!($cb, $lhs => bool); + }}; + + ($lhs:expr => $rhs:expr) => {{ + _require!($cb, $lhs => $rhs); + }}; + + ($name:expr, $lhs:expr => $rhs:expr) => {{ + _require!($cb, $name, $lhs => $rhs); + }}; + + // Lookups build from table + // only reduce flag is allowed + ($values:tt =>> @$tag:expr, $options:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, values =>> @$tag, options); + }}; + ($values:tt =>> @$tag:expr) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, values =>> @$tag, options); + }}; + ($descr:expr, $values:tt =>> @$tag:expr, $options:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, $descr, values =>> @$tag, options); + }}; + ($descr:expr, $values:tt =>> @$tag:expr) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, $descr, values =>> @$tag, options); + }}; + + + + ($values:tt => @$tag:expr, $options:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, values => @$tag, options); + }}; + ($values:tt => @$tag:expr) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, values => @$tag, options); + }}; + ($descr:expr, $values:tt => @$tag:expr, $options:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, $descr, values => @$tag, options); + }}; + ($descr:expr, $values:tt => @$tag:expr) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, $descr, values => @$tag, options); + }}; + + + (@$tag:expr, $options:tt => $values:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, @$tag, options => values); + }}; + (@$tag:expr => $values:tt) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, @$tag, options => values); + }}; + (@$tag:expr, $options:tt =>> $values:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, @$tag, options =>> values); + }}; + (@$tag:expr =>> $values:tt) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, @$tag, options =>> values); + }}; - } + } #[allow(unused_macros)] macro_rules! ifx { - ($condition:tt => $when_true:block elsex $when_false:block) => {{ - _ifx!($cb, ($condition) => $when_true elsex $when_false) - }}; - ($condition:expr => $when_true:block elsex $when_false:block) => {{ - _ifx!($cb, $condition => $when_true elsex $when_false) - }}; - - ($condition:tt => $when_true:block) => {{ - _ifx!($cb, $condition => $when_true) - }}; - ($condition:expr => $when_true:block) => {{ - _ifx!($cb, $condition => $when_true) - }}; - } + ($condition:tt => $when_true:block elsex $when_false:block) => {{ + _ifx!($cb, ($condition) => $when_true elsex $when_false) + }}; + ($condition:expr => $when_true:block elsex $when_false:block) => {{ + _ifx!($cb, $condition => $when_true elsex $when_false) + }}; + + ($condition:tt => $when_true:block) => {{ + _ifx!($cb, $condition => $when_true) + }}; + ($condition:expr => $when_true:block) => {{ + _ifx!($cb, $condition => $when_true) + }}; + } #[allow(unused_macros)] macro_rules! matchx { diff --git a/zkevm-circuits/src/circuit_tools/memory.rs b/zkevm-circuits/src/circuit_tools/memory.rs index 8d2ec50db1..5a428d43b1 100644 --- a/zkevm-circuits/src/circuit_tools/memory.rs +++ b/zkevm-circuits/src/circuit_tools/memory.rs @@ -1,24 +1,21 @@ //! Memory -use crate::{ - util::{query_expression, Expr}, -}; -use eth_types::{Field}; +use crate::util::{query_expression, Expr}; +use eth_types::Field; use halo2_proofs::{ circuit::Value, - plonk::{ - Advice, Column, ConstraintSystem, Error, Expression, - }, + plonk::{Advice, Column, ConstraintSystem, Error, Expression}, poly::Rotation, }; use itertools::Itertools; use std::{ collections::HashMap, - ops::{Index, IndexMut}, marker::PhantomData, + marker::PhantomData, + ops::{Index, IndexMut}, }; use super::{ cached_region::CachedRegion, - cell_manager::{CellType, CellManager}, + cell_manager::{CellManager, CellType}, constraint_builder::ConstraintBuilder, }; @@ -27,7 +24,7 @@ pub(crate) struct Memory> { // TODO(Cecilia): want to use dynamic dispatch // i.e. dyn MemoryBank but trait with generic param is not object safe pub(crate) banks: HashMap, - _phantom: PhantomData + _phantom: PhantomData, } impl> Index for Memory { @@ -54,30 +51,26 @@ impl> IndexMut for Memory> Memory { pub(crate) fn new( - cm: &mut CellManager, + cm: &mut CellManager, meta: &mut ConstraintSystem, tags: Vec<(C, C, u8)>, offset: usize, ) -> Self { let mut banks = HashMap::new(); - tags - .into_iter() - .for_each(|(data_tag, table_tag, phase)| { - banks.insert( - data_tag, - MB::new(meta, cm, (data_tag, table_tag), phase, offset) - ); - }); - Self { + tags.into_iter().for_each(|(data_tag, table_tag, phase)| { + banks.insert( + data_tag, + MB::new(meta, cm, (data_tag, table_tag), phase, offset), + ); + }); + Self { banks, - _phantom: PhantomData + _phantom: PhantomData, } } pub(crate) fn get_columns(&self) -> Vec> { - self.banks - .values() - .fold( Vec::new(),|mut acc, bank| { + self.banks.values().fold(Vec::new(), |mut acc, bank| { acc.extend(bank.columns().iter()); acc }) @@ -109,11 +102,25 @@ impl> Memory { } } - pub(crate) trait MemoryBank: Clone { - fn new(meta: &mut ConstraintSystem, cm: &mut CellManager, tag: (C, C), phase: u8, offset: usize) -> Self; - fn store(&mut self, cb: &mut ConstraintBuilder, values: &[Expression]) -> Expression; - fn load(&mut self, cb: &mut ConstraintBuilder, load_offset: Expression, values: &[Expression]); + fn new( + meta: &mut ConstraintSystem, + cm: &mut CellManager, + tag: (C, C), + phase: u8, + offset: usize, + ) -> Self; + fn store( + &mut self, + cb: &mut ConstraintBuilder, + values: &[Expression], + ) -> Expression; + fn load( + &mut self, + cb: &mut ConstraintBuilder, + load_offset: Expression, + values: &[Expression], + ); fn columns(&self) -> Vec>; fn tag(&self) -> (C, C); fn witness_store(&mut self, offset: usize, values: &[F]); @@ -141,7 +148,11 @@ impl RwBank { [&[self.cur.expr() + 1.expr()], values].concat().to_vec() } - pub(crate) fn prepend_offset(&self, values: &[Expression], offset: Expression) -> Vec> { + pub(crate) fn prepend_offset( + &self, + values: &[Expression], + offset: Expression, + ) -> Vec> { [&[self.cur.expr() - offset], values].concat().to_vec() } } @@ -149,31 +160,33 @@ impl RwBank { impl MemoryBank for RwBank { fn new( meta: &mut ConstraintSystem, - cm: &mut CellManager, + cm: &mut CellManager, tag: (C, C), phase: u8, offset: usize, ) -> Self { - let rw: Vec> = [tag.0, tag.1].iter() + let rw: Vec> = [tag.0, tag.1] + .iter() .map(|t| { let config = (t.clone(), 1usize, phase, false); cm.add_celltype(meta, config, offset); cm.get_typed_columns(t.clone())[0].column - }).collect(); + }) + .collect(); let key = meta.advice_column(); let (cur, next) = query_expression(meta, |meta| { ( meta.query_advice(key, Rotation(0)), - meta.query_advice(key, Rotation(1)) - ) + meta.query_advice(key, Rotation(1)), + ) }); - Self { - tag, - key, - reads: rw[0], - writes: rw[1], - store_offsets: Vec::new(), - stored_values: Vec::new(), + Self { + tag, + key, + reads: rw[0], + writes: rw[1], + store_offsets: Vec::new(), + stored_values: Vec::new(), cur, next, local_conditions: Vec::new(), @@ -181,38 +194,39 @@ impl MemoryBank for RwBank { } fn store( - &mut self, - cb: &mut ConstraintBuilder, - values: &[Expression] + &mut self, + cb: &mut ConstraintBuilder, + values: &[Expression], ) -> Expression { let values = self.prepend_key(values); cb.store_table( Box::leak(format!("{:?} store", self.tag.1).into_boxed_str()), - self.tag.1, - values.clone(), - true, - true, - false + self.tag.1, + values.clone(), + true, + true, + false, ); - self.local_conditions.push((cb.region_id, cb.get_condition_expr())); + self.local_conditions + .push((cb.region_id, cb.get_condition_expr())); values[0].expr() } fn load( - &mut self, - cb: &mut ConstraintBuilder, - load_offset: Expression, - values: &[Expression] + &mut self, + cb: &mut ConstraintBuilder, + load_offset: Expression, + values: &[Expression], ) { let values = self.prepend_offset(values, load_offset); cb.add_lookup( - Box::leak(format!("{:?} load", self.tag.0).into_boxed_str()), - self.tag.0, - values, - false, - true, - true, - false + Box::leak(format!("{:?} load", self.tag.0).into_boxed_str()), + self.tag.0, + values, + false, + true, + true, + false, ); } @@ -224,11 +238,7 @@ impl MemoryBank for RwBank { vec![self.key, self.reads, self.writes] } - fn build_constraints( - &self, - cb: &mut ConstraintBuilder, - q_start: Expression - ) { + fn build_constraints(&self, cb: &mut ConstraintBuilder, q_start: Expression) { let condition = self .local_conditions .iter() @@ -241,7 +251,7 @@ impl MemoryBank for RwBank { let description = format!("Dynamic lookup table {:?}", self.tag()); require!(condition => bool); require!(description, self.next => self.cur.expr() + condition.expr()); - }); + }); } fn witness_store(&mut self, offset: usize, values: &[F]) { @@ -253,11 +263,7 @@ impl MemoryBank for RwBank { self.stored_values[self.stored_values.len() - 1 - offset].clone() } - fn assign( - &self, - region: &mut CachedRegion<'_, '_, F>, - height: usize, - ) -> Result<(), Error> { + fn assign(&self, region: &mut CachedRegion<'_, '_, F>, height: usize) -> Result<(), Error> { // Pad to the full circuit (necessary for reads) let mut store_offsets = self.store_offsets.clone(); store_offsets.push(height); @@ -277,4 +283,4 @@ impl MemoryBank for RwBank { } Ok(()) } -} \ No newline at end of file +} diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index 5307f4ebd6..be7ad68289 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -31,10 +31,10 @@ pub mod table; #[macro_use] #[allow(missing_docs)] pub mod taiko_pi_circuit; +pub mod circuit_tools; pub mod taiko_super_circuit; #[cfg(any(feature = "test", test))] pub mod test_util; -pub mod circuit_tools; pub mod anchor_tx_circuit; pub mod tx_circuit; diff --git a/zkevm-circuits/src/table/keccak_table.rs b/zkevm-circuits/src/table/keccak_table.rs index 14b33d6ecf..2a283c504d 100644 --- a/zkevm-circuits/src/table/keccak_table.rs +++ b/zkevm-circuits/src/table/keccak_table.rs @@ -1,5 +1,3 @@ - - use super::*; /// Keccak Table, used to verify keccak hashing from RLC'ed input. diff --git a/zkevm-circuits/src/taiko_pi_circuit.rs b/zkevm-circuits/src/taiko_pi_circuit.rs index 0e76bd32e0..24301e60ce 100644 --- a/zkevm-circuits/src/taiko_pi_circuit.rs +++ b/zkevm-circuits/src/taiko_pi_circuit.rs @@ -1,28 +1,27 @@ - - - -use eth_types::{Field, ToBigEndian, ToWord, H256, H160, U256}; +use eth_types::{Field, ToBigEndian, ToWord, H160, H256, U256}; use ethers_core::abi::*; use ethers_core::utils::keccak256; -use halo2_proofs::circuit::{Value, Layouter, SimpleFloorPlanner, AssignedCell}; +use halo2_proofs::circuit::{AssignedCell, Layouter, SimpleFloorPlanner, Value}; -use std::convert::TryInto; -use std::marker::PhantomData; use gadgets::util::{Expr, Scalar}; -use halo2_proofs::plonk::{Expression, ConstraintSystem, Selector, Instance, Column, Circuit}; - -use halo2_proofs::plonk::Error; +use halo2_proofs::plonk::{Circuit, Column, ConstraintSystem, Expression, Instance, Selector}; +use std::{convert::TryInto, marker::PhantomData}; + +use crate::{ + assign, circuit, + circuit_tools::{ + cached_region::CachedRegion, + cell_manager::{Cell, CellColumn, CellManager, CellType}, + constraint_builder::{ConstraintBuilder, ExprVec, RLCable, TO_FIX}, + }, + evm_circuit::{table::Table, util::rlc}, + table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}, + util::{Challenges, SubCircuit, SubCircuitConfig}, + witness::{self, BlockContext}, +}; use core::result::Result; -use crate::circuit_tools::cached_region::CachedRegion; -use crate::circuit_tools::cell_manager::{Cell, CellType, CellManager, CellColumn}; -use crate::circuit_tools::constraint_builder::{ConstraintBuilder, TO_FIX, RLCable, ExprVec}; -use crate::evm_circuit::table::Table; -use crate::evm_circuit::util::rlc; -use crate::util::{Challenges, SubCircuitConfig, SubCircuit}; -use crate::witness::{self, BlockContext}; -use crate::{circuit, assign}; -use crate::table::{byte_table::ByteTable, BlockContextFieldTag, BlockTable, KeccakTable, LookupTable}; +use halo2_proofs::plonk::Error; const BYTE_POW_BASE: u64 = 1 << 8; const PADDING_LEN: usize = 32; @@ -45,7 +44,7 @@ impl FieldGadget { fn config(cb: &mut ConstraintBuilder, len: usize) -> Self { Self { field: cb.query_cells_dyn(PiCellType::Byte, len), - len + len, } } @@ -54,7 +53,7 @@ impl FieldGadget { } fn rlc_acc(&self, r: Expression) -> Expression { - //0.expr() + // 0.expr() self.bytes_expr().rlc_rev(&r) } @@ -62,21 +61,25 @@ impl FieldGadget { assert!(self.len == 32); let hi = self.bytes_expr()[..16].to_vec(); let low = self.bytes_expr()[16..].to_vec(); - [hi.rlc_rev(&BYTE_POW_BASE.expr()), low.rlc_rev(&BYTE_POW_BASE.expr())] + [ + hi.rlc_rev(&BYTE_POW_BASE.expr()), + low.rlc_rev(&BYTE_POW_BASE.expr()), + ] } fn assign( - &self, + &self, region: &mut CachedRegion<'_, '_, F>, offset: usize, bytes: &[F], ) -> Result>, Error> { assert!(bytes.len() == self.len); - let cells = self.field.iter().zip(bytes.iter()).map( - |(cell, byte)| { - assign!(region, cell, offset => *byte).unwrap() - } - ).collect(); + let cells = self + .field + .iter() + .zip(bytes.iter()) + .map(|(cell, byte)| assign!(region, cell, offset => *byte).unwrap()) + .collect(); Ok(cells) } } @@ -98,7 +101,7 @@ impl CellType for PiCellType { match phase { 1 => PiCellType::StoragePhase1, 2 => PiCellType::StoragePhase2, - _ => unimplemented!() + _ => unimplemented!(), } } } @@ -108,7 +111,6 @@ impl Default for PiCellType { } } - #[derive(Debug, Clone)] pub struct PublicData { evidence: Token, @@ -127,13 +129,49 @@ impl Default for PublicData { impl PublicData { fn new(block: &witness::Block) -> Self { - let meta_hash = Token::FixedBytes(block.protocol_instance.meta_hash.hash().to_word().to_be_bytes().to_vec()); - let parent_hash = Token::FixedBytes(block.protocol_instance.parent_hash.to_word().to_be_bytes().to_vec()); - let block_hash = Token::FixedBytes(block.protocol_instance.block_hash.to_word().to_be_bytes().to_vec()); - let signal_root = Token::FixedBytes(block.protocol_instance.signal_root.to_word().to_be_bytes().to_vec()); - let graffiti = Token::FixedBytes(block.protocol_instance.graffiti.to_word().to_be_bytes().to_vec()); + let meta_hash = Token::FixedBytes( + block + .protocol_instance + .meta_hash + .hash() + .to_word() + .to_be_bytes() + .to_vec(), + ); + let parent_hash = Token::FixedBytes( + block + .protocol_instance + .parent_hash + .to_word() + .to_be_bytes() + .to_vec(), + ); + let block_hash = Token::FixedBytes( + block + .protocol_instance + .block_hash + .to_word() + .to_be_bytes() + .to_vec(), + ); + let signal_root = Token::FixedBytes( + block + .protocol_instance + .signal_root + .to_word() + .to_be_bytes() + .to_vec(), + ); + let graffiti = Token::FixedBytes( + block + .protocol_instance + .graffiti + .to_word() + .to_be_bytes() + .to_vec(), + ); let prover = Token::Address(block.protocol_instance.prover); - Self { + Self { evidence: Token::FixedArray(vec![ meta_hash, parent_hash, @@ -141,9 +179,9 @@ impl PublicData { signal_root, graffiti, prover, - ]), + ]), block_context: block.context.clone(), - _phantom: PhantomData + _phantom: PhantomData, } } @@ -153,8 +191,8 @@ impl PublicData { tokens[idx] = match tokens[idx].clone() { Token::Bytes(_) => Token::Bytes(bytes), Token::FixedBytes(_) => Token::FixedBytes(bytes), - Token::Address(_) => Token::Address( - H160::from(&bytes.try_into().expect("Wrong number of bytes for address") + Token::Address(_) => Token::Address(H160::from( + &bytes.try_into().expect("Wrong number of bytes for address"), )), _ => unreachable!(), }; @@ -207,10 +245,7 @@ impl PublicData { } fn keccak_assignment(&self) -> Vec { - self.keccak() - .iter() - .map(|b| F::from(*b as u64)) - .collect() + self.keccak().iter().map(|b| F::from(*b as u64)).collect() } fn total_len(&self) -> usize { @@ -220,8 +255,6 @@ impl PublicData { fn field_len(&self, idx: usize) -> usize { self.encode_field(idx).len() } - - } #[derive(Clone, Debug)] @@ -242,13 +275,13 @@ pub struct TaikoPiCircuitConfig { block_table: BlockTable, keccak_table: KeccakTable, - byte_table: ByteTable, + byte_table: ByteTable, annotation_configs: Vec>, } pub struct TaikoPiCircuitConfigArgs { - /// + /// pub evidence: PublicData, /// BlockTable pub block_table: BlockTable, @@ -260,7 +293,6 @@ pub struct TaikoPiCircuitConfigArgs { pub challenges: Challenges>, } - impl SubCircuitConfig for TaikoPiCircuitConfig { type ConfigArgs = TaikoPiCircuitConfigArgs; /// Return a new TaikoPiCircuitConfig @@ -286,93 +318,105 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { 0, 32, ); - let mut cb: ConstraintBuilder = ConstraintBuilder::new(4, Some(cm.clone()), Some(evm_word.expr())); - cb.preload_tables(meta, + let mut cb: ConstraintBuilder = + ConstraintBuilder::new(4, Some(cm.clone()), Some(evm_word.expr())); + cb.preload_tables( + meta, &[ - (PiCellType::Lookup(Table::Keccak), &keccak_table), - (PiCellType::Lookup(Table::Bytecode), &byte_table), - (PiCellType::Lookup(Table::Block), &block_table) - ] - ); + (PiCellType::Lookup(Table::Keccak), &keccak_table), + (PiCellType::Lookup(Table::Bytecode), &byte_table), + (PiCellType::Lookup(Table::Block), &block_table), + ], + ); let q_enable = meta.complex_selector(); let keccak_instance = meta.instance_column(); meta.enable_equality(keccak_instance); let meta_hash = FieldGadget::config(&mut cb, evidence.field_len(META_HASH)); - let parent_hash =( + let parent_hash = ( cb.query_one(S1), FieldGadget::config(&mut cb, evidence.field_len(PARENT_HASH)), - cb.query_one(S2) + cb.query_one(S2), ); - let block_hash =( + let block_hash = ( cb.query_one(S1), FieldGadget::config(&mut cb, evidence.field_len(BLOCK_HASH)), - cb.query_one(S2) + cb.query_one(S2), ); let signal_root = FieldGadget::config(&mut cb, evidence.field_len(SIGNAL_ROOT)); let graffiti = FieldGadget::config(&mut cb, evidence.field_len(GRAFFITI)); let prover = FieldGadget::config(&mut cb, evidence.field_len(PROVER)); - + let total_acc = cb.query_one(S2); let keccak_bytes = FieldGadget::config(&mut cb, PADDING_LEN); let keccak_hi_lo = [cb.query_one(S1), cb.query_one(S1)]; - meta.create_gate( - "PI acc constraints", - |meta| { - circuit!([meta, cb], { - for (block_number, block_hash, block_hash_rlc) in [parent_hash.clone() , block_hash.clone()] { - require!(block_hash_rlc.expr() => block_hash.rlc_acc(evm_word.expr())); - require!( - ( - BlockContextFieldTag::BlockHash.expr(), - block_number.expr(), - block_hash_rlc.expr() - ) => @PiCellType::Lookup(Table::Block), (TO_FIX) - ); - } - let acc_val = [ - meta_hash.clone(), - parent_hash.1.clone(), - block_hash.1.clone(), - signal_root.clone(), - graffiti.clone(), - prover.clone(), - ].iter().fold(0.expr(), |acc, gadget| { - let mult = (0..gadget.len).fold(1.expr(), |acc, _| acc * keccak_r.expr()); - acc * mult + gadget.rlc_acc(keccak_r.expr()) - }); - require!(total_acc.expr() => acc_val); + meta.create_gate("PI acc constraints", |meta| { + circuit!([meta, cb], { + for (block_number, block_hash, block_hash_rlc) in + [parent_hash.clone(), block_hash.clone()] + { + require!(block_hash_rlc.expr() => block_hash.rlc_acc(evm_word.expr())); require!( ( - 1.expr(), - total_acc.expr(), - evidence.total_len().expr(), - keccak_bytes.rlc_acc(evm_word.expr()) - ) - => @PiCellType::Lookup(Table::Keccak), (TO_FIX) + BlockContextFieldTag::BlockHash.expr(), + block_number.expr(), + block_hash_rlc.expr() + ) => @PiCellType::Lookup(Table::Block), (TO_FIX) ); - let hi_lo = keccak_bytes.hi_low_field(); - keccak_hi_lo.iter().zip(hi_lo.iter()).for_each(|(cell, epxr)| { + } + let acc_val = [ + meta_hash.clone(), + parent_hash.1.clone(), + block_hash.1.clone(), + signal_root.clone(), + graffiti.clone(), + prover.clone(), + ] + .iter() + .fold(0.expr(), |acc, gadget| { + let mult = (0..gadget.len).fold(1.expr(), |acc, _| acc * keccak_r.expr()); + acc * mult + gadget.rlc_acc(keccak_r.expr()) + }); + require!(total_acc.expr() => acc_val); + require!( + ( + 1.expr(), + total_acc.expr(), + evidence.total_len().expr(), + keccak_bytes.rlc_acc(evm_word.expr()) + ) + => @PiCellType::Lookup(Table::Keccak), (TO_FIX) + ); + let hi_lo = keccak_bytes.hi_low_field(); + keccak_hi_lo + .iter() + .zip(hi_lo.iter()) + .for_each(|(cell, epxr)| { require!(cell.expr() => epxr); cb.enable_equality(cell.column()); }); - }); - cb.build_constraints(Some(meta.query_selector(q_enable))) - } - ); + }); + cb.build_constraints(Some(meta.query_selector(q_enable))) + }); cb.build_lookups( - meta, + meta, &[cm.clone()], &[ (PiCellType::Byte, PiCellType::Lookup(Table::Bytecode)), - (PiCellType::Lookup(Table::Keccak), PiCellType::Lookup(Table::Keccak)), - (PiCellType::Lookup(Table::Block), PiCellType::Lookup(Table::Block)), + ( + PiCellType::Lookup(Table::Keccak), + PiCellType::Lookup(Table::Keccak), + ), + ( + PiCellType::Lookup(Table::Block), + PiCellType::Lookup(Table::Block), + ), ], - Some(q_enable) + Some(q_enable), ); let annotation_configs = cm.columns().to_vec(); Self { - q_enable, + q_enable, keccak_instance, meta_hash, parent_hash, @@ -386,7 +430,7 @@ impl SubCircuitConfig for TaikoPiCircuitConfig { block_table, keccak_table, byte_table, - annotation_configs + annotation_configs, } } } @@ -451,9 +495,7 @@ pub struct TaikoPiCircuit { impl TaikoPiCircuit { /// Creates a new TaikoPiCircuit pub fn new(evidence: PublicData) -> Self { - Self { - evidence: evidence, - } + Self { evidence: evidence } } } @@ -477,7 +519,7 @@ impl SubCircuit for TaikoPiCircuit { /// Compute the public inputs for this circuit. fn instance(&self) -> Vec> { - vec![ self.evidence.keccak_hi_low().to_vec()] + vec![self.evidence.keccak_hi_low().to_vec()] } /// Make the assignments to the PiCircuit @@ -494,7 +536,7 @@ impl SubCircuit for TaikoPiCircuit { #[cfg(any(feature = "test", test))] impl Circuit for TaikoPiCircuit { - type Config = (TaikoPiCircuitConfig, Challenges); + type Config = (TaikoPiCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; type Params = PublicData; @@ -510,10 +552,7 @@ impl Circuit for TaikoPiCircuit { Self::configure_with_params(meta, PublicData::default()) } - fn configure_with_params( - meta: &mut ConstraintSystem, - params: Self::Params, - ) -> Self::Config { + fn configure_with_params(meta: &mut ConstraintSystem, params: Self::Params) -> Self::Config { let block_table = BlockTable::construct(meta); let keccak_table = KeccakTable::construct(meta); let byte_table = ByteTable::construct(meta); @@ -528,9 +567,9 @@ impl Circuit for TaikoPiCircuit { keccak_table, byte_table, challenges: challenge_exprs, - } + }, ), - challenges + challenges, ) } @@ -554,7 +593,6 @@ impl Circuit for TaikoPiCircuit { self.synthesize_sub(&config, &challenges, &mut layouter) } - } #[cfg(test)] @@ -668,7 +706,6 @@ mod taiko_pi_circuit_test { } } - #[test] fn test_simple_pi() { let mut evidence = mock_public_data(); @@ -681,7 +718,6 @@ mod taiko_pi_circuit_test { assert_eq!(run::(k, evidence, None), Ok(())); } - #[test] fn test_verify() { let mut block = witness::Block::::default(); @@ -700,8 +736,4 @@ mod taiko_pi_circuit_test { assert_eq!(run::(k, evidence, None), Ok(())); } - - } - - diff --git a/zkevm-circuits/src/taiko_super_circuit.rs b/zkevm-circuits/src/taiko_super_circuit.rs index ca57fd4665..a876a3d9ef 100644 --- a/zkevm-circuits/src/taiko_super_circuit.rs +++ b/zkevm-circuits/src/taiko_super_circuit.rs @@ -7,7 +7,9 @@ pub mod test; use crate::{ anchor_tx_circuit::{AnchorTxCircuit, AnchorTxCircuitConfig, AnchorTxCircuitConfigArgs}, table::{byte_table::ByteTable, BlockTable, KeccakTable, PiTable, TxTable}, - taiko_pi_circuit::{TaikoPiCircuit, TaikoPiCircuitConfig, TaikoPiCircuitConfigArgs, PublicData}, + taiko_pi_circuit::{ + PublicData, TaikoPiCircuit, TaikoPiCircuitConfig, TaikoPiCircuitConfigArgs, + }, util::{log2_ceil, Challenges, SubCircuit, SubCircuitConfig}, witness::{block_convert, Block, ProtocolInstance}, }; From f66a5837afd8cfdd21a6f41fc433d3c6b1151910 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Mon, 25 Sep 2023 19:37:03 +0700 Subject: [PATCH 41/43] fmt --all --- .../src/circuit_tools/constraint_builder.rs | 188 +++++++++--------- 1 file changed, 94 insertions(+), 94 deletions(-) diff --git a/zkevm-circuits/src/circuit_tools/constraint_builder.rs b/zkevm-circuits/src/circuit_tools/constraint_builder.rs index 0a9dbfd601..abdab076d5 100644 --- a/zkevm-circuits/src/circuit_tools/constraint_builder.rs +++ b/zkevm-circuits/src/circuit_tools/constraint_builder.rs @@ -1507,104 +1507,104 @@ macro_rules! circuit { #[allow(unused_macros)] macro_rules! require { - ($lhs:expr => bool) => {{ - _require!($cb, $lhs => bool); - }}; - - ($lhs:expr => $rhs:expr) => {{ - _require!($cb, $lhs => $rhs); - }}; - - ($name:expr, $lhs:expr => $rhs:expr) => {{ - _require!($cb, $name, $lhs => $rhs); - }}; - - // Lookups build from table - // only reduce flag is allowed - ($values:tt =>> @$tag:expr, $options:tt) => {{ - let values = _to_values_vec!($values); - let options = _to_options_vec!($options); - _require!($cb, values =>> @$tag, options); - }}; - ($values:tt =>> @$tag:expr) => {{ - let values = _to_values_vec!($values); - let options = Vec::new(); - _require!($cb, values =>> @$tag, options); - }}; - ($descr:expr, $values:tt =>> @$tag:expr, $options:tt) => {{ - let values = _to_values_vec!($values); - let options = _to_options_vec!($options); - _require!($cb, $descr, values =>> @$tag, options); - }}; - ($descr:expr, $values:tt =>> @$tag:expr) => {{ - let values = _to_values_vec!($values); - let options = Vec::new(); - _require!($cb, $descr, values =>> @$tag, options); - }}; - - - - ($values:tt => @$tag:expr, $options:tt) => {{ - let values = _to_values_vec!($values); - let options = _to_options_vec!($options); - _require!($cb, values => @$tag, options); - }}; - ($values:tt => @$tag:expr) => {{ - let values = _to_values_vec!($values); - let options = Vec::new(); - _require!($cb, values => @$tag, options); - }}; - ($descr:expr, $values:tt => @$tag:expr, $options:tt) => {{ - let values = _to_values_vec!($values); - let options = _to_options_vec!($options); - _require!($cb, $descr, values => @$tag, options); - }}; - ($descr:expr, $values:tt => @$tag:expr) => {{ - let values = _to_values_vec!($values); - let options = Vec::new(); - _require!($cb, $descr, values => @$tag, options); - }}; - - - (@$tag:expr, $options:tt => $values:tt) => {{ - let values = _to_values_vec!($values); - let options = _to_options_vec!($options); - _require!($cb, @$tag, options => values); - }}; - (@$tag:expr => $values:tt) => {{ - let values = _to_values_vec!($values); - let options = Vec::new(); - _require!($cb, @$tag, options => values); - }}; - (@$tag:expr, $options:tt =>> $values:tt) => {{ - let values = _to_values_vec!($values); - let options = _to_options_vec!($options); - _require!($cb, @$tag, options =>> values); - }}; - (@$tag:expr =>> $values:tt) => {{ - let values = _to_values_vec!($values); - let options = Vec::new(); - _require!($cb, @$tag, options =>> values); - }}; + ($lhs:expr => bool) => {{ + _require!($cb, $lhs => bool); + }}; + + ($lhs:expr => $rhs:expr) => {{ + _require!($cb, $lhs => $rhs); + }}; + + ($name:expr, $lhs:expr => $rhs:expr) => {{ + _require!($cb, $name, $lhs => $rhs); + }}; + + // Lookups build from table + // only reduce flag is allowed + ($values:tt =>> @$tag:expr, $options:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, values =>> @$tag, options); + }}; + ($values:tt =>> @$tag:expr) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, values =>> @$tag, options); + }}; + ($descr:expr, $values:tt =>> @$tag:expr, $options:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, $descr, values =>> @$tag, options); + }}; + ($descr:expr, $values:tt =>> @$tag:expr) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, $descr, values =>> @$tag, options); + }}; + + + + ($values:tt => @$tag:expr, $options:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, values => @$tag, options); + }}; + ($values:tt => @$tag:expr) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, values => @$tag, options); + }}; + ($descr:expr, $values:tt => @$tag:expr, $options:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, $descr, values => @$tag, options); + }}; + ($descr:expr, $values:tt => @$tag:expr) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, $descr, values => @$tag, options); + }}; + + + (@$tag:expr, $options:tt => $values:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, @$tag, options => values); + }}; + (@$tag:expr => $values:tt) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, @$tag, options => values); + }}; + (@$tag:expr, $options:tt =>> $values:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, @$tag, options =>> values); + }}; + (@$tag:expr =>> $values:tt) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, @$tag, options =>> values); + }}; - } + } #[allow(unused_macros)] macro_rules! ifx { - ($condition:tt => $when_true:block elsex $when_false:block) => {{ - _ifx!($cb, ($condition) => $when_true elsex $when_false) - }}; - ($condition:expr => $when_true:block elsex $when_false:block) => {{ - _ifx!($cb, $condition => $when_true elsex $when_false) - }}; - - ($condition:tt => $when_true:block) => {{ - _ifx!($cb, $condition => $when_true) - }}; - ($condition:expr => $when_true:block) => {{ - _ifx!($cb, $condition => $when_true) - }}; - } + ($condition:tt => $when_true:block elsex $when_false:block) => {{ + _ifx!($cb, ($condition) => $when_true elsex $when_false) + }}; + ($condition:expr => $when_true:block elsex $when_false:block) => {{ + _ifx!($cb, $condition => $when_true elsex $when_false) + }}; + + ($condition:tt => $when_true:block) => {{ + _ifx!($cb, $condition => $when_true) + }}; + ($condition:expr => $when_true:block) => {{ + _ifx!($cb, $condition => $when_true) + }}; + } #[allow(unused_macros)] macro_rules! matchx { From e884118a683a33a7092010fc584fc7230a7d5e4f Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Mon, 25 Sep 2023 19:40:31 +0700 Subject: [PATCH 42/43] clippy --all --- .../src/circuit_tools/cell_manager.rs | 2 +- .../src/circuit_tools/constraint_builder.rs | 20 +++++++++---------- zkevm-circuits/src/circuit_tools/memory.rs | 6 +++--- zkevm-circuits/src/taiko_pi_circuit.rs | 4 ++-- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/zkevm-circuits/src/circuit_tools/cell_manager.rs b/zkevm-circuits/src/circuit_tools/cell_manager.rs index 0d5c8c53c1..cdcafd3541 100644 --- a/zkevm-circuits/src/circuit_tools/cell_manager.rs +++ b/zkevm-circuits/src/circuit_tools/cell_manager.rs @@ -265,7 +265,7 @@ impl CellManager { config: (C, usize, u8, bool), offset: usize, ) { - if self.get_typed_columns(config.0).len() != 0 { + if !self.get_typed_columns(config.0).is_empty() { panic!("CellManager: cell type {:?} already exists", config.0); } let config = CellConfig::from(config); diff --git a/zkevm-circuits/src/circuit_tools/constraint_builder.rs b/zkevm-circuits/src/circuit_tools/constraint_builder.rs index abdab076d5..e33826f249 100644 --- a/zkevm-circuits/src/circuit_tools/constraint_builder.rs +++ b/zkevm-circuits/src/circuit_tools/constraint_builder.rs @@ -207,7 +207,7 @@ impl ConstraintBuilder { query_expression(meta, |meta| { for (tag, table) in tables { self.fixed_tables - .insert(tag.clone(), table.table_exprs(meta)); + .insert(*tag, table.table_exprs(meta)); } }) } @@ -403,7 +403,7 @@ impl ConstraintBuilder { pub(crate) fn build_equalities(&self, meta: &mut ConstraintSystem) { self.equalities .iter() - .for_each(|c| meta.enable_equality(c.clone())); + .for_each(|c| meta.enable_equality(*c)); } pub(crate) fn build_fixed_path( @@ -416,7 +416,7 @@ impl ConstraintBuilder { let (data_tag, table_tag) = tag; let challenge = self.lookup_challenge.clone().unwrap(); if let Some(table) = self.fixed_tables.get(table_tag) { - let table_expr = rlc::expr(&table, challenge.expr()); + let table_expr = rlc::expr(table, challenge.expr()); for cm in cell_managers { for col in cm.get_typed_columns(*data_tag) { meta.lookup_any(format!("{:?}", data_tag), |meta| { @@ -451,10 +451,8 @@ impl ConstraintBuilder { // Halo2. self.fixed_tables .get(table_tag) - .expect(&format!( - "Fixed table {:?} not found for dynamic lookup", - table_tag - )) + .unwrap_or_else(|| panic!("Fixed table {:?} not found for dynamic lookup", + table_tag)) .clone() } else { // (v1, v2, v3) => cond * (t1, t2, t3) @@ -575,7 +573,7 @@ impl ConstraintBuilder { let challenge = self.lookup_challenge.clone().unwrap(); let mut local_compression = |values: &[Expression]| -> Expression { - let rlc = rlc::expr(&values, challenge.expr()) * local_condition.expr(); + let rlc = rlc::expr(values, challenge.expr()) * local_condition.expr(); match reduce { true => { let reduced_rlc = self.split_expression("compression", rlc); @@ -586,9 +584,9 @@ impl ConstraintBuilder { }; match (compress, reduce) { - (true, true) => vec![local_compression(&values)], - (true, false) => vec![local_compression(&values)], - (false, true) => values.iter().map(|_v| local_compression(&values)).collect(), + (true, true) => vec![local_compression(values)], + (true, false) => vec![local_compression(values)], + (false, true) => values.iter().map(|_v| local_compression(values)).collect(), (false, false) => values .iter() .map(|v| v.expr() * local_condition.expr()) diff --git a/zkevm-circuits/src/circuit_tools/memory.rs b/zkevm-circuits/src/circuit_tools/memory.rs index 5a428d43b1..ebdf5da465 100644 --- a/zkevm-circuits/src/circuit_tools/memory.rs +++ b/zkevm-circuits/src/circuit_tools/memory.rs @@ -98,7 +98,7 @@ impl> Memory { } pub(crate) fn tags(&self) -> Vec { - self.banks.iter().map(|(_, bank)| bank.tag().0).collect() + self.banks.values().map(|bank| bank.tag().0).collect() } } @@ -168,9 +168,9 @@ impl MemoryBank for RwBank { let rw: Vec> = [tag.0, tag.1] .iter() .map(|t| { - let config = (t.clone(), 1usize, phase, false); + let config = (*t, 1usize, phase, false); cm.add_celltype(meta, config, offset); - cm.get_typed_columns(t.clone())[0].column + cm.get_typed_columns(*t)[0].column }) .collect(); let key = meta.advice_column(); diff --git a/zkevm-circuits/src/taiko_pi_circuit.rs b/zkevm-circuits/src/taiko_pi_circuit.rs index 24301e60ce..97173b602c 100644 --- a/zkevm-circuits/src/taiko_pi_circuit.rs +++ b/zkevm-circuits/src/taiko_pi_circuit.rs @@ -1,4 +1,4 @@ -use eth_types::{Field, ToBigEndian, ToWord, H160, H256, U256}; +use eth_types::{Field, ToBigEndian, ToWord, H160, U256}; use ethers_core::abi::*; use ethers_core::utils::keccak256; @@ -495,7 +495,7 @@ pub struct TaikoPiCircuit { impl TaikoPiCircuit { /// Creates a new TaikoPiCircuit pub fn new(evidence: PublicData) -> Self { - Self { evidence: evidence } + Self { evidence } } } From 2cb3e17490488edfe68f3c07c1adc61d17113335 Mon Sep 17 00:00:00 2001 From: Cecilia Zhang Date: Mon, 25 Sep 2023 20:01:08 +0700 Subject: [PATCH 43/43] update --- .../src/circuit_tools/constraint_builder.rs | 196 +++++++++--------- 1 file changed, 98 insertions(+), 98 deletions(-) diff --git a/zkevm-circuits/src/circuit_tools/constraint_builder.rs b/zkevm-circuits/src/circuit_tools/constraint_builder.rs index e33826f249..6759a0f0e9 100644 --- a/zkevm-circuits/src/circuit_tools/constraint_builder.rs +++ b/zkevm-circuits/src/circuit_tools/constraint_builder.rs @@ -206,8 +206,7 @@ impl ConstraintBuilder { ) { query_expression(meta, |meta| { for (tag, table) in tables { - self.fixed_tables - .insert(*tag, table.table_exprs(meta)); + self.fixed_tables.insert(*tag, table.table_exprs(meta)); } }) } @@ -451,8 +450,9 @@ impl ConstraintBuilder { // Halo2. self.fixed_tables .get(table_tag) - .unwrap_or_else(|| panic!("Fixed table {:?} not found for dynamic lookup", - table_tag)) + .unwrap_or_else(|| { + panic!("Fixed table {:?} not found for dynamic lookup", table_tag) + }) .clone() } else { // (v1, v2, v3) => cond * (t1, t2, t3) @@ -1505,104 +1505,104 @@ macro_rules! circuit { #[allow(unused_macros)] macro_rules! require { - ($lhs:expr => bool) => {{ - _require!($cb, $lhs => bool); - }}; - - ($lhs:expr => $rhs:expr) => {{ - _require!($cb, $lhs => $rhs); - }}; - - ($name:expr, $lhs:expr => $rhs:expr) => {{ - _require!($cb, $name, $lhs => $rhs); - }}; - - // Lookups build from table - // only reduce flag is allowed - ($values:tt =>> @$tag:expr, $options:tt) => {{ - let values = _to_values_vec!($values); - let options = _to_options_vec!($options); - _require!($cb, values =>> @$tag, options); - }}; - ($values:tt =>> @$tag:expr) => {{ - let values = _to_values_vec!($values); - let options = Vec::new(); - _require!($cb, values =>> @$tag, options); - }}; - ($descr:expr, $values:tt =>> @$tag:expr, $options:tt) => {{ - let values = _to_values_vec!($values); - let options = _to_options_vec!($options); - _require!($cb, $descr, values =>> @$tag, options); - }}; - ($descr:expr, $values:tt =>> @$tag:expr) => {{ - let values = _to_values_vec!($values); - let options = Vec::new(); - _require!($cb, $descr, values =>> @$tag, options); - }}; - - - - ($values:tt => @$tag:expr, $options:tt) => {{ - let values = _to_values_vec!($values); - let options = _to_options_vec!($options); - _require!($cb, values => @$tag, options); - }}; - ($values:tt => @$tag:expr) => {{ - let values = _to_values_vec!($values); - let options = Vec::new(); - _require!($cb, values => @$tag, options); - }}; - ($descr:expr, $values:tt => @$tag:expr, $options:tt) => {{ - let values = _to_values_vec!($values); - let options = _to_options_vec!($options); - _require!($cb, $descr, values => @$tag, options); - }}; - ($descr:expr, $values:tt => @$tag:expr) => {{ - let values = _to_values_vec!($values); - let options = Vec::new(); - _require!($cb, $descr, values => @$tag, options); - }}; - - - (@$tag:expr, $options:tt => $values:tt) => {{ - let values = _to_values_vec!($values); - let options = _to_options_vec!($options); - _require!($cb, @$tag, options => values); - }}; - (@$tag:expr => $values:tt) => {{ - let values = _to_values_vec!($values); - let options = Vec::new(); - _require!($cb, @$tag, options => values); - }}; - (@$tag:expr, $options:tt =>> $values:tt) => {{ - let values = _to_values_vec!($values); - let options = _to_options_vec!($options); - _require!($cb, @$tag, options =>> values); - }}; - (@$tag:expr =>> $values:tt) => {{ - let values = _to_values_vec!($values); - let options = Vec::new(); - _require!($cb, @$tag, options =>> values); - }}; + ($lhs:expr => bool) => {{ + _require!($cb, $lhs => bool); + }}; + + ($lhs:expr => $rhs:expr) => {{ + _require!($cb, $lhs => $rhs); + }}; + + ($name:expr, $lhs:expr => $rhs:expr) => {{ + _require!($cb, $name, $lhs => $rhs); + }}; + + // Lookups build from table + // only reduce flag is allowed + ($values:tt =>> @$tag:expr, $options:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, values =>> @$tag, options); + }}; + ($values:tt =>> @$tag:expr) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, values =>> @$tag, options); + }}; + ($descr:expr, $values:tt =>> @$tag:expr, $options:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, $descr, values =>> @$tag, options); + }}; + ($descr:expr, $values:tt =>> @$tag:expr) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, $descr, values =>> @$tag, options); + }}; + + + + ($values:tt => @$tag:expr, $options:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, values => @$tag, options); + }}; + ($values:tt => @$tag:expr) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, values => @$tag, options); + }}; + ($descr:expr, $values:tt => @$tag:expr, $options:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, $descr, values => @$tag, options); + }}; + ($descr:expr, $values:tt => @$tag:expr) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, $descr, values => @$tag, options); + }}; + + + (@$tag:expr, $options:tt => $values:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, @$tag, options => values); + }}; + (@$tag:expr => $values:tt) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, @$tag, options => values); + }}; + (@$tag:expr, $options:tt =>> $values:tt) => {{ + let values = _to_values_vec!($values); + let options = _to_options_vec!($options); + _require!($cb, @$tag, options =>> values); + }}; + (@$tag:expr =>> $values:tt) => {{ + let values = _to_values_vec!($values); + let options = Vec::new(); + _require!($cb, @$tag, options =>> values); + }}; - } + } #[allow(unused_macros)] macro_rules! ifx { - ($condition:tt => $when_true:block elsex $when_false:block) => {{ - _ifx!($cb, ($condition) => $when_true elsex $when_false) - }}; - ($condition:expr => $when_true:block elsex $when_false:block) => {{ - _ifx!($cb, $condition => $when_true elsex $when_false) - }}; - - ($condition:tt => $when_true:block) => {{ - _ifx!($cb, $condition => $when_true) - }}; - ($condition:expr => $when_true:block) => {{ - _ifx!($cb, $condition => $when_true) - }}; - } + ($condition:tt => $when_true:block elsex $when_false:block) => {{ + _ifx!($cb, ($condition) => $when_true elsex $when_false) + }}; + ($condition:expr => $when_true:block elsex $when_false:block) => {{ + _ifx!($cb, $condition => $when_true elsex $when_false) + }}; + + ($condition:tt => $when_true:block) => {{ + _ifx!($cb, $condition => $when_true) + }}; + ($condition:expr => $when_true:block) => {{ + _ifx!($cb, $condition => $when_true) + }}; + } #[allow(unused_macros)] macro_rules! matchx {