#[macro_use] extern crate criterion; extern crate halo2; use halo2::arithmetic::FieldExt; use halo2::circuit::{Cell, Layouter, SimpleFloorPlanner}; use halo2::pasta::{EqAffine, Fp}; use halo2::plonk::*; use halo2::poly::{commitment::Params, Rotation}; use halo2::transcript::{Blake2bRead, Blake2bWrite, Challenge255}; use std::marker::PhantomData; use criterion::{BenchmarkId, Criterion}; fn criterion_benchmark(c: &mut Criterion) { /// 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, sa: Column, sb: Column, sc: Column, sm: Column, } trait StandardCs { fn raw_multiply( &self, layouter: &mut impl Layouter, f: F, ) -> Result<(Cell, Cell, Cell), Error> where F: FnMut() -> Result<(FF, FF, FF), Error>; fn raw_add( &self, layouter: &mut impl Layouter, f: F, ) -> Result<(Cell, Cell, Cell), Error> where F: FnMut() -> Result<(FF, FF, FF), Error>; fn copy(&self, layouter: &mut impl Layouter, a: Cell, b: Cell) -> Result<(), Error>; } #[derive(Clone)] struct MyCircuit { a: Option, k: u32, } 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() -> Result<(FF, FF, FF), Error>, { layouter.assign_region( || "raw_multiply", |mut region| { let mut value = None; let lhs = region.assign_advice( || "lhs", self.config.a, 0, || { value = Some(f()?); Ok(value.ok_or(Error::Synthesis)?.0) }, )?; let rhs = region.assign_advice( || "rhs", self.config.b, 0, || Ok(value.ok_or(Error::Synthesis)?.1), )?; let out = region.assign_advice( || "out", self.config.c, 0, || Ok(value.ok_or(Error::Synthesis)?.2), )?; region.assign_fixed(|| "a", self.config.sa, 0, || Ok(FF::zero()))?; region.assign_fixed(|| "b", self.config.sb, 0, || Ok(FF::zero()))?; region.assign_fixed(|| "c", self.config.sc, 0, || Ok(FF::one()))?; region.assign_fixed(|| "a * b", self.config.sm, 0, || Ok(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() -> Result<(FF, FF, FF), Error>, { layouter.assign_region( || "raw_add", |mut region| { let mut value = None; let lhs = region.assign_advice( || "lhs", self.config.a, 0, || { value = Some(f()?); Ok(value.ok_or(Error::Synthesis)?.0) }, )?; let rhs = region.assign_advice( || "rhs", self.config.b, 0, || Ok(value.ok_or(Error::Synthesis)?.1), )?; let out = region.assign_advice( || "out", self.config.c, 0, || Ok(value.ok_or(Error::Synthesis)?.2), )?; region.assign_fixed(|| "a", self.config.sa, 0, || Ok(FF::one()))?; region.assign_fixed(|| "b", self.config.sb, 0, || Ok(FF::one()))?; region.assign_fixed(|| "c", self.config.sc, 0, || Ok(FF::one()))?; region.assign_fixed(|| "a * b", self.config.sm, 0, || Ok(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)) } } impl Circuit for MyCircuit { type Config = PlonkConfig; type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { Self { a: None, k: self.k } } fn configure(meta: &mut ConstraintSystem) -> PlonkConfig { meta.set_minimum_degree(5); let a = meta.advice_column(); let b = meta.advice_column(); let c = meta.advice_column(); meta.enable_equality(a.into()); meta.enable_equality(b.into()); meta.enable_equality(c.into()); let sm = meta.fixed_column(); let sa = meta.fixed_column(); let sb = meta.fixed_column(); let sc = meta.fixed_column(); meta.create_gate("Combined add-mult", |meta| { let a = meta.query_advice(a, Rotation::cur()); 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)] }); PlonkConfig { a, b, c, sa, sb, sc, sm, } } fn synthesize( &self, config: PlonkConfig, mut layouter: impl Layouter, ) -> Result<(), Error> { let cs = StandardPlonk::new(config); for _ in 0..((1 << (self.k - 1)) - 3) { let mut a_squared = None; let (a0, _, c0) = cs.raw_multiply(&mut layouter, || { a_squared = self.a.map(|a| a.square()); Ok(( self.a.ok_or(Error::Synthesis)?, self.a.ok_or(Error::Synthesis)?, a_squared.ok_or(Error::Synthesis)?, )) })?; let (a1, b1, _) = cs.raw_add(&mut layouter, || { let fin = a_squared.and_then(|a2| self.a.map(|a| a + a2)); Ok(( self.a.ok_or(Error::Synthesis)?, a_squared.ok_or(Error::Synthesis)?, fin.ok_or(Error::Synthesis)?, )) })?; cs.copy(&mut layouter, a0, a1)?; cs.copy(&mut layouter, b1, c0)?; } Ok(()) } } fn keygen(k: u32) -> (Params, ProvingKey) { let params: Params = Params::new(k); let empty_circuit: MyCircuit = MyCircuit { a: None, k }; let vk = keygen_vk(¶ms, &empty_circuit).expect("keygen_vk should not fail"); let pk = keygen_pk(¶ms, vk, &empty_circuit).expect("keygen_pk should not fail"); (params, pk) } fn prover(k: u32, params: &Params, pk: &ProvingKey) -> Vec { let circuit: MyCircuit = MyCircuit { a: Some(Fp::rand()), k, }; let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); create_proof(params, pk, &[circuit], &[&[]], &mut transcript) .expect("proof generation should not fail"); transcript.finalize() } fn verifier(params: &Params, vk: &VerifyingKey, proof: &[u8]) { let msm = params.empty_msm(); let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(proof); let guard = verify_proof(params, vk, msm, &[&[]], &mut transcript).unwrap(); let msm = guard.clone().use_challenges(); assert!(msm.eval()); } let k_range = 8..=16; let mut keygen_group = c.benchmark_group("plonk-keygen"); keygen_group.sample_size(10); for k in k_range.clone() { keygen_group.bench_with_input(BenchmarkId::from_parameter(k), &k, |b, &k| { b.iter(|| keygen(k)); }); } keygen_group.finish(); let mut prover_group = c.benchmark_group("plonk-prover"); prover_group.sample_size(10); for k in k_range.clone() { let (params, pk) = keygen(k); prover_group.bench_with_input( BenchmarkId::from_parameter(k), &(k, ¶ms, &pk), |b, &(k, params, pk)| { b.iter(|| prover(k, params, pk)); }, ); } prover_group.finish(); let mut verifier_group = c.benchmark_group("plonk-verifier"); for k in k_range { let (params, pk) = keygen(k); let proof = prover(k, ¶ms, &pk); verifier_group.bench_with_input( BenchmarkId::from_parameter(k), &(¶ms, pk.get_vk(), &proof[..]), |b, &(params, vk, proof)| { b.iter(|| verifier(params, vk, proof)); }, ); } verifier_group.finish(); } criterion_group!(benches, criterion_benchmark); criterion_main!(benches);