use ff::Field; use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner, Value}, pasta::Fp, plonk::{ create_proof, keygen_pk, keygen_vk, verify_proof, Advice, Circuit, Column, ConstraintSystem, Error, Instance, SingleVerifier, }, poly::commitment::Params, transcript::{Blake2bRead, Blake2bWrite, Challenge255}, }; use pasta_curves::{pallas, vesta}; use halo2_gadgets::poseidon::{ primitives::{self as poseidon, generate_constants, ConstantLength, Mds, Spec}, Hash, Pow5Chip, Pow5Config, }; use std::convert::TryInto; use std::marker::PhantomData; use criterion::{criterion_group, criterion_main, Criterion}; use rand::rngs::OsRng; #[derive(Clone, Copy)] struct HashCircuit where S: Spec + Clone + Copy, { message: Value<[Fp; L]>, _spec: PhantomData, } #[derive(Debug, Clone)] struct MyConfig { input: [Column; L], expected: Column, poseidon_config: Pow5Config, } impl Circuit for HashCircuit where S: Spec + Copy + Clone, { type Config = MyConfig; type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { Self { message: Value::unknown(), _spec: PhantomData, } } fn configure(meta: &mut ConstraintSystem) -> Self::Config { let state = (0..WIDTH).map(|_| meta.advice_column()).collect::>(); let expected = meta.instance_column(); meta.enable_equality(expected); let partial_sbox = meta.advice_column(); let rc_a = (0..WIDTH).map(|_| meta.fixed_column()).collect::>(); let rc_b = (0..WIDTH).map(|_| meta.fixed_column()).collect::>(); meta.enable_constant(rc_b[0]); Self::Config { input: state[..RATE].try_into().unwrap(), expected, poseidon_config: Pow5Chip::configure::( meta, state.try_into().unwrap(), partial_sbox, rc_a.try_into().unwrap(), rc_b.try_into().unwrap(), ), } } fn synthesize( &self, config: Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { let chip = Pow5Chip::construct(config.poseidon_config.clone()); let message = layouter.assign_region( || "load message", |mut region| { let message_word = |i: usize| { let value = self.message.map(|message_vals| message_vals[i]); region.assign_advice( || format!("load message_{}", i), config.input[i], 0, || value, ) }; let message: Result, Error> = (0..L).map(message_word).collect(); Ok(message?.try_into().unwrap()) }, )?; let hasher = Hash::<_, _, S, ConstantLength, WIDTH, RATE>::init( chip, layouter.namespace(|| "init"), )?; let output = hasher.hash(layouter.namespace(|| "hash"), message)?; layouter.constrain_instance(output.cell(), config.expected, 0) } } #[derive(Debug, Clone, Copy)] struct MySpec; impl Spec for MySpec { fn full_rounds() -> usize { 8 } fn partial_rounds() -> usize { 56 } fn sbox(val: Fp) -> Fp { val.pow_vartime(&[5]) } fn secure_mds() -> usize { 0 } fn constants() -> (Vec<[Fp; WIDTH]>, Mds, Mds) { generate_constants::<_, Self, WIDTH, RATE>() } } const K: u32 = 7; fn bench_poseidon( name: &str, c: &mut Criterion, ) where S: Spec + Copy + Clone, { // Initialize the polynomial commitment parameters let params: Params = Params::new(K); let empty_circuit = HashCircuit:: { message: Value::unknown(), _spec: PhantomData, }; // Initialize the proving key 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"); let prover_name = name.to_string() + "-prover"; let verifier_name = name.to_string() + "-verifier"; let mut rng = OsRng; let message = (0..L) .map(|_| pallas::Base::random(rng)) .collect::>() .try_into() .unwrap(); let output = poseidon::Hash::<_, S, ConstantLength, WIDTH, RATE>::init().hash(message); let circuit = HashCircuit:: { message: Value::known(message), _spec: PhantomData, }; c.bench_function(&prover_name, |b| { b.iter(|| { let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); create_proof( ¶ms, &pk, &[circuit], &[&[&[output]]], &mut rng, &mut transcript, ) .expect("proof generation should not fail") }) }); // Create a proof let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); create_proof( ¶ms, &pk, &[circuit], &[&[&[output]]], &mut rng, &mut transcript, ) .expect("proof generation should not fail"); let proof = transcript.finalize(); c.bench_function(&verifier_name, |b| { b.iter(|| { let strategy = SingleVerifier::new(¶ms); let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]); assert!(verify_proof( ¶ms, pk.get_vk(), strategy, &[&[&[output]]], &mut transcript ) .is_ok()); }); }); } fn criterion_benchmark(c: &mut Criterion) { bench_poseidon::, 3, 2, 2>("WIDTH = 3, RATE = 2", c); bench_poseidon::, 9, 8, 8>("WIDTH = 9, RATE = 8", c); bench_poseidon::, 12, 11, 11>("WIDTH = 12, RATE = 11", c); } criterion_group!(benches, criterion_benchmark); criterion_main!(benches);