Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: avoid superfluous clones in ProverKey and VerifierKey for Nova & SuperNova #285

Merged
merged 4 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor: Serialization with Abomonation
- Extended functionality in `lib.rs` with new `PublicParams` structure, efficient abomonationwith `FlatPublicParams`,
- Updated `mod.rs` for conditional feature `abomonate`, added `AuxParams` struct, and implemented conversions between `AuxParams` and `FlatAuxParams`.
- demoed the new use of Abomonation in the minroot example
  • Loading branch information
huitseeker committed Jan 29, 2024
commit 78741ebb9911c35ce894cb599f95f74b268d144f
20 changes: 13 additions & 7 deletions examples/minroot.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Demonstrates how to use Nova to produce a recursive proof of the correct execution of
//! iterations of the `MinRoot` function, thereby realizing a Nova-based verifiable delay function (VDF).
//! We execute a configurable number of iterations of the `MinRoot` function per step of Nova's recursion.
#[cfg(feature = "abomonate")]
use arecibo::FlatPublicParams;
use arecibo::{
provider::{Bn256EngineKZG, GrumpkinEngine},
traits::{
Expand Down Expand Up @@ -232,14 +234,16 @@ fn main() {
);
println!("PublicParams::setup, took {:?} ", start.elapsed());
#[cfg(feature = "abomonate")]
{
let pp = {
use abomonation::encode;
let mut file = std::fs::File::create(utils::FILEPATH).unwrap();
let flat_params = FlatPublicParams::try_from(pp).expect("error encoding pps!");
unsafe {
encode(&pp, &mut file).unwrap();
encode(&flat_params, &mut file).unwrap();
}
println!("Encoded!");
}
PublicParams::from(flat_params)
};

println!(
"Number of constraints per step (primary circuit): {}",
Expand Down Expand Up @@ -270,16 +274,18 @@ fn main() {
reader.read_to_end(&mut bytes).unwrap();
if let Some((result, remaining)) = unsafe {
decode::<
PublicParams<
FlatPublicParams<
E1,
E2,
MinRootCircuit<<E1 as Engine>::GE>,
TrivialCircuit<<E2 as Engine>::Scalar>,
>,
>(&mut bytes)
} {
assert!(*result == pp, "decoded parameters not equal to original!");
let result_pp = PublicParams::from(result.clone());
assert!(result_pp == pp, "decoded parameters not equal to original!");
assert!(remaining.is_empty());
println!("Decoded!");
} else {
println!("Decoding failure!");
}
Expand Down Expand Up @@ -352,8 +358,8 @@ fn main() {
type E2 = GrumpkinEngine;
type EE1 = arecibo::provider::hyperkzg::EvaluationEngine<Bn256, E1>;
type EE2 = arecibo::provider::ipa_pc::EvaluationEngine<E2>;
type S1 = arecibo::spartan::snark::RelaxedR1CSSNARK<E1, EE1>; // non-preprocessing SNARK
type S2 = arecibo::spartan::snark::RelaxedR1CSSNARK<E2, EE2>; // non-preprocessing SNARK
type S1 = arecibo::spartan::ppsnark::RelaxedR1CSSNARK<E1, EE1>; // non-preprocessing SNARK
type S2 = arecibo::spartan::ppsnark::RelaxedR1CSSNARK<E2, EE2>; // non-preprocessing SNARK

let res = CompressedSNARK::<_, _, _, _, S1, S2>::prove(&pp, &pk, &recursive_snark);
println!(
Expand Down
103 changes: 95 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,37 @@ impl<E: Engine> R1CSWithArity<E> {
}

/// A type that holds public parameters of Nova
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(bound = "")]
pub struct PublicParams<E1, E2, C1, C2>
where
E1: Engine<Base = <E2 as Engine>::Scalar>,
E2: Engine<Base = <E1 as Engine>::Scalar>,
C1: StepCircuit<E1::Scalar>,
C2: StepCircuit<E2::Scalar>,
{
F_arity_primary: usize,
F_arity_secondary: usize,
ro_consts_primary: ROConstants<E1>,
ro_consts_circuit_primary: ROConstantsCircuit<E2>,
ck_primary: Arc<CommitmentKey<E1>>,
circuit_shape_primary: R1CSWithArity<E1>,
ro_consts_secondary: ROConstants<E2>,
ro_consts_circuit_secondary: ROConstantsCircuit<E1>,
ck_secondary: Arc<CommitmentKey<E2>>,
circuit_shape_secondary: R1CSWithArity<E2>,
augmented_circuit_params_primary: NovaAugmentedCircuitParams,
augmented_circuit_params_secondary: NovaAugmentedCircuitParams,
#[serde(skip, default = "OnceCell::new")]
digest: OnceCell<E1::Scalar>,
_p: PhantomData<(C1, C2)>,
}

// Ensure to include necessary crates and features in your Cargo.toml
// e.g., abomonation, serde, etc., with the appropriate feature flags.

/// A version of [`crate::PublicParams`] that is amenable to fast ser/de using Abomonation
#[cfg(feature = "abomonate")]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Abomonation)]
#[serde(bound = "")]
#[abomonation_bounds(
Expand All @@ -99,7 +130,7 @@ where
<E1::Scalar as PrimeField>::Repr: Abomonation,
<E2::Scalar as PrimeField>::Repr: Abomonation,
)]
pub struct PublicParams<E1, E2, C1, C2>
pub struct FlatPublicParams<E1, E2, C1, C2>
where
E1: Engine<Base = <E2 as Engine>::Scalar>,
E2: Engine<Base = <E1 as Engine>::Scalar>,
Expand All @@ -110,22 +141,78 @@ where
F_arity_secondary: usize,
ro_consts_primary: ROConstants<E1>,
ro_consts_circuit_primary: ROConstantsCircuit<E2>,
#[abomonate_with(CommitmentKey<E1>)]
ck_primary: Arc<CommitmentKey<E1>>,
ck_primary: CommitmentKey<E1>,
circuit_shape_primary: R1CSWithArity<E1>,
ro_consts_secondary: ROConstants<E2>,
ro_consts_circuit_secondary: ROConstantsCircuit<E1>,
#[abomonate_with(CommitmentKey<E2>)]
ck_secondary: Arc<CommitmentKey<E2>>,
ck_secondary: CommitmentKey<E2>,
circuit_shape_secondary: R1CSWithArity<E2>,
augmented_circuit_params_primary: NovaAugmentedCircuitParams,
augmented_circuit_params_secondary: NovaAugmentedCircuitParams,
#[abomonation_skip]
#[serde(skip, default = "OnceCell::new")]
digest: OnceCell<E1::Scalar>,
_p: PhantomData<(C1, C2)>,
}

#[cfg(feature = "abomonate")]
impl<E1, E2, C1, C2> TryFrom<PublicParams<E1, E2, C1, C2>> for FlatPublicParams<E1, E2, C1, C2>
where
E1: Engine<Base = <E2 as Engine>::Scalar>,
E2: Engine<Base = <E1 as Engine>::Scalar>,
C1: StepCircuit<E1::Scalar>,
C2: StepCircuit<E2::Scalar>,
{
type Error = &'static str;

fn try_from(value: PublicParams<E1, E2, C1, C2>) -> Result<Self, Self::Error> {
let ck_primary =
Arc::try_unwrap(value.ck_primary).map_err(|_| "Failed to unwrap Arc for ck_primary")?;
let ck_secondary =
Arc::try_unwrap(value.ck_secondary).map_err(|_| "Failed to unwrap Arc for ck_secondary")?;
Ok(Self {
F_arity_primary: value.F_arity_primary,
F_arity_secondary: value.F_arity_secondary,
ro_consts_primary: value.ro_consts_primary,
ro_consts_circuit_primary: value.ro_consts_circuit_primary,
ck_primary,
circuit_shape_primary: value.circuit_shape_primary,
ro_consts_secondary: value.ro_consts_secondary,
ro_consts_circuit_secondary: value.ro_consts_circuit_secondary,
ck_secondary,
circuit_shape_secondary: value.circuit_shape_secondary,
augmented_circuit_params_primary: value.augmented_circuit_params_primary,
augmented_circuit_params_secondary: value.augmented_circuit_params_secondary,
_p: PhantomData,
})
}
}

#[cfg(feature = "abomonate")]
impl<E1, E2, C1, C2> From<FlatPublicParams<E1, E2, C1, C2>> for PublicParams<E1, E2, C1, C2>
where
E1: Engine<Base = <E2 as Engine>::Scalar>,
E2: Engine<Base = <E1 as Engine>::Scalar>,
C1: StepCircuit<E1::Scalar>,
C2: StepCircuit<E2::Scalar>,
{
fn from(value: FlatPublicParams<E1, E2, C1, C2>) -> Self {
Self {
F_arity_primary: value.F_arity_primary,
F_arity_secondary: value.F_arity_secondary,
ro_consts_primary: value.ro_consts_primary,
ro_consts_circuit_primary: value.ro_consts_circuit_primary,
ck_primary: Arc::new(value.ck_primary),
circuit_shape_primary: value.circuit_shape_primary,
ro_consts_secondary: value.ro_consts_secondary,
ro_consts_circuit_secondary: value.ro_consts_circuit_secondary,
ck_secondary: Arc::new(value.ck_secondary),
circuit_shape_secondary: value.circuit_shape_secondary,
augmented_circuit_params_primary: value.augmented_circuit_params_primary,
augmented_circuit_params_secondary: value.augmented_circuit_params_secondary,
digest: OnceCell::new(),
_p: PhantomData,
}
}
}

impl<E1, E2, C1, C2> SimpleDigestible for PublicParams<E1, E2, C1, C2>
where
E1: Engine<Base = <E2 as Engine>::Scalar>,
Expand Down
87 changes: 81 additions & 6 deletions src/supernova/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@ use crate::{
Commitment, CommitmentKey, R1CSWithArity,
};

#[cfg(feature = "abomonate")]
use abomonation::Abomonation;
#[cfg(feature = "abomonate")]
use abomonation_derive::Abomonation;
use bellpepper_core::SynthesisError;
use ff::{Field, PrimeField};
use ff::Field;
#[cfg(feature = "abomonate")]
use ff::PrimeField;
use itertools::Itertools as _;
use once_cell::sync::OnceCell;
use rayon::prelude::*;
Expand Down Expand Up @@ -108,6 +112,29 @@ where
/// Auxiliary [PublicParams] information about the commitment keys and
/// secondary circuit. This is used as a helper struct when reconstructing
/// [PublicParams] downstream in lurk.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(bound = "")]
pub struct AuxParams<E1, E2>
where
E1: Engine<Base = <E2 as Engine>::Scalar>,
E2: Engine<Base = <E1 as Engine>::Scalar>,
{
ro_consts_primary: ROConstants<E1>,
ro_consts_circuit_primary: ROConstantsCircuit<E2>,
ck_primary: Arc<CommitmentKey<E1>>, // This is shared between all circuit params
augmented_circuit_params_primary: SuperNovaAugmentedCircuitParams,

ro_consts_secondary: ROConstants<E2>,
ro_consts_circuit_secondary: ROConstantsCircuit<E1>,
ck_secondary: Arc<CommitmentKey<E2>>,
circuit_shape_secondary: R1CSWithArity<E2>,
augmented_circuit_params_secondary: SuperNovaAugmentedCircuitParams,

digest: E1::Scalar,
}

/// A variant of [`crate::supernova::AuxParams`] that is suitable for fast ser/de using Abomonation
#[cfg(feature = "abomonate")]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Abomonation)]
#[serde(bound = "")]
#[abomonation_bounds(
Expand All @@ -117,28 +144,76 @@ where
<E1::Scalar as PrimeField>::Repr: Abomonation,
<E2::Scalar as PrimeField>::Repr: Abomonation,
)]
pub struct AuxParams<E1, E2>
pub struct FlatAuxParams<E1, E2>
where
E1: Engine<Base = <E2 as Engine>::Scalar>,
E2: Engine<Base = <E1 as Engine>::Scalar>,
{
ro_consts_primary: ROConstants<E1>,
ro_consts_circuit_primary: ROConstantsCircuit<E2>,
#[abomonate_with(CommitmentKey<E1>)]
ck_primary: Arc<CommitmentKey<E1>>, // This is shared between all circuit params
ck_primary: CommitmentKey<E1>, // This is shared between all circuit params
augmented_circuit_params_primary: SuperNovaAugmentedCircuitParams,

ro_consts_secondary: ROConstants<E2>,
ro_consts_circuit_secondary: ROConstantsCircuit<E1>,
#[abomonate_with(CommitmentKey<E2>)]
ck_secondary: Arc<CommitmentKey<E2>>,
ck_secondary: CommitmentKey<E2>,
circuit_shape_secondary: R1CSWithArity<E2>,
augmented_circuit_params_secondary: SuperNovaAugmentedCircuitParams,

#[abomonate_with(<E1::Scalar as PrimeField>::Repr)]
digest: E1::Scalar,
}

