diff --git a/Cargo.toml b/Cargo.toml index 62f3d27..475291a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,10 @@ criterion = "0.3" name = "arithmetic" harness = false +[[bench]] +name = "plonk" +harness = false + [dependencies] subtle = "2.2.1" crossbeam-utils = "0.7" diff --git a/benches/plonk.rs b/benches/plonk.rs new file mode 100644 index 0000000..02f5632 --- /dev/null +++ b/benches/plonk.rs @@ -0,0 +1,284 @@ +#[macro_use] +extern crate criterion; + +extern crate halo2; +use halo2::arithmetic::Field; +use halo2::plonk::*; +use halo2::poly::commitment::Params; +use halo2::transcript::DummyHash; +use halo2::tweedle::{EqAffine, Fp, Fq}; + +use std::marker::PhantomData; + +use criterion::Criterion; + +fn bench_with_k(name: &str, k: u32, c: &mut Criterion) { + /// This represents an advice column at a certain row in the ConstraintSystem + #[derive(Copy, Clone, Debug)] + pub struct Variable(Column, usize); + + // Initialize the polynomial commitment parameters + let params: Params = Params::new::>(k); + + struct PLONKConfig { + a: Column, + b: Column, + c: Column, + + sa: Column, + sb: Column, + sc: Column, + sm: Column, + + perm: usize, + } + + trait StandardCS { + fn raw_multiply(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error> + where + F: FnOnce() -> Result<(FF, FF, FF), Error>; + fn raw_add(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error> + where + F: FnOnce() -> Result<(FF, FF, FF), Error>; + fn copy(&mut self, a: Variable, b: Variable) -> Result<(), Error>; + } + + struct MyCircuit { + a: Option, + k: u32, + } + + struct StandardPLONK<'a, F: Field, CS: Assignment + 'a> { + cs: &'a mut CS, + config: PLONKConfig, + current_gate: usize, + _marker: PhantomData, + } + + impl<'a, FF: Field, CS: Assignment> StandardPLONK<'a, FF, CS> { + fn new(cs: &'a mut CS, config: PLONKConfig) -> Self { + StandardPLONK { + cs, + config, + current_gate: 0, + _marker: PhantomData, + } + } + } + + impl<'a, FF: Field, CS: Assignment> StandardCS for StandardPLONK<'a, FF, CS> { + fn raw_multiply(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error> + where + F: FnOnce() -> Result<(FF, FF, FF), Error>, + { + let index = self.current_gate; + self.current_gate += 1; + let mut value = None; + self.cs.assign_advice(self.config.a, index, || { + value = Some(f()?); + Ok(value.ok_or(Error::SynthesisError)?.0) + })?; + self.cs.assign_advice(self.config.b, index, || { + Ok(value.ok_or(Error::SynthesisError)?.1) + })?; + self.cs.assign_advice(self.config.c, index, || { + Ok(value.ok_or(Error::SynthesisError)?.2) + })?; + + self.cs + .assign_fixed(self.config.sa, index, || Ok(FF::zero()))?; + self.cs + .assign_fixed(self.config.sb, index, || Ok(FF::zero()))?; + self.cs + .assign_fixed(self.config.sc, index, || Ok(FF::one()))?; + self.cs + .assign_fixed(self.config.sm, index, || Ok(FF::one()))?; + Ok(( + Variable(self.config.a, index), + Variable(self.config.b, index), + Variable(self.config.c, index), + )) + } + fn raw_add(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error> + where + F: FnOnce() -> Result<(FF, FF, FF), Error>, + { + let index = self.current_gate; + self.current_gate += 1; + let mut value = None; + self.cs.assign_advice(self.config.a, index, || { + value = Some(f()?); + Ok(value.ok_or(Error::SynthesisError)?.0) + })?; + self.cs.assign_advice(self.config.b, index, || { + Ok(value.ok_or(Error::SynthesisError)?.1) + })?; + self.cs.assign_advice(self.config.c, index, || { + Ok(value.ok_or(Error::SynthesisError)?.2) + })?; + + self.cs + .assign_fixed(self.config.sa, index, || Ok(FF::one()))?; + self.cs + .assign_fixed(self.config.sb, index, || Ok(FF::one()))?; + self.cs + .assign_fixed(self.config.sc, index, || Ok(FF::one()))?; + self.cs + .assign_fixed(self.config.sm, index, || Ok(FF::zero()))?; + Ok(( + Variable(self.config.a, index), + Variable(self.config.b, index), + Variable(self.config.c, index), + )) + } + fn copy(&mut self, left: Variable, right: Variable) -> Result<(), Error> { + let left_column = match left.0 { + x if x == self.config.a => 0, + x if x == self.config.b => 1, + x if x == self.config.c => 2, + _ => unreachable!(), + }; + let right_column = match right.0 { + x if x == self.config.a => 0, + x if x == self.config.b => 1, + x if x == self.config.c => 2, + _ => unreachable!(), + }; + + self.cs + .copy(self.config.perm, left_column, left.1, right_column, right.1) + } + } + + impl Circuit for MyCircuit { + type Config = PLONKConfig; + + fn configure(meta: &mut ConstraintSystem) -> PLONKConfig { + let a = meta.advice_column(); + let b = meta.advice_column(); + let c = meta.advice_column(); + + let perm = meta.permutation(&[a, b, c]); + + let sm = meta.fixed_column(); + let sa = meta.fixed_column(); + let sb = meta.fixed_column(); + let sc = meta.fixed_column(); + + meta.create_gate(|meta| { + let a = meta.query_advice(a, 0); + let b = meta.query_advice(b, 0); + let c = meta.query_advice(c, 0); + + let sa = meta.query_fixed(sa, 0); + let sb = meta.query_fixed(sb, 0); + let sc = meta.query_fixed(sc, 0); + let sm = meta.query_fixed(sm, 0); + + a.clone() * sa + b.clone() * sb + a * b * sm + (c * sc * (-F::one())) + }); + + PLONKConfig { + a, + b, + c, + sa, + sb, + sc, + sm, + perm, + } + } + + fn synthesize( + &self, + cs: &mut impl Assignment, + config: PLONKConfig, + ) -> Result<(), Error> { + let mut cs = StandardPLONK::new(cs, config); + + for _ in 0..(1 << (self.k - 1)) { + let mut a_squared = None; + let (a0, _, c0) = cs.raw_multiply(|| { + a_squared = self.a.map(|a| a.square()); + Ok(( + self.a.ok_or(Error::SynthesisError)?, + self.a.ok_or(Error::SynthesisError)?, + a_squared.ok_or(Error::SynthesisError)?, + )) + })?; + let (a1, b1, _) = cs.raw_add(|| { + let fin = a_squared.and_then(|a2| self.a.map(|a| a + a2)); + Ok(( + self.a.ok_or(Error::SynthesisError)?, + a_squared.ok_or(Error::SynthesisError)?, + fin.ok_or(Error::SynthesisError)?, + )) + })?; + cs.copy(a0, a1)?; + cs.copy(b1, c0)?; + } + + Ok(()) + } + } + + let empty_circuit: MyCircuit = MyCircuit { a: None, k }; + + // Initialize the proving key + let pk = keygen(¶ms, &empty_circuit).expect("keygen should not fail"); + + let prover_name = name.to_string() + "-prover"; + let verifier_name = name.to_string() + "-verifier"; + + c.bench_function(&prover_name, |b| { + b.iter(|| { + let circuit: MyCircuit = MyCircuit { + a: Some(Fp::random()), + k, + }; + + // Create a proof + let proof = + Proof::create::, DummyHash, _>(¶ms, &pk, &circuit, &[]) + .expect("proof generation should not fail"); + + proof + }); + }); + + let circuit: MyCircuit = MyCircuit { + a: Some(Fp::random()), + k, + }; + + // Create a proof + let proof = Proof::create::, DummyHash, _>(¶ms, &pk, &circuit, &[]) + .expect("proof generation should not fail"); + + c.bench_function(&verifier_name, |b| { + b.iter(|| { + let msm = params.empty_msm(); + let guard = proof + .verify::, DummyHash>(¶ms, pk.get_vk(), msm, &[]) + .unwrap(); + let msm = guard.clone().use_challenges(); + assert!(msm.eval()); + }); + }); +} + +fn criterion_benchmark(c: &mut Criterion) { + bench_with_k("plonk-k=8", 8, c); + bench_with_k("plonk-k=9", 9, c); + bench_with_k("plonk-k=10", 10, c); + bench_with_k("plonk-k=11", 11, c); + bench_with_k("plonk-k=12", 12, c); + bench_with_k("plonk-k=13", 13, c); + bench_with_k("plonk-k=14", 14, c); + bench_with_k("plonk-k=15", 15, c); + bench_with_k("plonk-k=16", 16, c); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches);