use halo2::{ circuit::{SimpleFloorPlanner, Cell, Chip, Layouter}, pasta::{EqAffine, Fp}, plonk::{Advice, Any, Circuit, Column, ConstraintSystem, Error, Expression, Selector, create_proof, verify_proof, keygen_vk, keygen_pk, Permutation}, poly::{commitment::{Blind, Params}, Rotation}, transcript::{Blake2bRead, Blake2bWrite, Challenge255}, }; use group::Curve; use std::time::Instant; #[derive(Clone, Debug)] struct CoolConfig { a_col: Column, b_col: Column, permute: Permutation, s_range: Selector, s_mul: Selector, s_pub: Selector, } struct CoolChip { config: CoolConfig } impl Chip for CoolChip { type Config = CoolConfig; type Loaded = (); fn config(&self) -> &Self::Config { &self.config } fn loaded(&self) -> &Self::Loaded { &() } } #[derive(Clone, Debug)] struct Number { cell: Cell, value: Option, } impl CoolChip { fn construct(config: CoolConfig) -> Self { Self { config } } fn configure(cs: &mut ConstraintSystem) -> CoolConfig { let a_col = cs.advice_column(); let b_col = cs.advice_column(); let instance = cs.instance_column(); let permute = { // Convert advice columns into an "any" columns. let cols: [Column; 2] = [a_col.into(), b_col.into()]; Permutation::new(cs, &cols) }; let s_range = cs.selector(); let s_mul = cs.selector(); let s_pub = cs.selector(); cs.create_gate("check", |cs| { let a = cs.query_advice(a_col, Rotation::cur()); let s_range = cs.query_selector(s_range); vec![s_range * (a - Expression::Constant(Fp::from(2)))] }); cs.create_gate("mul", |cs| { let lhs = cs.query_advice(a_col, Rotation::cur()); let rhs = cs.query_advice(b_col, Rotation::cur()); let out = cs.query_advice(a_col, Rotation::next()); let s_mul = cs.query_selector(s_mul); vec![s_mul * (lhs * rhs + out * -Fp::one())] }); cs.create_gate("public input", |cs| { let a = cs.query_advice(b_col, Rotation::cur()); let p = cs.query_instance(instance, Rotation::cur()); let s = cs.query_selector(s_pub); vec![s * (p + a * -Fp::one())] }); CoolConfig { a_col, b_col, permute, s_range, s_mul, s_pub } } fn alloc_left( &self, layouter: &mut impl Layouter, value: Option ) -> Result { layouter.assign_region( || "load left private input", |mut region| { let cell = region.assign_advice( || "private input 'a'", self.config.a_col, 0, || value.ok_or(Error::SynthesisError), )?; Ok(Number { cell, value }) } ) } fn check( &self, layouter: &mut impl Layouter, number: Number ) -> Result<(), Error> { layouter.assign_region( || "load private inputs", |mut region| { self.config.s_range.enable(&mut region, 0)?; let a = region.assign_advice( || "lhs", self.config.a_col, 0, || number.value.ok_or(Error::SynthesisError), )?; region.constrain_equal(&self.config.permute, number.cell, a)?; Ok(()) }, ) } fn mul( &self, layouter: &mut impl Layouter, a: Number, b: Number ) -> Result { let mut out = None; layouter.assign_region( || "mul", |mut region| { self.config.s_mul.enable(&mut region, 0)?; let lhs = region.assign_advice( || "lhs", self.config.a_col, 0, || a.value.ok_or(Error::SynthesisError), )?; let rhs = region.assign_advice( || "rhs", self.config.b_col, 0, || b.value.ok_or(Error::SynthesisError), )?; region.constrain_equal(&self.config.permute, a.cell, lhs)?; region.constrain_equal(&self.config.permute, b.cell, rhs)?; let value = a.value.and_then(|a| b.value.map(|b| a * b)); let cell = region.assign_advice( || "lhs * rhs", self.config.a_col, 1, || value.ok_or(Error::SynthesisError), )?; out = Some(Number { cell, value }); Ok(()) }, )?; Ok(out.unwrap()) } fn expose_public(&self, layouter: &mut impl Layouter, num: Number) -> Result<(), Error> { layouter.assign_region( || "expose public", |mut region| { self.config.s_pub.enable(&mut region, 0)?; let out = region.assign_advice( || "public advice", self.config.b_col, 0, || num.value.ok_or(Error::SynthesisError), )?; region.constrain_equal(&self.config.permute, num.cell, out)?; Ok(()) }, ) } } #[derive(Clone)] struct CoolCircuit { // Private input. a: Option, } impl Circuit for CoolCircuit { type Config = CoolConfig; type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { Self { a: None } } fn configure(cs: &mut ConstraintSystem) -> Self::Config { CoolChip::configure(cs) } fn synthesize(&self, config: Self::Config, mut layouter: impl Layouter) -> Result<(), Error> { let chip = CoolChip::construct(config); let a = chip.alloc_left(&mut layouter, self.a)?; chip.check(&mut layouter, a.clone())?; let a2 = chip.mul(&mut layouter, a.clone(), a)?; chip.expose_public(&mut layouter, a2)?; Ok(()) } } fn main() { let k = 6; let start = Instant::now(); let params: Params = Params::new(k); let empty_circuit = CoolCircuit { a: None }; 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"); println!("Setup: [{:?}]", start.elapsed()); let start = Instant::now(); let circuit = CoolCircuit { a: Some(Fp::from(2)), }; let mut public_inputs = pk.get_vk().get_domain().empty_lagrange(); public_inputs[4] = Fp::from(4); // Create a proof let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); create_proof(¶ms, &pk, &[circuit], &[&[public_inputs.clone()]], &mut transcript) .expect("proof generation should not fail"); let proof = transcript.finalize(); println!("Prove: [{:?}]", start.elapsed()); let pubinput = params .commit_lagrange(&public_inputs, Blind::default()) .to_affine(); let pubinput_slice = &[pubinput]; let start = Instant::now(); let msm = params.empty_msm(); let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]); let verification = verify_proof(¶ms, pk.get_vk(), msm, &[pubinput_slice], &mut transcript); if let Err(err) = verification { panic!("error {:?}", err); } let guard = verification.unwrap(); let msm = guard.clone().use_challenges(); assert!(msm.eval()); println!("Verify: [{:?}]", start.elapsed()); }