#[cfg(feature = "abomonate")]
impl<E1, E2> TryFrom<AuxParams<E1, E2>> for FlatAuxParams<E1, E2>
where
E1: Engine<Base = <E2 as Engine>::Scalar>,
E2: Engine<Base = <E1 as Engine>::Scalar>,
{
type Error = &'static str;

fn try_from(value: AuxParams<E1, E2>) -> Result<Self, Self::Error> {
let ck_primary =
Arc::try_unwrap(value.ck_primary).map_err(|_| "Failed to unwrap Arc for ck_primary")?;
let ck_secondary =
Arc::try_unwrap(value.ck_secondary).map_err(|_| "Failed to unwrap Arc for ck_secondary")?;
Ok(Self {
ro_consts_primary: value.ro_consts_primary,
ro_consts_circuit_primary: value.ro_consts_circuit_primary,
ck_primary,
augmented_circuit_params_primary: value.augmented_circuit_params_primary,
ro_consts_secondary: value.ro_consts_secondary,
ro_consts_circuit_secondary: value.ro_consts_circuit_secondary,
ck_secondary,
circuit_shape_secondary: value.circuit_shape_secondary,
augmented_circuit_params_secondary: value.augmented_circuit_params_secondary,
digest: value.digest,
})
}
}

#[cfg(feature = "abomonate")]
impl<E1, E2> From<FlatAuxParams<E1, E2>> for AuxParams<E1, E2>
where
E1: Engine<Base = <E2 as Engine>::Scalar>,
E2: Engine<Base = <E1 as Engine>::Scalar>,
{
fn from(value: FlatAuxParams<E1, E2>) -> Self {
Self {
ro_consts_primary: value.ro_consts_primary,
ro_consts_circuit_primary: value.ro_consts_circuit_primary,
ck_primary: Arc::new(value.ck_primary),
augmented_circuit_params_primary: value.augmented_circuit_params_primary,
ro_consts_secondary: value.ro_consts_secondary,
ro_consts_circuit_secondary: value.ro_consts_circuit_secondary,
ck_secondary: Arc::new(value.ck_secondary),
circuit_shape_secondary: value.circuit_shape_secondary,
augmented_circuit_params_secondary: value.augmented_circuit_params_secondary,
digest: value.digest,
}
}
}

impl<E1, E2, C1, C2> Index<usize> for PublicParams<E1, E2, C1, C2>
where
E1: Engine<Base = <E2 as Engine>::Scalar>,
Expand Down
1 change: 1 addition & 0 deletions src/supernova/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::supernova::circuit::{
};
use crate::traits::snark::default_ck_hint;
use crate::{bellpepper::test_shape_cs::TestShapeCS, gadgets::utils::alloc_one};
use abomonation::Abomonation;
use bellpepper_core::num::AllocatedNum;
use bellpepper_core::{ConstraintSystem, SynthesisError};
use core::marker::PhantomData;
Expand Down
Loading