Skip to content

Commit

Permalink
[feat] update gen_dummy_snark to help with keygen (#48)
Browse files Browse the repository at this point in the history
* chore: update `gen_dummy_snark` with circuit-params

* chore: better doc comment

* chore: remove dbg

* feat: refactor into `gen_dummy_snark_from_vk`

This function does not need to know the `ConcreteCircuit` type

* feat: add `KeygenAggregationCircuitIntent` for keygen

Trait to help keygen of aggregation circuits.

* chore: fix clippy

* chore: rename function

* chore: add `AggregationDependencyIntentOwned`

* chore: From impl

* chore: add `gen_dummy_snark_from_protocol`

Also added `NativeKzgAccumulationScheme` trait

* chore: remove redundancy
  • Loading branch information
jonathanpwang authored Dec 13, 2023
1 parent 5c5791f commit 707b1ad
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 56 deletions.
63 changes: 32 additions & 31 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

111 changes: 88 additions & 23 deletions snark-verifier-sdk/src/halo2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use snark_verifier::{
system::halo2::{compile, Config},
util::arithmetic::Rotation,
util::transcript::TranscriptWrite,
verifier::plonk::PlonkProof,
verifier::plonk::{PlonkProof, PlonkProtocol},
};
use std::{
fs::{self, File},
Expand All @@ -46,6 +46,7 @@ use std::{
};

pub mod aggregation;
pub mod utils;

// Poseidon parameters
// We use the same ones Scroll uses for security: https://github.com/scroll-tech/poseidon-circuit/blob/714f50c7572a4ff6f2b1fa51a9604a99cd7b6c71/src/poseidon/primitives/bn256/fp.rs
Expand Down Expand Up @@ -274,38 +275,67 @@ pub fn read_snark(path: impl AsRef<Path>) -> Result<Snark, bincode::Error> {
bincode::deserialize_from(f)
}

pub trait NativeKzgAccumulationScheme = PolynomialCommitmentScheme<
G1Affine,
NativeLoader,
VerifyingKey = KzgSuccinctVerifyingKey<G1Affine>,
Output = KzgAccumulator<G1Affine, NativeLoader>,
> + AccumulationScheme<
G1Affine,
NativeLoader,
Accumulator = KzgAccumulator<G1Affine, NativeLoader>,
VerifyingKey = KzgAsVerifyingKey,
> + CostEstimation<G1Affine, Input = Vec<Query<Rotation>>>;

// copied from snark_verifier --example recursion
pub fn gen_dummy_snark<ConcreteCircuit, AS>(
params: &ParamsKZG<Bn256>,
vk: Option<&VerifyingKey<G1Affine>>,
num_instance: Vec<usize>,
circuit_params: ConcreteCircuit::Params,
) -> Snark
where
ConcreteCircuit: CircuitExt<Fr>,
AS: PolynomialCommitmentScheme<
G1Affine,
NativeLoader,
VerifyingKey = KzgSuccinctVerifyingKey<G1Affine>,
Output = KzgAccumulator<G1Affine, NativeLoader>,
> + AccumulationScheme<
G1Affine,
NativeLoader,
Accumulator = KzgAccumulator<G1Affine, NativeLoader>,
VerifyingKey = KzgAsVerifyingKey,
> + CostEstimation<G1Affine, Input = Vec<Query<Rotation>>>,
ConcreteCircuit::Params: Clone,
AS: NativeKzgAccumulationScheme,
{
struct CsProxy<F, C>(PhantomData<(F, C)>);
#[derive(Clone)]
struct CsProxy<F: Field, C: Circuit<F>> {
params: C::Params,
_marker: PhantomData<F>,
}

impl<F: Field, C: CircuitExt<F>> Circuit<F> for CsProxy<F, C> {
impl<F: Field, C: Circuit<F>> CsProxy<F, C> {
pub fn new(params: C::Params) -> Self {
Self { params, _marker: PhantomData }
}
}

impl<F: Field, C: CircuitExt<F>> Circuit<F> for CsProxy<F, C>
where
C::Params: Clone,
{
type Config = C::Config;
type FloorPlanner = C::FloorPlanner;
type Params = C::Params;

fn without_witnesses(&self) -> Self {
CsProxy(PhantomData)
Self::new(self.params.clone())
}

fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
C::configure(meta)
fn params(&self) -> Self::Params {
self.params.clone()
}

fn configure_with_params(
meta: &mut ConstraintSystem<F>,
params: Self::Params,
) -> Self::Config {
C::configure_with_params(meta, params)
}

fn configure(_: &mut ConstraintSystem<F>) -> Self::Config {
unreachable!("must use configure_with_params")
}

fn synthesize(
Expand All @@ -330,15 +360,50 @@ where

let dummy_vk = vk
.is_none()
.then(|| keygen_vk(params, &CsProxy::<Fr, ConcreteCircuit>(PhantomData)).unwrap());
let protocol = compile(
.then(|| keygen_vk(params, &CsProxy::<Fr, ConcreteCircuit>::new(circuit_params)).unwrap());

gen_dummy_snark_from_vk::<AS>(
params,
vk.or(dummy_vk.as_ref()).unwrap(),
Config::kzg()
.with_num_instance(num_instance.clone())
.with_accumulator_indices(ConcreteCircuit::accumulator_indices()),
num_instance,
ConcreteCircuit::accumulator_indices(),
)
}

/// Creates a dummy snark in the correct shape corresponding to the given verifying key.
/// This dummy snark will **not** verify.
/// This snark can be used as a placeholder input into an aggregation circuit expecting a snark
/// with this verifying key.
///
/// Note that this function does not need to know the concrete `Circuit` type.
pub fn gen_dummy_snark_from_vk<AS>(
params: &ParamsKZG<Bn256>,
vk: &VerifyingKey<G1Affine>,
num_instance: Vec<usize>,
accumulator_indices: Option<Vec<(usize, usize)>>,
) -> Snark
where
AS: NativeKzgAccumulationScheme,
{
let protocol = compile(
params,
vk,
Config::kzg().with_num_instance(num_instance).with_accumulator_indices(accumulator_indices),
);
let instances = num_instance.into_iter().map(|n| vec![Fr::default(); n]).collect();
gen_dummy_snark_from_protocol::<AS>(protocol)
}

/// Creates a dummy snark in the correct shape corresponding to the given Plonk protocol.
/// This dummy snark will **not** verify.
/// This snark can be used as a placeholder input into an aggregation circuit expecting a snark
/// with this protocol.
///
/// Note that this function does not need to know the concrete `Circuit` type.
pub fn gen_dummy_snark_from_protocol<AS>(protocol: PlonkProtocol<G1Affine>) -> Snark
where
AS: NativeKzgAccumulationScheme,
{
let instances = protocol.num_instance.iter().map(|&n| vec![Fr::default(); n]).collect();
let proof = {
let mut transcript = PoseidonTranscript::<NativeLoader, _>::new::<SECURE_MDS>(Vec::new());
for _ in 0..protocol
Expand Down
10 changes: 8 additions & 2 deletions snark-verifier-sdk/src/halo2/aggregation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ pub struct PreprocessedAndDomainAsWitness {

#[derive(Clone, Debug)]
pub struct SnarkAggregationWitness<'a> {
/// The (flattened) public instances from previous snarks that were aggregated, now collected as PRIVATE assigned values.
/// * If previous snark was from aggregation circuit, the previous instances will still contain the old KZG accumulator.
///
/// The user can optionally append these private witnesses to `inner.assigned_instances` to expose them.
pub previous_instances: Vec<Vec<AssignedValue<Fr>>>,
pub accumulator: KzgAccumulator<G1Affine, Rc<Halo2Loader<'a>>>,
/// This returns the assigned `preprocessed` and `transcript_initial_state` values as a vector of assigned values, one for each aggregated snark.
Expand Down Expand Up @@ -295,8 +299,10 @@ impl TryFrom<BaseCircuitParams> for AggregationConfigParams {
pub struct AggregationCircuit {
/// Circuit builder consisting of virtual region managers
pub builder: BaseCircuitBuilder<Fr>,
// the public instances from previous snarks that were aggregated, now collected as PRIVATE assigned values
// the user can optionally append these to `inner.assigned_instances` to expose them
/// The (flattened) public instances from previous snarks that were aggregated, now collected as PRIVATE assigned values.
/// * If previous snark was from aggregation circuit, the previous instances will still contain the old KZG accumulator.
///
/// The user can optionally append these private witnesses to `inner.assigned_instances` to expose them.
#[getset(get = "pub")]
previous_instances: Vec<Vec<AssignedValue<Fr>>>,
/// This returns the assigned `preprocessed_digest` (vkey), optional `transcript_initial_state`, `domain.n` (optional), and `omega` (optional) values as a vector of assigned values, one for each aggregated snark.
Expand Down
91 changes: 91 additions & 0 deletions snark-verifier-sdk/src/halo2/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use halo2_base::{
halo2_proofs::{
halo2curves::bn256::{Fr, G1Affine},
plonk::{Circuit, VerifyingKey},
},
utils::fs::read_params,
};

use crate::{CircuitExt, Snark, SHPLONK};

use super::{aggregation::AggregationCircuit, gen_dummy_snark_from_vk};

#[derive(Clone, Copy, Debug)]
pub struct AggregationDependencyIntent<'a> {
pub vk: &'a VerifyingKey<G1Affine>,
pub num_instance: &'a [usize],
pub is_aggregation: bool,
}

#[derive(Clone, Debug)]
pub struct AggregationDependencyIntentOwned {
pub vk: VerifyingKey<G1Affine>,
pub num_instance: Vec<usize>,
pub is_aggregation: bool,
}

/// This trait should be implemented on the minimal circuit configuration data necessary to
/// completely determine an aggregation circuit
/// (independent of circuit inputs or specific snarks to be aggregated).
/// This is used to generate a _dummy_ instantiation of a concrete `Circuit` type for the purposes of key generation.
/// This dummy instantiation just needs to have the correct arithmetization format, but the witnesses do not need to
/// satisfy constraints.
///
/// This trait is specialized for aggregation circuits, which need to aggregate **dependency** snarks.
/// The aggregation circuit should only depend on the verifying key of each dependency snark.
pub trait KeygenAggregationCircuitIntent {
/// Concrete circuit type. Defaults to [`AggregationCircuit`].
type AggregationCircuit: Circuit<Fr> = AggregationCircuit;

/// The **ordered** list of [`VerifyingKey`]s of the circuits to be aggregated.
fn intent_of_dependencies(&self) -> Vec<AggregationDependencyIntent>;

/// Builds a _dummy_ instantiation of `Self::AggregationCircuit` for the purposes of key generation.
/// Assumes that `snarks` is an ordered list of [`Snark`]s, where the `i`th snark corresponds to the `i`th [`VerifyingKey`] in `vk_of_dependencies`.
/// The `snarks` only need to have the correct witness sizes (e.g., proof length) but the
/// snarks do _not_ need to verify.
///
/// May specify additional custom logic for building the aggregation circuit from the snarks.
fn build_keygen_circuit_from_snarks(self, snarks: Vec<Snark>) -> Self::AggregationCircuit;

/// Builds a _dummy_ instantiation of `Self::AggregationCircuit` for the purposes of key generation.
///
/// Generates dummy snarks from the verifying keys in `vk_of_dependencies`, **assuming** that SHPLONK is
/// used for the multi-open scheme.
/// To do so, it will try to read KZG trusted setup files from the directory set by environmental variable
/// `PARAMS_DIR` or `./params/`.
// The `params` is not actually used, so this requirement should be removed in the future:
// requires refactoring `compile`.
fn build_keygen_circuit_shplonk(self) -> Self::AggregationCircuit
where
Self: Sized,
{
let snarks = self
.intent_of_dependencies()
.into_iter()
.map(|AggregationDependencyIntent { vk, num_instance, is_aggregation }| {
let k = vk.get_domain().k();
let params = read_params(k);
let accumulator_indices =
is_aggregation.then_some(AggregationCircuit::accumulator_indices().unwrap());
gen_dummy_snark_from_vk::<SHPLONK>(
&params,
vk,
num_instance.to_vec(),
accumulator_indices,
)
})
.collect();
self.build_keygen_circuit_from_snarks(snarks)
}
}

impl<'a> From<&'a AggregationDependencyIntentOwned> for AggregationDependencyIntent<'a> {
fn from(intent: &'a AggregationDependencyIntentOwned) -> Self {
Self {
vk: &intent.vk,
num_instance: &intent.num_instance,
is_aggregation: intent.is_aggregation,
}
}
}

0 comments on commit 707b1ad

Please sign in to comment.