diff --git a/arithmetic/curves/src/batch_pairing.rs b/arithmetic/curves/src/batch_pairing.rs new file mode 100644 index 0000000000..0f68265b4d --- /dev/null +++ b/arithmetic/curves/src/batch_pairing.rs @@ -0,0 +1,188 @@ +use group::{Curve, GroupEncoding}; +use std::collections::HashMap; + +use crate::pairing::{Engine, MultiMillerLoop}; + +/// Dynamically batches tuples of points and returns output compatible with MultiMillerLoop +pub struct PairingBatcher { + /// Mapping of g2 repr to overcome trait bounds + g2_to_g2: HashMap, E::G2>, + /// Mapping of all G2 points serialized with correlated G1 points + g2_to_g1: HashMap, E::G1>, + /// challenge + challenge: E::Scalar, + /// running challenge + running_challenge: E::Scalar, + /// is finalized + finalized: bool, +} + +impl PairingBatcher { + pub fn new(challenge: E::Scalar) -> Self { + Self { + g2_to_g2: HashMap::default(), + g2_to_g1: HashMap::default(), + challenge, + running_challenge: E::Scalar::from(1), + finalized: false, + } + } + + /// Adds new pairing equation that needs to be checked + pub fn add_pairing(&mut self, pairs: &[(E::G1Affine, E::G2Affine)]) { + let g2_reprs: Vec<_> = pairs + .iter() + .map(|&(_, g2)| g2.to_bytes().as_ref().to_vec()) + .collect(); + + // For each g2 point + let mut is_present: bool = false; + for repr in g2_reprs.iter() { + if self.g2_to_g1.get(repr).is_some() { + is_present = true; + break; + } + } + + let g2_points: Vec = pairs.iter().map(|&(_, g2)| g2.into()).collect(); + let g1_points: Vec = if is_present { + let running_challenge = self.running_challenge * self.challenge; + self.running_challenge = running_challenge; + pairs + .iter() + .map(|&(g1, _)| g1 * running_challenge) + .collect() + } else { + pairs.iter().map(|pair| pair.0.into()).collect() + }; + + self.update_mapping(&g2_reprs, &g1_points, &g2_points); + } + + fn update_mapping(&mut self, g2_reprs: &[Vec], g1_points: &[E::G1], g2_points: &[E::G2]) { + assert_eq!(g1_points.len(), g2_reprs.len()); + assert_eq!(g2_points.len(), g2_reprs.len()); + + g2_reprs + .iter() + .zip(g1_points.iter()) + .zip(g2_points.iter()) + .for_each(|((g2_repr, g1), g2)| { + self.g2_to_g1 + .entry(g2_repr.to_vec()) + .and_modify(|g1_point: &mut ::G1| *g1_point += g1) + .or_insert(*g1); + self.g2_to_g2.insert(g2_repr.to_vec(), *g2); + }); + } + + /// Returns output ready for MultiMillerLoop + pub fn finalize(mut self) -> Vec<(E::G1Affine, E::G2Prepared)> { + if self.finalized { + panic!("Batcher is already consumed!"); + } + self.finalized = true; + let g2_map = self.g2_to_g2.clone(); + self.g2_to_g1 + .iter() + .map(|(g2_repr, g1)| { + let g2 = g2_map.get(g2_repr).unwrap().to_affine(); + let g2_prepared: E::G2Prepared = g2.into(); + (g1.to_affine(), g2_prepared) + }) + .collect() + } +} + +#[cfg(test)] +mod tests { + use ff::Field; + use rand_core::OsRng; + + use super::*; + use crate::{ + bn256::{Bn256, Fr, G1Affine, G2Affine, G2Prepared, Gt, G1, G2}, + pairing::MillerLoopResult, + }; + + #[test] + fn test_bn256_batch_pairing() { + /* + e(a, b) = e(c, d) + e(j, b) = e(f, g) + e(e, d) = e(h, b) + */ + + let a = Fr::random(OsRng); + let b = Fr::random(OsRng); + let c = Fr::random(OsRng); + let d = a * b * c.invert().unwrap(); + let f = Fr::random(OsRng); + let j = Fr::random(OsRng); + let g = j * b * f.invert().unwrap(); + let e = Fr::random(OsRng); + let h = e * d * b.invert().unwrap(); + + let a: G1Affine = (G1::generator() * a).into(); + let b: G2Affine = (G2::generator() * b).to_affine(); + let c: G1Affine = (G1::generator() * c).into(); + let d: G2Affine = (G2::generator() * d).to_affine(); + let j: G1Affine = (G1::generator() * j).into(); + let f: G1Affine = (G1::generator() * f).into(); + let g: G2Affine = (G2::generator() * g).to_affine(); + let e: G1Affine = (G1::generator() * e).into(); + let h: G1Affine = (G1::generator() * h).into(); + + // Manual Miller loop + { + let b: G2Prepared = b.into(); + let d: G2Prepared = d.into(); + let g: G2Prepared = g.into(); + + let result: Gt = { + Bn256::multi_miller_loop(&[ + (&a, &b), + (&(-c), &d), + (&j, &b), + (&(-f), &g), + (&e, &d), + (&(-h), &b), + ]) + }; + + let pairing_result = result.final_exponentiation(); + assert_eq!(pairing_result, Gt::identity()); + } + + { + // Batched test + let mut pairing_batcher = PairingBatcher::::new(Fr::random(OsRng)); + + pairing_batcher.add_pairing(&[(a, b), ((-c), d)]); + pairing_batcher.add_pairing(&[(j, b), ((-f), g)]); + pairing_batcher.add_pairing(&[(e, d), ((-h), b)]); + + let batched_tuples = pairing_batcher.finalize(); + let result: Gt = Bn256::multi_miller_loop( + &batched_tuples + .iter() + .map(|(g1, g2)| (g1, g2)) + .collect::>(), + ); + + let pairing_result = result.final_exponentiation(); + assert_eq!(pairing_result, Gt::identity()); + + /* + e(a, b) = e(c, d) + e(j, b) = e(f, g) + e(e, d) = e(h, b) + + ==> + + e(a + [R]j + [R^2]h, b).e(c + [R^2]e, d).e([R]f, g) + */ + assert_eq!(3, batched_tuples.len()); + } + } +} diff --git a/arithmetic/curves/src/lib.rs b/arithmetic/curves/src/lib.rs index abdf1e2c57..2897c530c3 100644 --- a/arithmetic/curves/src/lib.rs +++ b/arithmetic/curves/src/lib.rs @@ -4,6 +4,7 @@ mod arithmetic; +pub mod batch_pairing; pub mod bn256; pub mod pairing; pub mod pasta; diff --git a/arithmetic/curves/src/pairing.rs b/arithmetic/curves/src/pairing.rs index 55586e7c9e..6a15064d8c 100644 --- a/arithmetic/curves/src/pairing.rs +++ b/arithmetic/curves/src/pairing.rs @@ -1,9 +1,10 @@ -use crate::{CurveAffine, FieldExt, Group as _Group}; +use crate::{serde::SerdeObject, CurveAffine, FieldExt, Group as _Group}; use core::ops::Mul; use group::{ prime::PrimeCurve, Group, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned, UncompressedEncoding, }; +use std::fmt::Debug; pub trait Engine: Sized + 'static + Clone { /// This is the scalar field of the engine's groups. @@ -34,7 +35,8 @@ pub trait Engine: Sized + 'static + Clone { + GroupOps + GroupOpsOwned + ScalarMul - + ScalarMulOwned; + + ScalarMulOwned + + _Group; /// The affine representation of an element in G2. type G2Affine: PairingCurveAffine< diff --git a/halo2_proofs/Cargo.toml b/halo2_proofs/Cargo.toml index 9d4535d4d5..f480af8749 100644 --- a/halo2_proofs/Cargo.toml +++ b/halo2_proofs/Cargo.toml @@ -64,6 +64,9 @@ criterion = "0.3" gumdrop = "0.8" proptest = "1" rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } +rand = "0.8" +rand_chacha = "0.3.1" +lazy_static = { version = "1.4.0"} [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dev-dependencies] getrandom = { version = "0.2", features = ["js"] } diff --git a/halo2_proofs/examples/serialization.rs b/halo2_proofs/examples/serialization.rs index 49474f924e..5069ccee55 100644 --- a/halo2_proofs/examples/serialization.rs +++ b/halo2_proofs/examples/serialization.rs @@ -129,7 +129,7 @@ impl Circuit for StandardPlonk { fn main() { let k = 4; let circuit = StandardPlonk(Fr::random(OsRng)); - let params = ParamsKZG::::setup(k, OsRng); + let params = ParamsKZG::::setup((1 << k) - 1, 0, OsRng); let vk = keygen_vk(¶ms, &circuit).expect("vk should not fail"); let pk = keygen_pk(¶ms, vk, &circuit).expect("pk should not fail"); @@ -168,7 +168,7 @@ fn main() { let strategy = SingleStrategy::new(¶ms); let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]); assert!(verify_proof::< - KZGCommitmentScheme, + Bn256, VerifierGWC<'_, Bn256>, Challenge255, Blake2bRead<&[u8], G1Affine, Challenge255>, diff --git a/halo2_proofs/examples/shuffle.rs b/halo2_proofs/examples/shuffle.rs index 71ba001848..04cf4d305f 100644 --- a/halo2_proofs/examples/shuffle.rs +++ b/halo2_proofs/examples/shuffle.rs @@ -1,359 +1,359 @@ -use ff::BatchInvert; -use halo2_proofs::{ - arithmetic::{CurveAffine, FieldExt}, - circuit::{Layouter, SimpleFloorPlanner, Value}, - dev::{metadata, FailureLocation, MockProver, VerifyFailure}, - halo2curves::pasta::EqAffine, - plonk::*, - poly::{ - commitment::ParamsProver, - ipa::{ - commitment::{IPACommitmentScheme, ParamsIPA}, - multiopen::{ProverIPA, VerifierIPA}, - strategy::AccumulatorStrategy, - }, - Rotation, VerificationStrategy, - }, - transcript::{ - Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer, - }, -}; -use rand_core::{OsRng, RngCore}; -use std::iter; - -fn rand_2d_array( - rng: &mut R, -) -> [[F; H]; W] { - [(); W].map(|_| [(); H].map(|_| F::random(&mut *rng))) -} - -fn shuffled( - original: [[F; H]; W], - rng: &mut R, -) -> [[F; H]; W] { - let mut shuffled = original; - - for row in (1..H).rev() { - let rand_row = (rng.next_u32() as usize) % row; - for column in shuffled.iter_mut() { - column.swap(row, rand_row); - } - } - - shuffled -} - -#[derive(Clone)] -struct MyConfig { - q_shuffle: Selector, - q_first: Selector, - q_last: Selector, - original: [Column; W], - shuffled: [Column; W], - theta: Challenge, - gamma: Challenge, - z: Column, -} - -impl MyConfig { - fn configure(meta: &mut ConstraintSystem) -> Self { - let [q_shuffle, q_first, q_last] = [(); 3].map(|_| meta.selector()); - // First phase - let original = [(); W].map(|_| meta.advice_column_in(FirstPhase)); - let shuffled = [(); W].map(|_| meta.advice_column_in(FirstPhase)); - let [theta, gamma] = [(); 2].map(|_| meta.challenge_usable_after(FirstPhase)); - // Second phase - let z = meta.advice_column_in(SecondPhase); - - meta.create_gate("z should start with 1", |meta| { - let q_first = meta.query_selector(q_first); - let z = meta.query_advice(z, Rotation::cur()); - let one = Expression::Constant(F::one()); - - vec![q_first * (one - z)] - }); - - meta.create_gate("z should end with 1", |meta| { - let q_last = meta.query_selector(q_last); - let z = meta.query_advice(z, Rotation::cur()); - let one = Expression::Constant(F::one()); - - vec![q_last * (one - z)] - }); - - meta.create_gate("z should have valid transition", |meta| { - let q_shuffle = meta.query_selector(q_shuffle); - let original = original.map(|advice| meta.query_advice(advice, Rotation::cur())); - let shuffled = shuffled.map(|advice| meta.query_advice(advice, Rotation::cur())); - let [theta, gamma] = [theta, gamma].map(|challenge| meta.query_challenge(challenge)); - let [z, z_w] = - [Rotation::cur(), Rotation::next()].map(|rotation| meta.query_advice(z, rotation)); - - // Compress - let original = original - .iter() - .cloned() - .reduce(|acc, a| acc * theta.clone() + a) - .unwrap(); - let shuffled = shuffled - .iter() - .cloned() - .reduce(|acc, a| acc * theta.clone() + a) - .unwrap(); - - vec![q_shuffle * (z * (original + gamma.clone()) - z_w * (shuffled + gamma))] - }); - - Self { - q_shuffle, - q_first, - q_last, - original, - shuffled, - theta, - gamma, - z, - } - } -} - -#[derive(Clone, Default)] -struct MyCircuit { - original: Value<[[F; H]; W]>, - shuffled: Value<[[F; H]; W]>, -} - -impl MyCircuit { - fn rand(rng: &mut R) -> Self { - let original = rand_2d_array::(rng); - let shuffled = shuffled(original, rng); - - Self { - original: Value::known(original), - shuffled: Value::known(shuffled), - } - } -} - -impl Circuit for MyCircuit { - type Config = MyConfig; - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - MyConfig::configure(meta) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - println!("Calling synthesize function"); - layouter.assign_region( - || "Shuffle original into shuffled", - |mut region| { - // Keygen - config.q_first.enable(&mut region, 0)?; - config.q_last.enable(&mut region, H)?; - for offset in 0..H { - config.q_shuffle.enable(&mut region, offset)?; - } - - // First phase - for (idx, (&column, values)) in config - .original - .iter() - .zip(self.original.transpose_array().iter()) - .enumerate() - { - for (offset, &value) in values.transpose_array().iter().enumerate() { - region.assign_advice( - // || format!("original[{}][{}]", idx, offset), - column, offset, value, - )?; - } - } - for (idx, (&column, values)) in config - .shuffled - .iter() - .zip(self.shuffled.transpose_array().iter()) - .enumerate() - { - for (offset, &value) in values.transpose_array().iter().enumerate() { - region.assign_advice( - // || format!("shuffled[{}][{}]", idx, offset), - column, offset, value, - )?; - } - } - - region.next_phase()?; - let theta = region.get_challenge(config.theta); - let gamma = region.get_challenge(config.gamma); - - // Second phase - let z = self.original.zip(self.shuffled).zip(theta).zip(gamma).map( - |(((original, shuffled), theta), gamma)| { - let mut product = vec![F::zero(); H]; - for (idx, product) in product.iter_mut().enumerate() { - let mut compressed = F::zero(); - for value in shuffled.iter() { - compressed *= theta; - compressed += value[idx]; - } - - *product = compressed + gamma - } - - product.iter_mut().batch_invert(); - - for (idx, product) in product.iter_mut().enumerate() { - let mut compressed = F::zero(); - for value in original.iter() { - compressed *= theta; - compressed += value[idx]; - } - - *product *= compressed + gamma - } - - #[allow(clippy::let_and_return)] - let z = iter::once(F::one()) - .chain(product) - .scan(F::one(), |state, cur| { - *state *= &cur; - Some(*state) - }) - .collect::>(); - - #[cfg(feature = "sanity-checks")] - assert_eq!(F::one(), *z.last().unwrap()); - - z - }, - ); - for (offset, value) in z.transpose_vec(H + 1).into_iter().enumerate() { - region.assign_advice( - // || format!("z[{}]", offset), - config.z, offset, value, - )?; - } - - Ok(()) - }, - ) - } -} - -fn test_mock_prover( - k: u32, - circuit: MyCircuit, - expected: Result<(), Vec<(metadata::Constraint, FailureLocation)>>, -) { - let prover = MockProver::run::<_>(k, &circuit, vec![]).unwrap(); - match (prover.verify(), expected) { - (Ok(_), Ok(_)) => {} - (Err(err), Err(expected)) => { - assert_eq!( - err.into_iter() - .map(|failure| match failure { - VerifyFailure::ConstraintNotSatisfied { - constraint, - location, - .. - } => (constraint, location), - _ => panic!("MockProver::verify has result unmatching expected"), - }) - .collect::>(), - expected - ) - } - (_, _) => panic!("MockProver::verify has result unmatching expected"), - }; -} - -fn test_prover( - k: u32, - circuit: MyCircuit, - expected: bool, -) { - let params = ParamsIPA::::new(k); - let vk = keygen_vk(¶ms, &circuit).unwrap(); - let pk = keygen_pk(¶ms, vk, &circuit).unwrap(); - - let proof = { - let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); - - println!("Begin create proof"); - create_proof::, ProverIPA, _, _, _, _>( - ¶ms, - &pk, - &[circuit], - &[&[]], - OsRng, - &mut transcript, - ) - .expect("proof generation should not fail"); - println!("End create proof"); - - transcript.finalize() - }; - - let accepted = { - let strategy = AccumulatorStrategy::new(¶ms); - let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]); - - verify_proof::, VerifierIPA, _, _, _>( - ¶ms, - pk.get_vk(), - strategy, - &[&[]], - &mut transcript, - ) - .map(|strategy| strategy.finalize()) - .unwrap_or_default() - }; - - assert_eq!(accepted, expected); -} - -fn main() { - const W: usize = 4; - const H: usize = 32; - const K: u32 = 8; - - let circuit = &MyCircuit::<_, W, H>::rand(&mut OsRng); - - { - test_mock_prover(K, circuit.clone(), Ok(())); - test_prover::(K, circuit.clone(), true); - } - - #[cfg(not(feature = "sanity-checks"))] - { - use std::ops::IndexMut; - - let mut circuit = circuit.clone(); - circuit.shuffled = circuit.shuffled.map(|mut shuffled| { - shuffled.index_mut(0).swap(0, 1); - shuffled - }); - - test_mock_prover( - K, - circuit.clone(), - Err(vec![( - ((1, "z should end with 1").into(), 0, "").into(), - FailureLocation::InRegion { - region: (0, "Shuffle original into shuffled").into(), - offset: 32, - }, - )]), - ); - test_prover::(K, circuit, false); - } -} +// use ff::BatchInvert; +// use halo2_proofs::{ +// arithmetic::{CurveAffine, FieldExt}, +// circuit::{Layouter, SimpleFloorPlanner, Value}, +// dev::{metadata, FailureLocation, MockProver, VerifyFailure}, +// halo2curves::pasta::EqAffine, +// plonk::*, +// poly::{ +// commitment::ParamsProver, +// ipa::{ +// commitment::{IPACommitmentScheme, ParamsIPA}, +// multiopen::{ProverIPA, VerifierIPA}, +// strategy::AccumulatorStrategy, +// }, +// Rotation, VerificationStrategy, +// }, +// transcript::{ +// Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer, +// }, +// }; +// use rand_core::{OsRng, RngCore}; +// use std::iter; + +// fn rand_2d_array( +// rng: &mut R, +// ) -> [[F; H]; W] { +// [(); W].map(|_| [(); H].map(|_| F::random(&mut *rng))) +// } + +// fn shuffled( +// original: [[F; H]; W], +// rng: &mut R, +// ) -> [[F; H]; W] { +// let mut shuffled = original; + +// for row in (1..H).rev() { +// let rand_row = (rng.next_u32() as usize) % row; +// for column in shuffled.iter_mut() { +// column.swap(row, rand_row); +// } +// } + +// shuffled +// } + +// #[derive(Clone)] +// struct MyConfig { +// q_shuffle: Selector, +// q_first: Selector, +// q_last: Selector, +// original: [Column; W], +// shuffled: [Column; W], +// theta: Challenge, +// gamma: Challenge, +// z: Column, +// } + +// impl MyConfig { +// fn configure(meta: &mut ConstraintSystem) -> Self { +// let [q_shuffle, q_first, q_last] = [(); 3].map(|_| meta.selector()); +// // First phase +// let original = [(); W].map(|_| meta.advice_column_in(FirstPhase)); +// let shuffled = [(); W].map(|_| meta.advice_column_in(FirstPhase)); +// let [theta, gamma] = [(); 2].map(|_| meta.challenge_usable_after(FirstPhase)); +// // Second phase +// let z = meta.advice_column_in(SecondPhase); + +// meta.create_gate("z should start with 1", |meta| { +// let q_first = meta.query_selector(q_first); +// let z = meta.query_advice(z, Rotation::cur()); +// let one = Expression::Constant(F::one()); + +// vec![q_first * (one - z)] +// }); + +// meta.create_gate("z should end with 1", |meta| { +// let q_last = meta.query_selector(q_last); +// let z = meta.query_advice(z, Rotation::cur()); +// let one = Expression::Constant(F::one()); + +// vec![q_last * (one - z)] +// }); + +// meta.create_gate("z should have valid transition", |meta| { +// let q_shuffle = meta.query_selector(q_shuffle); +// let original = original.map(|advice| meta.query_advice(advice, Rotation::cur())); +// let shuffled = shuffled.map(|advice| meta.query_advice(advice, Rotation::cur())); +// let [theta, gamma] = [theta, gamma].map(|challenge| meta.query_challenge(challenge)); +// let [z, z_w] = +// [Rotation::cur(), Rotation::next()].map(|rotation| meta.query_advice(z, rotation)); + +// // Compress +// let original = original +// .iter() +// .cloned() +// .reduce(|acc, a| acc * theta.clone() + a) +// .unwrap(); +// let shuffled = shuffled +// .iter() +// .cloned() +// .reduce(|acc, a| acc * theta.clone() + a) +// .unwrap(); + +// vec![q_shuffle * (z * (original + gamma.clone()) - z_w * (shuffled + gamma))] +// }); + +// Self { +// q_shuffle, +// q_first, +// q_last, +// original, +// shuffled, +// theta, +// gamma, +// z, +// } +// } +// } + +// #[derive(Clone, Default)] +// struct MyCircuit { +// original: Value<[[F; H]; W]>, +// shuffled: Value<[[F; H]; W]>, +// } + +// impl MyCircuit { +// fn rand(rng: &mut R) -> Self { +// let original = rand_2d_array::(rng); +// let shuffled = shuffled(original, rng); + +// Self { +// original: Value::known(original), +// shuffled: Value::known(shuffled), +// } +// } +// } + +// impl Circuit for MyCircuit { +// type Config = MyConfig; +// type FloorPlanner = SimpleFloorPlanner; + +// fn without_witnesses(&self) -> Self { +// Self::default() +// } + +// fn configure(meta: &mut ConstraintSystem) -> Self::Config { +// MyConfig::configure(meta) +// } + +// fn synthesize( +// &self, +// config: Self::Config, +// mut layouter: impl Layouter, +// ) -> Result<(), Error> { +// println!("Calling synthesize function"); +// layouter.assign_region( +// || "Shuffle original into shuffled", +// |mut region| { +// // Keygen +// config.q_first.enable(&mut region, 0)?; +// config.q_last.enable(&mut region, H)?; +// for offset in 0..H { +// config.q_shuffle.enable(&mut region, offset)?; +// } + +// // First phase +// for (idx, (&column, values)) in config +// .original +// .iter() +// .zip(self.original.transpose_array().iter()) +// .enumerate() +// { +// for (offset, &value) in values.transpose_array().iter().enumerate() { +// region.assign_advice( +// // || format!("original[{}][{}]", idx, offset), +// column, offset, value, +// )?; +// } +// } +// for (idx, (&column, values)) in config +// .shuffled +// .iter() +// .zip(self.shuffled.transpose_array().iter()) +// .enumerate() +// { +// for (offset, &value) in values.transpose_array().iter().enumerate() { +// region.assign_advice( +// // || format!("shuffled[{}][{}]", idx, offset), +// column, offset, value, +// )?; +// } +// } + +// region.next_phase()?; +// let theta = region.get_challenge(config.theta); +// let gamma = region.get_challenge(config.gamma); + +// // Second phase +// let z = self.original.zip(self.shuffled).zip(theta).zip(gamma).map( +// |(((original, shuffled), theta), gamma)| { +// let mut product = vec![F::zero(); H]; +// for (idx, product) in product.iter_mut().enumerate() { +// let mut compressed = F::zero(); +// for value in shuffled.iter() { +// compressed *= theta; +// compressed += value[idx]; +// } + +// *product = compressed + gamma +// } + +// product.iter_mut().batch_invert(); + +// for (idx, product) in product.iter_mut().enumerate() { +// let mut compressed = F::zero(); +// for value in original.iter() { +// compressed *= theta; +// compressed += value[idx]; +// } + +// *product *= compressed + gamma +// } + +// #[allow(clippy::let_and_return)] +// let z = iter::once(F::one()) +// .chain(product) +// .scan(F::one(), |state, cur| { +// *state *= &cur; +// Some(*state) +// }) +// .collect::>(); + +// #[cfg(feature = "sanity-checks")] +// assert_eq!(F::one(), *z.last().unwrap()); + +// z +// }, +// ); +// for (offset, value) in z.transpose_vec(H + 1).into_iter().enumerate() { +// region.assign_advice( +// // || format!("z[{}]", offset), +// config.z, offset, value, +// )?; +// } + +// Ok(()) +// }, +// ) +// } +// } + +// fn test_mock_prover( +// k: u32, +// circuit: MyCircuit, +// expected: Result<(), Vec<(metadata::Constraint, FailureLocation)>>, +// ) { +// let prover = MockProver::run::<_>(k, &circuit, vec![]).unwrap(); +// match (prover.verify(), expected) { +// (Ok(_), Ok(_)) => {} +// (Err(err), Err(expected)) => { +// assert_eq!( +// err.into_iter() +// .map(|failure| match failure { +// VerifyFailure::ConstraintNotSatisfied { +// constraint, +// location, +// .. +// } => (constraint, location), +// _ => panic!("MockProver::verify has result unmatching expected"), +// }) +// .collect::>(), +// expected +// ) +// } +// (_, _) => panic!("MockProver::verify has result unmatching expected"), +// }; +// } + +// fn test_prover( +// k: u32, +// circuit: MyCircuit, +// expected: bool, +// ) { +// let params = ParamsIPA::::new(k); +// let vk = keygen_vk(¶ms, &circuit).unwrap(); +// let pk = keygen_pk(¶ms, vk, &circuit).unwrap(); + +// let proof = { +// let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); + +// println!("Begin create proof"); +// create_proof::, ProverIPA, _, _, _, _>( +// ¶ms, +// &pk, +// &[circuit], +// &[&[]], +// OsRng, +// &mut transcript, +// ) +// .expect("proof generation should not fail"); +// println!("End create proof"); + +// transcript.finalize() +// }; + +// let accepted = { +// let strategy = AccumulatorStrategy::new(¶ms); +// let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]); + +// verify_proof::, VerifierIPA, _, _, _>( +// ¶ms, +// pk.get_vk(), +// strategy, +// &[&[]], +// &mut transcript, +// ) +// .map(|strategy| strategy.finalize()) +// .unwrap_or_default() +// }; + +// assert_eq!(accepted, expected); +// } + +// fn main() { +// const W: usize = 4; +// const H: usize = 32; +// const K: u32 = 8; + +// let circuit = &MyCircuit::<_, W, H>::rand(&mut OsRng); + +// { +// test_mock_prover(K, circuit.clone(), Ok(())); +// test_prover::(K, circuit.clone(), true); +// } + +// #[cfg(not(feature = "sanity-checks"))] +// { +// use std::ops::IndexMut; + +// let mut circuit = circuit.clone(); +// circuit.shuffled = circuit.shuffled.map(|mut shuffled| { +// shuffled.index_mut(0).swap(0, 1); +// shuffled +// }); + +// test_mock_prover( +// K, +// circuit.clone(), +// Err(vec![( +// ((1, "z should end with 1").into(), 0, "").into(), +// FailureLocation::InRegion { +// region: (0, "Shuffle original into shuffled").into(), +// offset: 32, +// }, +// )]), +// ); +// test_prover::(K, circuit, false); +// } +// } diff --git a/halo2_proofs/examples/simple-example.rs b/halo2_proofs/examples/simple-example.rs index 3531511f8f..e5adcf975d 100644 --- a/halo2_proofs/examples/simple-example.rs +++ b/halo2_proofs/examples/simple-example.rs @@ -3,7 +3,10 @@ use std::marker::PhantomData; use halo2_proofs::{ arithmetic::FieldExt, circuit::{AssignedCell, Chip, Layouter, Region, SimpleFloorPlanner, Value}, - plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Fixed, Instance, Selector}, + plonk::{ + static_lookup::{StaticCommittedTable, StaticTable, StaticTableId, StaticTableValues}, + Advice, Circuit, Column, ConstraintSystem, Error, Fixed, Instance, Selector, + }, poly::Rotation, }; @@ -230,6 +233,11 @@ impl NumericInstructions for FieldChip { Ok(()) } } +// static simple_table: StaticTable = StaticTable { +// opened, +// committed +// }; + // ANCHOR_END: instructions-impl // ANCHOR: circuit @@ -238,6 +246,7 @@ impl NumericInstructions for FieldChip { /// In this struct we store the private input variables. We use `Option` because /// they won't have any value during key generation. During proving, if any of these /// were `None` we would get an error. + #[derive(Default)] struct MyCircuit { constant: F, @@ -264,6 +273,13 @@ impl Circuit for MyCircuit { // Create a fixed column to load constants. let constant = meta.fixed_column(); + meta.lookup_static("lookup_bits", |meta| { + ( + meta.query_advice(vec![advice[0]], Rotation::cur()), + StaticTableId(String::from("bits_table")), + ) + }); + FieldChip::configure(meta, advice, instance, constant) } @@ -272,7 +288,10 @@ impl Circuit for MyCircuit { config: Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { - let field_chip = FieldChip::::construct(config); + layouter + .register_static_table::(StaticTableId(String::from("bits_table")), &simple_table)?; + + let field_chip: FieldChip = FieldChip::::construct(config); // Load our private values into the circuit. let a = field_chip.load_private(layouter.namespace(|| "load a"), self.a)?; diff --git a/halo2_proofs/src/arithmetic.rs b/halo2_proofs/src/arithmetic.rs index 69b63502bf..d2bb8c5c77 100644 --- a/halo2_proofs/src/arithmetic.rs +++ b/halo2_proofs/src/arithmetic.rs @@ -345,11 +345,15 @@ pub fn compute_inner_product(a: &[F], b: &[F]) -> F { /// Divides polynomial `a` in `X` by `X - b` with /// no remainder. -pub fn kate_division<'a, F: Field, I: IntoIterator>(a: I, mut b: F) -> Vec +/// +/// a(b) = eval +/// q = (a - eval) / (X - b) +pub fn kate_division<'a, F: Field, I: Clone + IntoIterator>(a: I, mut b: F) -> Vec where I::IntoIter: DoubleEndedIterator + ExactSizeIterator, { b = -b; + let mut a_tmp: Vec<_> = a.clone().into_iter().cloned().collect(); let a = a.into_iter(); let mut q = vec![F::zero(); a.len() - 1]; @@ -363,6 +367,22 @@ where tmp.mul_assign(&b); } + // KATE SANITY CHECK + { + // Note: b = -b + let eval: F = eval_polynomial(&a_tmp, -b); + a_tmp[0] -= eval; + // Note: b = -b + let scaled = q.iter().map(|&q| q * b).chain(Some(F::zero())); + let shifted = std::iter::once(F::zero()).chain(q.iter().cloned()); + + let coeffs_back = scaled.zip(shifted).map(|(a, b)| a + b); + + for (lhs, rhs) in coeffs_back.zip(a_tmp.into_iter()) { + assert_eq!(lhs, rhs); + } + } + q } diff --git a/halo2_proofs/src/circuit.rs b/halo2_proofs/src/circuit.rs index 294a636940..47786db170 100644 --- a/halo2_proofs/src/circuit.rs +++ b/halo2_proofs/src/circuit.rs @@ -3,10 +3,12 @@ use std::{convert::TryInto, fmt, marker::PhantomData}; use ff::Field; +use halo2curves::pairing::MultiMillerLoop; use crate::{ arithmetic::FieldExt, plonk::{ + static_lookup::{StaticTable, StaticTableId}, Advice, Any, Assigned, Challenge, Column, Error, Fixed, Instance, Selector, TableColumn, }, }; @@ -407,7 +409,8 @@ impl<'r, F: Field> Table<'r, F> { pub trait Layouter { /// Represents the type of the "root" of this layouter, so that nested namespaces /// can minimize indirection. - type Root: Layouter; + type Root: Layouter; + type E: MultiMillerLoop; /// Assign a region of gates to an absolute row number. /// @@ -441,6 +444,9 @@ pub trait Layouter { N: Fn() -> NR, NR: Into; + /// Register static table + fn register_static_table(&mut self, id: StaticTableId, table: StaticTable); + /// Constrains a [`Cell`] to equal an instance column's row value at an /// absolute position. fn constrain_instance(&mut self, cell: Cell, column: Column, row: usize); @@ -469,7 +475,7 @@ pub trait Layouter { fn pop_namespace(&mut self, gadget_name: Option); /// Enters into a namespace. - fn namespace(&mut self, name_fn: N) -> NamespacedLayouter<'_, F, Self::Root> + fn namespace(&mut self, name_fn: N) -> NamespacedLayouter<'_, Self::E, F, Self::Root> where NR: Into, N: FnOnce() -> NR, @@ -483,10 +489,18 @@ pub trait Layouter { /// This is a "namespaced" layouter which borrows a `Layouter` (pushing a namespace /// context) and, when dropped, pops out of the namespace context. #[derive(Debug)] -pub struct NamespacedLayouter<'a, F: Field, L: Layouter + 'a>(&'a mut L, PhantomData); - -impl<'a, F: Field, L: Layouter + 'a> Layouter for NamespacedLayouter<'a, F, L> { +pub struct NamespacedLayouter< + 'a, + E: MultiMillerLoop, + F: Field, + L: Layouter + 'a, +>(&'a mut L, PhantomData<(E, F)>); + +impl<'a, E: MultiMillerLoop, F: Field, L: Layouter + 'a> Layouter + for NamespacedLayouter<'a, E, F, L> +{ type Root = L::Root; + type E = E; fn assign_region(&mut self, name: N, assignment: A) -> Result where @@ -506,6 +520,10 @@ impl<'a, F: Field, L: Layouter + 'a> Layouter for NamespacedLayouter<'a, F self.0.assign_table(name, assignment) } + fn register_static_table(&mut self, id: StaticTableId, table: StaticTable) { + self.0.register_static_table(id, table); + } + fn constrain_instance(&mut self, cell: Cell, column: Column, row: usize) { self.0.constrain_instance(cell, column, row); } @@ -531,7 +549,9 @@ impl<'a, F: Field, L: Layouter + 'a> Layouter for NamespacedLayouter<'a, F } } -impl<'a, F: Field, L: Layouter + 'a> Drop for NamespacedLayouter<'a, F, L> { +impl<'a, E: MultiMillerLoop, F: Field, L: Layouter + 'a> Drop + for NamespacedLayouter<'a, E, F, L> +{ fn drop(&mut self) { let gadget_name = { #[cfg(feature = "gadget-traces")] diff --git a/halo2_proofs/src/circuit/floor_planner/single_pass.rs b/halo2_proofs/src/circuit/floor_planner/single_pass.rs index fc2aecdbcf..29ec90e984 100644 --- a/halo2_proofs/src/circuit/floor_planner/single_pass.rs +++ b/halo2_proofs/src/circuit/floor_planner/single_pass.rs @@ -3,8 +3,10 @@ use std::fmt; use std::marker::PhantomData; use ff::Field; +use halo2curves::pairing::MultiMillerLoop; use rustc_hash::FxHashMap; +use crate::plonk::static_lookup::{StaticTable, StaticTableId}; use crate::{ circuit::{ layouter::{RegionColumn, RegionLayouter, RegionShape, TableLayouter}, @@ -22,22 +24,28 @@ use crate::{ /// "business logic" in the circuit layout as closely as possible. It uses a single-pass /// layouter that does not reorder regions for optimal packing. #[derive(Debug)] -pub struct SimpleFloorPlanner; +pub struct SimpleFloorPlanner(PhantomData); -impl FloorPlanner for SimpleFloorPlanner { - fn synthesize, C: Circuit>( +impl FloorPlanner for SimpleFloorPlanner { + type E = E; + fn synthesize, C: Circuit>( cs: &mut CS, circuit: &C, config: C::Config, constants: Vec>, ) -> Result<(), Error> { - let layouter = SingleChipLayouter::new(cs, constants)?; + let layouter = SingleChipLayouter::::new(cs, constants)?; circuit.synthesize(config, layouter) } } /// A [`Layouter`] for a single-chip circuit. -pub struct SingleChipLayouter<'a, F: Field, CS: Assignment + 'a> { +pub struct SingleChipLayouter< + 'a, + E: MultiMillerLoop, + F: Field, + CS: Assignment + 'a, +> { cs: &'a mut CS, constants: Vec>, // Stores the starting row for each region. @@ -47,10 +55,14 @@ pub struct SingleChipLayouter<'a, F: Field, CS: Assignment + 'a> { columns: FxHashMap, /// Stores the table fixed columns. table_columns: Vec, - _marker: PhantomData, + // /// Stores all static tables that will be resolved in keygen + // static_tables: Vec<(StaticTableId, StaticTable)>, + _marker: PhantomData<(E, F)>, } -impl<'a, F: Field, CS: Assignment + 'a> fmt::Debug for SingleChipLayouter<'a, F, CS> { +impl<'a, E: MultiMillerLoop, F: Field, CS: Assignment + 'a> fmt::Debug + for SingleChipLayouter<'a, E, F, CS> +{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SingleChipLayouter") //.field("regions", &self.regions) @@ -59,7 +71,9 @@ impl<'a, F: Field, CS: Assignment + 'a> fmt::Debug for SingleChipLayouter<'a, } } -impl<'a, F: Field, CS: Assignment> SingleChipLayouter<'a, F, CS> { +impl<'a, E: MultiMillerLoop, F: Field, CS: Assignment> + SingleChipLayouter<'a, E, F, CS> +{ /// Creates a new single-chip layouter. pub fn new(cs: &'a mut CS, constants: Vec>) -> Result { let ret = SingleChipLayouter { @@ -68,14 +82,18 @@ impl<'a, F: Field, CS: Assignment> SingleChipLayouter<'a, F, CS> { // regions: vec![], columns: FxHashMap::default(), table_columns: vec![], + // static_tables: vec![], _marker: PhantomData, }; Ok(ret) } } -impl<'a, F: Field, CS: Assignment + 'a> Layouter for SingleChipLayouter<'a, F, CS> { +impl<'a, E: MultiMillerLoop, F: Field, CS: Assignment + 'a> Layouter + for SingleChipLayouter<'a, E, F, CS> +{ type Root = Self; + type E = E; fn assign_region(&mut self, name: N, assignment: A) -> Result where @@ -202,6 +220,10 @@ impl<'a, F: Field, CS: Assignment + 'a> Layouter for SingleChipLayouter<'a Ok(()) } + fn register_static_table(&mut self, id: StaticTableId, table: StaticTable) { + self.cs.register_static_table(id, table) + } + fn constrain_instance(&mut self, cell: Cell, instance: Column, row: usize) { self.cs.copy( cell.column, @@ -232,15 +254,21 @@ impl<'a, F: Field, CS: Assignment + 'a> Layouter for SingleChipLayouter<'a } } -struct SingleChipLayouterRegion<'r, 'a, F: Field, CS: Assignment + 'a> { - layouter: &'r mut SingleChipLayouter<'a, F, CS>, +struct SingleChipLayouterRegion< + 'r, + 'a, + E: MultiMillerLoop, + F: Field, + CS: Assignment + 'a, +> { + layouter: &'r mut SingleChipLayouter<'a, E, F, CS>, region_index: RegionIndex, /// Stores the constants to be assigned, and the cells to which they are copied. constants: Vec<(Assigned, Cell)>, } -impl<'r, 'a, F: Field, CS: Assignment + 'a> fmt::Debug - for SingleChipLayouterRegion<'r, 'a, F, CS> +impl<'r, 'a, E: MultiMillerLoop, F: Field, CS: Assignment + 'a> fmt::Debug + for SingleChipLayouterRegion<'r, 'a, E, F, CS> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SingleChipLayouterRegion") @@ -250,8 +278,10 @@ impl<'r, 'a, F: Field, CS: Assignment + 'a> fmt::Debug } } -impl<'r, 'a, F: Field, CS: Assignment + 'a> SingleChipLayouterRegion<'r, 'a, F, CS> { - fn new(layouter: &'r mut SingleChipLayouter<'a, F, CS>, region_index: RegionIndex) -> Self { +impl<'r, 'a, E: MultiMillerLoop, F: Field, CS: Assignment + 'a> + SingleChipLayouterRegion<'r, 'a, E, F, CS> +{ + fn new(layouter: &'r mut SingleChipLayouter<'a, E, F, CS>, region_index: RegionIndex) -> Self { SingleChipLayouterRegion { layouter, region_index, @@ -260,8 +290,8 @@ impl<'r, 'a, F: Field, CS: Assignment + 'a> SingleChipLayouterRegion<'r, 'a, } } -impl<'r, 'a, F: Field, CS: Assignment + 'a> RegionLayouter - for SingleChipLayouterRegion<'r, 'a, F, CS> +impl<'r, 'a, E: MultiMillerLoop, F: Field, CS: Assignment + 'a> + RegionLayouter for SingleChipLayouterRegion<'r, 'a, E, F, CS> { fn enable_selector<'v>( &'v mut self, @@ -462,7 +492,7 @@ impl<'r, 'a, F: Field, CS: Assignment + 'a> TableLayouter #[cfg(test)] mod tests { - use halo2curves::pasta::vesta; + use halo2curves::bn256::{Bn256, Fr}; use super::SimpleFloorPlanner; use crate::{ @@ -474,33 +504,26 @@ mod tests { fn not_enough_columns_for_constants() { struct MyCircuit {} - impl Circuit for MyCircuit { + impl Circuit for MyCircuit { type Config = Column; - type FloorPlanner = SimpleFloorPlanner; + type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { MyCircuit {} } - fn configure(meta: &mut crate::plonk::ConstraintSystem) -> Self::Config { + fn configure(meta: &mut crate::plonk::ConstraintSystem) -> Self::Config { meta.advice_column() } fn synthesize( &self, config: Self::Config, - mut layouter: impl crate::circuit::Layouter, + mut layouter: impl crate::circuit::Layouter, ) -> Result<(), crate::plonk::Error> { layouter.assign_region( || "assign constant", - |mut region| { - region.assign_advice_from_constant( - || "one", - config, - 0, - vesta::Scalar::one(), - ) - }, + |mut region| region.assign_advice_from_constant(|| "one", config, 0, Fr::one()), )?; Ok(()) diff --git a/halo2_proofs/src/circuit/floor_planner/v1.rs b/halo2_proofs/src/circuit/floor_planner/v1.rs index 657b7f2636..257bed4bcf 100644 --- a/halo2_proofs/src/circuit/floor_planner/v1.rs +++ b/halo2_proofs/src/circuit/floor_planner/v1.rs @@ -189,6 +189,12 @@ impl<'p, 'a, F: Field, CS: Assignment + 'a> Layouter for V1Pass<'p, 'a, F, } } + fn register_static_table(&mut self, id: StaticTableId, table: StaticTable) { + if let Pass::Assignment(pass) = &mut self.0 { + pass.plan.cs.register_static_table(id, &table) + } + } + fn constrain_instance( &mut self, cell: Cell, diff --git a/halo2_proofs/src/dev.rs b/halo2_proofs/src/dev.rs index 76f997d543..1e9d1ca0db 100644 --- a/halo2_proofs/src/dev.rs +++ b/halo2_proofs/src/dev.rs @@ -4,6 +4,7 @@ use std::collections::HashMap; use std::collections::HashSet; use std::fmt; use std::iter; +use std::marker::PhantomData; use std::ops::{Add, Mul, Neg, Range}; use std::sync::Arc; use std::time::{Duration, Instant}; @@ -15,12 +16,15 @@ use crate::{ arithmetic::{FieldExt, Group}, circuit, plonk::{ - permutation, Advice, Any, Assigned, Assignment, Challenge, Circuit, Column, ColumnType, + permutation, + static_lookup::{StaticTable, StaticTableId}, + Advice, Any, Assigned, Assignment, Challenge, Circuit, Column, ColumnType, ConstraintSystem, Error, Expression, Fixed, FloorPlanner, Instance, Phase, Selector, VirtualCell, }, poly::Rotation, }; +use halo2curves::pairing::MultiMillerLoop; use rayon::{ iter::{ IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator, ParallelIterator, @@ -287,7 +291,7 @@ impl Mul for Value { /// )); /// ``` #[derive(Debug)] -pub struct MockProver { +pub struct MockProver> { k: u32, n: u32, cs: ConstraintSystem, @@ -313,9 +317,12 @@ pub struct MockProver { // A range of available rows for assignment and copies. usable_rows: Range, + _marker: PhantomData, } -impl Assignment for MockProver { +impl> Assignment for MockProver { + type E = E; + fn enter_region(&mut self, name: N) where NR: Into, @@ -335,6 +342,15 @@ impl Assignment for MockProver { self.regions.push(self.current_region.take().unwrap()); } + fn register_static_table( + &mut self, + id: StaticTableId, + static_table: StaticTable, + ) { + // if ctx = prover then check that prover part is some and take it else panic + // if ctx = verifier then check that verifier part is some and take it else panic + } + fn enable_selector(&mut self, _: A, selector: &Selector, row: usize) -> Result<(), Error> where A: FnOnce() -> AR, @@ -481,10 +497,10 @@ impl Assignment for MockProver { } } -impl MockProver { +impl + std::marker::Sync> MockProver { /// Runs a synthetic keygen-and-prove operation on the given circuit, collecting data /// about the constraints and their assignments. - pub fn run>( + pub fn run>( k: u32, circuit: &ConcreteCircuit, instance: Vec>, @@ -562,6 +578,7 @@ impl MockProver { challenges, permutation, usable_rows: 0..usable_rows, + _marker: PhantomData, }; ConcreteCircuit::FloorPlanner::synthesize(&mut prover, circuit, config, constants)?; @@ -1378,7 +1395,8 @@ impl MockProver { #[cfg(test)] mod tests { - use halo2curves::pasta::Fp; + // use halo2curves::pasta::Fp; + use halo2curves::bn256::{Bn256, Fr as Fp}; use super::{FailureLocation, MockProver, VerifyFailure}; use crate::{ @@ -1402,9 +1420,9 @@ mod tests { struct FaultyCircuit {} - impl Circuit for FaultyCircuit { + impl Circuit for FaultyCircuit { type Config = FaultyCircuitConfig; - type FloorPlanner = SimpleFloorPlanner; + type FloorPlanner = SimpleFloorPlanner; fn configure(meta: &mut ConstraintSystem) -> Self::Config { let a = meta.advice_column(); @@ -1480,9 +1498,9 @@ mod tests { struct FaultyCircuit {} - impl Circuit for FaultyCircuit { + impl Circuit for FaultyCircuit { type Config = FaultyCircuitConfig; - type FloorPlanner = SimpleFloorPlanner; + type FloorPlanner = SimpleFloorPlanner; fn configure(meta: &mut ConstraintSystem) -> Self::Config { let a = meta.advice_column(); diff --git a/halo2_proofs/src/dev/failure.rs b/halo2_proofs/src/dev/failure.rs index 89c0c05d95..bf6a6772d8 100644 --- a/halo2_proofs/src/dev/failure.rs +++ b/halo2_proofs/src/dev/failure.rs @@ -3,6 +3,7 @@ use std::fmt; use std::iter; use group::ff::Field; +use halo2curves::pairing::MultiMillerLoop; use halo2curves::FieldExt; use super::{ @@ -382,8 +383,8 @@ fn render_constraint_not_satisfied( /// | x0 = 0x5 /// | x1 = 1 /// ``` -fn render_lookup( - prover: &MockProver, +fn render_lookup>( + prover: &MockProver, name: &str, lookup_index: usize, location: &FailureLocation, @@ -536,7 +537,10 @@ fn render_lookup( impl VerifyFailure { /// Emits this failure in pretty-printed format to stderr. - pub(super) fn emit(&self, prover: &MockProver) { + pub(super) fn emit>( + &self, + prover: &MockProver, + ) { match self { Self::CellNotAssigned { gate, diff --git a/halo2_proofs/src/dev/gates.rs b/halo2_proofs/src/dev/gates.rs index cfc71c021e..21905bccb3 100644 --- a/halo2_proofs/src/dev/gates.rs +++ b/halo2_proofs/src/dev/gates.rs @@ -4,6 +4,7 @@ use std::{ }; use ff::PrimeField; +use halo2curves::pairing::MultiMillerLoop; use crate::{ dev::util, @@ -103,7 +104,7 @@ pub struct CircuitGates { impl CircuitGates { /// Collects the gates from within the circuit. - pub fn collect>() -> Self { + pub fn collect>() -> Self { // Collect the graph details. let mut cs = ConstraintSystem::default(); let _ = C::configure(&mut cs); diff --git a/halo2_proofs/src/plonk.rs b/halo2_proofs/src/plonk.rs index e40d3deca2..68b580d761 100644 --- a/halo2_proofs/src/plonk.rs +++ b/halo2_proofs/src/plonk.rs @@ -7,7 +7,10 @@ use blake2b_simd::Params as Blake2bParams; use ff::PrimeField; -use group::ff::Field; +use group::{ff::Field, GroupEncoding}; +use halo2curves::pairing::{Engine, MultiMillerLoop}; +use halo2curves::serde::SerdeObject; +use halo2curves::CurveExt; use crate::arithmetic::{CurveAffine, FieldExt}; use crate::helpers::{ @@ -28,6 +31,7 @@ mod evaluation; mod keygen; mod lookup; pub(crate) mod permutation; +pub mod static_lookup; mod vanishing; mod prover; @@ -41,26 +45,41 @@ pub use prover::*; pub use verifier::*; use evaluation::Evaluator; +use std::collections::BTreeMap; +use std::fmt::Debug; use std::io; +use self::static_lookup::{ + StaticCommittedTable, StaticTable, StaticTableConfig, StaticTableId, StaticTableValues, +}; + /// This is a verifying key which allows for the verification of proofs for a /// particular circuit. #[derive(Clone, Debug)] -pub struct VerifyingKey { - domain: EvaluationDomain, - fixed_commitments: Vec, - permutation: permutation::VerifyingKey, - cs: ConstraintSystem, +pub struct VerifyingKey +where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeCurveAffine, + E::G2Affine: SerdeCurveAffine, +{ + domain: EvaluationDomain, + fixed_commitments: Vec, + permutation: permutation::VerifyingKey, + cs: ConstraintSystem, /// Cached maximum degree of `cs` (which doesn't change after construction). cs_degree: usize, /// The representative of this `VerifyingKey` in transcripts. - transcript_repr: C::Scalar, + transcript_repr: E::Scalar, selectors: Vec>, + static_table_mapping: BTreeMap, StaticCommittedTable>, } -impl VerifyingKey +impl VerifyingKey where - C::Scalar: SerdePrimeField, + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeCurveAffine, + E::G2Affine: SerdeCurveAffine, + E::Scalar: SerdePrimeField, { /// Writes a verifying key to a buffer. /// @@ -101,20 +120,20 @@ where /// Checks that field elements are less than modulus, and then checks that the point is on the curve. /// - `RawBytesUnchecked`: Reads an uncompressed curve element with coordinates in Montgomery form; /// does not perform any checks - pub fn read>( + pub fn read>( reader: &mut R, format: SerdeFormat, ) -> io::Result { let mut k = [0u8; 4]; reader.read_exact(&mut k)?; let k = u32::from_be_bytes(k); - let (domain, cs, _) = keygen::create_domain::(k); + let (domain, cs, _) = keygen::create_domain::(k); let mut num_fixed_columns = [0u8; 4]; reader.read_exact(&mut num_fixed_columns).unwrap(); let num_fixed_columns = u32::from_be_bytes(num_fixed_columns); let fixed_commitments: Vec<_> = (0..num_fixed_columns) - .map(|_| C::read(reader, format)) + .map(|_| E::G1Affine::read(reader, format)) .collect(); let permutation = permutation::VerifyingKey::read(reader, &cs.permutation, format); @@ -139,6 +158,8 @@ where permutation, cs, selectors, + // TODO: FIXME + BTreeMap::default(), )) } @@ -150,7 +171,7 @@ where } /// Reads a verification key from a slice of bytes using [`Self::read`]. - pub fn from_bytes>( + pub fn from_bytes>( mut bytes: &[u8], format: SerdeFormat, ) -> io::Result { @@ -158,9 +179,13 @@ where } } -impl VerifyingKey { +impl VerifyingKey +where + E::G1Affine: SerdeCurveAffine, + E::G2Affine: SerdeCurveAffine, +{ fn bytes_length(&self) -> usize { - 8 + (self.fixed_commitments.len() * C::default().to_bytes().as_ref().len()) + 8 + (self.fixed_commitments.len() * E::G1Affine::default().to_bytes().as_ref().len()) + self.permutation.bytes_length() + self.selectors.len() * (self @@ -171,11 +196,12 @@ impl VerifyingKey { } fn from_parts( - domain: EvaluationDomain, - fixed_commitments: Vec, - permutation: permutation::VerifyingKey, - cs: ConstraintSystem, + domain: EvaluationDomain, + fixed_commitments: Vec, + permutation: permutation::VerifyingKey, + cs: ConstraintSystem, selectors: Vec>, + static_table_mapping: BTreeMap, StaticCommittedTable>, ) -> Self { // Compute cached values. let cs_degree = cs.degree(); @@ -187,8 +213,9 @@ impl VerifyingKey { cs, cs_degree, // Temporary, this is not pinned. - transcript_repr: C::Scalar::zero(), + transcript_repr: E::Scalar::zero(), selectors, + static_table_mapping, }; let mut hasher = Blake2bParams::new() @@ -202,13 +229,13 @@ impl VerifyingKey { hasher.update(s.as_bytes()); // Hash in final Blake2bState - vk.transcript_repr = C::Scalar::from_bytes_wide(hasher.finalize().as_array()); + vk.transcript_repr = E::Scalar::from_bytes_wide(hasher.finalize().as_array()); vk } /// Hashes a verification key into a transcript. - pub fn hash_into, T: Transcript>( + pub fn hash_into, T: Transcript>( &self, transcript: &mut T, ) -> io::Result<()> { @@ -219,10 +246,10 @@ impl VerifyingKey { /// Obtains a pinned representation of this verification key that contains /// the minimal information necessary to reconstruct the verification key. - pub fn pinned(&self) -> PinnedVerificationKey<'_, C> { + pub fn pinned(&self) -> PinnedVerificationKey<'_, E::G1Affine> { PinnedVerificationKey { - base_modulus: C::Base::MODULUS, - scalar_modulus: C::Scalar::MODULUS, + base_modulus: ::Base::MODULUS, + scalar_modulus: ::MODULUS, domain: self.domain.pinned(), fixed_commitments: &self.fixed_commitments, permutation: &self.permutation, @@ -231,17 +258,17 @@ impl VerifyingKey { } /// Returns commitments of fixed polynomials - pub fn fixed_commitments(&self) -> &Vec { + pub fn fixed_commitments(&self) -> &Vec { &self.fixed_commitments } /// Returns `VerifyingKey` of permutation - pub fn permutation(&self) -> &permutation::VerifyingKey { + pub fn permutation(&self) -> &permutation::VerifyingKey { &self.permutation } /// Returns `ConstraintSystem` - pub fn cs(&self) -> &ConstraintSystem { + pub fn cs(&self) -> &ConstraintSystem { &self.cs } } @@ -261,27 +288,38 @@ pub struct PinnedVerificationKey<'a, C: CurveAffine> { /// This is a proving key which allows for the creation of proofs for a /// particular circuit. #[derive(Clone, Debug)] -pub struct ProvingKey { - vk: VerifyingKey, - l0: Polynomial, - l_last: Polynomial, - l_active_row: Polynomial, - fixed_values: Vec>, - fixed_polys: Vec>, - fixed_cosets: Vec>, - permutation: permutation::ProvingKey, - ev: Evaluator, +pub struct ProvingKey +where + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, +{ + vk: VerifyingKey, + l0: Polynomial, + l_last: Polynomial, + l_active_row: Polynomial, + fixed_values: Vec>, + fixed_polys: Vec>, + fixed_cosets: Vec>, + permutation: permutation::ProvingKey, + ev: Evaluator, + static_table_mapping: BTreeMap, StaticTableValues>, + static_table_configs: BTreeMap>, + b0_g1_bound: Vec, } -impl ProvingKey { +impl ProvingKey +where + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, +{ /// Get the underlying [`VerifyingKey`]. - pub fn get_vk(&self) -> &VerifyingKey { + pub fn get_vk(&self) -> &VerifyingKey { &self.vk } /// Gets the total number of bytes in the serialization of `self` fn bytes_length(&self) -> usize { - let scalar_len = C::Scalar::default().to_repr().as_ref().len(); + let scalar_len = E::Scalar::default().to_repr().as_ref().len(); self.vk.bytes_length() + 12 + scalar_len * (self.l0.len() + self.l_last.len() + self.l_active_row.len()) @@ -292,9 +330,11 @@ impl ProvingKey { } } -impl ProvingKey +impl ProvingKey where - C::Scalar: SerdePrimeField, + E::Scalar: SerdePrimeField, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, { /// Writes a proving key to a buffer. /// @@ -329,11 +369,11 @@ where /// Checks that field elements are less than modulus, and then checks that the point is on the curve. /// - `RawBytesUnchecked`: Reads an uncompressed curve element with coordinates in Montgomery form; /// does not perform any checks - pub fn read>( + pub fn read>( reader: &mut R, format: SerdeFormat, ) -> io::Result { - let vk = VerifyingKey::::read::(reader, format).unwrap(); + let vk = VerifyingKey::::read::(reader, format).unwrap(); let l0 = Polynomial::read(reader, format); let l_last = Polynomial::read(reader, format); let l_active_row = Polynomial::read(reader, format); @@ -341,7 +381,10 @@ where let fixed_polys = read_polynomial_vec(reader, format); let fixed_cosets = read_polynomial_vec(reader, format); let permutation = permutation::ProvingKey::read(reader, format); + // TODO: + // let static_tables = static_lookup::StaticTable::read(reader, format); let ev = Evaluator::new(vk.cs()); + Ok(Self { vk, l0, @@ -352,6 +395,10 @@ where fixed_cosets, permutation, ev, + // FIXME + static_table_mapping: BTreeMap::default(), + static_table_configs: BTreeMap::default(), + b0_g1_bound: vec![], }) } @@ -363,7 +410,7 @@ where } /// Reads a proving key from a slice of bytes using [`Self::read`]. - pub fn from_bytes>( + pub fn from_bytes>( mut bytes: &[u8], format: SerdeFormat, ) -> io::Result { @@ -371,9 +418,13 @@ where } } -impl VerifyingKey { +impl VerifyingKey +where + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, +{ /// Get the underlying [`EvaluationDomain`]. - pub fn get_domain(&self) -> &EvaluationDomain { + pub fn get_domain(&self) -> &EvaluationDomain { &self.domain } } diff --git a/halo2_proofs/src/plonk/circuit.rs b/halo2_proofs/src/plonk/circuit.rs index 4090b1c523..dd84aceb46 100644 --- a/halo2_proofs/src/plonk/circuit.rs +++ b/halo2_proofs/src/plonk/circuit.rs @@ -1,11 +1,13 @@ use core::cmp::max; use core::ops::{Add, Mul}; use ff::Field; +use halo2curves::pairing::{Engine, MultiMillerLoop}; use std::{ convert::TryFrom, ops::{Neg, Sub}, }; +use super::static_lookup::{self, StaticTable, StaticTableId}; use super::{lookup, permutation, Assigned, Error}; use crate::{ circuit::{Layouter, Region, Value}, @@ -521,6 +523,7 @@ impl Challenge { /// This trait allows a [`Circuit`] to direct some backend to assign a witness /// for a constraint system. pub trait Assignment { + type E: MultiMillerLoop; /// Creates a new region and enters into it. /// /// Panics if we are currently in a region (if `exit_region` was not called). @@ -542,6 +545,13 @@ pub trait Assignment { /// [`Layouter::assign_region`]: crate::circuit::Layouter#method.assign_region fn exit_region(&mut self); + /// Register a static table + fn register_static_table( + &mut self, + id: StaticTableId, + static_table: StaticTable, + ); + /// Enables a selector at the given row. fn enable_selector( &mut self, @@ -625,6 +635,7 @@ pub trait Assignment { /// The floor planner is chip-agnostic and applies its strategy to the circuit it is used /// within. pub trait FloorPlanner { + type E: MultiMillerLoop; /// Given the provided `cs`, synthesize the given circuit. /// /// `constants` is the list of fixed columns that the layouter may use to assign @@ -635,7 +646,7 @@ pub trait FloorPlanner { /// - Perform any necessary setup or measurement tasks, which may involve one or more /// calls to `Circuit::default().synthesize(config, &mut layouter)`. /// - Call `circuit.synthesize(config, &mut layouter)` exactly once. - fn synthesize, C: Circuit>( + fn synthesize::Scalar, E = Self::E>, C: Circuit>( cs: &mut CS, circuit: &C, config: C::Config, @@ -646,12 +657,12 @@ pub trait FloorPlanner { /// This is a trait that circuits provide implementations for so that the /// backend prover can ask the circuit to synthesize using some given /// [`ConstraintSystem`] implementation. -pub trait Circuit { +pub trait Circuit { /// This is a configuration object that stores things like columns. type Config: Clone; /// The floor planner used for this circuit. This is an associated type of the /// `Circuit` trait because its behaviour is circuit-critical. - type FloorPlanner: FloorPlanner; + type FloorPlanner: FloorPlanner; /// Returns a copy of this circuit with no witness values (i.e. all witnesses set to /// `None`). For most circuits, this will be equal to `Self::default()`. @@ -659,12 +670,16 @@ pub trait Circuit { /// The circuit is given an opportunity to describe the exact gate /// arrangement, column arrangement, etc. - fn configure(meta: &mut ConstraintSystem) -> Self::Config; + fn configure(meta: &mut ConstraintSystem) -> Self::Config; /// Given the provided `cs`, synthesize the circuit. The concrete type of /// the caller will be different depending on the context, and they may or /// may not expect to have a witness present. - fn synthesize(&self, config: Self::Config, layouter: impl Layouter) -> Result<(), Error>; + fn synthesize( + &self, + config: Self::Config, + layouter: impl Layouter, + ) -> Result<(), Error>; } /// Low-degree expression representing an identity that must hold over the committed columns. @@ -1376,6 +1391,8 @@ pub struct ConstraintSystem { // input expressions and a sequence of table expressions involved in the lookup. pub(crate) lookups: Vec>, + pub(crate) static_lookups: Vec>, + // Vector of fixed columns, which can be used to store constant values // that are copied into advice columns. pub(crate) constants: Vec>, @@ -1459,6 +1476,7 @@ impl Default for ConstraintSystem { instance_queries: Vec::new(), permutation: permutation::Argument::new(), lookups: Vec::new(), + static_lookups: Vec::new(), constants: vec![], minimum_degree: None, } @@ -1557,6 +1575,32 @@ impl ConstraintSystem { index } + /// cq lookup + pub fn lookup_static( + &mut self, + name: &'static str, + table_map: impl FnOnce(&mut VirtualCells<'_, F>) -> Vec<(Expression, StaticTableId)>, + ) -> usize { + let mut cells = VirtualCells::new(self); + let table_map = table_map(&mut cells) + .into_iter() + .map(|(input, table)| { + if input.contains_simple_selector() { + panic!("expression containing simple selector supplied to lookup argument"); + } + + (input, table) + }) + .collect(); + + let index = self.static_lookups.len(); + + self.static_lookups + .push(static_lookup::Argument::new(name, table_map)); + + index + } + fn query_fixed_index(&mut self, column: Column, at: Rotation) -> usize { // Return existing query, if it exists for (index, fixed_query) in self.fixed_queries.iter().enumerate() { @@ -1948,6 +1992,17 @@ impl ConstraintSystem { .unwrap_or(1), ); + // The static lookup argument also serves alongside the gates and must be accounted + // for. + degree = std::cmp::max( + degree, + self.static_lookups + .iter() + .map(|l| l.required_degree()) + .max() + .unwrap_or(1), + ); + // Account for each gate to ensure our quotient polynomial is the // correct degree and that our extended domain is the right size. degree = std::cmp::max( diff --git a/halo2_proofs/src/plonk/evaluation.rs b/halo2_proofs/src/plonk/evaluation.rs index f324a4d438..beeb26e166 100644 --- a/halo2_proofs/src/plonk/evaluation.rs +++ b/halo2_proofs/src/plonk/evaluation.rs @@ -1,7 +1,9 @@ use crate::multicore; use crate::plonk::lookup::prover::Committed; use crate::plonk::permutation::Argument; -use crate::plonk::{lookup, permutation, AdviceQuery, Any, FixedQuery, InstanceQuery, ProvingKey}; +use crate::plonk::{ + lookup, permutation, static_lookup, AdviceQuery, Any, FixedQuery, InstanceQuery, ProvingKey, +}; use crate::poly::Basis; use crate::{ arithmetic::{eval_polynomial, parallelize, CurveAffine, FieldExt}, @@ -16,8 +18,11 @@ use group::{ ff::{BatchInvert, Field}, Curve, }; +use halo2curves::pairing::MultiMillerLoop; +use halo2curves::serde::SerdeObject; use std::any::TypeId; use std::convert::TryInto; +use std::fmt::Debug; use std::num::ParseIntError; use std::slice; use std::{ @@ -277,9 +282,9 @@ impl Evaluator { } /// Evaluate h poly - pub(in crate::plonk) fn evaluate_h( + pub(in crate::plonk) fn evaluate_h( &self, - pk: &ProvingKey, + pk: &ProvingKey, advice_polys: &[&[Polynomial]], instance_polys: &[&[Polynomial]], challenges: &[C::ScalarExt], @@ -288,8 +293,14 @@ impl Evaluator { gamma: C::ScalarExt, theta: C::ScalarExt, lookups: &[Vec>], + static_lookups: &[Vec>], permutations: &[permutation::prover::Committed], - ) -> Polynomial { + ) -> Polynomial + where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + { let domain = &pk.vk.domain; let size = domain.extended_len(); let rot_scale = 1 << (domain.extended_k() - domain.k()); @@ -326,10 +337,11 @@ impl Evaluator { // Core expression evaluations let num_threads = multicore::current_num_threads(); - for (((advice, instance), lookups), permutation) in advice + for ((((advice, instance), lookups), static_lookups), permutation) in advice .iter() .zip(instance.iter()) .zip(lookups.iter()) + .zip(static_lookups.iter()) .zip(permutations.iter()) { // Custom gates @@ -517,6 +529,23 @@ impl Evaluator { } }); } + + // Static lookups + for lookup in static_lookups.iter() { + let b_coset = pk.vk.domain.coeff_to_extended(lookup.b.clone()); + let f_coset = pk.vk.domain.coeff_to_extended(lookup.f.clone()); + + // Lookup constraints + parallelize(&mut values, |values, start| { + for (i, value) in values.iter_mut().enumerate() { + let idx = start + i; + + *value = *value * y + + (b_coset[idx] * (f_coset[idx] * l_active_row[idx] + beta) + - E::Scalar::one()); + } + }); + } } values } diff --git a/halo2_proofs/src/plonk/keygen.rs b/halo2_proofs/src/plonk/keygen.rs index 2001f5cc32..f296d2529f 100644 --- a/halo2_proofs/src/plonk/keygen.rs +++ b/halo2_proofs/src/plonk/keygen.rs @@ -1,18 +1,22 @@ #![allow(clippy::int_plus_one)] use std::ops::Range; +use std::{collections::BTreeMap, fmt::Debug}; use ff::Field; use group::Curve; +use halo2curves::{pairing::MultiMillerLoop, serde::SerdeObject}; +use super::static_lookup::{StaticCommittedTable, StaticTableConfig, StaticTableValues}; use super::{ circuit::{ Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Fixed, FloorPlanner, Instance, Selector, }, evaluation::Evaluator, - permutation, Assigned, Challenge, Error, Expression, LagrangeCoeff, Polynomial, ProvingKey, - VerifyingKey, + permutation, + static_lookup::{StaticTable, StaticTableId}, + Assigned, Challenge, Error, Expression, LagrangeCoeff, Polynomial, ProvingKey, VerifyingKey, }; use crate::{ arithmetic::{parallelize, CurveAffine}, @@ -24,16 +28,16 @@ use crate::{ }, }; -pub(crate) fn create_domain( +pub(crate) fn create_domain( k: u32, ) -> ( - EvaluationDomain, - ConstraintSystem, + EvaluationDomain, + ConstraintSystem, ConcreteCircuit::Config, ) where - C: CurveAffine, - ConcreteCircuit: Circuit, + E: MultiMillerLoop, + ConcreteCircuit: Circuit, { let mut cs = ConstraintSystem::default(); let config = ConcreteCircuit::configure(&mut cs); @@ -45,19 +49,29 @@ where (domain, cs, config) } +#[derive(Debug)] +enum SynthCtx { + Prover, + Verifier, +} + /// Assembly to be used in circuit synthesis. #[derive(Debug)] -struct Assembly { +struct Assembly> { k: u32, fixed: Vec, LagrangeCoeff>>, permutation: permutation::keygen::Assembly, selectors: Vec>, // A range of available rows for assignment and copies. usable_rows: Range, + static_table_mapping: BTreeMap, StaticTable>, + ctx: SynthCtx, _marker: std::marker::PhantomData, } -impl Assignment for Assembly { +impl> Assignment for Assembly { + type E = E; + fn enter_region(&mut self, _: N) where NR: Into, @@ -70,6 +84,21 @@ impl Assignment for Assembly { // Do nothing; we don't care about regions in this context. } + fn register_static_table(&mut self, id: StaticTableId, static_table: StaticTable) { + // if ctx = prover then check that prover part is some and take it else panic + // if ctx = verifier then check that verifier part is some and take it else panic + match self.ctx { + SynthCtx::Prover => { + assert!(static_table.opened.is_some()); + } + SynthCtx::Verifier => { + assert!(static_table.committed.is_some()); + } + } + + self.static_table_mapping.insert(id, static_table); + } + fn enable_selector(&mut self, _: A, selector: &Selector, row: usize) -> Result<(), Error> where A: FnOnce() -> AR, @@ -176,27 +205,31 @@ impl Assignment for Assembly { } /// Generate a `VerifyingKey` from an instance of `Circuit`. -pub fn keygen_vk<'params, C, P, ConcreteCircuit>( +pub fn keygen_vk<'params, E, P, ConcreteCircuit>( params: &P, circuit: &ConcreteCircuit, -) -> Result, Error> +) -> Result, Error> where - C: CurveAffine, - P: Params<'params, C>, - ConcreteCircuit: Circuit, + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + P: Params<'params, E::G1Affine>, + ConcreteCircuit: Circuit, { - let (domain, cs, config) = create_domain::(params.k()); + let (domain, cs, config) = create_domain::(params.k()); if (params.n() as usize) < cs.minimum_rows() { return Err(Error::not_enough_rows_available(params.k())); } - let mut assembly: Assembly = Assembly { + let mut assembly: Assembly = Assembly { k: params.k(), fixed: vec![domain.empty_lagrange_assigned(); cs.num_fixed_columns], permutation: permutation::keygen::Assembly::new(params.n() as usize, &cs.permutation), selectors: vec![vec![false; params.n() as usize]; cs.num_selectors], usable_rows: 0..params.n() as usize - (cs.blinding_factors() + 1), + static_table_mapping: BTreeMap::default(), + ctx: SynthCtx::Verifier, _marker: std::marker::PhantomData, }; @@ -225,25 +258,36 @@ where .map(|poly| params.commit_lagrange(poly, Blind::default()).to_affine()) .collect(); + let static_table_mapping: BTreeMap, StaticCommittedTable> = assembly + .static_table_mapping + .iter() + .map(|(k, v)| (k.clone(), v.committed.clone().unwrap())) //safe to unwrap since this is checked in register_static_table method + .collect(); + Ok(VerifyingKey::from_parts( domain, fixed_commitments, permutation_vk, cs, assembly.selectors, + static_table_mapping, )) } /// Generate a `ProvingKey` from a `VerifyingKey` and an instance of `Circuit`. -pub fn keygen_pk<'params, C, P, ConcreteCircuit>( +pub fn keygen_pk<'params, E, P, ConcreteCircuit>( params: &P, - vk: VerifyingKey, + static_table_configs: BTreeMap>, + b0_g1_bound: Vec, + vk: VerifyingKey, circuit: &ConcreteCircuit, -) -> Result, Error> +) -> Result, Error> where - C: CurveAffine, - P: Params<'params, C>, - ConcreteCircuit: Circuit, + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + P: Params<'params, E::G1Affine>, + ConcreteCircuit: Circuit, { let mut cs = ConstraintSystem::default(); let config = ConcreteCircuit::configure(&mut cs); @@ -254,12 +298,14 @@ where return Err(Error::not_enough_rows_available(params.k())); } - let mut assembly: Assembly = Assembly { + let mut assembly: Assembly = Assembly { k: params.k(), fixed: vec![vk.domain.empty_lagrange_assigned(); cs.num_fixed_columns], permutation: permutation::keygen::Assembly::new(params.n() as usize, &cs.permutation), selectors: vec![vec![false; params.n() as usize]; cs.num_selectors], usable_rows: 0..params.n() as usize - (cs.blinding_factors() + 1), + static_table_mapping: BTreeMap::default(), + ctx: SynthCtx::Prover, _marker: std::marker::PhantomData, }; @@ -296,7 +342,7 @@ where // Compute l_0(X) // TODO: this can be done more efficiently let mut l0 = vk.domain.empty_lagrange(); - l0[0] = C::Scalar::one(); + l0[0] = E::Scalar::one(); let l0 = vk.domain.lagrange_to_coeff(l0); let l0 = vk.domain.coeff_to_extended(l0); @@ -304,7 +350,7 @@ where // and 0 otherwise over the domain. let mut l_blind = vk.domain.empty_lagrange(); for evaluation in l_blind[..].iter_mut().rev().take(cs.blinding_factors()) { - *evaluation = C::Scalar::one(); + *evaluation = E::Scalar::one(); } let l_blind = vk.domain.lagrange_to_coeff(l_blind); let l_blind = vk.domain.coeff_to_extended(l_blind); @@ -312,12 +358,12 @@ where // Compute l_last(X) which evaluates to 1 on the first inactive row (just // before the blinding factors) and 0 otherwise over the domain let mut l_last = vk.domain.empty_lagrange(); - l_last[params.n() as usize - cs.blinding_factors() - 1] = C::Scalar::one(); + l_last[params.n() as usize - cs.blinding_factors() - 1] = E::Scalar::one(); let l_last = vk.domain.lagrange_to_coeff(l_last); let l_last = vk.domain.coeff_to_extended(l_last); // Compute l_active_row(X) - let one = C::Scalar::one(); + let one = E::Scalar::one(); let mut l_active_row = vk.domain.empty_extended(); parallelize(&mut l_active_row, |values, start| { for (i, value) in values.iter_mut().enumerate() { @@ -328,6 +374,11 @@ where // Compute the optimized evaluation data structure let ev = Evaluator::new(&vk.cs); + let static_table_mapping: BTreeMap, StaticTableValues> = assembly + .static_table_mapping + .iter() + .map(|(k, v)| (k.clone(), v.opened.clone().unwrap())) //safe to unwrap since this is checked in register_static_table method + .collect(); Ok(ProvingKey { vk, @@ -339,5 +390,8 @@ where fixed_cosets, permutation: permutation_pk, ev, + static_table_mapping, + static_table_configs, + b0_g1_bound, }) } diff --git a/halo2_proofs/src/plonk/lookup/prover.rs b/halo2_proofs/src/plonk/lookup/prover.rs index f5d87d061e..e3dabcf1dc 100644 --- a/halo2_proofs/src/plonk/lookup/prover.rs +++ b/halo2_proofs/src/plonk/lookup/prover.rs @@ -17,10 +17,13 @@ use group::{ ff::{BatchInvert, Field}, Curve, }; +use halo2curves::pairing::MultiMillerLoop; +use halo2curves::serde::SerdeObject; use rand_core::RngCore; use std::{any::TypeId, convert::TryInto, num::ParseIntError, ops::Index}; use std::{ collections::BTreeMap, + fmt::Debug, iter, ops::{Mul, MulAssign}, }; @@ -64,30 +67,33 @@ impl Argument { pub(in crate::plonk) fn commit_permuted< 'a, 'params: 'a, - C, - P: Params<'params, C>, - E: EncodedChallenge, + E, + P: Params<'params, E::G1Affine>, + EC: EncodedChallenge, R: RngCore, - T: TranscriptWrite, + T: TranscriptWrite, >( &self, - pk: &ProvingKey, + pk: &ProvingKey, params: &P, - domain: &EvaluationDomain, - theta: ChallengeTheta, - advice_values: &'a [Polynomial], - fixed_values: &'a [Polynomial], - instance_values: &'a [Polynomial], - challenges: &'a [C::Scalar], + domain: &EvaluationDomain, + theta: ChallengeTheta, + advice_values: &'a [Polynomial], + fixed_values: &'a [Polynomial], + instance_values: &'a [Polynomial], + challenges: &'a [E::Scalar], mut rng: R, transcript: &mut T, - ) -> Result, Error> + ) -> Result, Error> where - C: CurveAffine, - C::Curve: Mul + MulAssign, + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + E::G1Affine: CurveAffine, + E::G1: Mul + MulAssign, { // Closure to get values of expressions and compress them - let compress_expressions = |expressions: &[Expression]| { + let compress_expressions = |expressions: &[Expression]| { let compressed_expression = expressions .iter() .map(|expression| { @@ -124,9 +130,9 @@ impl Argument { )?; // Closure to construct commitment to vector of values - let mut commit_values = |values: &Polynomial| { + let mut commit_values = |values: &Polynomial| { let poly = pk.vk.domain.lagrange_to_coeff(values.clone()); - let blind = Blind(C::Scalar::random(&mut rng)); + let blind = Blind(E::Scalar::random(&mut rng)); let commitment = params.commit_lagrange(values, blind).to_affine(); (poly, blind, commitment) }; @@ -166,19 +172,25 @@ impl Permuted { /// added to the Lookup and finally returned by the method. pub(in crate::plonk) fn commit_product< 'params, + E, P: Params<'params, C>, - E: EncodedChallenge, + EC: EncodedChallenge, R: RngCore, - T: TranscriptWrite, + T: TranscriptWrite, >( self, - pk: &ProvingKey, + pk: &ProvingKey, params: &P, beta: ChallengeBeta, gamma: ChallengeGamma, mut rng: R, transcript: &mut T, - ) -> Result, Error> { + ) -> Result, Error> + where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + { let blinding_factors = pk.vk.cs.blinding_factors(); // Goal is to compute the products of fractions // @@ -306,12 +318,17 @@ impl Permuted { } impl Committed { - pub(in crate::plonk) fn evaluate, T: TranscriptWrite>( + pub(in crate::plonk) fn evaluate, T: TranscriptWrite>( self, - pk: &ProvingKey, + pk: &ProvingKey, x: ChallengeX, transcript: &mut T, - ) -> Result, Error> { + ) -> Result, Error> + where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + { let domain = &pk.vk.domain; let x_inv = domain.rotate_omega(*x, Rotation::prev()); let x_next = domain.rotate_omega(*x, Rotation::next()); @@ -338,11 +355,16 @@ impl Committed { } impl Evaluated { - pub(in crate::plonk) fn open<'a>( + pub(in crate::plonk) fn open<'a, E>( &'a self, - pk: &'a ProvingKey, + pk: &'a ProvingKey, x: ChallengeX, - ) -> impl Iterator> + Clone { + ) -> impl Iterator> + Clone + where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + { let x_inv = pk.vk.domain.rotate_omega(*x, Rotation::prev()); let x_next = pk.vk.domain.rotate_omega(*x, Rotation::next()); @@ -388,32 +410,37 @@ type ExpressionPair = (Polynomial, Polynomial, R: RngCore>( - pk: &ProvingKey, +fn permute_expression_pair<'params, E, P: Params<'params, E::G1Affine>, R: RngCore>( + pk: &ProvingKey, params: &P, - domain: &EvaluationDomain, + domain: &EvaluationDomain, mut rng: R, - input_expression: &Polynomial, - table_expression: &Polynomial, -) -> Result, Error> { + input_expression: &Polynomial, + table_expression: &Polynomial, +) -> Result, Error> +where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, +{ let blinding_factors = pk.vk.cs.blinding_factors(); let usable_rows = params.n() as usize - (blinding_factors + 1); - let mut permuted_input_expression: Vec = input_expression.to_vec(); + let mut permuted_input_expression: Vec = input_expression.to_vec(); permuted_input_expression.truncate(usable_rows); // Sort input lookup expression values permuted_input_expression.sort(); // A BTreeMap of each unique element in the table expression and its count - let mut leftover_table_map: BTreeMap = table_expression + let mut leftover_table_map: BTreeMap = table_expression .iter() .take(usable_rows) .fold(BTreeMap::new(), |mut acc, coeff| { *acc.entry(*coeff).or_insert(0) += 1; acc }); - let mut permuted_table_coeffs = vec![C::Scalar::zero(); usable_rows]; + let mut permuted_table_coeffs = vec![E::Scalar::zero(); usable_rows]; let mut repeated_input_rows = permuted_input_expression .iter() @@ -448,8 +475,8 @@ fn permute_expression_pair<'params, C: CurveAffine, P: Params<'params, C>, R: Rn assert!(repeated_input_rows.is_empty()); permuted_input_expression - .extend((0..(blinding_factors + 1)).map(|_| C::Scalar::random(&mut rng))); - permuted_table_coeffs.extend((0..(blinding_factors + 1)).map(|_| C::Scalar::random(&mut rng))); + .extend((0..(blinding_factors + 1)).map(|_| E::Scalar::random(&mut rng))); + permuted_table_coeffs.extend((0..(blinding_factors + 1)).map(|_| E::Scalar::random(&mut rng))); assert_eq!(permuted_input_expression.len(), params.n() as usize); assert_eq!(permuted_table_coeffs.len(), params.n() as usize); diff --git a/halo2_proofs/src/plonk/lookup/verifier.rs b/halo2_proofs/src/plonk/lookup/verifier.rs index 88041c29b3..3e808135a7 100644 --- a/halo2_proofs/src/plonk/lookup/verifier.rs +++ b/halo2_proofs/src/plonk/lookup/verifier.rs @@ -11,6 +11,9 @@ use crate::{ transcript::{EncodedChallenge, TranscriptRead}, }; use ff::Field; +use halo2curves::pairing::MultiMillerLoop; +use halo2curves::serde::SerdeObject; +use std::fmt::Debug; pub struct PermutationCommitments { permuted_input_commitment: C, @@ -167,11 +170,16 @@ impl Evaluated { )) } - pub(in crate::plonk) fn queries<'r, M: MSM + 'r>( + pub(in crate::plonk) fn queries<'r, E: MultiMillerLoop, M: MSM + 'r>( &'r self, - vk: &'r VerifyingKey, - x: ChallengeX, - ) -> impl Iterator> + Clone { + vk: &'r VerifyingKey, + x: ChallengeX, + ) -> impl Iterator> + Clone + where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + { let x_inv = vk.domain.rotate_omega(*x, Rotation::prev()); let x_next = vk.domain.rotate_omega(*x, Rotation::next()); diff --git a/halo2_proofs/src/plonk/permutation/prover.rs b/halo2_proofs/src/plonk/permutation/prover.rs index cddc02348a..14267d4fd1 100644 --- a/halo2_proofs/src/plonk/permutation/prover.rs +++ b/halo2_proofs/src/plonk/permutation/prover.rs @@ -2,6 +2,7 @@ use group::{ ff::{BatchInvert, Field}, Curve, }; +use halo2curves::{pairing::MultiMillerLoop, serde::SerdeObject}; use rand_core::RngCore; use std::iter::{self, ExactSizeIterator}; @@ -17,6 +18,7 @@ use crate::{ }, transcript::{EncodedChallenge, TranscriptWrite}, }; +use std::fmt::Debug; pub(crate) struct CommittedSet { pub(crate) permutation_product_poly: Polynomial, @@ -44,24 +46,29 @@ pub(crate) struct Evaluated { impl Argument { pub(in crate::plonk) fn commit< 'params, - C: CurveAffine, - P: Params<'params, C>, - E: EncodedChallenge, + E, + P: Params<'params, E::G1Affine>, + EC: EncodedChallenge, R: RngCore, - T: TranscriptWrite, + T: TranscriptWrite, >( &self, params: &P, - pk: &plonk::ProvingKey, - pkey: &ProvingKey, - advice: &[Polynomial], - fixed: &[Polynomial], - instance: &[Polynomial], - beta: ChallengeBeta, - gamma: ChallengeGamma, + pk: &plonk::ProvingKey, + pkey: &ProvingKey, + advice: &[Polynomial], + fixed: &[Polynomial], + instance: &[Polynomial], + beta: ChallengeBeta, + gamma: ChallengeGamma, mut rng: R, transcript: &mut T, - ) -> Result, Error> { + ) -> Result, Error> + where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + { let domain = &pk.vk.domain; // How many columns can be included in a single permutation polynomial? @@ -73,10 +80,10 @@ impl Argument { let blinding_factors = pk.vk.cs.blinding_factors(); // Each column gets its own delta power. - let mut deltaomega = C::Scalar::one(); + let mut deltaomega = E::Scalar::one(); // Track the "last" value from the previous column set - let mut last_z = C::Scalar::one(); + let mut last_z = E::Scalar::one(); let mut sets = vec![]; @@ -93,7 +100,7 @@ impl Argument { // where p_j(X) is the jth column in this permutation, // and i is the ith row of the column. - let mut modified_values = vec![C::Scalar::one(); params.n() as usize]; + let mut modified_values = vec![E::Scalar::one(); params.n() as usize]; // Iterate over each column of the permutation for (&column, permuted_column_values) in columns.iter().zip(permutations.iter()) { @@ -136,7 +143,7 @@ impl Argument { deltaomega *= ω } }); - deltaomega *= &C::Scalar::DELTA; + deltaomega *= &E::Scalar::DELTA; } // The modified_values vector is a vector of products of fractions @@ -160,12 +167,12 @@ impl Argument { let mut z = domain.lagrange_from_vec(z); // Set blinding factors for z in &mut z[params.n() as usize - blinding_factors..] { - *z = C::Scalar::random(&mut rng); + *z = E::Scalar::random(&mut rng); } // Set new last_z last_z = z[params.n() as usize - (blinding_factors + 1)]; - let blind = Blind(C::Scalar::random(&mut rng)); + let blind = Blind(E::Scalar::random(&mut rng)); let permutation_product_commitment_projective = params.commit_lagrange(&z, blind); let permutation_product_blind = blind; @@ -233,12 +240,17 @@ impl super::ProvingKey { } impl Constructed { - pub(in crate::plonk) fn evaluate, T: TranscriptWrite>( + pub(in crate::plonk) fn evaluate, T: TranscriptWrite>( self, - pk: &plonk::ProvingKey, + pk: &plonk::ProvingKey, x: ChallengeX, transcript: &mut T, - ) -> Result, Error> { + ) -> Result, Error> + where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + { let domain = &pk.vk.domain; let blinding_factors = pk.vk.cs.blinding_factors(); @@ -280,11 +292,16 @@ impl Constructed { } impl Evaluated { - pub(in crate::plonk) fn open<'a>( + pub(in crate::plonk) fn open<'a, E>( &'a self, - pk: &'a plonk::ProvingKey, + pk: &'a plonk::ProvingKey, x: ChallengeX, - ) -> impl Iterator> + Clone { + ) -> impl Iterator> + Clone + where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + { let blinding_factors = pk.vk.cs.blinding_factors(); let x_next = pk.vk.domain.rotate_omega(*x, Rotation::next()); let x_last = pk diff --git a/halo2_proofs/src/plonk/permutation/verifier.rs b/halo2_proofs/src/plonk/permutation/verifier.rs index 2e7a707a07..8a9bd30713 100644 --- a/halo2_proofs/src/plonk/permutation/verifier.rs +++ b/halo2_proofs/src/plonk/permutation/verifier.rs @@ -1,4 +1,7 @@ use ff::Field; +use halo2curves::pairing::MultiMillerLoop; +use halo2curves::serde::SerdeObject; +use std::fmt::Debug; use std::iter; use super::super::{circuit::Any, ChallengeBeta, ChallengeGamma, ChallengeX}; @@ -31,14 +34,18 @@ pub struct Evaluated { impl Argument { pub(crate) fn read_product_commitments< - C: CurveAffine, - E: EncodedChallenge, - T: TranscriptRead, + E: MultiMillerLoop + Debug, + EC: EncodedChallenge, + T: TranscriptRead, >( &self, - vk: &plonk::VerifyingKey, + vk: &plonk::VerifyingKey, transcript: &mut T, - ) -> Result, Error> { + ) -> Result, Error> + where + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + { let chunk_len = vk.cs_degree - 2; let permutation_product_commitments = self @@ -99,9 +106,9 @@ impl Committed { } impl Evaluated { - pub(in crate::plonk) fn expressions<'a>( + pub(in crate::plonk) fn expressions<'a, E>( &'a self, - vk: &'a plonk::VerifyingKey, + vk: &'a plonk::VerifyingKey, p: &'a Argument, common: &'a CommonEvaluated, advice_evals: &'a [C::Scalar], @@ -113,7 +120,12 @@ impl Evaluated { beta: ChallengeBeta, gamma: ChallengeGamma, x: ChallengeX, - ) -> impl Iterator + 'a { + ) -> impl Iterator + 'a + where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + { let chunk_len = vk.cs_degree - 2; iter::empty() // Enforce only for the first set. @@ -199,11 +211,16 @@ impl Evaluated { ) } - pub(in crate::plonk) fn queries<'r, M: MSM + 'r>( + pub(in crate::plonk) fn queries<'r, E, M: MSM + 'r>( &'r self, - vk: &'r plonk::VerifyingKey, + vk: &'r plonk::VerifyingKey, x: ChallengeX, - ) -> impl Iterator> + Clone { + ) -> impl Iterator> + Clone + where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + { let blinding_factors = vk.cs.blinding_factors(); let x_next = vk.domain.rotate_omega(*x, Rotation::next()); let x_last = vk diff --git a/halo2_proofs/src/plonk/prover.rs b/halo2_proofs/src/plonk/prover.rs index efd4305a01..975c3bf717 100644 --- a/halo2_proofs/src/plonk/prover.rs +++ b/halo2_proofs/src/plonk/prover.rs @@ -1,9 +1,12 @@ use ff::Field; use group::Curve; +use halo2curves::pairing::MultiMillerLoop; +use halo2curves::serde::SerdeObject; use halo2curves::CurveExt; use rand_core::RngCore; use std::collections::BTreeSet; use std::env::var; +use std::fmt::Debug; use std::marker::PhantomData; use std::ops::RangeTo; use std::rc::Rc; @@ -20,8 +23,10 @@ use super::{ lookup, permutation, vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, Error, Expression, ProvingKey, }; +use crate::plonk::static_lookup::{self, StaticTable, StaticTableId}; use crate::poly::batch_invert_assigned_ref; use crate::poly::commitment::ParamsProver; +use crate::poly::kzg::commitment::KZGCommitmentScheme; use crate::transcript::Transcript; use crate::{ arithmetic::{eval_polynomial, CurveAffine, FieldExt}, @@ -46,22 +51,32 @@ use group::prime::PrimeCurveAffine; pub fn create_proof< 'params, 'a, - Scheme: CommitmentScheme, - P: Prover<'params, Scheme>, - E: EncodedChallenge, + E: MultiMillerLoop + Debug, + P: Prover<'params, E>, + EC: EncodedChallenge, R: RngCore + 'a, - T: TranscriptWrite, - ConcreteCircuit: Circuit, + T: TranscriptWrite, + ConcreteCircuit: Circuit, >( - params: &'params Scheme::ParamsProver, - pk: &ProvingKey, + params: &'params as CommitmentScheme>::ParamsProver, + pk: &ProvingKey, circuits: &[ConcreteCircuit], - instances: &[&[&'a [Scheme::Scalar]]], + instances: &[&[&'a [E::Scalar]]], mut rng: R, mut transcript: &'a mut T, -) -> Result<(), Error> { +) -> Result<(), Error> +where + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, +{ + assert_eq!(circuits.len(), instances.len()); for instance in instances.iter() { if instance.len() != pk.vk.cs.num_instance_columns { + println!("instance.len(): {}", instance.len()); + println!( + "pk.vk.cs.num_instance_columns: {}", + pk.vk.cs.num_instance_columns + ); return Err(Error::InvalidInstances); } } @@ -82,9 +97,9 @@ pub fn create_proof< pub instance_polys: Vec>, } - let instance: Vec> = instances + let instance: Vec> = instances .iter() - .map(|instance| -> Result, Error> { + .map(|instance| -> Result, Error> { let instance_values = instance .iter() .map(|values| { @@ -121,42 +136,44 @@ pub fn create_proof< pub advice_blinds: Vec>, } - struct WitnessCollection<'params, 'a, 'b, Scheme, P, C, E, R, T> + struct WitnessCollection<'params, 'a, 'b, E, P, EC, R, T> where - Scheme: CommitmentScheme, - P: Prover<'params, Scheme>, - C: CurveAffine, - E: EncodedChallenge, + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + P: Prover<'params, E>, + EC: EncodedChallenge, R: RngCore + 'a, - T: TranscriptWrite, + T: TranscriptWrite, { - params: &'params Scheme::ParamsProver, + params: &'params as CommitmentScheme>::ParamsProver, current_phase: sealed::Phase, - advice: Vec, LagrangeCoeff>>, - challenges: &'b mut HashMap, - instances: &'b [&'a [C::Scalar]], + advice: Vec, LagrangeCoeff>>, + challenges: &'b mut HashMap, + instances: &'b [&'a [E::Scalar]], usable_rows: RangeTo, - advice_single: AdviceSingle, - instance_single: &'b InstanceSingle, + advice_single: AdviceSingle, + instance_single: &'b InstanceSingle, rng: &'b mut R, transcript: &'b mut &'a mut T, column_indices: [Vec; 3], challenge_indices: [Vec; 3], unusable_rows_start: usize, - _marker: PhantomData<(P, E)>, + _marker: PhantomData<(P, EC)>, } - impl<'params, 'a, 'b, F, Scheme, P, C, E, R, T> Assignment - for WitnessCollection<'params, 'a, 'b, Scheme, P, C, E, R, T> + impl<'params, 'a, 'b, E, P, EC, R, T> Assignment + for WitnessCollection<'params, 'a, 'b, E, P, EC, R, T> where - F: FieldExt, - Scheme: CommitmentScheme, - P: Prover<'params, Scheme>, - C: CurveAffine, - E: EncodedChallenge, + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + P: Prover<'params, E>, + EC: EncodedChallenge, R: RngCore, - T: TranscriptWrite, + T: TranscriptWrite, { + type E = E; fn enter_region(&mut self, _: N) where NR: Into, @@ -169,6 +186,14 @@ pub fn create_proof< // Do nothing; we don't care about regions in this context. } + fn register_static_table( + &mut self, + id: StaticTableId, + static_table: StaticTable, + ) { + // This happens only in keygen + } + fn enable_selector(&mut self, _: A, _: &Selector, _: usize) -> Result<(), Error> where A: FnOnce() -> AR, @@ -179,7 +204,11 @@ pub fn create_proof< Ok(()) } - fn query_instance(&self, column: Column, row: usize) -> Result, Error> { + fn query_instance( + &self, + column: Column, + row: usize, + ) -> Result, Error> { if !self.usable_rows.contains(&row) { return Err(Error::not_enough_rows_available(self.params.k())); } @@ -197,8 +226,8 @@ pub fn create_proof< //_: A, column: Column, row: usize, - to: Value>, - ) -> Result>, Error> { + to: Value>, + ) -> Result>, Error> { // TODO: better to assign all at once, deal with phases later // Ignore assignment of advice column in different phase than current one. if self.current_phase != column.column_type().phase { @@ -226,11 +255,11 @@ pub fn create_proof< *advice_get_mut = to .assign() .expect("No Value::unknown() in advice column allowed during create_proof"); - let immutable_raw_ptr = advice_get_mut as *const Assigned; + let immutable_raw_ptr = advice_get_mut as *const Assigned; Ok(Value::known(unsafe { &*immutable_raw_ptr })) } - fn assign_fixed(&mut self, _: Column, _: usize, _: Assigned) { + fn assign_fixed(&mut self, _: Column, _: usize, _: Assigned) { // We only care about advice columns here } @@ -242,12 +271,12 @@ pub fn create_proof< &mut self, _: Column, _: usize, - _: Value>, + _: Value>, ) -> Result<(), Error> { Ok(()) } - fn get_challenge(&self, challenge: Challenge) -> Value { + fn get_challenge(&self, challenge: Challenge) -> Value { self.challenges .get(&challenge.index()) .cloned() @@ -289,8 +318,8 @@ pub fn create_proof< .map(|poly| self.params.commit_lagrange(poly, Blind::default())) .collect(); let mut instance_commitments = - vec![C::identity(); instance_commitments_projective.len()]; - C::CurveExt::batch_normalize( + vec![E::G1Affine::identity(); instance_commitments_projective.len()]; + ::CurveExt::batch_normalize( &instance_commitments_projective, &mut instance_commitments, ); @@ -305,7 +334,7 @@ pub fn create_proof< } } // Commit the advice columns in the current phase - let mut advice_values = batch_invert_assigned_ref::( + let mut advice_values = batch_invert_assigned_ref::( self.column_indices .get(phase) .expect("The API only supports 3 phases right now") @@ -316,21 +345,25 @@ pub fn create_proof< // Add blinding factors to advice columns for advice_values in &mut advice_values { for cell in &mut advice_values[self.unusable_rows_start..] { - *cell = F::random(&mut self.rng); + *cell = E::Scalar::random(&mut self.rng); } } // Compute commitments to advice column polynomials let blinds: Vec<_> = advice_values .iter() - .map(|_| Blind(F::random(&mut self.rng))) + .map(|_| Blind(E::Scalar::random(&mut self.rng))) .collect(); let advice_commitments_projective: Vec<_> = advice_values .iter() .zip(blinds.iter()) .map(|(poly, blind)| self.params.commit_lagrange(poly, *blind)) .collect(); - let mut advice_commitments = vec![C::identity(); advice_commitments_projective.len()]; - C::CurveExt::batch_normalize(&advice_commitments_projective, &mut advice_commitments); + let mut advice_commitments = + vec![E::G1Affine::identity(); advice_commitments_projective.len()]; + ::CurveExt::batch_normalize( + &advice_commitments_projective, + &mut advice_commitments, + ); let advice_commitments = advice_commitments; drop(advice_commitments_projective); @@ -369,7 +402,7 @@ pub fn create_proof< let (advice, challenges) = { let mut advice = Vec::with_capacity(instances.len()); - let mut challenges = HashMap::::with_capacity(meta.num_challenges); + let mut challenges = HashMap::::with_capacity(meta.num_challenges); let unusable_rows_start = params.n() as usize - (meta.blinding_factors() + 1); let phases = pk.vk.cs.phases().collect::>(); @@ -385,7 +418,7 @@ pub fn create_proof< for ((circuit, instances), instance_single) in circuits.iter().zip(instances).zip(instance.iter()) { - let mut witness: WitnessCollection = WitnessCollection { + let mut witness: WitnessCollection = WitnessCollection { params, current_phase: phases[0], advice: vec![domain.empty_lagrange_assigned(); meta.num_advice_columns], @@ -396,7 +429,7 @@ pub fn create_proof< // number of blinding factors and an extra row for use in the // permutation argument. usable_rows: ..unusable_rows_start, - advice_single: AdviceSingle:: { + advice_single: AdviceSingle:: { advice_polys: vec![domain.empty_lagrange(); meta.num_advice_columns], advice_blinds: vec![Blind::default(); meta.num_advice_columns], }, @@ -438,7 +471,7 @@ pub fn create_proof< // Sample theta challenge for keeping lookup columns linearly independent let theta: ChallengeTheta<_> = transcript.squeeze_challenge_scalar(); - let lookups: Vec>> = instance + let lookups: Vec>> = instance .iter() .zip(advice.iter()) .map(|(instance, advice)| -> Result, Error> { @@ -465,6 +498,33 @@ pub fn create_proof< }) .collect::, _>>()?; + // STATIC_LOOKUPS! + let static_lookups: Vec>> = instance + .iter() + .zip(advice.iter()) + .map(|(instance, advice)| -> Result, Error> { + // Construct and commit to permuted values for each lookup + pk.vk + .cs + .static_lookups + .iter() + .map(|lookup| { + lookup.commit( + pk, + params, + domain, + theta, + &challenges, + &advice.advice_polys, + &pk.fixed_values, + &instance.instance_values, + transcript, + ) + }) + .collect() + }) + .collect::, _>>()?; + // Sample beta challenge let beta: ChallengeBeta<_> = transcript.squeeze_challenge_scalar(); @@ -472,7 +532,7 @@ pub fn create_proof< let gamma: ChallengeGamma<_> = transcript.squeeze_challenge_scalar(); // Commit to permutations. - let permutations: Vec> = instance + let permutations: Vec> = instance .iter() .zip(advice.iter()) .map(|(instance, advice)| { @@ -491,7 +551,7 @@ pub fn create_proof< }) .collect::, _>>()?; - let lookups: Vec>> = lookups + let lookups: Vec>> = lookups .into_iter() .map(|lookups| -> Result, _> { // Construct and commit to products for each lookup @@ -502,6 +562,21 @@ pub fn create_proof< }) .collect::, _>>()?; + // STATIC_LOOKUPS! + let static_lookups: Vec>> = static_lookups + .into_iter() + .map(|static_lookups| -> Result, _> { + // Construct and commit to products for each lookup + static_lookups + .into_iter() + .map(|static_lookup| { + static_lookup + .commit_log_derivatives(pk, params, domain, beta, theta, transcript) + }) + .collect::, _>>() + }) + .collect::, _>>()?; + // Commit to the vanishing argument's random polynomial for blinding h(x_3) let vanishing = vanishing::Argument::commit(params, domain, &mut rng, transcript)?; @@ -509,7 +584,7 @@ pub fn create_proof< let y: ChallengeY<_> = transcript.squeeze_challenge_scalar(); // Calculate the advice polys - let advice: Vec> = advice + let advice: Vec> = advice .into_iter() .map( |AdviceSingle { @@ -544,6 +619,7 @@ pub fn create_proof< *gamma, *theta, &lookups, + &static_lookups, &permutations, ); @@ -615,13 +691,13 @@ pub fn create_proof< pk.permutation.evaluate(x, transcript)?; // Evaluate the permutations, if any, at omega^i x. - let permutations: Vec> = permutations + let permutations: Vec> = permutations .into_iter() .map(|permutation| -> Result<_, _> { permutation.construct().evaluate(pk, x, transcript) }) .collect::, _>>()?; // Evaluate the lookups, if any, at omega^i x. - let lookups: Vec>> = lookups + let lookups: Vec>> = lookups .into_iter() .map(|lookups| -> Result, _> { lookups @@ -631,39 +707,56 @@ pub fn create_proof< }) .collect::, _>>()?; + // STATIC_LOOKUPS! + let static_lookups: Vec>> = static_lookups + .into_iter() + .map(|static_lookups| -> Result, _> { + static_lookups + .into_iter() + .map(|lookup| lookup.evaluate(pk, x, transcript)) + .collect::, _>>() + }) + .collect::, _>>()?; + let instances = instance .iter() .zip(advice.iter()) .zip(permutations.iter()) .zip(lookups.iter()) - .flat_map(|(((instance, advice), permutation), lookups)| { - iter::empty() - .chain( - P::QUERY_INSTANCE - .then_some(pk.vk.cs.instance_queries.iter().map(move |&(column, at)| { - ProverQuery { - point: domain.rotate_omega(*x, at), - poly: &instance.instance_polys[column.index()], - blind: Blind::default(), - } - })) - .into_iter() - .flatten(), - ) - .chain( - pk.vk - .cs - .advice_queries - .iter() - .map(move |&(column, at)| ProverQuery { + .zip(static_lookups.iter()) + .flat_map( + |((((instance, advice), permutation), lookups), static_lookups)| { + iter::empty() + .chain( + P::QUERY_INSTANCE + .then_some(pk.vk.cs.instance_queries.iter().map( + move |&(column, at)| ProverQuery { + point: domain.rotate_omega(*x, at), + poly: &instance.instance_polys[column.index()], + blind: Blind::default(), + }, + )) + .into_iter() + .flatten(), + ) + .chain(pk.vk.cs.advice_queries.iter().map(move |&(column, at)| { + let prover_advice_query = ProverQuery { point: domain.rotate_omega(*x, at), poly: &advice.advice_polys[column.index()], blind: advice.advice_blinds[column.index()], - }), - ) - .chain(permutation.open(pk, x)) - .chain(lookups.iter().flat_map(move |p| p.open(pk, x)).into_iter()) - }) + }; + prover_advice_query + })) + .chain(permutation.open(pk, x)) + .chain(lookups.iter().flat_map(move |p| p.open(pk, x)).into_iter()) + .chain( + static_lookups + .iter() + .flat_map(move |p| p.open(x)) + .into_iter(), + ) + }, + ) .chain( pk.vk .cs diff --git a/halo2_proofs/src/plonk/static_lookup.rs b/halo2_proofs/src/plonk/static_lookup.rs new file mode 100644 index 0000000000..ce0e1e5322 --- /dev/null +++ b/halo2_proofs/src/plonk/static_lookup.rs @@ -0,0 +1,207 @@ +use ff::Field; +use group::prime::PrimeCurveAffine; +use halo2curves::{ + pairing::{Engine, MultiMillerLoop}, + FieldExt, +}; +use rand_core::OsRng; + +pub(crate) mod prover; +pub(crate) mod verifier; + +use std::{ + collections::{BTreeMap, BTreeSet}, + io, +}; + +use crate::{ + arithmetic::{best_multiexp, kate_division}, + helpers::SerdePrimeField, + poly::{kzg::commitment::ParamsKZG, EvaluationDomain}, + SerdeFormat, +}; + +use super::Expression; + +pub fn is_pow_2(x: usize) -> bool { + (x & (x - 1)) == 0 +} + +pub fn log2(x: usize) -> u32 { + (usize::BITS - 1) - x.leading_zeros() +} + +#[derive(Debug, Clone)] +pub struct StaticTable { + pub opened: Option>, + pub committed: Option>, +} + +/// Abstract type that allows to store MAP(table_id => static_table) in proving(verifying) key +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +pub struct StaticTableId(pub T); + +impl StaticTableId { + pub fn id(&self) -> &T { + &self.0 + } +} + +#[derive(Debug, Clone)] +pub struct StaticTableConfig { + size: usize, + g1_lagrange: Vec, + g_lagrange_opening_at_0: Vec, +} + +impl StaticTableConfig { + pub fn new( + size: usize, + g1_lagrange: Vec, + g_lagrange_opening_at_0: Vec, + ) -> Self { + Self { + size, + g1_lagrange, + g_lagrange_opening_at_0, + } + } +} + +#[derive(Clone, Debug)] +pub struct StaticTableValues { + size: usize, + // Mapping from value to its indices in the table + value_index_mapping: BTreeMap>, + // quotient commitments + qs: Vec, +} + +impl StaticTableValues { + pub fn new(values: &[E::Scalar], srs_g1: &[E::G1Affine]) -> Self { + let size = values.len(); + assert!(is_pow_2(size)); + + let mut value_index_mapping: BTreeMap> = BTreeMap::new(); + for (i, f) in values.iter().enumerate() { + value_index_mapping + .entry(*f) + .and_modify(|indices| assert!(indices.insert(i))) + .or_insert(BTreeSet::from([i])); + } + + // compute all qs + let domain = EvaluationDomain::::new(2, log2(size)); + let n = E::Scalar::from(size as u64); + let n_inv = n.invert().unwrap(); + + let w = domain.get_omega(); + + let roots_of_unity: Vec = + std::iter::successors(Some(E::Scalar::one()), |p| Some(*p * w)) + .take(size) + .collect(); + + let mut table_coeffs: Vec = values.to_vec(); + EvaluationDomain::::ifft( + table_coeffs.as_mut_slice(), + domain.get_omega_inv(), + log2(size), + domain.ifft_divisor(), + ); + + // TODO: THIS SHOULD BE DONE WITH FK METHOD + let qs: Vec = roots_of_unity + .iter() + .map(|&g_i| { + let quotient = kate_division(&table_coeffs, g_i); + let quotient = quotient + .iter() + .map(|&v| v * g_i * n_inv) + .collect::>(); + + best_multiexp("ient, &srs_g1[..quotient.len()]) + }) + .collect(); + + Self { + size, + value_index_mapping, + qs, + } + } + + pub fn commit( + &self, + srs_g1_len: usize, + srs_g2: &[E::G2Affine], + circuit_domain: usize, + ) -> StaticCommittedTable { + let domain = EvaluationDomain::::new(2, log2(self.size)); + // zv = x^n - 1 + assert!(is_pow_2(self.size)); + let zv = srs_g2[self.size] - srs_g2[0]; + + let table_size = self + .value_index_mapping + .values() + .fold(0, |acc, indices| acc + indices.len()); + let mut table_coeffs: Vec = vec![E::Scalar::zero(); table_size]; + for (value, indices) in self.value_index_mapping.iter() { + for index in indices.iter() { + table_coeffs[*index] = *value; + } + } + + EvaluationDomain::::ifft( + table_coeffs.as_mut_slice(), + domain.get_omega_inv(), + log2(self.size), + domain.ifft_divisor(), + ); + let t = best_multiexp(&table_coeffs, &srs_g2[..table_coeffs.len()]); + // NOTE: B0 bound is computed generically based on srs size instead of just table size SRS + // this allows using longer srs or just having multiple tables with different lengths + let b0_bound_index = srs_g1_len - 1 - (circuit_domain - 2); + + StaticCommittedTable { + zv: zv.into(), + t: t.into(), + x_b0_bound: srs_g2[b0_bound_index], + size: srs_g1_len, + } + } +} + +#[derive(Debug, Clone)] +pub struct StaticCommittedTable { + pub zv: E::G2Affine, + pub t: E::G2Affine, + pub x_b0_bound: E::G2Affine, + pub size: usize, +} + +#[derive(Debug, Clone)] +pub struct Argument { + input: Vec>, + table_ids: Vec>, +} + +impl Argument { + pub fn new(name: &'static str, table_map: Vec<(Expression, StaticTableId)>) -> Self { + let (input, table_ids) = table_map.into_iter().unzip(); + + Self { input, table_ids } + } + + pub(crate) fn required_degree(&self) -> usize { + /* + B(X)(q(X) * f(X) - \beta) - 1 + */ + let mut input_degree = 1; + for expr in self.input.iter() { + input_degree = std::cmp::max(input_degree, expr.degree()); + } + std::cmp::max(3, 2 + input_degree) + } +} diff --git a/halo2_proofs/src/plonk/static_lookup/prover.rs b/halo2_proofs/src/plonk/static_lookup/prover.rs new file mode 100644 index 0000000000..17d5f598e2 --- /dev/null +++ b/halo2_proofs/src/plonk/static_lookup/prover.rs @@ -0,0 +1,411 @@ +use ff::Field; +use halo2curves::{ + bn256::{G1Affine, G1}, + pairing::{Engine, MultiMillerLoop}, + serde::SerdeObject, + FieldExt, +}; + +// TODO: COMPUTE A(0) COMMITMENT FROM LAGRANGE AT 0 COMMITMENTS + +use crate::{ + arithmetic::{best_multiexp, eval_polynomial}, + plonk::{ + evaluation::evaluate, ChallengeBeta, ChallengeTheta, ChallengeX, Expression, ProvingKey, + }, + poly::{ + commitment::{Blind, Params, ParamsProver}, + kzg::commitment::ParamsKZG, + Coeff, EvaluationDomain, LagrangeCoeff, Polynomial, ProverQuery, + }, + transcript::{EncodedChallenge, TranscriptWrite}, +}; +use std::{ + collections::{BTreeMap, BTreeSet}, + fmt::Debug, + iter, +}; + +use crate::plonk::Error; +use group::{prime::PrimeCurveAffine, Curve, Group}; + +use super::StaticTableId; + +#[derive(Debug)] +pub struct Committed { + pub(in crate::plonk) f: Polynomial, + pub(in crate::plonk) m_sparse: BTreeMap, + pub(in crate::plonk) table_ids: Vec>, + pub(in crate::plonk) table_index_value_mappings: Vec>, +} + +#[derive(Debug, Clone)] +pub struct CommittedLogDerivative { + pub(in crate::plonk) b: Polynomial, + pub(in crate::plonk) b0: Polynomial, + pub(in crate::plonk) f: Polynomial, + pub(in crate::plonk) a_at_zero: E::Scalar, +} + +pub(in crate::plonk) struct Evaluated { + constructed: CommittedLogDerivative, +} + +impl super::Argument { + pub(in crate::plonk) fn commit<'a, E, EC, T>( + &self, + pk: &ProvingKey, + params: &ParamsKZG, + domain: &EvaluationDomain, + theta: ChallengeTheta, + challenges: &'a [E::Scalar], + advice_values: &'a [Polynomial], + fixed_values: &'a [Polynomial], + instance_values: &'a [Polynomial], + transcript: &mut T, + ) -> Result, Error> + where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + EC: EncodedChallenge, + T: TranscriptWrite, + { + // TODO: make nicer error + let tables: Vec<_> = self + .table_ids + .iter() + .map(|table_id| { + pk.static_table_mapping + .get(table_id) + .expect("Key not exists") + }) + .collect(); + + if !tables.iter().all(|&table| table.size == tables[0].size) { + panic!("Tables should all be of the same size"); + } + + let table_config = pk + .static_table_configs + .get(&tables[0].size) + .expect("Config does not exists"); + + let evaluate_expressions = |expressions: &[Expression]| { + expressions + .iter() + .map(|expression| { + pk.vk.domain.lagrange_from_vec(evaluate( + expression, + params.n() as usize, + 1, + fixed_values, + advice_values, + instance_values, + challenges, + )) + }) + .collect::>() + }; + + // Closure to get values of expressions and compress them + let compress_expressions = + |evaluated_expressions: &[Polynomial]| { + let compressed_expression = evaluated_expressions + .iter() + .fold(domain.empty_lagrange(), |acc, expression| { + acc * *theta + &expression + }); + compressed_expression + }; + + // Get values of input expressions involved in the lookup + let evaluated_expressions = evaluate_expressions(&self.input); + let f = compress_expressions(&evaluated_expressions); + + // NOTE: For completeness we just ignore blinding rows + // make sure to add selector and change cq as in our hackmd for soundness + let blinding_factors = pk.vk.cs.blinding_factors(); + let usable_rows = params.n() as usize - (blinding_factors + 1); + let mut m_sparse = BTreeMap::::default(); + + let mut table_index_value_mappings: Vec<_> = (0..tables.len()) + .map(|_| BTreeMap::::default()) + .collect(); + + for row in 0..usable_rows { + let mut idx_set: Option> = None; + for (table_idx, (evals, table)) in + evaluated_expressions.iter().zip(tables.iter()).enumerate() + { + let fi = evals.get(row).unwrap(); + let indices: &BTreeSet = table + .value_index_mapping + .get(fi) + .expect(&format!("{:?} not in table", *fi)); + + // append in new map + for index in indices.iter() { + table_index_value_mappings[table_idx].insert(*index, *fi); + } + + if let Some(prev_indices) = idx_set { + let intersection: Vec = + prev_indices.intersection(&indices).copied().collect(); + if intersection.is_empty() { + panic!("Vector lookup must be on the same table row") + } + idx_set = Some(intersection.into_iter().collect()) + } else { + idx_set = Some(indices.clone()) + } + } + + if let Some(idx_set) = idx_set { + m_sparse + .entry(*idx_set.first().unwrap()) + .and_modify(|m| *m += E::Scalar::one()) + .or_insert(E::Scalar::one()); + } else { + panic!("Lookup failed") + } + } + + // zk is not currently supported + let blind = Blind(E::Scalar::zero()); + let f_cm: E::G1Affine = params.commit_lagrange(&f, blind).into(); + + let mut m_cm = E::G1::identity(); + for (&index, &multiplicity) in m_sparse.iter() { + m_cm = table_config.g1_lagrange[index] * multiplicity + m_cm; + } + + let m_cm: E::G1Affine = m_cm.into(); + + transcript.write_point(f_cm)?; + transcript.write_point(m_cm)?; + + Ok(Committed { + f, + m_sparse, + table_ids: self.table_ids.clone(), + table_index_value_mappings, + }) + } +} + +impl Committed { + pub(in crate::plonk) fn commit_log_derivatives<'a, EC, T>( + &self, + pk: &ProvingKey, + params: &ParamsKZG, + domain: &EvaluationDomain, + beta: ChallengeBeta, + theta: ChallengeTheta, + transcript: &mut T, + ) -> Result, Error> + where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + EC: EncodedChallenge, + T: TranscriptWrite, + { + // TODO: make nicer error + let tables: Vec<_> = self + .table_ids + .iter() + .map(|table_id| { + pk.static_table_mapping + .get(table_id) + .expect("Key not exists") + }) + .collect(); + + // We already checked that they are all of the same size + let table_config = pk + .static_table_configs + .get(&tables[0].size) + .expect("Config does not exists"); + + let mut a_cm = E::G1::identity(); + let mut qa_cm = E::G1::identity(); + let mut a0_cm = E::G1::identity(); + + let compress_tables = |index: usize| { + tables.iter().enumerate().fold( + (E::Scalar::zero(), E::G1Affine::identity()), + |acc, (table_idx, &table)| { + let (values, qs) = acc; + + let values = values * *theta + + self.table_index_value_mappings[table_idx] + .get(&index) + .unwrap(); + let qs = qs * *theta + table.qs[index]; + + // TODO: do this in projectiv + (values, qs.into()) + }, + ) + }; + + let f_set: std::collections::BTreeSet = self.f.iter().cloned().collect(); + + // step 2&3&4: computes A sparse representation, a commitment and qa commitment in single pass + for (&index, &multiplicity) in self.m_sparse.iter() { + let (table_values, table_qs) = compress_tables(index); + let a_i = multiplicity * (table_values + *beta).invert().unwrap(); + + // sanity + assert!(f_set.get(&table_values).is_some()); + + // a_cm = table_g1_lagrange * a_i + a_cm; + a_cm = table_config.g1_lagrange[index] * a_i + a_cm; + qa_cm = table_qs * a_i + qa_cm; + // a0_cm = table_lagrange_0 * a_i + a0_cm; + a0_cm = table_config.g_lagrange_opening_at_0[index] * a_i + a0_cm; + } + + let blinding_factors = pk.vk.cs.blinding_factors(); + let usable_rows = params.n() as usize - (blinding_factors + 1); + let mut bs: Vec<_> = self + .f + .iter() + .take(usable_rows) + .map(|&fi| (fi + *beta).invert().unwrap()) + .collect(); + + let beta_inv = beta.invert().unwrap(); + bs.extend_from_slice(&vec![beta_inv; blinding_factors + 1]); + + EvaluationDomain::ifft( + bs.as_mut_slice(), + domain.get_omega_inv(), + domain.k(), + domain.ifft_divisor(), + ); + + // (b - b(0)) / X + let mut b0_poly_coeffs: Vec<::Scalar> = bs[1..].to_vec(); + + let n = 1 << domain.k(); + let b_poly = domain.coeff_from_vec(bs); + + #[cfg(feature = "sanity-checks")] + { + let mut selector = vec![E::Scalar::one(); usable_rows]; + selector.extend_from_slice(&vec![E::Scalar::zero(); n - usable_rows]); + assert_eq!(selector.len(), n); + let root = domain.get_omega(); + for i in 0..n { + assert_eq!( + E::Scalar::zero(), + eval_polynomial(&b_poly, root.pow(&[i as u64, 0, 0, 0])) + * (selector[i] * self.f[i] + *beta) + - E::Scalar::one() + ) + } + } + let p_cm = best_multiexp(&b0_poly_coeffs, &pk.b0_g1_bound); + + b0_poly_coeffs.push(E::Scalar::zero()); + let b0_poly: Polynomial<::Scalar, Coeff> = + domain.coeff_from_vec(b0_poly_coeffs.clone()); + + // write all commitements to transcript + transcript.write_point(a_cm.into())?; + transcript.write_point(qa_cm.into())?; + transcript.write_point(a0_cm.into())?; + + let b0_cm = params.commit(&b0_poly, Blind(E::Scalar::zero())); + + transcript.write_point(b0_cm.into())?; + transcript.write_point(p_cm.into())?; + + // Sumcheck identity: + // n * B(0) = N * A(0) + // A(0) = n * B(0) / N + let b_at_zero = eval_polynomial(&b_poly, E::Scalar::zero()); + let a_at_zero = { + let n_table_inv = E::Scalar::from(table_config.size as u64).invert().unwrap(); + let n = E::Scalar::from(n as u64); + let blinding_factors = E::Scalar::from(blinding_factors as u64); + (b_at_zero * n - (blinding_factors + E::Scalar::one()) * beta_inv) * n_table_inv + }; + + let mut f = self.f.to_vec(); + EvaluationDomain::ifft( + &mut f, + domain.get_omega_inv(), + domain.k(), + domain.ifft_divisor(), + ); + + let f = domain.coeff_from_vec(f); + + Ok(CommittedLogDerivative { + b: b_poly, + b0: b0_poly, + f, + a_at_zero, + }) + } +} + +impl CommittedLogDerivative { + pub(in crate::plonk) fn evaluate< + EC: EncodedChallenge, + T: TranscriptWrite, + >( + self, + pk: &ProvingKey, + x: ChallengeX, + transcript: &mut T, + ) -> Result, Error> + where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + { + let b0_eval = eval_polynomial(&self.b0, *x); + let f_eval = eval_polynomial(&self.f, *x); + + // Hash each advice evaluation + for eval in iter::empty() + .chain(Some(b0_eval)) + .chain(Some(f_eval)) + .chain(Some(self.a_at_zero)) + { + transcript.write_scalar(eval)?; + } + + Ok(Evaluated { + constructed: self.clone(), + }) + } +} + +impl Evaluated { + pub(in crate::plonk) fn open<'a>( + &'a self, + x: ChallengeX, + ) -> impl Iterator> + Clone + where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + { + iter::empty() + .chain(Some(ProverQuery { + point: *x, + poly: &self.constructed.b0, + blind: Blind(E::Scalar::zero()), + })) + .chain(Some(ProverQuery { + point: *x, + poly: &self.constructed.f, + blind: Blind(E::Scalar::zero()), + })) + } +} diff --git a/halo2_proofs/src/plonk/static_lookup/verifier.rs b/halo2_proofs/src/plonk/static_lookup/verifier.rs new file mode 100644 index 0000000000..081e33d032 --- /dev/null +++ b/halo2_proofs/src/plonk/static_lookup/verifier.rs @@ -0,0 +1,240 @@ +use std::iter; + +use super::super::{ + circuit::Expression, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, +}; +use super::{Argument, StaticTableId}; +use crate::poly::kzg::commitment::ParamsKZG; +use crate::{ + arithmetic::{CurveAffine, FieldExt}, + plonk::{Error, VerifyingKey}, + poly::{commitment::MSM, Rotation, VerifierQuery}, + transcript::{EncodedChallenge, TranscriptRead}, +}; +use ff::Field; +use group::{prime::PrimeCurveAffine, Group}; +use halo2curves::batch_pairing::PairingBatcher; +use halo2curves::pairing::{Engine, MultiMillerLoop}; +use halo2curves::serde::SerdeObject; +use std::fmt::Debug; + +pub struct CommittedWitness { + f: E::G1Affine, + m: E::G1Affine, + table_ids: Vec>, +} + +pub struct CommittedLogDerivative { + committed_witness: CommittedWitness, + a: E::G1Affine, + qa: E::G1Affine, + a0: E::G1Affine, + b0: E::G1Affine, + p: E::G1Affine, +} + +pub struct Evaluated { + committed: CommittedLogDerivative, + b0_eval: E::Scalar, + f_eval: E::Scalar, + a_at_zero: E::Scalar, +} + +impl Argument { + pub(in crate::plonk) fn read_committed< + E: MultiMillerLoop + Debug, + EC: EncodedChallenge, + T: TranscriptRead, + >( + &self, + transcript: &mut T, + ) -> Result, Error> { + let f = transcript.read_point()?; + let m = transcript.read_point()?; + + // TODO: CHECK THAT ALL TABLES ARE OF SAME SIZE + + Ok(CommittedWitness { + f, + m, + table_ids: self.table_ids.clone(), + }) + } +} + +impl CommittedWitness { + pub(in crate::plonk) fn read_committed_log_derivative< + EC: EncodedChallenge, + T: TranscriptRead, + >( + self, + transcript: &mut T, + ) -> Result, Error> { + let a = transcript.read_point()?; + let qa = transcript.read_point()?; + let a0 = transcript.read_point()?; + let b0 = transcript.read_point()?; + let p = transcript.read_point()?; + + Ok(CommittedLogDerivative { + committed_witness: self, + a, + qa, + a0, + b0, + p, + }) + } +} + +impl CommittedLogDerivative { + pub(crate) fn evaluate< + EC: EncodedChallenge, + T: TranscriptRead, + >( + self, + transcript: &mut T, + ) -> Result, Error> { + let b0_eval = transcript.read_scalar()?; + let f_eval = transcript.read_scalar()?; + let a_at_zero = transcript.read_scalar()?; + + Ok(Evaluated { + committed: self, + b0_eval, + f_eval, + a_at_zero, + }) + } +} + +impl Evaluated +where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, +{ + pub(in crate::plonk) fn register_pairings<'a>( + &self, + vk: &'a VerifyingKey, + params: &ParamsKZG, + pairing_batcher: &mut PairingBatcher, + beta: ChallengeBeta, + theta: ChallengeTheta, + ) -> Result<(), Error> { + // TODO: make nicer error + let tables: Vec<_> = self + .committed + .committed_witness + .table_ids + .iter() + .map(|table_id| { + vk.static_table_mapping + .get(table_id) + .expect("Key not exists") + }) + .collect(); + + // Check that A encodes the correct values: + // e(a, [T(x)]_2) = e(q_a, [Z_V(x)]_2) * e(m - ß * a, [1]_2) + // + // Check that B_0 has the appropriate degree: + // e(b_0, [x_b0_bound]_2) = e(p, [1]_2) + + // e(m - ß * a, [1]_2) + let m_minus_beta_a: E::G1Affine = + (self.committed.committed_witness.m - (self.committed.a * *beta).into()).into(); + + let a_at_zero_cm: E::G1Affine = + (::G1Affine::generator() * self.a_at_zero).into(); + + let compress_tables = || { + tables.iter().fold(E::G2Affine::identity(), |acc, table| { + let acc = acc * *theta + table.t; + + // TODO: do this in projective + acc.into() + }) + }; + + let table_t = compress_tables(); + + pairing_batcher.add_pairing(&[ + // e(a, [T(x)]_2) + (self.committed.a, table_t), + // e(-q_a, [Z_V(x)]_2) + ((-self.committed.qa).into(), tables[0].zv), + // e(- (m - ß * a), [1]_2) + ((-m_minus_beta_a).into(), params.g2()), + // e(b_0, [x_b0_bound]_2) + (self.committed.b0, tables[0].x_b0_bound), //TODO: MAKE THIS SHARED + // e(-p, [1]_2) + ((-self.committed.p).into(), params.g2()), + // e(a - [a0], [1]_2) + ((self.committed.a - a_at_zero_cm).into(), params.g2()), + // e(-a0, [x]_2) + ((-self.committed.a0).into(), params.s_g2()), + ]); + + Ok(()) + } + + pub(in crate::plonk) fn expressions<'a>( + &'a self, + vk: &'a VerifyingKey, + l_last: E::Scalar, + l_blind: E::Scalar, + beta: ChallengeBeta, + x: ChallengeX, + ) -> impl Iterator + 'a { + let active_rows = E::Scalar::one() - (l_last + l_blind); + + let tables: Vec<_> = self + .committed + .committed_witness + .table_ids + .iter() + .map(|table_id| { + vk.static_table_mapping + .get(&table_id) + .expect("Key does not exists") + }) + .collect(); + + let table_size = E::Scalar::from(tables[0].size as u64); + + let blinding_factors = vk.cs.blinding_factors(); + let unusable_rows = E::Scalar::from((blinding_factors + 1) as u64); + + let b_at_zero = { + let beta_inv = beta.invert().unwrap(); + let circuit_domain_inv = E::Scalar::from(vk.get_domain().n).invert().unwrap(); + (table_size * self.a_at_zero + unusable_rows * beta_inv) * circuit_domain_inv + }; + + let b_eval = self.b0_eval * *x + b_at_zero; + + std::iter::empty().chain(Some( + // b(l_active * f + beta) - 1 = 0 + b_eval * (active_rows * self.f_eval + *beta) - E::Scalar::one(), + )) + } + + pub(in crate::plonk) fn queries<'r, M: MSM + 'r>( + &'r self, + vk: &'r VerifyingKey, + x: ChallengeX, + ) -> impl Iterator> + Clone { + iter::empty() + .chain(Some(VerifierQuery::new_commitment( + &self.committed.b0, + *x, + self.b0_eval, + ))) + .chain(Some(VerifierQuery::new_commitment( + &self.committed.committed_witness.f, + *x, + self.f_eval, + ))) + } +} diff --git a/halo2_proofs/src/plonk/vanishing/verifier.rs b/halo2_proofs/src/plonk/vanishing/verifier.rs index 3570dee6c1..c99cfcb64a 100644 --- a/halo2_proofs/src/plonk/vanishing/verifier.rs +++ b/halo2_proofs/src/plonk/vanishing/verifier.rs @@ -1,6 +1,8 @@ +use std::fmt::Debug; use std::iter; use ff::Field; +use halo2curves::{pairing::MultiMillerLoop, serde::SerdeObject}; use crate::{ arithmetic::CurveAffine, @@ -54,13 +56,19 @@ impl Argument { impl Committed { pub(in crate::plonk) fn read_commitments_after_y< - E: EncodedChallenge, - T: TranscriptRead, + E, + EC: EncodedChallenge, + T: TranscriptRead, >( self, - vk: &VerifyingKey, + vk: &VerifyingKey, transcript: &mut T, - ) -> Result, Error> { + ) -> Result, Error> + where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + { // Obtain a commitment to h(X) in the form of multiple pieces of degree n - 1 let h_commitments = read_n_points(transcript, vk.domain.get_quotient_poly_degree())?; diff --git a/halo2_proofs/src/plonk/verifier.rs b/halo2_proofs/src/plonk/verifier.rs index e6a2327e7b..65c343a63d 100644 --- a/halo2_proofs/src/plonk/verifier.rs +++ b/halo2_proofs/src/plonk/verifier.rs @@ -1,43 +1,54 @@ use ff::Field; use group::Curve; +use halo2curves::batch_pairing::PairingBatcher; use rand_core::RngCore; +use std::fmt::Debug; use std::iter; use super::{ - vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, Error, - VerifyingKey, + static_lookup, vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, + ChallengeY, Error, VerifyingKey, }; use crate::arithmetic::{compute_inner_product, CurveAffine, FieldExt}; use crate::poly::commitment::{CommitmentScheme, Verifier}; +use crate::poly::kzg::commitment::KZGCommitmentScheme; use crate::poly::VerificationStrategy; use crate::poly::{ commitment::{Blind, Params, MSM}, Guard, VerifierQuery, }; -use crate::transcript::{read_n_points, read_n_scalars, EncodedChallenge, TranscriptRead}; +use crate::transcript::{ + read_n_points, read_n_scalars, ChallengeScalar, EncodedChallenge, TranscriptRead, +}; +use halo2curves::pairing::MultiMillerLoop; +use halo2curves::serde::SerdeObject; -#[cfg(feature = "batch")] -mod batch; -#[cfg(feature = "batch")] -pub use batch::BatchVerifier; +// #[cfg(feature = "batch")] +// mod batch; +// #[cfg(feature = "batch")] +// pub use batch::BatchVerifier; use crate::poly::commitment::ParamsVerifier; /// Returns a boolean indicating whether or not the proof is valid pub fn verify_proof< 'params, - Scheme: CommitmentScheme, - V: Verifier<'params, Scheme>, - E: EncodedChallenge, - T: TranscriptRead, - Strategy: VerificationStrategy<'params, Scheme, V>, + E: MultiMillerLoop + Debug, + V: Verifier<'params, E>, + EC: EncodedChallenge, + T: TranscriptRead, + Strategy: VerificationStrategy<'params, E, V, Output = Strategy>, >( - params: &'params Scheme::ParamsVerifier, - vk: &VerifyingKey, + params: &'params as CommitmentScheme>::ParamsVerifier, + vk: &VerifyingKey, strategy: Strategy, - instances: &[&[&[Scheme::Scalar]]], + instances: &[&[&[E::Scalar]]], transcript: &mut T, -) -> Result { +) -> Result, Error> +where + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, +{ // Check that instances matches the expected number of instance columns for instances in instances.iter() { if instances.len() != vk.cs.num_instance_columns { @@ -56,7 +67,7 @@ pub fn verify_proof< return Err(Error::InstanceTooLarge); } let mut poly = instance.to_vec(); - poly.resize(params.n() as usize, Scheme::Scalar::zero()); + poly.resize(params.n() as usize, E::Scalar::zero()); let poly = vk.domain.lagrange_from_vec(poly); Ok(params.commit_lagrange(&poly, Blind::default()).to_affine()) @@ -93,8 +104,8 @@ pub fn verify_proof< // Hash the prover's advice commitments into the transcript and squeeze challenges let (advice_commitments, challenges) = { let mut advice_commitments = - vec![vec![Scheme::Curve::default(); vk.cs.num_advice_columns]; num_proofs]; - let mut challenges = vec![Scheme::Scalar::zero(); vk.cs.num_challenges]; + vec![vec![E::G1Affine::default(); vk.cs.num_advice_columns]; num_proofs]; + let mut challenges = vec![E::Scalar::zero(); vk.cs.num_challenges]; for current_phase in vk.cs.phases() { for advice_commitments in advice_commitments.iter_mut() { @@ -133,6 +144,17 @@ pub fn verify_proof< }) .collect::, _>>()?; + let static_lookups = (0..num_proofs) + .map(|_| -> Result, _> { + // Hash each static lookup commitment + vk.cs + .static_lookups + .iter() + .map(|lookup| lookup.read_committed(transcript)) + .collect::>, _>>() + }) + .collect::, _>>()?; + // Sample beta challenge let beta: ChallengeBeta<_> = transcript.squeeze_challenge_scalar(); @@ -157,6 +179,16 @@ pub fn verify_proof< }) .collect::, _>>()?; + let static_lookups = static_lookups + .into_iter() + .map(|lookups| { + lookups + .into_iter() + .map(|lookup| lookup.read_committed_log_derivative(transcript)) + .collect::, _>>() + }) + .collect::, _>>()?; + let vanishing = vanishing::Argument::read_commitments_before_y(transcript)?; // Sample y challenge, which keeps the gates linearly independent. @@ -167,6 +199,7 @@ pub fn verify_proof< // Sample x challenge, which is used to ensure the circuit is // satisfied with high probability. let x: ChallengeX<_> = transcript.squeeze_challenge_scalar(); + let instance_evals = if V::QUERY_INSTANCE { (0..num_proofs) .map(|_| -> Result, _> { @@ -239,6 +272,16 @@ pub fn verify_proof< }) .collect::, _>>()?; + let static_lookups = static_lookups + .into_iter() + .map(|lookups| -> Result, _> { + lookups + .into_iter() + .map(|lookup| lookup.evaluate(transcript)) + .collect::, _>>() + }) + .collect::, _>>()?; + // This check ensures the circuit is satisfied so long as the polynomial // commitments open to the correct values. let vanishing = { @@ -251,9 +294,9 @@ pub fn verify_proof< .l_i_range(*x, xn, (-((blinding_factors + 1) as i32))..=0); assert_eq!(l_evals.len(), 2 + blinding_factors); let l_last = l_evals[0]; - let l_blind: Scheme::Scalar = l_evals[1..(1 + blinding_factors)] + let l_blind: E::Scalar = l_evals[1..(1 + blinding_factors)] .iter() - .fold(Scheme::Scalar::zero(), |acc, eval| acc + eval); + .fold(E::Scalar::zero(), |acc, eval| acc + eval); let l_0 = l_evals[1 + blinding_factors]; // Compute the expected value of h(x) @@ -262,63 +305,74 @@ pub fn verify_proof< .zip(instance_evals.iter()) .zip(permutations_evaluated.iter()) .zip(lookups_evaluated.iter()) - .flat_map(|(((advice_evals, instance_evals), permutation), lookups)| { - let challenges = &challenges; - let fixed_evals = &fixed_evals; - std::iter::empty() - // Evaluate the circuit using the custom gates provided - .chain(vk.cs.gates.iter().flat_map(move |gate| { - gate.polynomials().iter().map(move |poly| { - poly.evaluate( - &|scalar| scalar, - &|_| panic!("virtual selectors are removed during optimization"), - &|query| fixed_evals[query.index], - &|query| advice_evals[query.index], - &|query| instance_evals[query.index], - &|challenge| challenges[challenge.index()], - &|a| -a, - &|a, b| a + &b, - &|a, b| a * &b, - &|a, scalar| a * &scalar, - ) - }) - })) - .chain(permutation.expressions( - vk, - &vk.cs.permutation, - &permutations_common, - advice_evals, - fixed_evals, - instance_evals, - l_0, - l_last, - l_blind, - beta, - gamma, - x, - )) - .chain( - lookups - .iter() - .zip(vk.cs.lookups.iter()) - .flat_map(move |(p, argument)| { - p.expressions( - l_0, - l_last, - l_blind, - argument, - theta, - beta, - gamma, - advice_evals, - fixed_evals, - instance_evals, - challenges, + .zip(static_lookups.iter()) + .flat_map( + |((((advice_evals, instance_evals), permutation), lookups), static_lookups)| { + let challenges = &challenges; + let fixed_evals = &fixed_evals; + std::iter::empty() + // Evaluate the circuit using the custom gates provided + .chain(vk.cs.gates.iter().flat_map(move |gate| { + gate.polynomials().iter().map(move |poly| { + poly.evaluate( + &|scalar| scalar, + &|_| { + panic!("virtual selectors are removed during optimization") + }, + &|query| fixed_evals[query.index], + &|query| advice_evals[query.index], + &|query| instance_evals[query.index], + &|challenge| challenges[challenge.index()], + &|a| -a, + &|a, b| a + &b, + &|a, b| a * &b, + &|a, scalar| a * &scalar, ) }) - .into_iter(), - ) - }); + })) + .chain(permutation.expressions( + vk, + &vk.cs.permutation, + &permutations_common, + advice_evals, + fixed_evals, + instance_evals, + l_0, + l_last, + l_blind, + beta, + gamma, + x, + )) + .chain( + lookups + .iter() + .zip(vk.cs.lookups.iter()) + .flat_map(move |(p, argument)| { + p.expressions( + l_0, + l_last, + l_blind, + argument, + theta, + beta, + gamma, + advice_evals, + fixed_evals, + instance_evals, + challenges, + ) + }) + .into_iter(), + ) + .chain( + static_lookups + .iter() + .flat_map(move |p| p.expressions(vk, l_last, l_blind, beta, x)) + .into_iter(), + ) + }, + ); vanishing.verify(params, expressions, y, xn) }; @@ -330,13 +384,20 @@ pub fn verify_proof< .zip(advice_evals.iter()) .zip(permutations_evaluated.iter()) .zip(lookups_evaluated.iter()) + .zip(static_lookups.iter()) .flat_map( |( ( - (((instance_commitments, instance_evals), advice_commitments), advice_evals), - permutation, + ( + ( + ((instance_commitments, instance_evals), advice_commitments), + advice_evals, + ), + permutation, + ), + lookups, ), - lookups, + static_lookups, )| { iter::empty() .chain( @@ -355,11 +416,12 @@ pub fn verify_proof< ) .chain(vk.cs.advice_queries.iter().enumerate().map( move |(query_index, &(column, at))| { - VerifierQuery::new_commitment( + let verifier_advice_query = VerifierQuery::new_commitment( &advice_commitments[column.index()], vk.domain.rotate_omega(*x, at), advice_evals[query_index], - ) + ); + verifier_advice_query }, )) .chain(permutation.queries(vk, x)) @@ -369,6 +431,12 @@ pub fn verify_proof< .flat_map(move |p| p.queries(vk, x)) .into_iter(), ) + .chain( + static_lookups + .iter() + .flat_map(move |p| p.queries(vk, x)) + .into_iter(), + ) }, ) .chain( @@ -391,9 +459,31 @@ pub fn verify_proof< // polynomial commitments open to the correct values. let verifier = V::new(params); - strategy.process(|msm| { + let strategy = strategy.process(|msm| { verifier .verify_proof(transcript, queries, msm) .map_err(|_| Error::Opening) - }) + })?; + + // squeeze a challenge + let pairing_batcher_challenge: ChallengeScalar<_, ()> = transcript.squeeze_challenge_scalar(); + let mut pairing_batcher: PairingBatcher = + PairingBatcher::::new(*pairing_batcher_challenge); + + strategy.merge_with_pairing_batcher(&mut pairing_batcher); + + // now register all static lookups pairings + let _ = static_lookups + .into_iter() + .map(|static_lookups| -> Result, _> { + // Hash each lookup permuted commitment + static_lookups + .iter() + .map(|lookup| { + lookup.register_pairings(&vk, params, &mut pairing_batcher, beta, theta) + }) + .collect::, _>>() + }) + .collect::, _>>()?; + Ok(pairing_batcher) } diff --git a/halo2_proofs/src/poly.rs b/halo2_proofs/src/poly.rs index 219afebb77..212c61ad95 100644 --- a/halo2_proofs/src/poly.rs +++ b/halo2_proofs/src/poly.rs @@ -21,8 +21,8 @@ mod domain; mod query; mod strategy; -/// Inner product argument commitment scheme -pub mod ipa; +// /// Inner product argument commitment scheme +// pub mod ipa; /// KZG commitment scheme pub mod kzg; @@ -67,7 +67,7 @@ impl Basis for ExtendedLagrangeCoeff {} /// basis. #[derive(Clone, Debug)] pub struct Polynomial { - values: Vec, + pub(crate) values: Vec, _marker: PhantomData, } diff --git a/halo2_proofs/src/poly/commitment.rs b/halo2_proofs/src/poly/commitment.rs index e82515cfff..0c9c0f9594 100644 --- a/halo2_proofs/src/poly/commitment.rs +++ b/halo2_proofs/src/poly/commitment.rs @@ -1,4 +1,5 @@ use super::{ + kzg::commitment::KZGCommitmentScheme, query::{ProverQuery, VerifierQuery}, strategy::Guard, Coeff, LagrangeCoeff, Polynomial, @@ -7,6 +8,8 @@ use crate::poly::Error; use crate::transcript::{EncodedChallenge, TranscriptRead, TranscriptWrite}; use ff::Field; use group::Curve; +use halo2curves::pairing::MultiMillerLoop; +use halo2curves::serde::SerdeObject; use halo2curves::{CurveAffine, CurveExt, FieldExt}; use rand_core::RngCore; use std::{ @@ -34,7 +37,7 @@ pub trait CommitmentScheme { type ParamsVerifier: for<'params> ParamsVerifier<'params, Self::Curve>; /// Wrapper for parameter generator - fn new_params(k: u32) -> Self::ParamsProver; + fn new_params(k: u32, rng: &mut R) -> Self::ParamsProver; /// Wrapper for parameter reader fn read_params(reader: &mut R) -> io::Result; @@ -80,7 +83,7 @@ pub trait ParamsProver<'params, C: CurveAffine>: Params<'params, C> { type ParamsVerifier: ParamsVerifier<'params, C>; /// Returns new instance of parameters - fn new(k: u32) -> Self; + fn new(k: u32, rng: &mut R) -> Self; /// This computes a commitment to a polynomial described by the provided /// slice of coefficients. The commitment may be blinded by the blinding @@ -125,18 +128,22 @@ pub trait MSM: Clone + Debug + Send + Sync { } /// Common multi-open prover interface for various commitment schemes -pub trait Prover<'params, Scheme: CommitmentScheme> { +pub trait Prover<'params, E: MultiMillerLoop + Debug> +where + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, +{ /// Query instance or not const QUERY_INSTANCE: bool; /// Creates new prover instance - fn new(params: &'params Scheme::ParamsProver) -> Self; + fn new(params: &'params as CommitmentScheme>::ParamsProver) -> Self; /// Create a multi-opening proof fn create_proof< 'com, - E: EncodedChallenge, - T: TranscriptWrite, + EC: EncodedChallenge, + T: TranscriptWrite, R, I, >( @@ -146,15 +153,19 @@ pub trait Prover<'params, Scheme: CommitmentScheme> { queries: I, ) -> io::Result<()> where - I: IntoIterator> + Clone, + I: IntoIterator> + Clone, R: RngCore; } /// Common multi-open verifier interface for various commitment schemes -pub trait Verifier<'params, Scheme: CommitmentScheme> { +pub trait Verifier<'params, E: MultiMillerLoop + Debug> +where + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, +{ /// Unfinalized verification result. This is returned in verification /// to allow developer to compress or combined verification results - type Guard: Guard; + type Guard: Guard, MSMAccumulator = Self::MSMAccumulator>; /// Accumulator fot comressed verification type MSMAccumulator; @@ -163,13 +174,13 @@ pub trait Verifier<'params, Scheme: CommitmentScheme> { const QUERY_INSTANCE: bool; /// Creates new verifier instance - fn new(params: &'params Scheme::ParamsVerifier) -> Self; + fn new(params: &'params as CommitmentScheme>::ParamsVerifier) -> Self; /// Process the proof and returns unfinished result named `Guard` fn verify_proof< 'com, - E: EncodedChallenge, - T: TranscriptRead, + EC: EncodedChallenge< as CommitmentScheme>::Curve>, + T: TranscriptRead< as CommitmentScheme>::Curve, EC>, I, >( &self, @@ -182,8 +193,11 @@ pub trait Verifier<'params, Scheme: CommitmentScheme> { I: IntoIterator< Item = VerifierQuery< 'com, - Scheme::Curve, - >::MSM, + as CommitmentScheme>::Curve, + < as CommitmentScheme>::ParamsVerifier as Params< + 'params, + as CommitmentScheme>::Curve, + >>::MSM, >, > + Clone; } diff --git a/halo2_proofs/src/poly/domain.rs b/halo2_proofs/src/poly/domain.rs index 54c6b5ed18..eb6e33cf59 100644 --- a/halo2_proofs/src/poly/domain.rs +++ b/halo2_proofs/src/poly/domain.rs @@ -17,7 +17,7 @@ use std::marker::PhantomData; /// domain of size $2^{k} * j$ with $j \neq 0$. #[derive(Clone, Debug)] pub struct EvaluationDomain { - n: u64, + pub n: u64, k: u32, extended_k: u32, omega: G::Scalar, @@ -362,7 +362,8 @@ impl EvaluationDomain { }); } - fn ifft(a: &mut [G], omega_inv: G::Scalar, log_n: u32, divisor: G::Scalar) { + /// Inverse FFT + pub fn ifft(a: &mut [G], omega_inv: G::Scalar, log_n: u32, divisor: G::Scalar) { best_fft(a, omega_inv, log_n); parallelize(a, |a, _| { for a in a { @@ -403,6 +404,11 @@ impl EvaluationDomain { self.extended_omega } + /// Get the ifft divisor + pub fn ifft_divisor(&self) -> G::Scalar { + self.ifft_divisor + } + /// Multiplies a value by some power of $\omega$, essentially rotating over /// the domain. pub fn rotate_omega(&self, value: G::Scalar, rotation: Rotation) -> G::Scalar { diff --git a/halo2_proofs/src/poly/ipa/strategy.rs b/halo2_proofs/src/poly/ipa/strategy.rs index 6f3b4b7228..155631d7a6 100644 --- a/halo2_proofs/src/poly/ipa/strategy.rs +++ b/halo2_proofs/src/poly/ipa/strategy.rs @@ -114,7 +114,6 @@ impl<'params, C: CurveAffine> fn finalize(self) -> bool { self.msm.check() } -} /// A verifier that checks single proof #[derive(Debug)] diff --git a/halo2_proofs/src/poly/kzg/commitment.rs b/halo2_proofs/src/poly/kzg/commitment.rs index fbc1812e6b..99d4116ddb 100644 --- a/halo2_proofs/src/poly/kzg/commitment.rs +++ b/halo2_proofs/src/poly/kzg/commitment.rs @@ -6,7 +6,7 @@ use crate::poly::commitment::{Blind, CommitmentScheme, Params, ParamsProver, Par use crate::poly::{Coeff, LagrangeCoeff, Polynomial}; use crate::SerdeFormat; -use ff::{Field, PrimeField}; +use ff::{BatchInvert, Field, PrimeField}; use group::{prime::PrimeCurveAffine, Curve, Group as _}; use halo2curves::pairing::Engine; use rand_core::{OsRng, RngCore}; @@ -18,17 +18,165 @@ use std::io; use super::msm::MSMKZG; +pub fn is_pow_2(x: usize) -> bool { + (x & (x - 1)) == 0 +} + +fn log2(x: usize) -> u32 { + (usize::BITS - 1) - x.leading_zeros() +} + /// These are the public parameters for the polynomial commitment scheme. #[derive(Debug, Clone)] pub struct ParamsKZG { pub(crate) k: u32, pub(crate) n: u64, pub(crate) g: Vec, - pub(crate) g_lagrange: Vec, + // TODO: make pub(crate) + pub g_lagrange: Vec, pub(crate) g2: E::G2Affine, pub(crate) s_g2: E::G2Affine, } +#[derive(Debug, Clone)] +pub struct TableSRS { + pub(crate) g1: Vec, + pub(crate) g1_lagrange: Vec, + pub(crate) g_lagrange_opening_at_0: Vec, + pub(crate) g2: Vec, +} + +impl TableSRS { + /// Return G1 + pub fn g1(&self) -> &[E::G1Affine] { + &self.g1 + } + + /// Return G2 + pub fn g2(&self) -> &[E::G2Affine] { + &self.g2 + } + + /// Return G1 lagrange + pub fn g1_lagrange(&self) -> &[E::G1Affine] { + &self.g1_lagrange + } + + /// Return G1 lagrange openings at 0 + pub fn g_lagrange_opening_at_0(&self) -> &[E::G1Affine] { + &self.g_lagrange_opening_at_0 + } +} + +impl TableSRS { + /// FOR TESTING PURPOSES + pub fn setup_from_toxic_waste(max_g1_power: usize, max_g2_power: usize, s: E::Scalar) -> Self { + let g1_len = (max_g1_power + 1) as usize; + let g2_len = (max_g2_power + 1) as usize; + assert!(is_pow_2(g1_len)); + + // Calculate g = [G1, [s] G1, [s^2] G1, ..., [s^(n-1)] G1] in parallel. + let g1_gen = E::G1Affine::generator(); + let g2_gen = E::G2Affine::generator(); + + let mut g_projective = vec![E::G1::group_zero(); g1_len as usize]; + + // TODO: consider to merge this two parallelize + parallelize(&mut g_projective, |g, start| { + let mut current_g: E::G1 = g1_gen.into(); + + current_g *= s.pow_vartime(&[start as u64]); + for g in g.iter_mut() { + *g = current_g; + current_g *= s; + } + }); + + let mut g2_projective = vec![E::G2::group_zero(); g2_len as usize]; + parallelize(&mut g2_projective, |g, start| { + let mut current_g: E::G2 = g2_gen.into(); + + current_g *= s.pow_vartime(&[start as u64]); + for g in g.iter_mut() { + *g = current_g; + current_g *= s; + } + }); + + let g1 = { + let mut g1 = vec![E::G1Affine::identity(); g1_len as usize]; + parallelize(&mut g1, |g1, starts| { + E::G1::batch_normalize(&g_projective[starts..(starts + g1.len())], g1); + }); + g1 + }; + + let g2 = { + let mut g2 = vec![E::G2Affine::identity(); g2_len as usize]; + parallelize(&mut g2, |g2, starts| { + E::G2::batch_normalize(&g2_projective[starts..(starts + g2.len())], g2); + }); + g2 + }; + + let mut g_lagrange_projective = vec![E::G1::group_zero(); g1_len]; + let mut root = E::Scalar::ROOT_OF_UNITY_INV.invert().unwrap(); + + let k = log2(g1_len); // we asserted that g1_len is pow_2 + for _ in k..E::Scalar::S { + root = root.square(); + } + + let n_inv = Option::::from(E::Scalar::from(g1_len as u64).invert()) + .expect("inversion should be ok for n pow2"); + + // x^N - 1 + let multiplier = (s.pow_vartime(&[g1_len as u64]) - E::Scalar::one()) * n_inv; + parallelize(&mut g_lagrange_projective, |g, start| { + for (idx, g) in g.iter_mut().enumerate() { + let offset = start + idx; + let root_pow = root.pow_vartime(&[offset as u64]); + let scalar = multiplier * root_pow * (s - root_pow).invert().unwrap(); + *g = g1_gen * scalar; + } + }); + + let g1_lagrange = { + let mut g_lagrange = vec![E::G1Affine::identity(); g1_len]; + parallelize(&mut g_lagrange, |g_lagrange, starts| { + E::G1::batch_normalize( + &g_lagrange_projective[starts..(starts + g_lagrange.len())], + g_lagrange, + ); + }); + drop(g_lagrange_projective); + g_lagrange + }; + + // [(L_i(x) - L_i(0)) / x]_1 + // = omega^{-i} * [L_i(x)]_1 - (1 / N) * [x^{N-1}]_1 + let mut roots_of_unity_inv: Vec = + std::iter::successors(Some(E::Scalar::one()), |p| Some(*p * root)) + .take(g1_len) + .collect(); + roots_of_unity_inv.iter_mut().batch_invert(); + + // [x^{N - 1}]_1 * (1 / N) + let last_power_scaled = *g1.last().unwrap() * n_inv; + let g_lagrange_opening_at_0: Vec = g1_lagrange + .iter() + .zip(roots_of_unity_inv.iter()) + .map(|(&l_i, w_inv_i)| (l_i * w_inv_i - last_power_scaled).into()) + .collect(); + + Self { + g1, + g1_lagrange, + g_lagrange_opening_at_0, + g2, + } + } +} /// Umbrella commitment scheme construction for all KZG variants #[derive(Debug)] pub struct KZGCommitmentScheme { @@ -46,8 +194,8 @@ where type ParamsProver = ParamsKZG; type ParamsVerifier = ParamsVerifierKZG; - fn new_params(k: u32) -> Self::ParamsProver { - ParamsKZG::new(k) + fn new_params(k: u32, rng: &mut R) -> Self::ParamsProver { + ParamsKZG::new(k, rng) } fn read_params(reader: &mut R) -> io::Result { @@ -56,6 +204,77 @@ where } impl ParamsKZG { + /// Initializes parameters for the curve, draws toxic secret from given rng. + /// MUST NOT be used in production. + pub fn setup_from_toxic_waste(k: u32, s: E::Scalar) -> Self { + // Largest root of unity exponent of the Engine is `2^E::Scalar::S`, so we can + // only support FFTs of polynomials below degree `2^E::Scalar::S`. + assert!(k <= E::Scalar::S); + let n: u64 = 1 << k; + + // Calculate g = [G1, [s] G1, [s^2] G1, ..., [s^(n-1)] G1] in parallel. + let g1 = E::G1Affine::generator(); + + let mut g_projective = vec![E::G1::identity(); n as usize]; + parallelize(&mut g_projective, |g, start| { + let mut current_g: E::G1 = g1.into(); + current_g *= s.pow_vartime(&[start as u64]); + for g in g.iter_mut() { + *g = current_g; + current_g *= s; + } + }); + + let g = { + let mut g = vec![E::G1Affine::identity(); n as usize]; + parallelize(&mut g, |g, starts| { + E::G1::batch_normalize(&g_projective[starts..(starts + g.len())], g); + }); + g + }; + + let mut g_lagrange_projective = vec![E::G1::identity(); n as usize]; + let mut root = E::Scalar::ROOT_OF_UNITY_INV.invert().unwrap(); + for _ in k..E::Scalar::S { + root = root.square(); + } + let n_inv = Option::::from(E::Scalar::from(n).invert()) + .expect("inversion should be ok for n = 1<::generator(); + let s_g2 = (g2 * s).into(); + + Self { + k, + n, + g, + g_lagrange, + g2, + s_g2, + } + } + /// Initializes parameters for the curve, draws toxic secret from given rng. /// MUST NOT be used in production. pub fn setup(k: u32, rng: R) -> Self { @@ -68,7 +287,7 @@ impl ParamsKZG { let g1 = E::G1Affine::generator(); let s = ::random(rng); - let mut g_projective = vec![E::G1::group_zero(); n as usize]; + let mut g_projective = vec![E::G1::identity(); n as usize]; parallelize(&mut g_projective, |g, start| { let mut current_g: E::G1 = g1.into(); current_g *= s.pow_vartime(&[start as u64]); @@ -86,7 +305,7 @@ impl ParamsKZG { g }; - let mut g_lagrange_projective = vec![E::G1::group_zero(); n as usize]; + let mut g_lagrange_projective = vec![E::G1::identity(); n as usize]; let mut root = E::Scalar::ROOT_OF_UNITY_INV.invert().unwrap(); for _ in k..E::Scalar::S { root = root.square(); @@ -138,6 +357,11 @@ impl ParamsKZG { self.s_g2 } + /// Returns g1 generators + pub fn g1_srs(&self) -> &[E::G1Affine] { + &self.g + } + /// Writes parameters to buffer pub fn write_custom(&self, writer: &mut W, format: SerdeFormat) where @@ -308,8 +532,8 @@ where self } - fn new(k: u32) -> Self { - Self::setup(k, OsRng) + fn new(k: u32, rng: &mut R) -> Self { + unreachable!() } fn commit(&self, poly: &Polynomial, _: Blind) -> E::G1 { @@ -352,7 +576,7 @@ mod test { use crate::poly::EvaluationDomain; use halo2curves::bn256::{Bn256, Fr}; - let params = ParamsKZG::::new(K); + let params = ParamsKZG::::new(K, &mut OsRng); let domain = EvaluationDomain::new(1, K); let mut a = domain.empty_lagrange(); @@ -380,7 +604,7 @@ mod test { use crate::halo2curves::bn256::{Bn256, Fr}; use crate::poly::EvaluationDomain; - let params0 = ParamsKZG::::new(K); + let params0 = ParamsKZG::::new(K, &mut OsRng); let mut data = vec![]; as Params<_>>::write(¶ms0, &mut data).unwrap(); let params1: ParamsKZG = Params::read::<_>(&mut &data[..]).unwrap(); diff --git a/halo2_proofs/src/poly/kzg/msm.rs b/halo2_proofs/src/poly/kzg/msm.rs index 19754146a0..94d88e19ea 100644 --- a/halo2_proofs/src/poly/kzg/msm.rs +++ b/halo2_proofs/src/poly/kzg/msm.rs @@ -167,4 +167,16 @@ impl<'a, E: MultiMillerLoop + Debug> DualMSM<'a, E> { .is_identity(), ) } + + // reuturn pair to check + pub fn into_pair(self) -> [(E::G1Affine, E::G2Affine); 2] { + let left = self.left.eval(); + let right = -self.right.eval(); + + let (term_1, term_2) = ( + (left.into(), self.params.s_g2), + (right.into(), self.params.g2), + ); + [term_1, term_2] + } } diff --git a/halo2_proofs/src/poly/kzg/multiopen/gwc/prover.rs b/halo2_proofs/src/poly/kzg/multiopen/gwc/prover.rs index e7bff84ade..bf6594a0ec 100644 --- a/halo2_proofs/src/poly/kzg/multiopen/gwc/prover.rs +++ b/halo2_proofs/src/poly/kzg/multiopen/gwc/prover.rs @@ -14,7 +14,7 @@ use crate::transcript::{EncodedChallenge, TranscriptWrite}; use ff::Field; use group::Curve; -use halo2curves::pairing::Engine; +use halo2curves::pairing::{Engine, MultiMillerLoop}; use rand_core::RngCore; use std::fmt::Debug; use std::io::{self, Write}; @@ -27,7 +27,7 @@ pub struct ProverGWC<'params, E: Engine> { } /// Create a multi-opening proof -impl<'params, E: Engine + Debug> Prover<'params, KZGCommitmentScheme> for ProverGWC<'params, E> +impl<'params, E: MultiMillerLoop + Debug> Prover<'params, E> for ProverGWC<'params, E> where E::G1Affine: SerdeCurveAffine, E::G2Affine: SerdeCurveAffine, diff --git a/halo2_proofs/src/poly/kzg/multiopen/gwc/verifier.rs b/halo2_proofs/src/poly/kzg/multiopen/gwc/verifier.rs index 1ec003d638..fd03cee146 100644 --- a/halo2_proofs/src/poly/kzg/multiopen/gwc/verifier.rs +++ b/halo2_proofs/src/poly/kzg/multiopen/gwc/verifier.rs @@ -30,7 +30,7 @@ pub struct VerifierGWC<'params, E: Engine> { params: &'params ParamsKZG, } -impl<'params, E> Verifier<'params, KZGCommitmentScheme> for VerifierGWC<'params, E> +impl<'params, E> Verifier<'params, E> for VerifierGWC<'params, E> where E: MultiMillerLoop + Debug, E::G1Affine: SerdeCurveAffine, diff --git a/halo2_proofs/src/poly/kzg/multiopen/shplonk/prover.rs b/halo2_proofs/src/poly/kzg/multiopen/shplonk/prover.rs index 155ef9debb..76dd63f136 100644 --- a/halo2_proofs/src/poly/kzg/multiopen/shplonk/prover.rs +++ b/halo2_proofs/src/poly/kzg/multiopen/shplonk/prover.rs @@ -15,7 +15,7 @@ use crate::transcript::{EncodedChallenge, TranscriptWrite}; use ff::Field; use group::Curve; -use halo2curves::pairing::Engine; +use halo2curves::pairing::{Engine, MultiMillerLoop}; use rand_core::RngCore; use rayon::prelude::*; use rustc_hash::FxHashSet; @@ -104,8 +104,7 @@ impl<'a, E: Engine> ProverSHPLONK<'a, E> { } /// Create a multi-opening proof -impl<'params, E: Engine + Debug> Prover<'params, KZGCommitmentScheme> - for ProverSHPLONK<'params, E> +impl<'params, E: MultiMillerLoop + Debug> Prover<'params, E> for ProverSHPLONK<'params, E> where E::G1Affine: SerdeCurveAffine, E::G2Affine: SerdeCurveAffine, diff --git a/halo2_proofs/src/poly/kzg/multiopen/shplonk/verifier.rs b/halo2_proofs/src/poly/kzg/multiopen/shplonk/verifier.rs index 77861867bc..5658e5c6c2 100644 --- a/halo2_proofs/src/poly/kzg/multiopen/shplonk/verifier.rs +++ b/halo2_proofs/src/poly/kzg/multiopen/shplonk/verifier.rs @@ -35,7 +35,7 @@ pub struct VerifierSHPLONK<'params, E: Engine> { params: &'params ParamsKZG, } -impl<'params, E> Verifier<'params, KZGCommitmentScheme> for VerifierSHPLONK<'params, E> +impl<'params, E> Verifier<'params, E> for VerifierSHPLONK<'params, E> where E: MultiMillerLoop + Debug, E::G1Affine: SerdeCurveAffine, diff --git a/halo2_proofs/src/poly/kzg/strategy.rs b/halo2_proofs/src/poly/kzg/strategy.rs index ca4b4fb18a..6f94096530 100644 --- a/halo2_proofs/src/poly/kzg/strategy.rs +++ b/halo2_proofs/src/poly/kzg/strategy.rs @@ -10,7 +10,7 @@ use crate::{ plonk::Error, poly::{ commitment::{Verifier, MSM}, - ipa::msm::MSMIPA, + // ipa::msm::MSMIPA, strategy::{Guard, VerificationStrategy}, }, transcript::{EncodedChallenge, TranscriptRead}, @@ -18,6 +18,7 @@ use crate::{ use ff::Field; use group::Group; use halo2curves::{ + batch_pairing::PairingBatcher, pairing::{Engine, MillerLoopResult, MultiMillerLoop}, CurveAffine, }; @@ -84,13 +85,8 @@ impl<'params, E: MultiMillerLoop + Debug> SingleStrategy<'params, E> { impl< 'params, E: MultiMillerLoop + Debug, - V: Verifier< - 'params, - KZGCommitmentScheme, - MSMAccumulator = DualMSM<'params, E>, - Guard = GuardKZG<'params, E>, - >, - > VerificationStrategy<'params, KZGCommitmentScheme, V> for AccumulatorStrategy<'params, E> + V: Verifier<'params, E, MSMAccumulator = DualMSM<'params, E>, Guard = GuardKZG<'params, E>>, + > VerificationStrategy<'params, E, V> for AccumulatorStrategy<'params, E> where E::G1Affine: SerdeCurveAffine, E::G2Affine: SerdeCurveAffine, @@ -117,18 +113,18 @@ where fn finalize(self) -> bool { self.msm_accumulator.check() } + + fn merge_with_pairing_batcher(self, pairing_batcher: &mut PairingBatcher) { + let pair = self.msm_accumulator.into_pair(); + pairing_batcher.add_pairing(&pair); + } } impl< 'params, E: MultiMillerLoop + Debug, - V: Verifier< - 'params, - KZGCommitmentScheme, - MSMAccumulator = DualMSM<'params, E>, - Guard = GuardKZG<'params, E>, - >, - > VerificationStrategy<'params, KZGCommitmentScheme, V> for SingleStrategy<'params, E> + V: Verifier<'params, E, MSMAccumulator = DualMSM<'params, E>, Guard = GuardKZG<'params, E>>, + > VerificationStrategy<'params, E, V> for SingleStrategy<'params, E> where E::G1Affine: SerdeCurveAffine, E::G2Affine: SerdeCurveAffine, @@ -156,4 +152,8 @@ where fn finalize(self) -> bool { unreachable!(); } + + fn merge_with_pairing_batcher(self, _pairing_batcher: &mut PairingBatcher) { + unreachable!(); + } } diff --git a/halo2_proofs/src/poly/multiopen_test.rs b/halo2_proofs/src/poly/multiopen_test.rs index 1df8edaa03..b4ede69009 100644 --- a/halo2_proofs/src/poly/multiopen_test.rs +++ b/halo2_proofs/src/poly/multiopen_test.rs @@ -4,6 +4,7 @@ mod test { use crate::plonk::Error; use crate::poly::commitment::ParamsProver; use crate::poly::commitment::{Blind, ParamsVerifier, MSM}; + use crate::poly::kzg::commitment::KZGCommitmentScheme; use crate::poly::query::PolynomialPointer; use crate::poly::{ commitment::{CommitmentScheme, Params, Prover, Verifier}, @@ -18,46 +19,50 @@ mod test { }; use ff::Field; use group::{Curve, Group}; + use halo2curves::batch_pairing::PairingBatcher; + use halo2curves::pairing::{Engine, MillerLoopResult, MultiMillerLoop}; + use halo2curves::serde::SerdeObject; use halo2curves::CurveAffine; use rand_core::{OsRng, RngCore}; + use std::fmt::Debug; use std::io::{Read, Write}; - #[test] - fn test_roundtrip_ipa() { - use crate::poly::ipa::commitment::{IPACommitmentScheme, ParamsIPA}; - use crate::poly::ipa::multiopen::{ProverIPA, VerifierIPA}; - use crate::poly::ipa::strategy::AccumulatorStrategy; - use halo2curves::pasta::{Ep, EqAffine, Fp}; - - const K: u32 = 4; - - let params = ParamsIPA::::new(K); - - let proof = create_proof::< - IPACommitmentScheme, - ProverIPA<_>, - _, - Blake2bWrite<_, _, Challenge255<_>>, - >(¶ms); - - let verifier_params = params.verifier_params(); - - verify::< - IPACommitmentScheme, - VerifierIPA<_>, - _, - Blake2bRead<_, _, Challenge255<_>>, - AccumulatorStrategy<_>, - >(verifier_params, &proof[..], false); - - verify::< - IPACommitmentScheme, - VerifierIPA<_>, - _, - Blake2bRead<_, _, Challenge255<_>>, - AccumulatorStrategy<_>, - >(verifier_params, &proof[..], true); - } + // #[test] + // fn test_roundtrip_ipa() { + // use crate::poly::ipa::commitment::{IPACommitmentScheme, ParamsIPA}; + // use crate::poly::ipa::multiopen::{ProverIPA, VerifierIPA}; + // use crate::poly::ipa::strategy::AccumulatorStrategy; + // use halo2curves::pasta::{Ep, EqAffine, Fp}; + + // const K: u32 = 4; + + // let params = ParamsIPA::::new(K); + + // let proof = create_proof::< + // IPACommitmentScheme, + // ProverIPA<_>, + // _, + // Blake2bWrite<_, _, Challenge255<_>>, + // >(¶ms); + + // let verifier_params = params.verifier_params(); + + // verify::< + // IPACommitmentScheme, + // VerifierIPA<_>, + // _, + // Blake2bRead<_, _, Challenge255<_>>, + // AccumulatorStrategy<_>, + // >(verifier_params, &proof[..], false); + + // verify::< + // IPACommitmentScheme, + // VerifierIPA<_>, + // _, + // Blake2bRead<_, _, Challenge255<_>>, + // AccumulatorStrategy<_>, + // >(verifier_params, &proof[..], true); + // } #[test] fn test_roundtrip_gwc() { @@ -69,7 +74,7 @@ mod test { const K: u32 = 4; - let params = ParamsKZG::::new(K); + let params = ParamsKZG::::new(K, &mut OsRng); let proof = create_proof::<_, ProverGWC<_>, _, Blake2bWrite<_, _, Challenge255<_>>>(¶ms); @@ -83,7 +88,7 @@ mod test { ); verify::< - KZGCommitmentScheme, + Bn256, VerifierGWC<_>, _, Blake2bRead<_, _, Challenge255<_>>, @@ -101,19 +106,16 @@ mod test { const K: u32 = 4; - let params = ParamsKZG::::new(K); + let params = ParamsKZG::::new(K, &mut OsRng); - let proof = create_proof::< - KZGCommitmentScheme, - ProverSHPLONK<_>, - _, - Blake2bWrite<_, _, Challenge255<_>>, - >(¶ms); + let proof = create_proof::, _, Blake2bWrite<_, _, Challenge255<_>>>( + ¶ms, + ); let verifier_params = params.verifier_params(); verify::< - KZGCommitmentScheme, + Bn256, VerifierSHPLONK<_>, _, Blake2bRead<_, _, Challenge255<_>>, @@ -121,7 +123,7 @@ mod test { >(verifier_params, &proof[..], false); verify::< - KZGCommitmentScheme, + Bn256, VerifierSHPLONK<_>, _, Blake2bRead<_, _, Challenge255<_>>, @@ -132,21 +134,24 @@ mod test { fn verify< 'a, 'params, - Scheme: CommitmentScheme, - V: Verifier<'params, Scheme>, - E: EncodedChallenge, - T: TranscriptReadBuffer<&'a [u8], Scheme::Curve, E>, - Strategy: VerificationStrategy<'params, Scheme, V, Output = Strategy>, + E: MultiMillerLoop + Debug, + V: Verifier<'params, E>, + EC: EncodedChallenge, + T: TranscriptReadBuffer<&'a [u8], E::G1Affine, EC>, + Strategy: VerificationStrategy<'params, E, V, Output = Strategy>, >( - params: &'params Scheme::ParamsVerifier, + params: &'params as CommitmentScheme>::ParamsVerifier, proof: &'a [u8], should_fail: bool, - ) { + ) where + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + { let verifier = V::new(params); let mut transcript = T::init(proof); - let a = transcript.read_point().unwrap(); + let a: ::G1Affine = transcript.read_point().unwrap(); let b = transcript.read_point().unwrap(); let c = transcript.read_point().unwrap(); @@ -183,38 +188,56 @@ mod test { }) .unwrap(); - assert_eq!(strategy.finalize(), !should_fail); + let mut pairing_batcher = + PairingBatcher::::new(*transcript.squeeze_challenge_scalar::<()>()); + + strategy.merge_with_pairing_batcher(&mut pairing_batcher); + + let batched_tuples = pairing_batcher.finalize(); + let result = E::multi_miller_loop( + &batched_tuples + .iter() + .map(|(g1, g2)| (g1, g2)) + .collect::>(), + ); + + let pairing_result = result.final_exponentiation(); + assert_eq!(bool::from(pairing_result.is_identity()), !should_fail); } } fn create_proof< 'params, - Scheme: CommitmentScheme, - P: Prover<'params, Scheme>, - E: EncodedChallenge, - T: TranscriptWriterBuffer, Scheme::Curve, E>, + E: MultiMillerLoop + Debug, + P: Prover<'params, E>, + EC: EncodedChallenge, + T: TranscriptWriterBuffer, E::G1Affine, EC>, >( - params: &'params Scheme::ParamsProver, - ) -> Vec { + params: &'params as CommitmentScheme>::ParamsProver, + ) -> Vec + where + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, + { let domain = EvaluationDomain::new(1, params.k()); let mut ax = domain.empty_coeff(); for (i, a) in ax.iter_mut().enumerate() { - *a = <::Curve as CurveAffine>::ScalarExt::from( + *a = < as CommitmentScheme>::Curve as CurveAffine>::ScalarExt::from( 10 + i as u64, ); } let mut bx = domain.empty_coeff(); for (i, a) in bx.iter_mut().enumerate() { - *a = <::Curve as CurveAffine>::ScalarExt::from( + *a = < as CommitmentScheme>::Curve as CurveAffine>::ScalarExt::from( 100 + i as u64, ); } let mut cx = domain.empty_coeff(); for (i, a) in cx.iter_mut().enumerate() { - *a = <::Curve as CurveAffine>::ScalarExt::from( + *a = < as CommitmentScheme>::Curve as CurveAffine>::ScalarExt::from( 100 + i as u64, ); } diff --git a/halo2_proofs/src/poly/strategy.rs b/halo2_proofs/src/poly/strategy.rs index 36480d372f..9fc8a6eacd 100644 --- a/halo2_proofs/src/poly/strategy.rs +++ b/halo2_proofs/src/poly/strategy.rs @@ -1,11 +1,18 @@ +use std::fmt::Debug; + use halo2curves::CurveAffine; use rand_core::RngCore; -use super::commitment::{CommitmentScheme, Verifier, MSM}; +use super::{ + commitment::{CommitmentScheme, Verifier, MSM}, + kzg::commitment::KZGCommitmentScheme, +}; use crate::{ plonk::Error, transcript::{EncodedChallenge, TranscriptRead}, }; +use halo2curves::serde::SerdeObject; +use halo2curves::{batch_pairing::PairingBatcher, pairing::MultiMillerLoop}; /// Guards is unfinished verification result. Implement this to construct various /// verification strategies such as aggregation and recursion. @@ -15,12 +22,16 @@ pub trait Guard { } /// Trait representing a strategy for verifying Halo 2 proofs. -pub trait VerificationStrategy<'params, Scheme: CommitmentScheme, V: Verifier<'params, Scheme>> { +pub trait VerificationStrategy<'params, E: MultiMillerLoop + Debug, V: Verifier<'params, E>> +where + E::G1Affine: SerdeObject, + E::G2Affine: SerdeObject, +{ /// The output type of this verification strategy after processing a proof. type Output; /// Creates new verification strategy instance - fn new(params: &'params Scheme::ParamsVerifier) -> Self; + fn new(params: &'params as CommitmentScheme>::ParamsVerifier) -> Self; /// Obtains an MSM from the verifier strategy and yields back the strategy's /// output. @@ -34,4 +45,7 @@ pub trait VerificationStrategy<'params, Scheme: CommitmentScheme, V: Verifier<'p /// Returns `false` if *some* proof was invalid. If the caller needs to identify /// specific failing proofs, it must re-process the proofs separately. fn finalize(self) -> bool; + + /// Merges the pairing with a pairing batcher. + fn merge_with_pairing_batcher(self, pairing_batcher: &mut PairingBatcher); } diff --git a/halo2_proofs/tests/plonk_api.rs b/halo2_proofs/tests/plonk_api.rs index af63b5fb30..900136dd94 100644 --- a/halo2_proofs/tests/plonk_api.rs +++ b/halo2_proofs/tests/plonk_api.rs @@ -1,1027 +1,1026 @@ -#![allow(clippy::many_single_char_names)] -#![allow(clippy::op_ref)] - -use assert_matches::assert_matches; -use halo2_proofs::arithmetic::{Field, FieldExt}; -use halo2_proofs::circuit::{Cell, Layouter, SimpleFloorPlanner, Value}; -use halo2_proofs::dev::MockProver; -use halo2_proofs::plonk::{ - create_proof as create_plonk_proof, keygen_pk, keygen_vk, verify_proof as verify_plonk_proof, - Advice, Assigned, Circuit, Column, ConstraintSystem, Error, Fixed, ProvingKey, TableColumn, - VerifyingKey, -}; -use halo2_proofs::poly::commitment::{CommitmentScheme, ParamsProver, Prover, Verifier}; -use halo2_proofs::poly::Rotation; -use halo2_proofs::poly::VerificationStrategy; -use halo2_proofs::transcript::{ - Blake2bRead, Blake2bWrite, Challenge255, EncodedChallenge, TranscriptReadBuffer, - TranscriptWriterBuffer, -}; -use rand_core::{OsRng, RngCore}; -use std::marker::PhantomData; - -#[test] -fn plonk_api() { - const K: u32 = 5; - - /// This represents an advice column at a certain row in the ConstraintSystem - #[derive(Copy, Clone, Debug)] - pub struct Variable(Column, usize); - - #[derive(Clone)] - struct PlonkConfig { - a: Column, - b: Column, - c: Column, - d: Column, - e: Column, - - sa: Column, - sb: Column, - sc: Column, - sm: Column, - sp: Column, - sl: TableColumn, - } - - #[allow(clippy::type_complexity)] - trait StandardCs { - fn raw_multiply( - &self, - layouter: &mut impl Layouter, - f: F, - ) -> Result<(Cell, Cell, Cell), Error> - where - F: FnMut() -> Value<(Assigned, Assigned, Assigned)>; - fn raw_add( - &self, - layouter: &mut impl Layouter, - f: F, - ) -> Result<(Cell, Cell, Cell), Error> - where - F: FnMut() -> Value<(Assigned, Assigned, Assigned)>; - fn copy(&self, layouter: &mut impl Layouter, a: Cell, b: Cell) -> Result<(), Error>; - fn public_input(&self, layouter: &mut impl Layouter, f: F) -> Result - where - F: FnMut() -> Value; - fn lookup_table( - &self, - layouter: &mut impl Layouter, - values: &[FF], - ) -> Result<(), Error>; - } - - #[derive(Clone)] - struct MyCircuit { - a: Value, - lookup_table: Vec, - } - - struct StandardPlonk { - config: PlonkConfig, - _marker: PhantomData, - } - - impl StandardPlonk { - fn new(config: PlonkConfig) -> Self { - StandardPlonk { - config, - _marker: PhantomData, - } - } - } - - impl StandardCs for StandardPlonk { - fn raw_multiply( - &self, - layouter: &mut impl Layouter, - mut f: F, - ) -> Result<(Cell, Cell, Cell), Error> - where - F: FnMut() -> Value<(Assigned, Assigned, Assigned)>, - { - layouter.assign_region( - || "raw_multiply", - |mut region| { - let mut value = None; - let lhs = region.assign_advice( - || "lhs", - self.config.a, - 0, - || { - value = Some(f()); - value.unwrap().map(|v| v.0) - }, - )?; - region.assign_advice( - || "lhs^4", - self.config.d, - 0, - || value.unwrap().map(|v| v.0).square().square(), - )?; - let rhs = region.assign_advice( - || "rhs", - self.config.b, - 0, - || value.unwrap().map(|v| v.1), - )?; - region.assign_advice( - || "rhs^4", - self.config.e, - 0, - || value.unwrap().map(|v| v.1).square().square(), - )?; - let out = region.assign_advice( - || "out", - self.config.c, - 0, - || value.unwrap().map(|v| v.2), - )?; - - region.assign_fixed(|| "a", self.config.sa, 0, || Value::known(FF::zero()))?; - region.assign_fixed(|| "b", self.config.sb, 0, || Value::known(FF::zero()))?; - region.assign_fixed(|| "c", self.config.sc, 0, || Value::known(FF::one()))?; - region.assign_fixed( - || "a * b", - self.config.sm, - 0, - || Value::known(FF::one()), - )?; - Ok((lhs.cell(), rhs.cell(), out.cell())) - }, - ) - } - fn raw_add( - &self, - layouter: &mut impl Layouter, - mut f: F, - ) -> Result<(Cell, Cell, Cell), Error> - where - F: FnMut() -> Value<(Assigned, Assigned, Assigned)>, - { - layouter.assign_region( - || "raw_add", - |mut region| { - let mut value = None; - let lhs = region.assign_advice( - || "lhs", - self.config.a, - 0, - || { - value = Some(f()); - value.unwrap().map(|v| v.0) - }, - )?; - region.assign_advice( - || "lhs^4", - self.config.d, - 0, - || value.unwrap().map(|v| v.0).square().square(), - )?; - let rhs = region.assign_advice( - || "rhs", - self.config.b, - 0, - || value.unwrap().map(|v| v.1), - )?; - region.assign_advice( - || "rhs^4", - self.config.e, - 0, - || value.unwrap().map(|v| v.1).square().square(), - )?; - let out = region.assign_advice( - || "out", - self.config.c, - 0, - || value.unwrap().map(|v| v.2), - )?; - - region.assign_fixed(|| "a", self.config.sa, 0, || Value::known(FF::one()))?; - region.assign_fixed(|| "b", self.config.sb, 0, || Value::known(FF::one()))?; - region.assign_fixed(|| "c", self.config.sc, 0, || Value::known(FF::one()))?; - region.assign_fixed( - || "a * b", - self.config.sm, - 0, - || Value::known(FF::zero()), - )?; - Ok((lhs.cell(), rhs.cell(), out.cell())) - }, - ) - } - fn copy( - &self, - layouter: &mut impl Layouter, - left: Cell, - right: Cell, - ) -> Result<(), Error> { - layouter.assign_region( - || "copy", - |mut region| { - region.constrain_equal(left, right)?; - region.constrain_equal(left, right) - }, - ) - } - fn public_input(&self, layouter: &mut impl Layouter, mut f: F) -> Result - where - F: FnMut() -> Value, - { - layouter.assign_region( - || "public_input", - |mut region| { - let value = region.assign_advice(|| "value", self.config.a, 0, &mut f)?; - region.assign_fixed( - || "public", - self.config.sp, - 0, - || Value::known(FF::one()), - )?; - - Ok(value.cell()) - }, - ) - } - fn lookup_table( - &self, - layouter: &mut impl Layouter, - values: &[FF], - ) -> Result<(), Error> { - layouter.assign_table( - || "", - |mut table| { - for (index, &value) in values.iter().enumerate() { - table.assign_cell( - || "table col", - self.config.sl, - index, - || Value::known(value), - )?; - } - Ok(()) - }, - )?; - Ok(()) - } - } - - impl Circuit for MyCircuit { - type Config = PlonkConfig; - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self { - a: Value::unknown(), - lookup_table: self.lookup_table.clone(), - } - } - - fn configure(meta: &mut ConstraintSystem) -> PlonkConfig { - let e = meta.advice_column(); - let a = meta.advice_column(); - let b = meta.advice_column(); - let sf = meta.fixed_column(); - let c = meta.advice_column(); - let d = meta.advice_column(); - let p = meta.instance_column(); - - meta.enable_equality(a); - meta.enable_equality(b); - meta.enable_equality(c); - - let sm = meta.fixed_column(); - let sa = meta.fixed_column(); - let sb = meta.fixed_column(); - let sc = meta.fixed_column(); - let sp = meta.fixed_column(); - let sl = meta.lookup_table_column(); - - /* - * A B ... sl - * [ - * instance 0 ... 0 - * a a ... 0 - * a a^2 ... 0 - * a a ... 0 - * a a^2 ... 0 - * ... ... ... ... - * ... ... ... instance - * ... ... ... a - * ... ... ... a - * ... ... ... 0 - * ] - */ - - meta.lookup("lookup", |meta| { - let a_ = meta.query_any(a, Rotation::cur()); - vec![(a_, sl)] - }); - - meta.create_gate("Combined add-mult", |meta| { - let d = meta.query_advice(d, Rotation::next()); - let a = meta.query_advice(a, Rotation::cur()); - let sf = meta.query_fixed(sf, Rotation::cur()); - let e = meta.query_advice(e, Rotation::prev()); - let b = meta.query_advice(b, Rotation::cur()); - let c = meta.query_advice(c, Rotation::cur()); - - let sa = meta.query_fixed(sa, Rotation::cur()); - let sb = meta.query_fixed(sb, Rotation::cur()); - let sc = meta.query_fixed(sc, Rotation::cur()); - let sm = meta.query_fixed(sm, Rotation::cur()); - - vec![a.clone() * sa + b.clone() * sb + a * b * sm - (c * sc) + sf * (d * e)] - }); - - meta.create_gate("Public input", |meta| { - let a = meta.query_advice(a, Rotation::cur()); - let p = meta.query_instance(p, Rotation::cur()); - let sp = meta.query_fixed(sp, Rotation::cur()); - - vec![sp * (a - p)] - }); - - meta.enable_equality(sf); - meta.enable_equality(e); - meta.enable_equality(d); - meta.enable_equality(p); - meta.enable_equality(sm); - meta.enable_equality(sa); - meta.enable_equality(sb); - meta.enable_equality(sc); - meta.enable_equality(sp); - - PlonkConfig { - a, - b, - c, - d, - e, - sa, - sb, - sc, - sm, - sp, - sl, - } - } - - fn synthesize( - &self, - config: PlonkConfig, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - let cs = StandardPlonk::new(config); - - let _ = cs.public_input(&mut layouter, || Value::known(F::one() + F::one()))?; - - for _ in 0..10 { - let a: Value> = self.a.into(); - let mut a_squared = Value::unknown(); - let (a0, _, c0) = cs.raw_multiply(&mut layouter, || { - a_squared = a.square(); - a.zip(a_squared).map(|(a, a_squared)| (a, a, a_squared)) - })?; - let (a1, b1, _) = cs.raw_add(&mut layouter, || { - let fin = a_squared + a; - a.zip(a_squared) - .zip(fin) - .map(|((a, a_squared), fin)| (a, a_squared, fin)) - })?; - cs.copy(&mut layouter, a0, a1)?; - cs.copy(&mut layouter, b1, c0)?; - } - - cs.lookup_table(&mut layouter, &self.lookup_table)?; - - Ok(()) - } - } - - macro_rules! common { - ($scheme:ident) => {{ - let a = <$scheme as CommitmentScheme>::Scalar::from(2834758237) - * <$scheme as CommitmentScheme>::Scalar::ZETA; - let instance = <$scheme as CommitmentScheme>::Scalar::one() - + <$scheme as CommitmentScheme>::Scalar::one(); - let lookup_table = vec![ - instance, - a, - a, - <$scheme as CommitmentScheme>::Scalar::zero(), - ]; - (a, instance, lookup_table) - }}; - } - - macro_rules! bad_keys { - ($scheme:ident) => {{ - let (_, _, lookup_table) = common!($scheme); - let empty_circuit: MyCircuit<<$scheme as CommitmentScheme>::Scalar> = MyCircuit { - a: Value::unknown(), - lookup_table: lookup_table.clone(), - }; - - // Check that we get an error if we try to initialize the proving key with a value of - // k that is too small for the minimum required number of rows. - let much_too_small_params= <$scheme as CommitmentScheme>::ParamsProver::new(1); - assert_matches!( - keygen_vk(&much_too_small_params, &empty_circuit), - Err(Error::NotEnoughRowsAvailable { - current_k, - }) if current_k == 1 - ); - - // Check that we get an error if we try to initialize the proving key with a value of - // k that is too small for the number of rows the circuit uses. - let slightly_too_small_params = <$scheme as CommitmentScheme>::ParamsProver::new(K-1); - assert_matches!( - keygen_vk(&slightly_too_small_params, &empty_circuit), - Err(Error::NotEnoughRowsAvailable { - current_k, - }) if current_k == K - 1 - ); - }}; - } - - fn keygen( - params: &Scheme::ParamsProver, - ) -> ProvingKey { - let (_, _, lookup_table) = common!(Scheme); - let empty_circuit: MyCircuit = MyCircuit { - a: Value::unknown(), - lookup_table, - }; - - // Initialize the proving key - let vk = keygen_vk(params, &empty_circuit).expect("keygen_vk should not fail"); - - keygen_pk(params, vk, &empty_circuit).expect("keygen_pk should not fail") - } - - fn create_proof< - 'params, - Scheme: CommitmentScheme, - P: Prover<'params, Scheme>, - E: EncodedChallenge, - R: RngCore, - T: TranscriptWriterBuffer, Scheme::Curve, E>, - >( - rng: R, - params: &'params Scheme::ParamsProver, - pk: &ProvingKey, - ) -> Vec { - let (a, instance, lookup_table) = common!(Scheme); - - let circuit: MyCircuit = MyCircuit { - a: Value::known(a), - lookup_table, - }; - - let mut transcript = T::init(vec![]); - - create_plonk_proof::( - params, - pk, - &[circuit.clone(), circuit.clone()], - &[&[&[instance]], &[&[instance]]], - rng, - &mut transcript, - ) - .expect("proof generation should not fail"); - - // Check this circuit is satisfied. - let prover = match MockProver::run(K, &circuit, vec![vec![instance]]) { - Ok(prover) => prover, - Err(e) => panic!("{:?}", e), - }; - assert_eq!(prover.verify(), Ok(())); - - transcript.finalize() - } - - fn verify_proof< - 'a, - 'params, - Scheme: CommitmentScheme, - V: Verifier<'params, Scheme>, - E: EncodedChallenge, - T: TranscriptReadBuffer<&'a [u8], Scheme::Curve, E>, - Strategy: VerificationStrategy<'params, Scheme, V, Output = Strategy>, - >( - params_verifier: &'params Scheme::ParamsVerifier, - vk: &VerifyingKey, - proof: &'a [u8], - ) { - let (_, instance, _) = common!(Scheme); - let pubinputs = vec![instance]; - - let mut transcript = T::init(proof); - - let strategy = Strategy::new(params_verifier); - let strategy = verify_plonk_proof( - params_verifier, - vk, - strategy, - &[&[&pubinputs[..]], &[&pubinputs[..]]], - &mut transcript, - ) - .unwrap(); - - assert!(strategy.finalize()); - } - - fn test_plonk_api_gwc() { - use halo2_proofs::poly::kzg::commitment::{KZGCommitmentScheme, ParamsKZG}; - use halo2_proofs::poly::kzg::multiopen::{ProverGWC, VerifierGWC}; - use halo2_proofs::poly::kzg::strategy::AccumulatorStrategy; - use halo2curves::bn256::Bn256; - - type Scheme = KZGCommitmentScheme; - bad_keys!(Scheme); - - let params = ParamsKZG::::new(K); - let rng = OsRng; - - let pk = keygen::>(¶ms); - - let proof = create_proof::<_, ProverGWC<_>, _, _, Blake2bWrite<_, _, Challenge255<_>>>( - rng, ¶ms, &pk, - ); - - let verifier_params = params.verifier_params(); - - verify_proof::< - _, - VerifierGWC<_>, - _, - Blake2bRead<_, _, Challenge255<_>>, - AccumulatorStrategy<_>, - >(verifier_params, pk.get_vk(), &proof[..]); - } - - fn test_plonk_api_shplonk() { - use halo2_proofs::poly::kzg::commitment::{KZGCommitmentScheme, ParamsKZG}; - use halo2_proofs::poly::kzg::multiopen::{ProverSHPLONK, VerifierSHPLONK}; - use halo2_proofs::poly::kzg::strategy::AccumulatorStrategy; - use halo2curves::bn256::Bn256; - - type Scheme = KZGCommitmentScheme; - bad_keys!(Scheme); - - let params = ParamsKZG::::new(K); - let rng = OsRng; - - let pk = keygen::>(¶ms); - - let proof = create_proof::<_, ProverSHPLONK<_>, _, _, Blake2bWrite<_, _, Challenge255<_>>>( - rng, ¶ms, &pk, - ); - - let verifier_params = params.verifier_params(); - - verify_proof::< - _, - VerifierSHPLONK<_>, - _, - Blake2bRead<_, _, Challenge255<_>>, - AccumulatorStrategy<_>, - >(verifier_params, pk.get_vk(), &proof[..]); - } - - fn test_plonk_api_ipa() { - use halo2_proofs::poly::ipa::commitment::{IPACommitmentScheme, ParamsIPA}; - use halo2_proofs::poly::ipa::multiopen::{ProverIPA, VerifierIPA}; - use halo2_proofs::poly::ipa::strategy::AccumulatorStrategy; - use halo2curves::pasta::EqAffine; - - type Scheme = IPACommitmentScheme; - bad_keys!(Scheme); - - let params = ParamsIPA::::new(K); - let rng = OsRng; - - let pk = keygen::>(¶ms); - - let proof = create_proof::<_, ProverIPA<_>, _, _, Blake2bWrite<_, _, Challenge255<_>>>( - rng, ¶ms, &pk, - ); - - let verifier_params = params.verifier_params(); - - verify_proof::< - _, - VerifierIPA<_>, - _, - Blake2bRead<_, _, Challenge255<_>>, - AccumulatorStrategy<_>, - >(verifier_params, pk.get_vk(), &proof[..]); - - // Check that the verification key has not changed unexpectedly - { - //panic!("{:#?}", pk.get_vk().pinned()); - assert_eq!( - format!("{:#?}", pk.get_vk().pinned()), - r#####"PinnedVerificationKey { - base_modulus: "0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001", - scalar_modulus: "0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001", - domain: PinnedEvaluationDomain { - k: 5, - extended_k: 7, - omega: 0x0cc3380dc616f2e1daf29ad1560833ed3baea3393eceb7bc8fa36376929b78cc, - }, - cs: PinnedConstraintSystem { - num_fixed_columns: 7, - num_advice_columns: 5, - num_instance_columns: 1, - num_selectors: 0, - gates: [ - Sum( - Sum( - Sum( - Sum( - Product( - Advice { - query_index: 0, - column_index: 1, - rotation: Rotation( - 0, - ), - }, - Fixed { - query_index: 2, - column_index: 2, - rotation: Rotation( - 0, - ), - }, - ), - Product( - Advice { - query_index: 1, - column_index: 2, - rotation: Rotation( - 0, - ), - }, - Fixed { - query_index: 3, - column_index: 3, - rotation: Rotation( - 0, - ), - }, - ), - ), - Product( - Product( - Advice { - query_index: 0, - column_index: 1, - rotation: Rotation( - 0, - ), - }, - Advice { - query_index: 1, - column_index: 2, - rotation: Rotation( - 0, - ), - }, - ), - Fixed { - query_index: 5, - column_index: 1, - rotation: Rotation( - 0, - ), - }, - ), - ), - Negated( - Product( - Advice { - query_index: 2, - column_index: 3, - rotation: Rotation( - 0, - ), - }, - Fixed { - query_index: 4, - column_index: 4, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Product( - Fixed { - query_index: 1, - column_index: 0, - rotation: Rotation( - 0, - ), - }, - Product( - Advice { - query_index: 3, - column_index: 4, - rotation: Rotation( - 1, - ), - }, - Advice { - query_index: 4, - column_index: 0, - rotation: Rotation( - -1, - ), - }, - ), - ), - ), - Product( - Fixed { - query_index: 6, - column_index: 5, - rotation: Rotation( - 0, - ), - }, - Sum( - Advice { - query_index: 0, - column_index: 1, - rotation: Rotation( - 0, - ), - }, - Negated( - Instance { - query_index: 0, - column_index: 0, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - ], - advice_queries: [ - ( - Column { - index: 1, - column_type: Advice, - }, - Rotation( - 0, - ), - ), - ( - Column { - index: 2, - column_type: Advice, - }, - Rotation( - 0, - ), - ), - ( - Column { - index: 3, - column_type: Advice, - }, - Rotation( - 0, - ), - ), - ( - Column { - index: 4, - column_type: Advice, - }, - Rotation( - 1, - ), - ), - ( - Column { - index: 0, - column_type: Advice, - }, - Rotation( - -1, - ), - ), - ( - Column { - index: 0, - column_type: Advice, - }, - Rotation( - 0, - ), - ), - ( - Column { - index: 4, - column_type: Advice, - }, - Rotation( - 0, - ), - ), - ], - instance_queries: [ - ( - Column { - index: 0, - column_type: Instance, - }, - Rotation( - 0, - ), - ), - ], - fixed_queries: [ - ( - Column { - index: 6, - column_type: Fixed, - }, - Rotation( - 0, - ), - ), - ( - Column { - index: 0, - column_type: Fixed, - }, - Rotation( - 0, - ), - ), - ( - Column { - index: 2, - column_type: Fixed, - }, - Rotation( - 0, - ), - ), - ( - Column { - index: 3, - column_type: Fixed, - }, - Rotation( - 0, - ), - ), - ( - Column { - index: 4, - column_type: Fixed, - }, - Rotation( - 0, - ), - ), - ( - Column { - index: 1, - column_type: Fixed, - }, - Rotation( - 0, - ), - ), - ( - Column { - index: 5, - column_type: Fixed, - }, - Rotation( - 0, - ), - ), - ], - permutation: Argument { - columns: [ - Column { - index: 1, - column_type: Advice, - }, - Column { - index: 2, - column_type: Advice, - }, - Column { - index: 3, - column_type: Advice, - }, - Column { - index: 0, - column_type: Fixed, - }, - Column { - index: 0, - column_type: Advice, - }, - Column { - index: 4, - column_type: Advice, - }, - Column { - index: 0, - column_type: Instance, - }, - Column { - index: 1, - column_type: Fixed, - }, - Column { - index: 2, - column_type: Fixed, - }, - Column { - index: 3, - column_type: Fixed, - }, - Column { - index: 4, - column_type: Fixed, - }, - Column { - index: 5, - column_type: Fixed, - }, - ], - }, - lookups: [ - Argument { - input_expressions: [ - Advice { - query_index: 0, - column_index: 1, - rotation: Rotation( - 0, - ), - }, - ], - table_expressions: [ - Fixed { - query_index: 0, - column_index: 6, - rotation: Rotation( - 0, - ), - }, - ], - }, - ], - constants: [], - minimum_degree: None, - }, - fixed_commitments: [ - (0x2bbc94ef7b22aebef24f9a4b0cc1831882548b605171366017d45c3e6fd92075, 0x082b801a6e176239943bfb759fb02138f47a5c8cc4aa7fa0af559fde4e3abd97), - (0x2bf5082b105b2156ed0e9c5b8e42bf2a240b058f74a464d080e9585274dd1e84, 0x222ad83cee7777e7a160585e212140e5e770dd8d1df788d869b5ee483a5864fb), - (0x374a656456a0aae7429b23336f825752b575dd5a44290ff614946ee59d6a20c0, 0x054491e187e6e3460e7601fb54ae10836d34d420026f96316f0c5c62f86db9b8), - (0x374a656456a0aae7429b23336f825752b575dd5a44290ff614946ee59d6a20c0, 0x054491e187e6e3460e7601fb54ae10836d34d420026f96316f0c5c62f86db9b8), - (0x02e62cd68370b13711139a08cbcdd889e800a272b9ea10acc90880fff9d89199, 0x1a96c468cb0ce77065d3a58f1e55fea9b72d15e44c01bba1e110bd0cbc6e9bc6), - (0x224ef42758215157d3ee48fb8d769da5bddd35e5929a90a4a89736f5c4b5ae9b, 0x11bc3a1e08eb320cde764f1492ecef956d71e996e2165f7a9a30ad2febb511c1), - (0x2d5415bf917fcac32bfb705f8ca35cb12d9bad52aa33ccca747350f9235d3a18, 0x2b2921f815fad504052512743963ef20ed5b401d20627793b006413e73fe4dd4), - ], - permutation: VerifyingKey { - commitments: [ - (0x1347b4b385837977a96b87f199c6a9a81520015539d1e8fa79429bb4ca229a00, 0x2168e404cabef513654d6ff516cde73f0ba87e3dc84e4b940ed675b5f66f3884), - (0x0e6d69cd2455ec43be640f6397ed65c9e51b1d8c0fd2216339314ff37ade122a, 0x222ed6dc8cfc9ea26dcc10b9d4add791ada60f2b5a63ee1e4635f88aa0c96654), - (0x13c447846f48c41a5e0675ccf88ebc0cdef2c96c51446d037acb866d24255785, 0x1f0b5414fc5e8219dbfab996eed6129d831488b2386a8b1a63663938903bd63a), - (0x1aae6470aa662b8fda003894ddef5fedd03af318b3231683039d2fac9cab05b9, 0x08832d91ae69e99cd07d096c7a4a284a69e6a16227cbb07932a0cdc56914f3a6), - (0x0850521b0f8ac7dd0550fe3e25c840837076e9635067ed623b81d5cbac5944d9, 0x0c25d65d1038d0a92c72e5fccd96c1caf07801c3c8233290bb292e0c38c256fa), - (0x12febcf696badd970750eabf75dd3ced4c2f54f93519bcee23849025177d2014, 0x0a05ab3cd42c9fbcc1bbfcf9269951640cc9920761c87cf8e211ba73c8d9f90f), - (0x053904bdde8cfead3b517bb4f6ded3e699f8b94ca6156a9dd2f92a2a05a7ec5a, 0x16753ff97c0d82ff586bb7a07bf7f27a92df90b3617fa5e75d4f55c3b0ef8711), - (0x3804548f6816452747a5b542fa5656353fc989db40d69e9e27d6f973b5deebb0, 0x389a44d5037866dd83993af75831a5f90a18ad5244255aa5bd2c922cc5853055), - (0x003a9f9ca71c7c0b832c802220915f6fc8d840162bdde6b0ea05d25fb95559e3, 0x091247ca19d6b73887cd7f68908cbf0db0b47459b7c82276bbdb8a1c937e2438), - (0x3eaa38689d9e391c8a8fafab9568f20c45816321d38f309d4cc37f4b1601af72, 0x247f8270a462ea88450221a56aa6b55d2bc352b80b03501e99ea983251ceea13), - (0x394437571f9de32dccdc546fd4737772d8d92593c85438aa3473243997d5acc8, 0x14924ec6e3174f1fab7f0ce7070c22f04bbd0a0ecebdfc5c94be857f25493e95), - (0x3d907e0591343bd285c2c846f3e871a6ac70d80ec29e9500b8cb57f544e60202, 0x1034e48df35830244cabea076be8a16d67d7896e27c6ac22b285d017105da9c3), - ], - }, -}"##### - ); - } - } - - test_plonk_api_ipa(); - test_plonk_api_gwc(); - test_plonk_api_shplonk(); -} +// #![allow(clippy::many_single_char_names)] +// #![allow(clippy::op_ref)] + +// use assert_matches::assert_matches; +// use halo2_proofs::arithmetic::{Field, FieldExt}; +// use halo2_proofs::circuit::{Cell, Layouter, SimpleFloorPlanner, Value}; +// use halo2_proofs::dev::MockProver; +// use halo2_proofs::plonk::{ +// create_proof as create_plonk_proof, keygen_pk, keygen_vk, verify_proof as verify_plonk_proof, +// Advice, Assigned, Circuit, Column, ConstraintSystem, Error, Fixed, ProvingKey, TableColumn, +// VerifyingKey, +// }; +// use halo2_proofs::poly::commitment::{CommitmentScheme, ParamsProver, Prover, Verifier}; +// use halo2_proofs::poly::Rotation; +// use halo2_proofs::poly::VerificationStrategy; +// use halo2_proofs::transcript::{ +// Blake2bRead, Blake2bWrite, Challenge255, EncodedChallenge, TranscriptReadBuffer, +// TranscriptWriterBuffer, +// }; +// use rand_core::{OsRng, RngCore}; +// use std::marker::PhantomData; + +// #[test] +// fn plonk_api() { +// const K: u32 = 5; + +// /// This represents an advice column at a certain row in the ConstraintSystem +// #[derive(Copy, Clone, Debug)] +// pub struct Variable(Column, usize); + +// #[derive(Clone)] +// struct PlonkConfig { +// a: Column, +// b: Column, +// c: Column, +// d: Column, +// e: Column, + +// sa: Column, +// sb: Column, +// sc: Column, +// sm: Column, +// sp: Column, +// sl: TableColumn, +// } + +// #[allow(clippy::type_complexity)] +// trait StandardCs { +// fn raw_multiply( +// &self, +// layouter: &mut impl Layouter, +// f: F, +// ) -> Result<(Cell, Cell, Cell), Error> +// where +// F: FnMut() -> Value<(Assigned, Assigned, Assigned)>; +// fn raw_add( +// &self, +// layouter: &mut impl Layouter, +// f: F, +// ) -> Result<(Cell, Cell, Cell), Error> +// where +// F: FnMut() -> Value<(Assigned, Assigned, Assigned)>; +// fn copy(&self, layouter: &mut impl Layouter, a: Cell, b: Cell) -> Result<(), Error>; +// fn public_input(&self, layouter: &mut impl Layouter, f: F) -> Result +// where +// F: FnMut() -> Value; +// fn lookup_table( +// &self, +// layouter: &mut impl Layouter, +// values: &[FF], +// ) -> Result<(), Error>; +// } + +// #[derive(Clone)] +// struct MyCircuit { +// a: Value, +// lookup_table: Vec, +// } + +// struct StandardPlonk { +// config: PlonkConfig, +// _marker: PhantomData, +// } + +// impl StandardPlonk { +// fn new(config: PlonkConfig) -> Self { +// StandardPlonk { +// config, +// _marker: PhantomData, +// } +// } +// } + +// impl StandardCs for StandardPlonk { +// fn raw_multiply( +// &self, +// layouter: &mut impl Layouter, +// mut f: F, +// ) -> Result<(Cell, Cell, Cell), Error> +// where +// F: FnMut() -> Value<(Assigned, Assigned, Assigned)>, +// { +// layouter.assign_region( +// || "raw_multiply", +// |mut region| { +// let mut value = None; +// let lhs = region.assign_advice( +// || "lhs", +// self.config.a, +// 0, +// || { +// value = Some(f()); +// value.unwrap().map(|v| v.0) +// }, +// )?; +// region.assign_advice( +// || "lhs^4", +// self.config.d, +// 0, +// || value.unwrap().map(|v| v.0).square().square(), +// )?; +// let rhs = region.assign_advice( +// || "rhs", +// self.config.b, +// 0, +// || value.unwrap().map(|v| v.1), +// )?; +// region.assign_advice( +// || "rhs^4", +// self.config.e, +// 0, +// || value.unwrap().map(|v| v.1).square().square(), +// )?; +// let out = region.assign_advice( +// || "out", +// self.config.c, +// 0, +// || value.unwrap().map(|v| v.2), +// )?; + +// region.assign_fixed(|| "a", self.config.sa, 0, || Value::known(FF::zero()))?; +// region.assign_fixed(|| "b", self.config.sb, 0, || Value::known(FF::zero()))?; +// region.assign_fixed(|| "c", self.config.sc, 0, || Value::known(FF::one()))?; +// region.assign_fixed( +// || "a * b", +// self.config.sm, +// 0, +// || Value::known(FF::one()), +// )?; +// Ok((lhs.cell(), rhs.cell(), out.cell())) +// }, +// ) +// } +// fn raw_add( +// &self, +// layouter: &mut impl Layouter, +// mut f: F, +// ) -> Result<(Cell, Cell, Cell), Error> +// where +// F: FnMut() -> Value<(Assigned, Assigned, Assigned)>, +// { +// layouter.assign_region( +// || "raw_add", +// |mut region| { +// let mut value = None; +// let lhs = region.assign_advice( +// || "lhs", +// self.config.a, +// 0, +// || { +// value = Some(f()); +// value.unwrap().map(|v| v.0) +// }, +// )?; +// region.assign_advice( +// || "lhs^4", +// self.config.d, +// 0, +// || value.unwrap().map(|v| v.0).square().square(), +// )?; +// let rhs = region.assign_advice( +// || "rhs", +// self.config.b, +// 0, +// || value.unwrap().map(|v| v.1), +// )?; +// region.assign_advice( +// || "rhs^4", +// self.config.e, +// 0, +// || value.unwrap().map(|v| v.1).square().square(), +// )?; +// let out = region.assign_advice( +// || "out", +// self.config.c, +// 0, +// || value.unwrap().map(|v| v.2), +// )?; + +// region.assign_fixed(|| "a", self.config.sa, 0, || Value::known(FF::one()))?; +// region.assign_fixed(|| "b", self.config.sb, 0, || Value::known(FF::one()))?; +// region.assign_fixed(|| "c", self.config.sc, 0, || Value::known(FF::one()))?; +// region.assign_fixed( +// || "a * b", +// self.config.sm, +// 0, +// || Value::known(FF::zero()), +// )?; +// Ok((lhs.cell(), rhs.cell(), out.cell())) +// }, +// ) +// } +// fn copy( +// &self, +// layouter: &mut impl Layouter, +// left: Cell, +// right: Cell, +// ) -> Result<(), Error> { +// layouter.assign_region( +// || "copy", +// |mut region| { +// region.constrain_equal(left, right)?; +// region.constrain_equal(left, right) +// }, +// ) +// } +// fn public_input(&self, layouter: &mut impl Layouter, mut f: F) -> Result +// where +// F: FnMut() -> Value, +// { +// layouter.assign_region( +// || "public_input", +// |mut region| { +// let value = region.assign_advice(|| "value", self.config.a, 0, &mut f)?; +// region.assign_fixed( +// || "public", +// self.config.sp, +// 0, +// || Value::known(FF::one()), +// )?; + +// Ok(value.cell()) +// }, +// ) +// } +// fn lookup_table( +// &self, +// layouter: &mut impl Layouter, +// values: &[FF], +// ) -> Result<(), Error> { +// layouter.assign_table( +// || "", +// |mut table| { +// for (index, &value) in values.iter().enumerate() { +// table.assign_cell( +// || "table col", +// self.config.sl, +// index, +// || Value::known(value), +// )?; +// } +// Ok(()) +// }, +// )?; +// Ok(()) +// } +// } + +// impl Circuit for MyCircuit { +// type Config = PlonkConfig; +// type FloorPlanner = SimpleFloorPlanner; + +// fn without_witnesses(&self) -> Self { +// Self { +// a: Value::unknown(), +// lookup_table: self.lookup_table.clone(), +// } +// } + +// fn configure(meta: &mut ConstraintSystem) -> PlonkConfig { +// let e = meta.advice_column(); +// let a = meta.advice_column(); +// let b = meta.advice_column(); +// let sf = meta.fixed_column(); +// let c = meta.advice_column(); +// let d = meta.advice_column(); +// let p = meta.instance_column(); + +// meta.enable_equality(a); +// meta.enable_equality(b); +// meta.enable_equality(c); + +// let sm = meta.fixed_column(); +// let sa = meta.fixed_column(); +// let sb = meta.fixed_column(); +// let sc = meta.fixed_column(); +// let sp = meta.fixed_column(); +// let sl = meta.lookup_table_column(); + +// /* +// * A B ... sl +// * [ +// * instance 0 ... 0 +// * a a ... 0 +// * a a^2 ... 0 +// * a a ... 0 +// * a a^2 ... 0 +// * ... ... ... ... +// * ... ... ... instance +// * ... ... ... a +// * ... ... ... a +// * ... ... ... 0 +// * ] +// */ +// meta.lookup("lookup", |meta| { +// let a_ = meta.query_any(a, Rotation::cur()); +// vec![(a_, sl)] +// }); + +// meta.create_gate("Combined add-mult", |meta| { +// let d = meta.query_advice(d, Rotation::next()); +// let a = meta.query_advice(a, Rotation::cur()); +// let sf = meta.query_fixed(sf, Rotation::cur()); +// let e = meta.query_advice(e, Rotation::prev()); +// let b = meta.query_advice(b, Rotation::cur()); +// let c = meta.query_advice(c, Rotation::cur()); + +// let sa = meta.query_fixed(sa, Rotation::cur()); +// let sb = meta.query_fixed(sb, Rotation::cur()); +// let sc = meta.query_fixed(sc, Rotation::cur()); +// let sm = meta.query_fixed(sm, Rotation::cur()); + +// vec![a.clone() * sa + b.clone() * sb + a * b * sm - (c * sc) + sf * (d * e)] +// }); + +// meta.create_gate("Public input", |meta| { +// let a = meta.query_advice(a, Rotation::cur()); +// let p = meta.query_instance(p, Rotation::cur()); +// let sp = meta.query_fixed(sp, Rotation::cur()); + +// vec![sp * (a - p)] +// }); + +// meta.enable_equality(sf); +// meta.enable_equality(e); +// meta.enable_equality(d); +// meta.enable_equality(p); +// meta.enable_equality(sm); +// meta.enable_equality(sa); +// meta.enable_equality(sb); +// meta.enable_equality(sc); +// meta.enable_equality(sp); + +// PlonkConfig { +// a, +// b, +// c, +// d, +// e, +// sa, +// sb, +// sc, +// sm, +// sp, +// sl, +// } +// } + +// fn synthesize( +// &self, +// config: PlonkConfig, +// mut layouter: impl Layouter, +// ) -> Result<(), Error> { +// let cs = StandardPlonk::new(config); + +// let _ = cs.public_input(&mut layouter, || Value::known(F::one() + F::one()))?; + +// for _ in 0..10 { +// let a: Value> = self.a.into(); +// let mut a_squared = Value::unknown(); +// let (a0, _, c0) = cs.raw_multiply(&mut layouter, || { +// a_squared = a.square(); +// a.zip(a_squared).map(|(a, a_squared)| (a, a, a_squared)) +// })?; +// let (a1, b1, _) = cs.raw_add(&mut layouter, || { +// let fin = a_squared + a; +// a.zip(a_squared) +// .zip(fin) +// .map(|((a, a_squared), fin)| (a, a_squared, fin)) +// })?; +// cs.copy(&mut layouter, a0, a1)?; +// cs.copy(&mut layouter, b1, c0)?; +// } + +// cs.lookup_table(&mut layouter, &self.lookup_table)?; + +// Ok(()) +// } +// } + +// macro_rules! common { +// ($scheme:ident) => {{ +// let a = <$scheme as CommitmentScheme>::Scalar::from(2834758237) +// * <$scheme as CommitmentScheme>::Scalar::ZETA; +// let instance = <$scheme as CommitmentScheme>::Scalar::one() +// + <$scheme as CommitmentScheme>::Scalar::one(); +// let lookup_table = vec![ +// instance, +// a, +// a, +// <$scheme as CommitmentScheme>::Scalar::zero(), +// ]; +// (a, instance, lookup_table) +// }}; +// } + +// macro_rules! bad_keys { +// ($scheme:ident) => {{ +// let (_, _, lookup_table) = common!($scheme); +// let empty_circuit: MyCircuit<<$scheme as CommitmentScheme>::Scalar> = MyCircuit { +// a: Value::unknown(), +// lookup_table: lookup_table.clone(), +// }; + +// // Check that we get an error if we try to initialize the proving key with a value of +// // k that is too small for the minimum required number of rows. +// let much_too_small_params= <$scheme as CommitmentScheme>::ParamsProver::new(1); +// assert_matches!( +// keygen_vk(&much_too_small_params, &empty_circuit), +// Err(Error::NotEnoughRowsAvailable { +// current_k, +// }) if current_k == 1 +// ); + +// // Check that we get an error if we try to initialize the proving key with a value of +// // k that is too small for the number of rows the circuit uses. +// let slightly_too_small_params = <$scheme as CommitmentScheme>::ParamsProver::new(K-1); +// assert_matches!( +// keygen_vk(&slightly_too_small_params, &empty_circuit), +// Err(Error::NotEnoughRowsAvailable { +// current_k, +// }) if current_k == K - 1 +// ); +// }}; +// } + +// fn keygen( +// params: &Scheme::ParamsProver, +// ) -> ProvingKey { +// let (_, _, lookup_table) = common!(Scheme); +// let empty_circuit: MyCircuit = MyCircuit { +// a: Value::unknown(), +// lookup_table, +// }; + +// // Initialize the proving key +// let vk = keygen_vk(params, &empty_circuit).expect("keygen_vk should not fail"); + +// keygen_pk(params, vk, &empty_circuit).expect("keygen_pk should not fail") +// } + +// fn create_proof< +// 'params, +// Scheme: CommitmentScheme, +// P: Prover<'params, Scheme>, +// E: EncodedChallenge, +// R: RngCore, +// T: TranscriptWriterBuffer, Scheme::Curve, E>, +// >( +// rng: R, +// params: &'params Scheme::ParamsProver, +// pk: &ProvingKey, +// ) -> Vec { +// let (a, instance, lookup_table) = common!(Scheme); + +// let circuit: MyCircuit = MyCircuit { +// a: Value::known(a), +// lookup_table, +// }; + +// let mut transcript = T::init(vec![]); + +// create_plonk_proof::( +// params, +// pk, +// &[circuit.clone(), circuit.clone()], +// &[&[&[instance]], &[&[instance]]], +// rng, +// &mut transcript, +// ) +// .expect("proof generation should not fail"); + +// // Check this circuit is satisfied. +// let prover = match MockProver::run(K, &circuit, vec![vec![instance]]) { +// Ok(prover) => prover, +// Err(e) => panic!("{:?}", e), +// }; +// assert_eq!(prover.verify(), Ok(())); + +// transcript.finalize() +// } + +// fn verify_proof< +// 'a, +// 'params, +// Scheme: CommitmentScheme, +// V: Verifier<'params, Scheme>, +// E: EncodedChallenge, +// T: TranscriptReadBuffer<&'a [u8], Scheme::Curve, E>, +// Strategy: VerificationStrategy<'params, Scheme, V, Output = Strategy>, +// >( +// params_verifier: &'params Scheme::ParamsVerifier, +// vk: &VerifyingKey, +// proof: &'a [u8], +// ) { +// let (_, instance, _) = common!(Scheme); +// let pubinputs = vec![instance]; + +// let mut transcript = T::init(proof); + +// let strategy = Strategy::new(params_verifier); +// let strategy = verify_plonk_proof( +// params_verifier, +// vk, +// strategy, +// &[&[&pubinputs[..]], &[&pubinputs[..]]], +// &mut transcript, +// ) +// .unwrap(); + +// assert!(strategy.finalize()); +// } + +// fn test_plonk_api_gwc() { +// use halo2_proofs::poly::kzg::commitment::{KZGCommitmentScheme, ParamsKZG}; +// use halo2_proofs::poly::kzg::multiopen::{ProverGWC, VerifierGWC}; +// use halo2_proofs::poly::kzg::strategy::AccumulatorStrategy; +// use halo2curves::bn256::Bn256; + +// type Scheme = KZGCommitmentScheme; +// bad_keys!(Scheme); + +// let params = ParamsKZG::::new(K); +// let rng = OsRng; + +// let pk = keygen::>(¶ms); + +// let proof = create_proof::<_, ProverGWC<_>, _, _, Blake2bWrite<_, _, Challenge255<_>>>( +// rng, ¶ms, &pk, +// ); + +// let verifier_params = params.verifier_params(); + +// verify_proof::< +// _, +// VerifierGWC<_>, +// _, +// Blake2bRead<_, _, Challenge255<_>>, +// AccumulatorStrategy<_>, +// >(verifier_params, pk.get_vk(), &proof[..]); +// } + +// fn test_plonk_api_shplonk() { +// use halo2_proofs::poly::kzg::commitment::{KZGCommitmentScheme, ParamsKZG}; +// use halo2_proofs::poly::kzg::multiopen::{ProverSHPLONK, VerifierSHPLONK}; +// use halo2_proofs::poly::kzg::strategy::AccumulatorStrategy; +// use halo2curves::bn256::Bn256; + +// type Scheme = KZGCommitmentScheme; +// bad_keys!(Scheme); + +// let params = ParamsKZG::::new(K); +// let rng = OsRng; + +// let pk = keygen::>(¶ms); + +// let proof = create_proof::<_, ProverSHPLONK<_>, _, _, Blake2bWrite<_, _, Challenge255<_>>>( +// rng, ¶ms, &pk, +// ); + +// let verifier_params = params.verifier_params(); + +// verify_proof::< +// _, +// VerifierSHPLONK<_>, +// _, +// Blake2bRead<_, _, Challenge255<_>>, +// AccumulatorStrategy<_>, +// >(verifier_params, pk.get_vk(), &proof[..]); +// } + +// fn test_plonk_api_ipa() { +// use halo2_proofs::poly::ipa::commitment::{IPACommitmentScheme, ParamsIPA}; +// use halo2_proofs::poly::ipa::multiopen::{ProverIPA, VerifierIPA}; +// use halo2_proofs::poly::ipa::strategy::AccumulatorStrategy; +// use halo2curves::pasta::EqAffine; + +// type Scheme = IPACommitmentScheme; +// bad_keys!(Scheme); + +// let params = ParamsIPA::::new(K); +// let rng = OsRng; + +// let pk = keygen::>(¶ms); + +// let proof = create_proof::<_, ProverIPA<_>, _, _, Blake2bWrite<_, _, Challenge255<_>>>( +// rng, ¶ms, &pk, +// ); + +// let verifier_params = params.verifier_params(); + +// verify_proof::< +// _, +// VerifierIPA<_>, +// _, +// Blake2bRead<_, _, Challenge255<_>>, +// AccumulatorStrategy<_>, +// >(verifier_params, pk.get_vk(), &proof[..]); + +// // Check that the verification key has not changed unexpectedly +// { +// //panic!("{:#?}", pk.get_vk().pinned()); +// assert_eq!( +// format!("{:#?}", pk.get_vk().pinned()), +// r#####"PinnedVerificationKey { +// base_modulus: "0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001", +// scalar_modulus: "0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001", +// domain: PinnedEvaluationDomain { +// k: 5, +// extended_k: 7, +// omega: 0x0cc3380dc616f2e1daf29ad1560833ed3baea3393eceb7bc8fa36376929b78cc, +// }, +// cs: PinnedConstraintSystem { +// num_fixed_columns: 7, +// num_advice_columns: 5, +// num_instance_columns: 1, +// num_selectors: 0, +// gates: [ +// Sum( +// Sum( +// Sum( +// Sum( +// Product( +// Advice { +// query_index: 0, +// column_index: 1, +// rotation: Rotation( +// 0, +// ), +// }, +// Fixed { +// query_index: 2, +// column_index: 2, +// rotation: Rotation( +// 0, +// ), +// }, +// ), +// Product( +// Advice { +// query_index: 1, +// column_index: 2, +// rotation: Rotation( +// 0, +// ), +// }, +// Fixed { +// query_index: 3, +// column_index: 3, +// rotation: Rotation( +// 0, +// ), +// }, +// ), +// ), +// Product( +// Product( +// Advice { +// query_index: 0, +// column_index: 1, +// rotation: Rotation( +// 0, +// ), +// }, +// Advice { +// query_index: 1, +// column_index: 2, +// rotation: Rotation( +// 0, +// ), +// }, +// ), +// Fixed { +// query_index: 5, +// column_index: 1, +// rotation: Rotation( +// 0, +// ), +// }, +// ), +// ), +// Negated( +// Product( +// Advice { +// query_index: 2, +// column_index: 3, +// rotation: Rotation( +// 0, +// ), +// }, +// Fixed { +// query_index: 4, +// column_index: 4, +// rotation: Rotation( +// 0, +// ), +// }, +// ), +// ), +// ), +// Product( +// Fixed { +// query_index: 1, +// column_index: 0, +// rotation: Rotation( +// 0, +// ), +// }, +// Product( +// Advice { +// query_index: 3, +// column_index: 4, +// rotation: Rotation( +// 1, +// ), +// }, +// Advice { +// query_index: 4, +// column_index: 0, +// rotation: Rotation( +// -1, +// ), +// }, +// ), +// ), +// ), +// Product( +// Fixed { +// query_index: 6, +// column_index: 5, +// rotation: Rotation( +// 0, +// ), +// }, +// Sum( +// Advice { +// query_index: 0, +// column_index: 1, +// rotation: Rotation( +// 0, +// ), +// }, +// Negated( +// Instance { +// query_index: 0, +// column_index: 0, +// rotation: Rotation( +// 0, +// ), +// }, +// ), +// ), +// ), +// ], +// advice_queries: [ +// ( +// Column { +// index: 1, +// column_type: Advice, +// }, +// Rotation( +// 0, +// ), +// ), +// ( +// Column { +// index: 2, +// column_type: Advice, +// }, +// Rotation( +// 0, +// ), +// ), +// ( +// Column { +// index: 3, +// column_type: Advice, +// }, +// Rotation( +// 0, +// ), +// ), +// ( +// Column { +// index: 4, +// column_type: Advice, +// }, +// Rotation( +// 1, +// ), +// ), +// ( +// Column { +// index: 0, +// column_type: Advice, +// }, +// Rotation( +// -1, +// ), +// ), +// ( +// Column { +// index: 0, +// column_type: Advice, +// }, +// Rotation( +// 0, +// ), +// ), +// ( +// Column { +// index: 4, +// column_type: Advice, +// }, +// Rotation( +// 0, +// ), +// ), +// ], +// instance_queries: [ +// ( +// Column { +// index: 0, +// column_type: Instance, +// }, +// Rotation( +// 0, +// ), +// ), +// ], +// fixed_queries: [ +// ( +// Column { +// index: 6, +// column_type: Fixed, +// }, +// Rotation( +// 0, +// ), +// ), +// ( +// Column { +// index: 0, +// column_type: Fixed, +// }, +// Rotation( +// 0, +// ), +// ), +// ( +// Column { +// index: 2, +// column_type: Fixed, +// }, +// Rotation( +// 0, +// ), +// ), +// ( +// Column { +// index: 3, +// column_type: Fixed, +// }, +// Rotation( +// 0, +// ), +// ), +// ( +// Column { +// index: 4, +// column_type: Fixed, +// }, +// Rotation( +// 0, +// ), +// ), +// ( +// Column { +// index: 1, +// column_type: Fixed, +// }, +// Rotation( +// 0, +// ), +// ), +// ( +// Column { +// index: 5, +// column_type: Fixed, +// }, +// Rotation( +// 0, +// ), +// ), +// ], +// permutation: Argument { +// columns: [ +// Column { +// index: 1, +// column_type: Advice, +// }, +// Column { +// index: 2, +// column_type: Advice, +// }, +// Column { +// index: 3, +// column_type: Advice, +// }, +// Column { +// index: 0, +// column_type: Fixed, +// }, +// Column { +// index: 0, +// column_type: Advice, +// }, +// Column { +// index: 4, +// column_type: Advice, +// }, +// Column { +// index: 0, +// column_type: Instance, +// }, +// Column { +// index: 1, +// column_type: Fixed, +// }, +// Column { +// index: 2, +// column_type: Fixed, +// }, +// Column { +// index: 3, +// column_type: Fixed, +// }, +// Column { +// index: 4, +// column_type: Fixed, +// }, +// Column { +// index: 5, +// column_type: Fixed, +// }, +// ], +// }, +// lookups: [ +// Argument { +// input_expressions: [ +// Advice { +// query_index: 0, +// column_index: 1, +// rotation: Rotation( +// 0, +// ), +// }, +// ], +// table_expressions: [ +// Fixed { +// query_index: 0, +// column_index: 6, +// rotation: Rotation( +// 0, +// ), +// }, +// ], +// }, +// ], +// constants: [], +// minimum_degree: None, +// }, +// fixed_commitments: [ +// (0x2bbc94ef7b22aebef24f9a4b0cc1831882548b605171366017d45c3e6fd92075, 0x082b801a6e176239943bfb759fb02138f47a5c8cc4aa7fa0af559fde4e3abd97), +// (0x2bf5082b105b2156ed0e9c5b8e42bf2a240b058f74a464d080e9585274dd1e84, 0x222ad83cee7777e7a160585e212140e5e770dd8d1df788d869b5ee483a5864fb), +// (0x374a656456a0aae7429b23336f825752b575dd5a44290ff614946ee59d6a20c0, 0x054491e187e6e3460e7601fb54ae10836d34d420026f96316f0c5c62f86db9b8), +// (0x374a656456a0aae7429b23336f825752b575dd5a44290ff614946ee59d6a20c0, 0x054491e187e6e3460e7601fb54ae10836d34d420026f96316f0c5c62f86db9b8), +// (0x02e62cd68370b13711139a08cbcdd889e800a272b9ea10acc90880fff9d89199, 0x1a96c468cb0ce77065d3a58f1e55fea9b72d15e44c01bba1e110bd0cbc6e9bc6), +// (0x224ef42758215157d3ee48fb8d769da5bddd35e5929a90a4a89736f5c4b5ae9b, 0x11bc3a1e08eb320cde764f1492ecef956d71e996e2165f7a9a30ad2febb511c1), +// (0x2d5415bf917fcac32bfb705f8ca35cb12d9bad52aa33ccca747350f9235d3a18, 0x2b2921f815fad504052512743963ef20ed5b401d20627793b006413e73fe4dd4), +// ], +// permutation: VerifyingKey { +// commitments: [ +// (0x1347b4b385837977a96b87f199c6a9a81520015539d1e8fa79429bb4ca229a00, 0x2168e404cabef513654d6ff516cde73f0ba87e3dc84e4b940ed675b5f66f3884), +// (0x0e6d69cd2455ec43be640f6397ed65c9e51b1d8c0fd2216339314ff37ade122a, 0x222ed6dc8cfc9ea26dcc10b9d4add791ada60f2b5a63ee1e4635f88aa0c96654), +// (0x13c447846f48c41a5e0675ccf88ebc0cdef2c96c51446d037acb866d24255785, 0x1f0b5414fc5e8219dbfab996eed6129d831488b2386a8b1a63663938903bd63a), +// (0x1aae6470aa662b8fda003894ddef5fedd03af318b3231683039d2fac9cab05b9, 0x08832d91ae69e99cd07d096c7a4a284a69e6a16227cbb07932a0cdc56914f3a6), +// (0x0850521b0f8ac7dd0550fe3e25c840837076e9635067ed623b81d5cbac5944d9, 0x0c25d65d1038d0a92c72e5fccd96c1caf07801c3c8233290bb292e0c38c256fa), +// (0x12febcf696badd970750eabf75dd3ced4c2f54f93519bcee23849025177d2014, 0x0a05ab3cd42c9fbcc1bbfcf9269951640cc9920761c87cf8e211ba73c8d9f90f), +// (0x053904bdde8cfead3b517bb4f6ded3e699f8b94ca6156a9dd2f92a2a05a7ec5a, 0x16753ff97c0d82ff586bb7a07bf7f27a92df90b3617fa5e75d4f55c3b0ef8711), +// (0x3804548f6816452747a5b542fa5656353fc989db40d69e9e27d6f973b5deebb0, 0x389a44d5037866dd83993af75831a5f90a18ad5244255aa5bd2c922cc5853055), +// (0x003a9f9ca71c7c0b832c802220915f6fc8d840162bdde6b0ea05d25fb95559e3, 0x091247ca19d6b73887cd7f68908cbf0db0b47459b7c82276bbdb8a1c937e2438), +// (0x3eaa38689d9e391c8a8fafab9568f20c45816321d38f309d4cc37f4b1601af72, 0x247f8270a462ea88450221a56aa6b55d2bc352b80b03501e99ea983251ceea13), +// (0x394437571f9de32dccdc546fd4737772d8d92593c85438aa3473243997d5acc8, 0x14924ec6e3174f1fab7f0ce7070c22f04bbd0a0ecebdfc5c94be857f25493e95), +// (0x3d907e0591343bd285c2c846f3e871a6ac70d80ec29e9500b8cb57f544e60202, 0x1034e48df35830244cabea076be8a16d67d7896e27c6ac22b285d017105da9c3), +// ], +// }, +// }"##### +// ); +// } +// } + +// test_plonk_api_ipa(); +// test_plonk_api_gwc(); +// test_plonk_api_shplonk(); +// } diff --git a/halo2_proofs/tests/static_lookup.rs b/halo2_proofs/tests/static_lookup.rs new file mode 100644 index 0000000000..5036948981 --- /dev/null +++ b/halo2_proofs/tests/static_lookup.rs @@ -0,0 +1,241 @@ +use group::Group; +use rand::SeedableRng; +use std::collections::BTreeMap; + +use ff::Field; +use halo2_proofs::{ + circuit::{SimpleFloorPlanner, Value}, + dev::MockProver, + plonk::{ + create_proof, keygen_pk, keygen_vk, + static_lookup::{StaticTable, StaticTableConfig, StaticTableId, StaticTableValues}, + verify_proof, Advice, Circuit, Column, + }, + poly::{ + commitment::ParamsProver, + kzg::{ + commitment::{ParamsKZG, TableSRS}, + multiopen::{ProverGWC, VerifierGWC}, + strategy::AccumulatorStrategy, + }, + Rotation, VerificationStrategy, + }, + transcript::{ + Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer, + }, +}; +use halo2curves::{ + bn256::Bn256, + pairing::{Engine, MillerLoopResult, MultiMillerLoop}, + FieldExt, +}; +use rand_core::OsRng; + +#[derive(Clone)] +struct MyCircuit { + values: Vec, + table: StaticTable, + table_2: StaticTable, +} + +#[derive(Clone)] +struct MyConfig { + advice: Column, + advice_2: Column, +} + +impl MyCircuit { + fn assign_pairs( + &self, + config: MyConfig, + mut layouter: impl halo2_proofs::circuit::Layouter, + ) -> Result<(), halo2_proofs::plonk::Error> { + layouter.assign_region( + || "", + |mut region| { + for (i, &value) in self.values.iter().enumerate() { + region.assign_advice( + config.advice, + i, + Value::known(::Scalar::from(value)), + )?; + region.assign_advice( + config.advice_2, + i, + Value::known(::Scalar::from(value * 2)), + )?; + } + + Ok(()) + }, + ) + } +} + +impl, F: Field + FieldExt> Circuit for MyCircuit { + type Config = MyConfig; + + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + self.clone() + } + + fn configure(meta: &mut halo2_proofs::plonk::ConstraintSystem) -> Self::Config { + let advice = meta.advice_column(); + let advice_2 = meta.advice_column(); + meta.lookup_static("lookup_bits", |meta| { + vec![ + ( + meta.query_advice(advice, Rotation::cur()), + StaticTableId(String::from("table")), + ), + ( + meta.query_advice(advice_2, Rotation::cur()), + StaticTableId(String::from("table_2")), + ), + ] + }); + + MyConfig { advice, advice_2 } + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl halo2_proofs::circuit::Layouter, + ) -> Result<(), halo2_proofs::plonk::Error> { + layouter.register_static_table(StaticTableId(String::from("table")), self.table.clone()); + layouter + .register_static_table(StaticTableId(String::from("table_2")), self.table_2.clone()); + + self.assign_pairs(config, layouter.namespace(|| "assign pairs")) + } +} + +// ascii of cq +static SEED: [u8; 32] = [ + 99, 113, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +]; + +fn generate_table( + params: &TableSRS, + table_range: usize, + k: usize, +) -> (StaticTable, StaticTable) { + use halo2curves::bn256::Fr; + + let mut table_values: Vec<_> = (0..table_range).map(|i| Fr::from(i as u64)).collect(); + // Replace the second row with 0 to test for duplicated values in table + table_values[1] = Fr::zero(); + + let mut table_2_values: Vec<_> = (0..table_range).map(|i| Fr::from(i as u64 * 2)).collect(); + // Replace the second row with 0 to test for duplicated values in table + table_2_values[1] = Fr::zero(); + + let table = StaticTableValues::new(&table_values, ¶ms.g1()); + let table_2 = StaticTableValues::new(&table_2_values, ¶ms.g1()); + + let n = 1 << k; + let committed = table.commit(params.g1().len(), params.g2(), n); + let committed_2 = table_2.commit(params.g1().len(), params.g2(), n); + + let t1 = StaticTable { + opened: Some(table), + committed: Some(committed), + }; + + let t2 = StaticTable { + opened: Some(table_2), + committed: Some(committed_2), + }; + + (t1, t2) +} + +#[test] +fn static_lookup_e2e() { + const K: u32 = 3; + let mut rng = rand_chacha::ChaCha8Rng::from_seed(SEED); + let s = ::Scalar::random(&mut rng); + + let table_16_size = 16; + + let table_16_srs = + TableSRS::::setup_from_toxic_waste(table_16_size - 1, table_16_size, s); + let (table, table_2) = generate_table(&table_16_srs, 16, K as usize); + let circuit = MyCircuit { + values: vec![0, 2], + table, + table_2, + }; + + let prover = MockProver::run(K, &circuit, vec![]).unwrap(); + prover.assert_satisfied(); + + let params = ParamsKZG::::setup_from_toxic_waste(K, s); + + let config = StaticTableConfig::new( + table_16_size, + table_16_srs.g1_lagrange().to_vec(), + table_16_srs.g_lagrange_opening_at_0().to_vec(), + ); + let mut configs = BTreeMap::new(); + configs.insert(table_16_size, config); + + let b0_g1_bound = table_16_srs.g1()[((1 << K) + 1)..].to_vec(); + + // Initialize keys + let vk = keygen_vk(¶ms, &circuit).expect("keygen_vk should not fail"); + let pk = + keygen_pk(¶ms, configs, b0_g1_bound, vk, &circuit).expect("keygen_pk should not fail"); + + // Create proof + let proof = { + let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); + // Create a proof + create_proof::, _, _, _, _>( + ¶ms, + &pk, + &[circuit], + &[&[]], + OsRng, + &mut transcript, + ) + .unwrap(); + + transcript.finalize() + }; + + let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]); + + let verifier_params = params.verifier_params(); + let strategy = VerificationStrategy::>::new(verifier_params); + + let p_batcher = verify_proof::< + Bn256, + VerifierGWC<_>, + _, + Blake2bRead<_, _, Challenge255<_>>, + AccumulatorStrategy<_>, + >( + verifier_params, + pk.get_vk(), + strategy, + &[&[]], + &mut transcript, + ) + .unwrap(); + + let batched_tuples = p_batcher.finalize(); + let result = Bn256::multi_miller_loop( + &batched_tuples + .iter() + .map(|(g1, g2)| (g1, g2)) + .collect::>(), + ); + + let pairing_result = result.final_exponentiation(); + assert!(bool::from(pairing_result.is_identity())); +}