halo2/halo2_gadgets/benches/poseidon.rs

260 lines
7.3 KiB
Rust

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, 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<S, const WIDTH: usize, const RATE: usize, const L: usize>
where
S: Spec<Fp, WIDTH, RATE> + Clone + Copy,
{
message: Value<[Fp; L]>,
// For the purpose of this test, witness the result.
// TODO: Move this into an instance column.
output: Value<Fp>,
_spec: PhantomData<S>,
}
#[derive(Debug, Clone)]
struct MyConfig<const WIDTH: usize, const RATE: usize, const L: usize> {
input: [Column<Advice>; L],
poseidon_config: Pow5Config<Fp, WIDTH, RATE>,
}
impl<S, const WIDTH: usize, const RATE: usize, const L: usize> Circuit<Fp>
for HashCircuit<S, WIDTH, RATE, L>
where
S: Spec<Fp, WIDTH, RATE> + Copy + Clone,
{
type Config = MyConfig<WIDTH, RATE, L>;
type FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self {
Self {
message: Value::unknown(),
output: Value::unknown(),
_spec: PhantomData,
}
}
fn configure(meta: &mut ConstraintSystem<Fp>) -> Self::Config {
let state = (0..WIDTH).map(|_| meta.advice_column()).collect::<Vec<_>>();
let partial_sbox = meta.advice_column();
let rc_a = (0..WIDTH).map(|_| meta.fixed_column()).collect::<Vec<_>>();
let rc_b = (0..WIDTH).map(|_| meta.fixed_column()).collect::<Vec<_>>();
meta.enable_constant(rc_b[0]);
Self::Config {
input: state[..RATE].try_into().unwrap(),
poseidon_config: Pow5Chip::configure::<S>(
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<Fp>,
) -> 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<Vec<_>, Error> = (0..L).map(message_word).collect();
Ok(message?.try_into().unwrap())
},
)?;
let hasher = Hash::<_, _, S, ConstantLength<L>, WIDTH, RATE>::init(
chip,
layouter.namespace(|| "init"),
)?;
let output = hasher.hash(layouter.namespace(|| "hash"), message)?;
layouter.assign_region(
|| "constrain output",
|mut region| {
let expected_var =
region.assign_advice(|| "load output", config.input[0], 0, || self.output)?;
region.constrain_equal(output.cell(), expected_var.cell())
},
)
}
}
#[derive(Debug, Clone, Copy)]
struct MySpec<const WIDTH: usize, const RATE: usize>;
impl Spec<Fp, 3, 2> for MySpec<3, 2> {
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; 3]>, Mds<Fp, 3>, Mds<Fp, 3>) {
generate_constants::<_, Self, 3, 2>()
}
}
impl Spec<Fp, 9, 8> for MySpec<9, 8> {
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; 9]>, Mds<Fp, 9>, Mds<Fp, 9>) {
generate_constants::<_, Self, 9, 8>()
}
}
impl Spec<Fp, 12, 11> for MySpec<12, 11> {
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; 12]>, Mds<Fp, 12>, Mds<Fp, 12>) {
generate_constants::<_, Self, 12, 11>()
}
}
const K: u32 = 6;
fn bench_poseidon<S, const WIDTH: usize, const RATE: usize, const L: usize>(
name: &str,
c: &mut Criterion,
) where
S: Spec<Fp, WIDTH, RATE> + Copy + Clone,
{
// Initialize the polynomial commitment parameters
let params: Params<vesta::Affine> = Params::new(K);
let empty_circuit = HashCircuit::<S, WIDTH, RATE, L> {
message: Value::unknown(),
output: Value::unknown(),
_spec: PhantomData,
};
// Initialize the proving key
let vk = keygen_vk(&params, &empty_circuit).expect("keygen_vk should not fail");
let pk = keygen_pk(&params, 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::<Vec<_>>()
.try_into()
.unwrap();
let output = poseidon::Hash::<_, S, ConstantLength<L>, WIDTH, RATE>::init().hash(message);
let circuit = HashCircuit::<S, WIDTH, RATE, L> {
message: Value::known(message),
output: Value::known(output),
_spec: PhantomData,
};
c.bench_function(&prover_name, |b| {
b.iter(|| {
// Create a proof
let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]);
create_proof(&params, &pk, &[circuit], &[&[]], &mut rng, &mut transcript)
.expect("proof generation should not fail")
})
});
// Create a proof
let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]);
create_proof(&params, &pk, &[circuit], &[&[]], &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(&params);
let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]);
assert!(verify_proof(&params, pk.get_vk(), strategy, &[&[]], &mut transcript).is_ok());
});
});
}
fn criterion_benchmark(c: &mut Criterion) {
bench_poseidon::<MySpec<3, 2>, 3, 2, 2>("WIDTH = 3, RATE = 2", c);
bench_poseidon::<MySpec<9, 8>, 9, 8, 8>("WIDTH = 9, RATE = 8", c);
bench_poseidon::<MySpec<12, 11>, 12, 11, 11>("WIDTH = 12, RATE = 11", c);
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);