diff --git a/README.md b/README.md new file mode 100644 index 00000000..bcd16c74 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# PLONK Verifier + +Generic PLONK verifier. diff --git a/src/lib.rs b/src/lib.rs index 9333b614..b193cc5c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![feature(int_log)] #![feature(int_roundings)] +#![feature(assert_matches)] #![allow(clippy::type_complexity)] #![allow(clippy::too_many_arguments)] #![allow(clippy::upper_case_acronyms)] diff --git a/src/loader/evm.rs b/src/loader/evm.rs index a18b36af..db71e176 100644 --- a/src/loader/evm.rs +++ b/src/loader/evm.rs @@ -1,4 +1,4 @@ -use crate::util::PrimeField; +use crate::{scheme::kzg::Cost, util::PrimeField}; use ethereum_types::U256; use std::iter; @@ -58,3 +58,13 @@ where .chain(proof) .collect() } + +pub fn estimate_gas(cost: Cost) -> usize { + let proof_size = cost.num_commitment * 64 + (cost.num_evaluation + cost.num_statement) * 32; + + let intrinsic_cost = 21000; + let calldata_cost = (proof_size as f64 * 15.25).ceil() as usize; + let ec_operation_cost = 113100 + (cost.num_msm - 2) * 6350; + + intrinsic_cost + calldata_cost + ec_operation_cost +} diff --git a/src/loader/halo2/loader.rs b/src/loader/halo2/loader.rs index bb22592d..775e891b 100644 --- a/src/loader/halo2/loader.rs +++ b/src/loader/halo2/loader.rs @@ -9,11 +9,11 @@ use halo2_wrong_ecc::{ rns::{Integer, Rns}, IntegerInstructions, Range, }, + maingate::{ + AssignedValue, CombinationOptionCommon, MainGate, MainGateInstructions, RegionCtx, Term, + }, AssignedPoint, BaseFieldEccChip, EccConfig, }; -use halo2_wrong_maingate::{ - AssignedValue, CombinationOptionCommon, MainGate, MainGateInstructions, RegionCtx, Term, -}; use rand::rngs::OsRng; use std::{ cell::RefCell, diff --git a/src/protocol.rs b/src/protocol.rs index 43faa879..e69e45e9 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -8,7 +8,7 @@ pub struct Protocol { pub zk: bool, pub domain: Domain, pub preprocessed: Vec, - pub num_statement: usize, + pub num_statement: Vec, pub num_auxiliary: Vec, pub num_challenge: Vec, pub evaluations: Vec, @@ -20,7 +20,9 @@ pub struct Protocol { impl Protocol { pub fn vanishing_poly(&self) -> usize { - self.preprocessed.len() + self.num_statement + self.num_auxiliary.iter().sum::() + self.preprocessed.len() + + self.num_statement.len() + + self.num_auxiliary.iter().sum::() } } @@ -36,6 +38,13 @@ impl Snark { statements: Vec::Scalar>>, proof: Vec, ) -> Self { + assert_eq!( + protocol.num_statement, + statements + .iter() + .map(|statements| statements.len()) + .collect::>() + ); Snark { protocol, statements, diff --git a/src/protocol/halo2.rs b/src/protocol/halo2.rs index 02cb481a..be15c3a4 100644 --- a/src/protocol/halo2.rs +++ b/src/protocol/halo2.rs @@ -18,6 +18,7 @@ mod test; pub struct Config { zk: bool, query_instance: bool, + num_instance: Vec, num_proof: usize, accumulator_indices: Option>, } @@ -26,6 +27,7 @@ pub fn compile(vk: &VerifyingKey, config: Config) -> let cs = vk.cs(); let Config { zk, + num_instance, query_instance, num_proof, accumulator_indices, @@ -42,7 +44,7 @@ pub fn compile(vk: &VerifyingKey, config: Config) -> .map(Into::into) .collect(); - let polynomials = &Polynomials::new(cs, zk, query_instance, num_proof); + let polynomials = &Polynomials::new(cs, zk, query_instance, num_instance, num_proof); let evaluations = iter::empty() .chain((0..num_proof).flat_map(move |t| polynomials.instance_queries(t))) @@ -110,7 +112,7 @@ struct Polynomials<'a, F: FieldExt> { num_proof: usize, num_fixed: usize, num_permutation_fixed: usize, - num_instance: usize, + num_instance: Vec, num_advice: Vec, num_challenge: Vec, advice_index: Vec, @@ -122,7 +124,13 @@ struct Polynomials<'a, F: FieldExt> { } impl<'a, F: FieldExt> Polynomials<'a, F> { - fn new(cs: &'a ConstraintSystem, zk: bool, query_instance: bool, num_proof: usize) -> Self { + fn new( + cs: &'a ConstraintSystem, + zk: bool, + query_instance: bool, + num_instance: Vec, + num_proof: usize, + ) -> Self { let degree = if zk { cs.degree::() } else { @@ -156,6 +164,8 @@ impl<'a, F: FieldExt> Polynomials<'a, F> { assert_eq!(num_advice.iter().sum::(), cs.num_advice_columns()); assert_eq!(num_challenge.iter().sum::(), cs.num_challenges()); + assert_eq!(cs.num_instance_columns(), num_instance.len()); + Self { cs, zk, @@ -163,7 +173,7 @@ impl<'a, F: FieldExt> Polynomials<'a, F> { num_proof, num_fixed: cs.num_fixed_columns(), num_permutation_fixed: cs.permutation().get_columns().len(), - num_instance: cs.num_instance_columns(), + num_instance, num_advice, num_challenge, advice_index, @@ -183,8 +193,11 @@ impl<'a, F: FieldExt> Polynomials<'a, F> { self.num_fixed + self.num_permutation_fixed } - fn num_statement(&self) -> usize { - self.num_proof * self.num_instance + fn num_statement(&self) -> Vec { + iter::repeat(self.num_instance.clone()) + .take(self.num_proof) + .flatten() + .collect() } fn num_auxiliary(&self) -> Vec { @@ -219,7 +232,7 @@ impl<'a, F: FieldExt> Polynomials<'a, F> { } fn auxiliary_offset(&self) -> usize { - self.instance_offset() + self.num_statement() + self.instance_offset() + self.num_statement().len() } fn cs_auxiliary_offset(&self) -> usize { @@ -240,7 +253,7 @@ impl<'a, F: FieldExt> Polynomials<'a, F> { ) -> Query { let offset = match column_type.into() { Any::Fixed => 0, - Any::Instance => self.instance_offset() + t * self.num_instance, + Any::Instance => self.instance_offset() + t * self.num_instance.len(), Any::Advice(advice) => { column_index = self.advice_index[column_index]; let phase_offset = self.num_proof @@ -638,7 +651,7 @@ impl<'a, F: FieldExt> Polynomials<'a, F> { accumulator_indices .iter() .cloned() - .map(|(poly, row)| (poly + t * self.num_instance, row)) + .map(|(poly, row)| (poly + t * self.num_instance.len(), row)) .collect() }) .collect() diff --git a/src/protocol/halo2/test.rs b/src/protocol/halo2/test.rs index 4314a4da..cfbcefef 100644 --- a/src/protocol/halo2/test.rs +++ b/src/protocol/halo2/test.rs @@ -1,5 +1,6 @@ use crate::{ protocol::halo2::{compile, Config}, + scheme::kzg::{Cost, CostEstimation, PlonkAccumulationScheme}, util::{CommonPolynomial, Expression, Query}, }; use halo2_curves::bn256::{Bn256, Fr, G1}; @@ -18,13 +19,15 @@ use rand_chacha::{ rand_core::{RngCore, SeedableRng}, ChaCha20Rng, }; +use std::assert_matches::assert_matches; mod circuit; mod kzg; pub use circuit::{ - maingate::{MainGateWithRange, MainGateWithRangeConfig}, - plookup::Plookuper, + maingate::{ + MainGateWithPlookup, MainGateWithPlookupConfig, MainGateWithRange, MainGateWithRangeConfig, + }, standard::StandardPlonk, }; @@ -97,6 +100,7 @@ fn test_compile_standard_plonk() { Config { zk: false, query_instance: false, + num_instance: vec![1], num_proof: 1, accumulator_indices: None, }, @@ -108,7 +112,7 @@ fn test_compile_standard_plonk() { let t = Query::new(13, Rotation::cur()); assert_eq!(protocol.preprocessed.len(), 8); - assert_eq!(protocol.num_statement, 1); + assert_eq!(protocol.num_statement, vec![1]); assert_eq!(protocol.num_auxiliary, vec![3, 0, 1]); assert_eq!(protocol.num_challenge, vec![1, 2, 0]); assert_eq!( @@ -159,4 +163,14 @@ fn test_compile_standard_plonk() { ] }) ); + + assert_matches!( + PlonkAccumulationScheme::estimate_cost(&protocol), + Cost { + num_commitment: 9, + num_evaluation: 13, + num_msm: 20, + .. + } + ); } diff --git a/src/protocol/halo2/test/circuit/maingate.rs b/src/protocol/halo2/test/circuit/maingate.rs index bc92aeb2..28ec3855 100644 --- a/src/protocol/halo2/test/circuit/maingate.rs +++ b/src/protocol/halo2/test/circuit/maingate.rs @@ -1,14 +1,22 @@ +use crate::protocol::halo2::test::circuit::plookup::PlookupConfig; use halo2_proofs::{ arithmetic::FieldExt, - circuit::{floor_planner::V1, Layouter, Value}, - plonk::{Circuit, ConstraintSystem, Error}, + circuit::{floor_planner::V1, Chip, Layouter, Value}, + plonk::{Any, Circuit, Column, ConstraintSystem, Error, Fixed}, + poly::Rotation, }; -use halo2_wrong_ecc::EccConfig; -use halo2_wrong_maingate::{ - MainGate, MainGateConfig, MainGateInstructions, RangeChip, RangeConfig, RangeInstructions, - RegionCtx, +use halo2_wrong_ecc::{ + maingate::{ + decompose, AssignedValue, MainGate, MainGateConfig, MainGateInstructions, RangeChip, + RangeConfig, RangeInstructions, RegionCtx, Term, + }, + EccConfig, }; use rand::RngCore; +use std::{ + collections::{BTreeMap, BTreeSet}, + iter, +}; #[derive(Clone)] pub struct MainGateWithRangeConfig { @@ -17,10 +25,6 @@ pub struct MainGateWithRangeConfig { } impl MainGateWithRangeConfig { - pub fn ecc_config(&self) -> EccConfig { - EccConfig::new(self.range_config.clone(), self.main_gate_config.clone()) - } - pub fn configure( meta: &mut ConstraintSystem, composition_bits: Vec, @@ -35,6 +39,10 @@ impl MainGateWithRangeConfig { } } + pub fn ecc_config(&self) -> EccConfig { + EccConfig::new(self.range_config.clone(), self.main_gate_config.clone()) + } + pub fn load_table(&self, layouter: &mut impl Layouter) -> Result<(), Error> { let range_chip = RangeChip::::new(self.range_config.clone()); range_chip.load_table(layouter)?; @@ -68,7 +76,7 @@ impl Circuit for MainGateWithRange { } fn configure(meta: &mut ConstraintSystem) -> Self::Config { - MainGateWithRangeConfig::configure(meta, vec![8], vec![1, 7]) + MainGateWithRangeConfig::configure(meta, vec![8], vec![4, 7]) } fn synthesize( @@ -86,8 +94,285 @@ impl Circuit for MainGateWithRange { let mut offset = 0; let mut ctx = RegionCtx::new(&mut region, &mut offset); range_chip.decompose(&mut ctx, Value::known(F::from(u64::MAX)), 8, 64)?; - range_chip.decompose(&mut ctx, Value::known(self.0[0]), 8, 33)?; - let (a, _) = range_chip.decompose(&mut ctx, Value::known(self.0[0]), 8, 39)?; + range_chip.decompose(&mut ctx, Value::known(F::from(u32::MAX as u64)), 8, 39)?; + let a = range_chip.assign(&mut ctx, Value::known(self.0[0]), 8, 68)?; + let b = main_gate.sub_sub_with_constant(&mut ctx, &a, &a, &a, F::from(2))?; + let cond = main_gate.assign_bit(&mut ctx, Value::known(F::one()))?; + main_gate.select(&mut ctx, &a, &b, &cond)?; + + Ok(a) + }, + )?; + + main_gate.expose_public(layouter, a, 0)?; + + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct PlookupRangeConfig { + main_gate_config: MainGateConfig, + plookup_config: PlookupConfig, + table: [Column; 2], + q_limb: [Column; 2], + q_overflow: [Column; 2], + bits: BTreeMap, +} + +pub struct PlookupRangeChip { + n: usize, + config: PlookupRangeConfig, + main_gate: MainGate, +} + +impl PlookupRangeChip { + pub fn new(config: PlookupRangeConfig, n: usize) -> Self { + let main_gate = MainGate::new(config.main_gate_config.clone()); + Self { + n, + config, + main_gate, + } + } + + pub fn configure( + meta: &mut ConstraintSystem, + main_gate_config: MainGateConfig, + bits: impl IntoIterator, + ) -> PlookupRangeConfig { + let table = [(); 2].map(|_| meta.fixed_column()); + let q_limb = [(); 2].map(|_| meta.fixed_column()); + let q_overflow = [(); 2].map(|_| meta.fixed_column()); + let plookup_config = PlookupConfig::configure( + meta, + |meta| { + let [a, b, c, d, _] = main_gate_config.advices(); + let limbs = [a, b, c, d].map(|column| meta.query_advice(column, Rotation::cur())); + let overflow = meta.query_advice(a, Rotation::cur()); + let q_limb = q_limb.map(|column| meta.query_fixed(column, Rotation::cur())); + let q_overflow = q_overflow.map(|column| meta.query_fixed(column, Rotation::cur())); + iter::empty() + .chain(limbs.into_iter().zip(iter::repeat(q_limb))) + .chain(Some((overflow, q_overflow))) + .map(|(value, [selector, tag])| [tag, selector * value]) + .collect() + }, + table.map(Column::::from), + None, + None, + None, + None, + ); + let bits = bits + .into_iter() + .collect::>() + .into_iter() + .enumerate() + .map(|(tag, bit)| (bit, tag)) + .collect(); + PlookupRangeConfig { + main_gate_config, + plookup_config, + table, + q_limb, + q_overflow, + bits, + } + } + + pub fn assign_inner(&self, layouter: impl Layouter, n: usize) -> Result<(), Error> { + self.config.plookup_config.assign(layouter, n) + } +} + +impl Chip for PlookupRangeChip { + type Config = PlookupRangeConfig; + + type Loaded = (); + + fn config(&self) -> &Self::Config { + &self.config + } + + fn loaded(&self) -> &Self::Loaded { + &() + } +} + +impl RangeInstructions for PlookupRangeChip { + fn assign( + &self, + ctx: &mut RegionCtx<'_, '_, F>, + value: Value, + limb_bit: usize, + bit: usize, + ) -> Result, Error> { + let (assigned, _) = self.decompose(ctx, value, limb_bit, bit)?; + Ok(assigned) + } + + fn decompose( + &self, + ctx: &mut RegionCtx<'_, '_, F>, + value: Value, + limb_bit: usize, + bit: usize, + ) -> Result<(AssignedValue, Vec>), Error> { + let (num_limbs, overflow) = (bit / limb_bit, bit % limb_bit); + + let num_limbs = num_limbs + if overflow > 0 { 1 } else { 0 }; + let terms = value + .map(|value| decompose(value, num_limbs, limb_bit)) + .transpose_vec(num_limbs) + .into_iter() + .zip((0..num_limbs).map(|i| F::from(2).pow(&[(limb_bit * i) as u64, 0, 0, 0]))) + .map(|(limb, base)| Term::Unassigned(limb, base)) + .collect::>(); + + self.main_gate + .decompose(ctx, &terms, F::zero(), |ctx, is_last| { + ctx.assign_fixed(|| "", self.config.q_limb[0], F::one())?; + ctx.assign_fixed( + || "", + self.config.q_limb[1], + F::from(*self.config.bits.get(&limb_bit).unwrap() as u64), + )?; + if is_last && overflow != 0 { + ctx.assign_fixed(|| "", self.config.q_overflow[0], F::one())?; + ctx.assign_fixed( + || "", + self.config.q_overflow[1], + F::from(*self.config.bits.get(&limb_bit).unwrap() as u64), + )?; + } + Ok(()) + }) + } + + fn load_table(&self, layouter: &mut impl Layouter) -> Result<(), Error> { + layouter.assign_region( + || "", + |mut region| { + let mut offset = 0; + + for (bit, tag) in self.config.bits.iter() { + let tag = F::from(*tag as u64); + let table_values: Vec = (0..1 << bit).map(|e| F::from(e)).collect(); + for value in table_values.iter() { + region.assign_fixed( + || "table tag", + self.config.table[0], + offset, + || Value::known(tag), + )?; + region.assign_fixed( + || "table value", + self.config.table[1], + offset, + || Value::known(*value), + )?; + offset += 1; + } + } + + for offset in offset..self.n { + region.assign_fixed( + || "table tag", + self.config.table[0], + offset, + || Value::known(F::zero()), + )?; + region.assign_fixed( + || "table value", + self.config.table[1], + offset, + || Value::known(F::zero()), + )?; + } + + Ok(()) + }, + )?; + + Ok(()) + } +} + +#[derive(Clone)] +pub struct MainGateWithPlookupConfig { + main_gate_config: MainGateConfig, + plookup_range_config: PlookupRangeConfig, +} + +impl MainGateWithPlookupConfig { + pub fn configure( + meta: &mut ConstraintSystem, + bits: impl IntoIterator, + ) -> Self { + let main_gate_config = MainGate::configure(meta); + let plookup_range_config = + PlookupRangeChip::configure(meta, main_gate_config.clone(), bits); + + assert_eq!(meta.degree::(), 3); + + MainGateWithPlookupConfig { + main_gate_config, + plookup_range_config, + } + } +} + +#[derive(Clone, Default)] +pub struct MainGateWithPlookup { + n: usize, + inner: Vec, +} + +impl MainGateWithPlookup { + pub fn new(k: u32, inner: Vec) -> Self { + Self { n: 1 << k, inner } + } + + pub fn instances(&self) -> Vec> { + vec![self.inner.clone()] + } +} + +impl Circuit for MainGateWithPlookup { + type Config = MainGateWithPlookupConfig; + type FloorPlanner = V1; + + fn without_witnesses(&self) -> Self { + Self { + n: self.n, + inner: vec![F::zero()], + } + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + MainGateWithPlookupConfig::configure(meta, [1, 7, 8]) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let main_gate = MainGate::::new(config.main_gate_config.clone()); + let range_chip = PlookupRangeChip::new(config.plookup_range_config, self.n); + + range_chip.load_table(&mut layouter)?; + range_chip.assign_inner(layouter.namespace(|| ""), self.n)?; + + let a = layouter.assign_region( + || "", + |mut region| { + let mut offset = 0; + let mut ctx = RegionCtx::new(&mut region, &mut offset); + range_chip.decompose(&mut ctx, Value::known(F::from(u64::MAX)), 8, 64)?; + range_chip.decompose(&mut ctx, Value::known(F::from(u32::MAX as u64)), 8, 39)?; + let a = range_chip.assign(&mut ctx, Value::known(self.inner[0]), 8, 68)?; let b = main_gate.sub_sub_with_constant(&mut ctx, &a, &a, &a, F::from(2))?; let cond = main_gate.assign_bit(&mut ctx, Value::known(F::one()))?; main_gate.select(&mut ctx, &a, &b, &cond)?; diff --git a/src/protocol/halo2/test/circuit/plookup.rs b/src/protocol/halo2/test/circuit/plookup.rs index f007f336..c40b1b47 100644 --- a/src/protocol/halo2/test/circuit/plookup.rs +++ b/src/protocol/halo2/test/circuit/plookup.rs @@ -1,15 +1,14 @@ use crate::util::{BatchInvert, Field}; use halo2_proofs::{ arithmetic::FieldExt, - circuit::{floor_planner::V1, Layouter, Value}, + circuit::{Layouter, Value}, plonk::{ - Advice, Any, Challenge, Circuit, Column, ConstraintSystem, Error, Expression, FirstPhase, - Fixed, SecondPhase, Selector, ThirdPhase, VirtualCells, + Advice, Any, Challenge, Column, ConstraintSystem, Error, Expression, FirstPhase, + SecondPhase, Selector, ThirdPhase, VirtualCells, }, poly::Rotation, }; use itertools::{EitherOrBoth, Itertools}; -use rand::RngCore; use std::{collections::BTreeMap, convert::TryFrom, iter, ops::Mul}; fn query( @@ -97,7 +96,7 @@ fn challenge_usable_after(meta: &mut ConstraintSystem, phase: u8 } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ShuffleConfig { l_0: Selector, zs: Vec>, @@ -260,7 +259,7 @@ impl ShuffleConfig { } } - fn assign(&self, mut layouter: impl Layouter, n: usize) -> Result<(), Error> { + pub fn assign(&self, mut layouter: impl Layouter, n: usize) -> Result<(), Error> { if ZK { todo!() } @@ -354,16 +353,15 @@ fn powers>(one: T, base: T) -> impl Iterator(inputs: &[Vec], table: &[F]) -> Vec { - let mut input_counts = - inputs - .iter() - .flatten() - .fold(BTreeMap::<_, usize>::new(), |mut map, value| { - map.entry(value) - .and_modify(|count| *count += 1) - .or_insert(1); - map - }); + let mut input_counts = inputs + .iter() + .flatten() + .fold(BTreeMap::new(), |mut map, value| { + map.entry(value) + .and_modify(|count| *count += 1) + .or_insert(1); + map + }); let mut ordered = Vec::with_capacity((inputs.len() + 1) * inputs[0].len()); for (count, value) in table.iter().dedup_with_count() { @@ -386,7 +384,7 @@ fn ordered_multiset(inputs: &[Vec], table: &[F]) -> Vec { } #[allow(dead_code)] -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct PlookupConfig { shuffle: ShuffleConfig, compressed_inputs: Vec>, @@ -540,7 +538,7 @@ impl PlookupConfig { } } - fn assign(&self, mut layouter: impl Layouter, n: usize) -> Result<(), Error> { + pub fn assign(&self, mut layouter: impl Layouter, n: usize) -> Result<(), Error> { if ZK { todo!() } @@ -585,127 +583,14 @@ impl PlookupConfig { } } -#[derive(Clone)] -pub struct Plookuper { - n: usize, - inputs: Value<[Vec<[F; W]>; T]>, - table: Vec<[F; W]>, -} - -impl Plookuper { - pub fn rand(mut rng: R, n: usize) -> Self { - let m = rng.next_u32() as usize % n; - let mut table = iter::repeat_with(|| [(); W].map(|_| F::random(&mut rng))) - .take(m) - .collect::>(); - table.extend( - iter::repeat( - table - .first() - .cloned() - .unwrap_or_else(|| [(); W].map(|_| F::random(&mut rng))), - ) - .take(n - m), - ); - let inputs = [(); T].map(|_| { - iter::repeat_with(|| table[rng.next_u32() as usize % n]) - .take(n) - .collect() - }); - Self { - n, - inputs: Value::known(inputs), - table, - } - } - - pub fn instances(&self) -> Vec> { - Vec::new() - } -} - -impl Circuit - for Plookuper -{ - type Config = ( - [[Column; W]; T], - [Column; W], - PlookupConfig, - ); - type FloorPlanner = V1; - - fn without_witnesses(&self) -> Self { - Self { - n: self.n, - inputs: Value::unknown(), - table: self.table.clone(), - } - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let inputs = [(); T].map(|_| [(); W].map(|_| meta.advice_column())); - let table = [(); W].map(|_| meta.fixed_column()); - let plookup = PlookupConfig::configure( - meta, - |meta| { - inputs - .iter() - .map(|input| input.map(|column| meta.query_advice(column, Rotation::cur()))) - .collect() - }, - table.map(|fixed| fixed.into()), - None, - None, - None, - None, - ); - - (inputs, table, plookup) - } - - fn synthesize( - &self, - (inputs, table, plookup): Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - layouter.assign_region( - || "", - |mut region| { - for (offset, value) in self.table.iter().enumerate() { - for (column, value) in table.iter().zip(value.iter()) { - region.assign_fixed(|| "", *column, offset, || Value::known(*value))?; - } - } - Ok(()) - }, - )?; - layouter.assign_region( - || "", - |mut region| { - for (idx, columns) in inputs.iter().enumerate() { - let values = self.inputs.as_ref().map(|inputs| inputs[idx].clone()); - for (offset, value) in values.transpose_vec(self.n).into_iter().enumerate() { - for (column, value) in columns.iter().zip(value.transpose_array()) { - region.assign_advice(|| "", *column, offset, || value)?; - } - } - } - Ok(()) - }, - )?; - plookup.assign(layouter.namespace(|| "Plookup"), self.n)?; - Ok(()) - } -} - #[cfg(test)] mod test { - use super::{Plookuper, ShuffleConfig}; + use super::{PlookupConfig, ShuffleConfig}; use halo2_curves::{bn256::Fr, FieldExt}; use halo2_proofs::{ circuit::{floor_planner::V1, Layouter, Value}, dev::{metadata::Constraint, FailureLocation, MockProver, VerifyFailure}, - plonk::{Advice, Circuit, Column, ConstraintSystem, Error}, + plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Fixed}, poly::Rotation, }; use rand::{rngs::OsRng, RngCore}; @@ -737,7 +622,8 @@ mod test { } impl Shuffler { - pub fn rand(mut rng: R, n: usize) -> Self { + pub fn rand(k: u32, mut rng: R) -> Self { + let n = 1 << k; let lhs = [(); T].map(|_| { let rng = &mut rng; iter::repeat_with(|| F::random(&mut *rng)) @@ -831,6 +717,117 @@ mod test { } } + #[derive(Clone)] + pub struct Plookuper { + n: usize, + inputs: Value<[Vec<[F; W]>; T]>, + table: Vec<[F; W]>, + } + + impl Plookuper { + pub fn rand(k: u32, mut rng: R) -> Self { + let n = 1 << k; + let m = rng.next_u32() as usize % n; + let mut table = iter::repeat_with(|| [(); W].map(|_| F::random(&mut rng))) + .take(m) + .collect::>(); + table.extend( + iter::repeat( + table + .first() + .cloned() + .unwrap_or_else(|| [(); W].map(|_| F::random(&mut rng))), + ) + .take(n - m), + ); + let inputs = [(); T].map(|_| { + iter::repeat_with(|| table[rng.next_u32() as usize % n]) + .take(n) + .collect() + }); + Self { + n, + inputs: Value::known(inputs), + table, + } + } + } + + impl Circuit + for Plookuper + { + type Config = ( + [[Column; W]; T], + [Column; W], + PlookupConfig, + ); + type FloorPlanner = V1; + + fn without_witnesses(&self) -> Self { + Self { + n: self.n, + inputs: Value::unknown(), + table: self.table.clone(), + } + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let inputs = [(); T].map(|_| [(); W].map(|_| meta.advice_column())); + let table = [(); W].map(|_| meta.fixed_column()); + let plookup = PlookupConfig::configure( + meta, + |meta| { + inputs + .iter() + .map(|input| input.map(|column| meta.query_advice(column, Rotation::cur()))) + .collect() + }, + table.map(|fixed| fixed.into()), + None, + None, + None, + None, + ); + + (inputs, table, plookup) + } + + fn synthesize( + &self, + (inputs, table, plookup): Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + layouter.assign_region( + || "", + |mut region| { + for (offset, value) in self.table.iter().enumerate() { + for (column, value) in table.iter().zip(value.iter()) { + region.assign_fixed(|| "", *column, offset, || Value::known(*value))?; + } + } + Ok(()) + }, + )?; + layouter.assign_region( + || "", + |mut region| { + for (idx, columns) in inputs.iter().enumerate() { + let values = self.inputs.as_ref().map(|inputs| inputs[idx].clone()); + for (offset, value) in values.transpose_vec(self.n).into_iter().enumerate() + { + for (column, value) in columns.iter().zip(value.transpose_array()) { + region.assign_advice(|| "", *column, offset, || value)?; + } + } + } + Ok(()) + }, + )?; + plookup.assign(layouter.namespace(|| "Plookup"), self.n)?; + Ok(()) + } + } + #[allow(dead_code)] fn assert_constraint_not_satisfied( result: Result<(), Vec>, @@ -865,8 +862,7 @@ mod test { const ZK: bool = false; let k = 9; - let n = 1 << k; - let circuit = Shuffler::::rand(OsRng, n); + let circuit = Shuffler::::rand(k, OsRng); let mut cs = ConstraintSystem::default(); Shuffler::::configure(&mut cs); @@ -878,6 +874,7 @@ mod test { #[cfg(not(feature = "sanity-check"))] { + let n = 1 << k; let mut circuit = circuit; circuit.lhs = mem::take(&mut circuit.lhs).map(|mut value| { value[0][0] += Fr::one(); @@ -910,8 +907,7 @@ mod test { const ZK: bool = false; let k = 9; - let n = 1 << k; - let circuit = Plookuper::::rand(OsRng, n); + let circuit = Plookuper::::rand(k, OsRng); let mut cs = ConstraintSystem::default(); Plookuper::::configure(&mut cs); @@ -923,6 +919,7 @@ mod test { #[cfg(not(feature = "sanity-check"))] { + let n = 1 << k; let mut circuit = circuit; circuit.inputs = mem::take(&mut circuit.inputs).map(|mut inputs| { inputs[0][0][0] += Fr::one(); diff --git a/src/protocol/halo2/test/kzg.rs b/src/protocol/halo2/test/kzg.rs index ca6bd62d..ae2f51b1 100644 --- a/src/protocol/halo2/test/kzg.rs +++ b/src/protocol/halo2/test/kzg.rs @@ -1,6 +1,6 @@ use crate::{ - protocol::halo2::test::MainGateWithRange, - util::{fe_to_limbs, Field}, + protocol::halo2::test::{MainGateWithPlookup, MainGateWithRange}, + util::fe_to_limbs, }; use halo2_curves::{pairing::Engine, CurveAffine}; use halo2_proofs::poly::{ @@ -8,7 +8,7 @@ use halo2_proofs::poly::{ kzg::commitment::{KZGCommitmentScheme, ParamsKZG}, }; use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng}; -use std::{fmt::Debug, fs, iter}; +use std::{fmt::Debug, fs}; mod halo2; mod native; @@ -38,17 +38,27 @@ pub fn read_or_create_srs(k: u32) -> ParamsKZG { pub fn main_gate_with_range_with_mock_kzg_accumulator( ) -> MainGateWithRange { let g = read_or_create_srs::(3).get_g(); - let (g1, s_g1) = (g[0], g[1]); + let [g1, s_g1] = [g[0], g[1]].map(|point| point.coordinates().unwrap()); MainGateWithRange::new( - iter::once(E::Scalar::zero()) - .chain({ - let g1 = g1.coordinates().unwrap(); - let s_g1 = s_g1.coordinates().unwrap(); - [*s_g1.x(), *s_g1.y(), *g1.x(), *g1.y()] - .iter() - .cloned() - .flat_map(fe_to_limbs::<_, _, LIMBS, BITS>) - }) + [*s_g1.x(), *s_g1.y(), *g1.x(), *g1.y()] + .iter() + .cloned() + .flat_map(fe_to_limbs::<_, _, LIMBS, BITS>) + .collect(), + ) +} + +pub fn main_gate_with_plookup_with_mock_kzg_accumulator( + k: u32, +) -> MainGateWithPlookup { + let g = read_or_create_srs::(3).get_g(); + let [g1, s_g1] = [g[0], g[1]].map(|point| point.coordinates().unwrap()); + MainGateWithPlookup::new( + k, + [*s_g1.x(), *s_g1.y(), *g1.x(), *g1.y()] + .iter() + .cloned() + .flat_map(fe_to_limbs::<_, _, LIMBS, BITS>) .collect(), ) } @@ -59,6 +69,7 @@ macro_rules! halo2_kzg_config { $crate::protocol::halo2::Config { zk: $zk, query_instance: false, + num_instance: Vec::new(), num_proof: $num_proof, accumulator_indices: None, } @@ -67,6 +78,7 @@ macro_rules! halo2_kzg_config { $crate::protocol::halo2::Config { zk: $zk, query_instance: false, + num_instance: Vec::new(), num_proof: $num_proof, accumulator_indices: Some($accumulator_indices), } @@ -102,19 +114,16 @@ macro_rules! halo2_kzg_prepare { pk }; - let protocol = compile::(pk.get_vk(), $config); - + let mut config = $config; + config.num_instance = circuits[0].instances().iter().map(|instances| instances.len()).collect(); + let protocol = compile::(pk.get_vk(), config); assert_eq!( protocol.preprocessed.len(), - BTreeSet::<[u8; 32]>::from_iter( - protocol.preprocessed.iter().map(|ec_point| ec_point - .to_bytes() - .as_ref() - .to_vec() - .try_into() - .unwrap()) - ) - .len() + protocol.preprocessed + .iter() + .map(|ec_point| ec_point.to_bytes().as_ref().to_vec().try_into().unwrap()) + .collect::>() + .len(), ); (params, pk, protocol, circuits) diff --git a/src/protocol/halo2/test/kzg/evm.rs b/src/protocol/halo2/test/kzg/evm.rs index e72553d7..e4441a54 100644 --- a/src/protocol/halo2/test/kzg/evm.rs +++ b/src/protocol/halo2/test/kzg/evm.rs @@ -4,7 +4,10 @@ use crate::{ loader::evm::EvmTranscript, protocol::halo2::{ test::{ - kzg::{halo2::Accumulation, main_gate_with_range_with_mock_kzg_accumulator, LIMBS}, + kzg::{ + halo2::Accumulation, main_gate_with_plookup_with_mock_kzg_accumulator, + main_gate_with_range_with_mock_kzg_accumulator, LIMBS, + }, StandardPlonk, }, util::evm::ChallengeEvm, @@ -114,18 +117,18 @@ test!( test!( zk_main_gate_with_range_with_mock_kzg_accumulator, 9, - halo2_kzg_config!(true, 1, (0..4 * LIMBS).map(|idx| (0, idx + 1)).collect()), + halo2_kzg_config!(true, 1, (0..4 * LIMBS).map(|idx| (0, idx)).collect()), main_gate_with_range_with_mock_kzg_accumulator::() ); test!( - #[ignore = "cause it requires 64GB memory to run"], + #[ignore = "cause it requires 16GB memory to run"], zk_accumulation_two_snark, 21, halo2_kzg_config!(true, 1, (0..4 * LIMBS).map(|idx| (0, idx)).collect()), Accumulation::two_snark(true) ); test!( - #[ignore = "cause it requires 128GB memory to run"], + #[ignore = "cause it requires 32GB memory to run"], zk_accumulation_two_snark_with_accumulator, 22, halo2_kzg_config!(true, 1, (0..4 * LIMBS).map(|idx| (0, idx)).collect()), @@ -140,18 +143,24 @@ test!( test!( main_gate_with_range_with_mock_kzg_accumulator, 9, - halo2_kzg_config!(false, 1, (0..4 * LIMBS).map(|idx| (0, idx + 1)).collect()), + halo2_kzg_config!(false, 1, (0..4 * LIMBS).map(|idx| (0, idx)).collect()), main_gate_with_range_with_mock_kzg_accumulator::() ); test!( - #[ignore = "cause it requires 64GB memory to run"], + main_gate_with_plookup_with_mock_kzg_accumulator, + 9, + halo2_kzg_config!(false, 1, (0..4 * LIMBS).map(|idx| (0, idx)).collect()), + main_gate_with_plookup_with_mock_kzg_accumulator::(9) +); +test!( + #[ignore = "cause it requires 16GB memory to run"], accumulation_two_snark, 21, halo2_kzg_config!(false, 1, (0..4 * LIMBS).map(|idx| (0, idx)).collect()), Accumulation::two_snark(false) ); test!( - #[ignore = "cause it requires 128GB memory to run"], + #[ignore = "cause it requires 32GB memory to run"], accumulation_two_snark_with_accumulator, 22, halo2_kzg_config!(false, 1, (0..4 * LIMBS).map(|idx| (0, idx)).collect()), diff --git a/src/protocol/halo2/test/kzg/halo2.rs b/src/protocol/halo2/test/kzg/halo2.rs index 96fdc6b7..b52092ce 100644 --- a/src/protocol/halo2/test/kzg/halo2.rs +++ b/src/protocol/halo2/test/kzg/halo2.rs @@ -29,8 +29,7 @@ use halo2_proofs::{ }, transcript::{Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer}, }; -use halo2_wrong_ecc; -use halo2_wrong_maingate::RegionCtx; +use halo2_wrong_ecc::{self, maingate::RegionCtx}; use halo2_wrong_transcript::NativeRepresentation; use paste::paste; use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng}; @@ -352,28 +351,28 @@ macro_rules! test { } test!( - #[ignore = "cause it requires 64GB memory to run"], + #[ignore = "cause it requires 16GB memory to run"], zk_accumulation_two_snark, 21, halo2_kzg_config!(true, 1, Accumulation::accumulator_indices()), Accumulation::two_snark(true) ); test!( - #[ignore = "cause it requires 128GB memory to run"], + #[ignore = "cause it requires 32GB memory to run"], zk_accumulation_two_snark_with_accumulator, 22, halo2_kzg_config!(true, 1, Accumulation::accumulator_indices()), Accumulation::two_snark_with_accumulator(true) ); test!( - #[ignore = "cause it requires 64GB memory to run"], + #[ignore = "cause it requires 16GB memory to run"], accumulation_two_snark, 21, halo2_kzg_config!(false, 1, Accumulation::accumulator_indices()), Accumulation::two_snark(false) ); test!( - #[ignore = "cause it requires 128GB memory to run"], + #[ignore = "cause it requires 32GB memory to run"], accumulation_two_snark_with_accumulator, 22, halo2_kzg_config!(false, 1, Accumulation::accumulator_indices()), diff --git a/src/protocol/halo2/test/kzg/native.rs b/src/protocol/halo2/test/kzg/native.rs index f9520bb3..4273e7d0 100644 --- a/src/protocol/halo2/test/kzg/native.rs +++ b/src/protocol/halo2/test/kzg/native.rs @@ -2,8 +2,11 @@ use crate::{ collect_slice, halo2_kzg_config, halo2_kzg_create_snark, halo2_kzg_native_verify, halo2_kzg_prepare, protocol::halo2::test::{ - kzg::{main_gate_with_range_with_mock_kzg_accumulator, LIMBS}, - Plookuper, StandardPlonk, + kzg::{ + main_gate_with_plookup_with_mock_kzg_accumulator, + main_gate_with_range_with_mock_kzg_accumulator, LIMBS, + }, + StandardPlonk, }, scheme::kzg::{PlonkAccumulationScheme, ShplonkAccumulationScheme}, }; @@ -65,7 +68,7 @@ test!( test!( zk_main_gate_with_range_with_mock_kzg_accumulator, 9, - halo2_kzg_config!(true, 2, (0..4 * LIMBS).map(|idx| (0, idx + 1)).collect()), + halo2_kzg_config!(true, 2, (0..4 * LIMBS).map(|idx| (0, idx)).collect()), main_gate_with_range_with_mock_kzg_accumulator::() ); test!( @@ -77,12 +80,12 @@ test!( test!( main_gate_with_range_with_mock_kzg_accumulator, 9, - halo2_kzg_config!(false, 2, (0..4 * LIMBS).map(|idx| (0, idx + 1)).collect()), + halo2_kzg_config!(false, 2, (0..4 * LIMBS).map(|idx| (0, idx)).collect()), main_gate_with_range_with_mock_kzg_accumulator::() ); test!( - plookup_rand, + main_gate_with_plookup_with_mock_kzg_accumulator, 9, - halo2_kzg_config!(false, 2), - Plookuper::<_, 2, 5, false>::rand(ChaCha20Rng::from_seed(Default::default()), 1 << 9) + halo2_kzg_config!(false, 1, (0..4 * LIMBS).map(|idx| (0, idx)).collect()), + main_gate_with_plookup_with_mock_kzg_accumulator::(9) ); diff --git a/src/scheme/kzg.rs b/src/scheme/kzg.rs index 971022e2..e1f949ab 100644 --- a/src/scheme/kzg.rs +++ b/src/scheme/kzg.rs @@ -4,12 +4,14 @@ use crate::{ }; mod accumulation; +mod cost; mod msm; pub use accumulation::{ plonk::PlonkAccumulationScheme, shplonk::ShplonkAccumulationScheme, AccumulationScheme, AccumulationStrategy, Accumulator, SameCurveAccumulation, }; +pub use cost::{Cost, CostEstimation}; pub use msm::MSM; pub fn langranges( diff --git a/src/scheme/kzg/accumulation/plonk.rs b/src/scheme/kzg/accumulation/plonk.rs index 4fd37f03..96c6ad00 100644 --- a/src/scheme/kzg/accumulation/plonk.rs +++ b/src/scheme/kzg/accumulation/plonk.rs @@ -3,6 +3,7 @@ use crate::{ protocol::Protocol, scheme::kzg::{ accumulation::{AccumulationScheme, AccumulationStrategy, Accumulator}, + cost::{Cost, CostEstimation}, langranges, msm::MSM, }, @@ -114,7 +115,12 @@ impl> PlonkProof { statements: Vec>, transcript: &mut T, ) -> Result { - if statements.len() != protocol.num_statement { + if protocol.num_statement + != statements + .iter() + .map(|statements| statements.len()) + .collect::>() + { return Err(Error::InvalidInstances); } for statements in statements.iter() { @@ -191,7 +197,7 @@ impl> PlonkProof { .enumerate(), ) .chain({ - let auxiliary_offset = protocol.preprocessed.len() + protocol.num_statement; + let auxiliary_offset = protocol.preprocessed.len() + protocol.num_statement.len(); self.auxiliaries .iter() .cloned() @@ -339,3 +345,29 @@ fn rotation_sets(protocol: &Protocol) -> Vec { sets }) } + +impl CostEstimation for PlonkAccumulationScheme { + fn estimate_cost(protocol: &Protocol) -> Cost { + let num_quotient = protocol + .relations + .iter() + .map(Expression::degree) + .max() + .unwrap() + - 1; + let num_w = rotation_sets(protocol).len(); + let num_accumulator = protocol + .accumulator_indices + .as_ref() + .map(|accumulator_indices| accumulator_indices.len()) + .unwrap_or_default(); + + let num_statement = protocol.num_statement.iter().sum(); + let num_commitment = protocol.num_auxiliary.iter().sum::() + num_quotient + num_w; + let num_evaluation = protocol.evaluations.len(); + let num_msm = + protocol.preprocessed.len() + num_commitment + 1 + num_w + 2 * num_accumulator; + + Cost::new(num_statement, num_commitment, num_evaluation, num_msm) + } +} diff --git a/src/scheme/kzg/accumulation/shplonk.rs b/src/scheme/kzg/accumulation/shplonk.rs index 7e16c2f1..ac716a56 100644 --- a/src/scheme/kzg/accumulation/shplonk.rs +++ b/src/scheme/kzg/accumulation/shplonk.rs @@ -3,6 +3,7 @@ use crate::{ protocol::Protocol, scheme::kzg::{ accumulation::{AccumulationScheme, AccumulationStrategy, Accumulator}, + cost::{Cost, CostEstimation}, langranges, msm::MSM, }, @@ -110,7 +111,12 @@ impl> ShplonkProof { statements: Vec>, transcript: &mut T, ) -> Result { - if statements.len() != protocol.num_statement { + if protocol.num_statement + != statements + .iter() + .map(|statements| statements.len()) + .collect::>() + { return Err(Error::InvalidInstances); } for statements in statements.iter() { @@ -191,7 +197,7 @@ impl> ShplonkProof { .enumerate(), ) .chain({ - let auxiliary_offset = protocol.preprocessed.len() + protocol.num_statement; + let auxiliary_offset = protocol.preprocessed.len() + protocol.num_statement.len(); self.auxiliaries .iter() .cloned() @@ -463,36 +469,17 @@ fn intermediate_sets>( z: &L::LoadedScalar, z_prime: &L::LoadedScalar, ) -> Vec> { - let mut superset = BTreeSet::new(); - let poly_rotations = protocol.queries.iter().fold( - Vec::<(usize, Vec, BTreeSet)>::new(), - |mut poly_rotations, query| { - superset.insert(query.rotation); - - if let Some(pos) = poly_rotations - .iter() - .position(|(poly, _, _)| *poly == query.poly) - { - let (_, rotations, set) = &mut poly_rotations[pos]; - if !set.contains(&query.rotation) { - rotations.push(query.rotation); - set.insert(query.rotation); - } - } else { - poly_rotations.push(( - query.poly, - vec![query.rotation], - BTreeSet::from_iter([query.rotation]), - )); - } - poly_rotations - }, - ); + let rotations_sets = rotations_sets(protocol); + let superset = rotations_sets + .iter() + .flat_map(|set| set.rotations.iter()) + .cloned() + .collect::>(); let size = 2.max( - (poly_rotations + (rotations_sets .iter() - .map(|(_, rotations, _)| rotations.len()) + .map(|set| set.rotations.len()) .max() .unwrap() - 1) @@ -514,35 +501,94 @@ fn intermediate_sets>( ); let mut z_s_1 = None; - poly_rotations.into_iter().fold( - Vec::>::new(), - |mut intermediate_sets, (poly, rotations, set)| { - if let Some(pos) = intermediate_sets.iter().position(|intermediate_set| { - BTreeSet::from_iter(intermediate_set.rotations.iter().cloned()) == set + rotations_sets + .into_iter() + .map(|set| { + let intermetidate_set = IntermediateSet { + polys: set.polys, + ..IntermediateSet::new( + &protocol.domain, + loader, + set.rotations, + &powers_of_z, + z_prime, + &z_prime_minus_z_omega_i, + &z_s_1, + ) + }; + if z_s_1.is_none() { + z_s_1 = Some(intermetidate_set.z_s.clone()); + }; + intermetidate_set + }) + .collect() +} + +struct RotationsSet { + rotations: Vec, + polys: Vec, +} + +fn rotations_sets(protocol: &Protocol) -> Vec { + let poly_rotations = protocol.queries.iter().fold( + Vec::<(usize, Vec)>::new(), + |mut poly_rotations, query| { + if let Some(pos) = poly_rotations + .iter() + .position(|(poly, _)| *poly == query.poly) + { + let (_, rotations) = &mut poly_rotations[pos]; + if !rotations.contains(&query.rotation) { + rotations.push(query.rotation); + } + } else { + poly_rotations.push((query.poly, vec![query.rotation])); + } + poly_rotations + }, + ); + + poly_rotations + .into_iter() + .fold(Vec::::new(), |mut sets, (poly, rotations)| { + if let Some(pos) = sets.iter().position(|set| { + BTreeSet::from_iter(set.rotations.iter()) == BTreeSet::from_iter(rotations.iter()) }) { - let intermediate_set = &mut intermediate_sets[pos]; - if !intermediate_set.polys.contains(&poly) { - intermediate_set.polys.push(poly); + let set = &mut sets[pos]; + if !set.polys.contains(&poly) { + set.polys.push(poly); } } else { - let intermetidate_set = IntermediateSet { + let set = RotationsSet { + rotations, polys: vec![poly], - ..IntermediateSet::new( - &protocol.domain, - loader, - rotations, - &powers_of_z, - z_prime, - &z_prime_minus_z_omega_i, - &z_s_1, - ) }; - if z_s_1.is_none() { - z_s_1 = Some(intermetidate_set.z_s.clone()); - } - intermediate_sets.push(intermetidate_set); + sets.push(set); } - intermediate_sets - }, - ) + sets + }) +} + +impl CostEstimation for ShplonkAccumulationScheme { + fn estimate_cost(protocol: &Protocol) -> Cost { + let num_quotient = protocol + .relations + .iter() + .map(Expression::degree) + .max() + .unwrap() + - 1; + let num_accumulator = protocol + .accumulator_indices + .as_ref() + .map(|accumulator_indices| accumulator_indices.len()) + .unwrap_or_default(); + + let num_statement = protocol.num_statement.iter().sum(); + let num_commitment = protocol.num_auxiliary.iter().sum::() + num_quotient + 2; + let num_evaluation = protocol.evaluations.len(); + let num_msm = protocol.preprocessed.len() + num_commitment + 3 + 2 * num_accumulator; + + Cost::new(num_statement, num_commitment, num_evaluation, num_msm) + } } diff --git a/src/scheme/kzg/cost.rs b/src/scheme/kzg/cost.rs new file mode 100644 index 00000000..f83f7dd1 --- /dev/null +++ b/src/scheme/kzg/cost.rs @@ -0,0 +1,29 @@ +use crate::{protocol::Protocol, util::Curve}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Cost { + pub num_statement: usize, + pub num_commitment: usize, + pub num_evaluation: usize, + pub num_msm: usize, +} + +impl Cost { + pub fn new( + num_statement: usize, + num_commitment: usize, + num_evaluation: usize, + num_msm: usize, + ) -> Self { + Self { + num_statement, + num_commitment, + num_evaluation, + num_msm, + } + } +} + +pub trait CostEstimation { + fn estimate_cost(protocol: &Protocol) -> Cost; +} diff --git a/src/util/expression.rs b/src/util/expression.rs index 88761401..912edf55 100644 --- a/src/util/expression.rs +++ b/src/util/expression.rs @@ -63,7 +63,7 @@ where zn, zn_minus_one_inv: Fraction::one_over(zn_minus_one), identity: z.clone(), - lagrange: BTreeMap::from_iter(langranges.into_iter().zip(lagrange_evals)), + lagrange: langranges.into_iter().zip(lagrange_evals).collect(), } }