use crate::{
gates::{
circuit::{builder::RangeCircuitBuilder, BaseCircuitParams, CircuitBuilderStage},
flex_gate::threads::SinglePhaseCoreManager,
GateChip, RangeChip,
},
halo2_proofs::{
dev::MockProver,
halo2curves::bn256::{Bn256, Fr, G1Affine},
plonk::{
create_proof, keygen_pk, keygen_vk, verify_proof, Circuit, ProvingKey, VerifyingKey,
},
poly::commitment::ParamsProver,
poly::kzg::{
commitment::KZGCommitmentScheme, commitment::ParamsKZG, multiopen::ProverSHPLONK,
multiopen::VerifierSHPLONK, strategy::SingleStrategy,
},
transcript::{
Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer,
},
},
Context,
};
use ark_std::{end_timer, perf_trace::TimerInfo, start_timer};
use rand::{rngs::StdRng, SeedableRng};
use super::fs::gen_srs;
pub fn gen_proof_with_instances(
params: &ParamsKZG<Bn256>,
pk: &ProvingKey<G1Affine>,
circuit: impl Circuit<Fr>,
instances: &[&[Fr]],
) -> Vec<u8> {
let rng = StdRng::seed_from_u64(0);
let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]);
create_proof::<
KZGCommitmentScheme<Bn256>,
ProverSHPLONK<'_, Bn256>,
Challenge255<_>,
_,
Blake2bWrite<Vec<u8>, G1Affine, _>,
_,
>(params, pk, &[circuit], &[instances], rng, &mut transcript)
.expect("prover should not fail");
transcript.finalize()
}
pub fn gen_proof(
params: &ParamsKZG<Bn256>,
pk: &ProvingKey<G1Affine>,
circuit: impl Circuit<Fr>,
) -> Vec<u8> {
gen_proof_with_instances(params, pk, circuit, &[])
}
pub fn check_proof_with_instances(
params: &ParamsKZG<Bn256>,
vk: &VerifyingKey<G1Affine>,
proof: &[u8],
instances: &[&[Fr]],
expect_satisfied: bool,
) {
let verifier_params = params.verifier_params();
let strategy = SingleStrategy::new(params);
let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(proof);
let res = verify_proof::<
KZGCommitmentScheme<Bn256>,
VerifierSHPLONK<'_, Bn256>,
Challenge255<G1Affine>,
Blake2bRead<&[u8], G1Affine, Challenge255<G1Affine>>,
SingleStrategy<'_, Bn256>,
>(verifier_params, vk, strategy, &[instances], &mut transcript);
if expect_satisfied {
res.unwrap();
} else {
assert!(res.is_err());
}
}
pub fn check_proof(
params: &ParamsKZG<Bn256>,
vk: &VerifyingKey<G1Affine>,
proof: &[u8],
expect_satisfied: bool,
) {
check_proof_with_instances(params, vk, proof, &[], expect_satisfied);
}
pub struct BaseTester {
k: u32,
lookup_bits: Option<usize>,
expect_satisfied: bool,
unusable_rows: usize,
}
impl Default for BaseTester {
fn default() -> Self {
Self { k: 10, lookup_bits: Some(9), expect_satisfied: true, unusable_rows: 9 }
}
}
pub fn base_test() -> BaseTester {
BaseTester::default()
}
impl BaseTester {
pub fn k(mut self, k: u32) -> Self {
self.k = k;
self.lookup_bits = Some(k as usize - 1);
self
}
pub fn lookup_bits(mut self, lookup_bits: usize) -> Self {
assert!(lookup_bits < self.k as usize, "lookup_bits must be less than k");
self.lookup_bits = Some(lookup_bits);
self
}
pub fn expect_satisfied(mut self, expect_satisfied: bool) -> Self {
self.expect_satisfied = expect_satisfied;
self
}
pub fn unusable_rows(mut self, unusable_rows: usize) -> Self {
self.unusable_rows = unusable_rows;
self
}
pub fn run<R>(&self, f: impl FnOnce(&mut Context<Fr>, &RangeChip<Fr>) -> R) -> R {
self.run_builder(|builder, range| f(builder.main(), range))
}
pub fn run_gate<R>(&self, f: impl FnOnce(&mut Context<Fr>, &GateChip<Fr>) -> R) -> R {
self.run(|ctx, range| f(ctx, &range.gate))
}
pub fn run_builder<R>(
&self,
f: impl FnOnce(&mut SinglePhaseCoreManager<Fr>, &RangeChip<Fr>) -> R,
) -> R {
let mut builder = RangeCircuitBuilder::default().use_k(self.k as usize);
if let Some(lb) = self.lookup_bits {
builder.set_lookup_bits(lb)
}
let range = RangeChip::new(self.lookup_bits.unwrap_or(0), builder.lookup_manager().clone());
let res = f(builder.pool(0), &range);
let t_cells_lookup =
builder.lookup_manager().iter().map(|lm| lm.total_rows()).sum::<usize>();
let lookup_bits = if t_cells_lookup == 0 { None } else { self.lookup_bits };
builder.config_params.lookup_bits = lookup_bits;
builder.calculate_params(Some(self.unusable_rows));
if self.expect_satisfied {
MockProver::run(self.k, &builder, vec![]).unwrap().assert_satisfied();
} else {
assert!(MockProver::run(self.k, &builder, vec![]).unwrap().verify().is_err());
}
res
}
pub fn bench_builder<I: Clone>(
&self,
init_input: I,
logic_input: I,
f: impl Fn(&mut SinglePhaseCoreManager<Fr>, &RangeChip<Fr>, I),
) -> BenchStats {
let mut builder =
RangeCircuitBuilder::from_stage(CircuitBuilderStage::Keygen).use_k(self.k as usize);
if let Some(lb) = self.lookup_bits {
builder.set_lookup_bits(lb)
}
let range = RangeChip::new(self.lookup_bits.unwrap_or(0), builder.lookup_manager().clone());
f(builder.pool(0), &range, init_input);
let t_cells_lookup =
builder.lookup_manager().iter().map(|lm| lm.total_rows()).sum::<usize>();
let lookup_bits = if t_cells_lookup == 0 { None } else { self.lookup_bits };
builder.config_params.lookup_bits = lookup_bits;
let config_params = builder.calculate_params(Some(self.unusable_rows));
let params = gen_srs(self.k);
let vk_time = start_timer!(|| "Generating vkey");
let vk = keygen_vk(¶ms, &builder).unwrap();
end_timer!(vk_time);
let pk_time = start_timer!(|| "Generating pkey");
let pk = keygen_pk(¶ms, vk, &builder).unwrap();
end_timer!(pk_time);
let break_points = builder.break_points();
drop(builder);
let proof_time = start_timer!(|| "Proving time");
let mut builder = RangeCircuitBuilder::prover(config_params.clone(), break_points);
let range = RangeChip::new(self.lookup_bits.unwrap_or(0), builder.lookup_manager().clone());
f(builder.pool(0), &range, logic_input);
let proof = gen_proof(¶ms, &pk, builder);
end_timer!(proof_time);
let proof_size = proof.len();
let verify_time = start_timer!(|| "Verify time");
check_proof(¶ms, pk.get_vk(), &proof, self.expect_satisfied);
end_timer!(verify_time);
BenchStats { config_params, vk_time, pk_time, proof_time, proof_size, verify_time }
}
}
pub struct BenchStats {
pub config_params: BaseCircuitParams,
pub vk_time: TimerInfo,
pub pk_time: TimerInfo,
pub proof_time: TimerInfo,
pub proof_size: usize,
pub verify_time: TimerInfo,
}