use halo2_base::{
gates::{
circuit::{builder::BaseCircuitBuilder, BaseCircuitParams},
flex_gate::MultiPhaseThreadBreakPoints,
},
halo2_proofs::{
halo2curves::bn256::{Bn256, Fr, G1Affine},
plonk::{Circuit, ProvingKey},
poly::kzg::commitment::ParamsKZG,
},
utils::ScalarField,
};
use serde::{Deserialize, Serialize};
use snark_verifier_sdk::gen_pk;
use std::{fs::File, io, path::Path};
use crate::{
rlc::{circuit::RlcCircuitParams, virtual_region::RlcThreadBreakPoints},
utils::eth_circuit::ETH_LOOKUP_BITS,
};
pub trait Halo2CircuitPinning: Serialize + Sized + for<'de> Deserialize<'de> {
type CircuitParams;
type BreakPoints;
fn new(params: Self::CircuitParams, break_points: Self::BreakPoints) -> Self;
fn params(&self) -> Self::CircuitParams;
fn break_points(&self) -> Self::BreakPoints;
fn k(&self) -> usize;
fn from_path<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> {
let p = serde_json::from_reader(File::open(&path)?)?;
Ok(p)
}
fn write<P: AsRef<Path>>(&self, path: P) -> anyhow::Result<()> {
serde_json::to_writer_pretty(File::create(path)?, self)?;
Ok(())
}
}
pub trait CircuitPinningInstructions {
type Pinning: Halo2CircuitPinning;
fn pinning(&self) -> Self::Pinning;
}
pub trait PinnableCircuit: CircuitPinningInstructions + Sized + Circuit<Fr> {
fn read_pk(
path: impl AsRef<Path>,
circuit_params: <Self as Circuit<Fr>>::Params,
) -> io::Result<ProvingKey<G1Affine>> {
snark_verifier_sdk::read_pk::<Self>(path.as_ref(), circuit_params)
}
fn create_pk(
&self,
params: &ParamsKZG<Bn256>,
pk_path: impl AsRef<Path>,
pinning_path: impl AsRef<Path>,
) -> anyhow::Result<(ProvingKey<G1Affine>, Self::Pinning)> {
let circuit_params = self.params();
if let Ok(pk) = Self::read_pk(pk_path.as_ref(), circuit_params) {
let pinning = Self::Pinning::from_path(pinning_path.as_ref())?;
Ok((pk, pinning))
} else {
let pk = gen_pk(params, self, Some(pk_path.as_ref()));
let pinning = self.pinning();
pinning.write(pinning_path)?;
Ok((pk, pinning))
}
}
}
impl<C> PinnableCircuit for C where C: CircuitPinningInstructions + Sized + Circuit<Fr> {}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct RlcCircuitPinning {
pub params: RlcCircuitParams,
pub break_points: RlcThreadBreakPoints,
}
impl Halo2CircuitPinning for RlcCircuitPinning {
type CircuitParams = RlcCircuitParams;
type BreakPoints = RlcThreadBreakPoints;
fn new(params: Self::CircuitParams, break_points: Self::BreakPoints) -> Self {
Self { params, break_points }
}
fn from_path<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> {
let mut pinning: Self = serde_json::from_reader(File::open(&path)?)?;
if pinning.params.base.lookup_bits.is_none() {
pinning.params.base.lookup_bits = Some(ETH_LOOKUP_BITS);
}
Ok(pinning)
}
fn params(&self) -> Self::CircuitParams {
self.params.clone()
}
fn break_points(&self) -> RlcThreadBreakPoints {
self.break_points.clone()
}
fn k(&self) -> usize {
self.params.base.k
}
}
#[derive(Clone, Debug, Serialize, Deserialize, Default, Hash)]
pub struct BaseCircuitPinning {
pub params: BaseCircuitParams,
pub break_points: MultiPhaseThreadBreakPoints,
}
impl Halo2CircuitPinning for BaseCircuitPinning {
type CircuitParams = BaseCircuitParams;
type BreakPoints = MultiPhaseThreadBreakPoints;
fn new(params: Self::CircuitParams, break_points: Self::BreakPoints) -> Self {
Self { params, break_points }
}
fn params(&self) -> Self::CircuitParams {
self.params.clone()
}
fn break_points(&self) -> MultiPhaseThreadBreakPoints {
self.break_points.clone()
}
fn k(&self) -> usize {
self.params.k
}
}
impl<F: ScalarField> CircuitPinningInstructions for BaseCircuitBuilder<F> {
type Pinning = BaseCircuitPinning;
fn pinning(&self) -> Self::Pinning {
let break_points = self.break_points();
let params = self.params();
Self::Pinning::new(params, break_points)
}
}
#[cfg(feature = "aggregation")]
pub mod aggregation {
use halo2_base::{
gates::flex_gate::MultiPhaseThreadBreakPoints,
halo2_proofs::{
halo2curves::bn256::{Bn256, G1Affine},
plonk::Circuit,
},
};
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use snark_verifier::{pcs::kzg::KzgDecidingKey, verifier::plonk::PlonkProtocol};
use snark_verifier_sdk::halo2::aggregation::{AggregationCircuit, AggregationConfigParams};
use super::{CircuitPinningInstructions, Halo2CircuitPinning};
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct AggregationCircuitPinning {
pub params: AggregationConfigParams,
pub break_points: MultiPhaseThreadBreakPoints,
}
impl Halo2CircuitPinning for AggregationCircuitPinning {
type CircuitParams = AggregationConfigParams;
type BreakPoints = MultiPhaseThreadBreakPoints;
fn new(params: Self::CircuitParams, break_points: Self::BreakPoints) -> Self {
Self { params, break_points }
}
fn params(&self) -> Self::CircuitParams {
self.params
}
fn break_points(&self) -> MultiPhaseThreadBreakPoints {
self.break_points.clone()
}
fn k(&self) -> usize {
self.params.degree as usize
}
}
impl CircuitPinningInstructions for AggregationCircuit {
type Pinning = AggregationCircuitPinning;
fn pinning(&self) -> Self::Pinning {
let break_points = self.break_points();
let params = self.params();
AggregationCircuitPinning::new(params, break_points)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct GenericAggPinning<AggParams> {
pub params: AggParams,
pub num_instance: Vec<usize>,
pub accumulator_indices: Vec<(usize, usize)>,
pub agg_vk_hash_data: Option<((usize, usize), String)>,
pub dk: KzgDecidingKey<Bn256>,
pub break_points: MultiPhaseThreadBreakPoints,
}
#[serde_as]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct GenericAggParams {
#[serde_as(as = "Vec<crate::utils::snark_verifier::Base64Bytes>")]
pub to_agg: Vec<PlonkProtocol<G1Affine>>,
pub agg_params: AggregationConfigParams,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct AggTreeId {
pub circuit_id: String,
pub children: Vec<AggTreeId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub aggregate_vk_hash: Option<String>,
}
}