Skip to content

Commit

Permalink
Rewire batched SNARK to act as a regular SNARK (microsoft#320)
Browse files Browse the repository at this point in the history
* feat: Implement RelaxedR1CSSNARK in batched.rs

- Extended `snark` functionality with the addition of `RelaxedR1CSSNARKTrait`.
- Added implementation for `RelaxedR1CSSNARKTrait` in `BatchedRelaxedR1CSSNARK`,
- Leveraged unsafe Rust for creating slices for the `U` and `W` single elements in the new `RelaxedR1CSSNARKTrait` methods.

* feat: Implement batched Spartan tests

- Included testing for the batched workflow across  non-trivial circuits, and "spark" compression.

* feat: Implement new benchmarks for batched compressed SNARKs

- Two new benchmark targets have been added: `bench_compressed_batched_snark` and `bench_compressed_batched_snark_with_computational_commitments`

* fix: use slice::from_ref instead of unsafe
  • Loading branch information
huitseeker authored Feb 13, 2024
1 parent 9064bfe commit 4ae3a57
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 5 deletions.
63 changes: 61 additions & 2 deletions benches/compressed-snark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ cfg_if::cfg_if! {
criterion_group! {
name = compressed_snark;
config = Criterion::default().warm_up_time(Duration::from_millis(3000)).with_profiler(pprof::criterion::PProfProfiler::new(100, pprof::criterion::Output::Flamegraph(None)));
targets = bench_compressed_snark, bench_compressed_snark_with_computational_commitments
targets = bench_compressed_snark, bench_compressed_snark_with_computational_commitments, bench_compressed_batched_snark, bench_compressed_batched_snark_with_computational_commitments
}
} else {
criterion_group! {
name = compressed_snark;
config = Criterion::default().warm_up_time(Duration::from_millis(3000));
targets = bench_compressed_snark, bench_compressed_snark_with_computational_commitments
targets = bench_compressed_snark, bench_compressed_snark_with_computational_commitments, bench_compressed_batched_snark, bench_compressed_batched_snark_with_computational_commitments
}
}
}
Expand Down Expand Up @@ -186,6 +186,65 @@ fn bench_compressed_snark_with_computational_commitments(c: &mut Criterion) {
}
}

// SNARKs without computation commitmnets
type BS1 = arecibo::spartan::batched::BatchedRelaxedR1CSSNARK<E1, EE1>;
type BS2 = arecibo::spartan::batched::BatchedRelaxedR1CSSNARK<E2, EE2>;
// SNARKs with computation commitmnets
type BSS1 = arecibo::spartan::batched_ppsnark::BatchedRelaxedR1CSSNARK<E1, EE1>;
type BSS2 = arecibo::spartan::batched_ppsnark::BatchedRelaxedR1CSSNARK<E2, EE2>;

fn bench_compressed_batched_snark(c: &mut Criterion) {
// we vary the number of constraints in the step circuit
for &num_cons_in_augmented_circuit in [
NUM_CONS_VERIFIER_CIRCUIT_PRIMARY,
16384,
32768,
65536,
131072,
262144,
]
.iter()
{
// number of constraints in the step circuit
let num_cons = num_cons_in_augmented_circuit - NUM_CONS_VERIFIER_CIRCUIT_PRIMARY;

let mut group = c.benchmark_group("BatchedCompressedSNARK");
group.sampling_mode(SamplingMode::Flat);
group.sample_size(NUM_SAMPLES);
group.noise_threshold(noise_threshold_env().unwrap_or(0.05));

bench_compressed_snark_internal::<BS1, BS2>(&mut group, num_cons);

group.finish();
}
}

fn bench_compressed_batched_snark_with_computational_commitments(c: &mut Criterion) {
// we vary the number of constraints in the step circuit
for &num_cons_in_augmented_circuit in [
NUM_CONS_VERIFIER_CIRCUIT_PRIMARY,
16384,
32768,
65536,
131072,
262144,
]
.iter()
{
// number of constraints in the step circuit
let num_cons = num_cons_in_augmented_circuit - NUM_CONS_VERIFIER_CIRCUIT_PRIMARY;

let mut group = c.benchmark_group("BatchedCompressedSNARK-Commitments");
group.sampling_mode(SamplingMode::Flat);
group.sample_size(NUM_SAMPLES);
group.noise_threshold(noise_threshold_env().unwrap_or(0.05));

bench_compressed_snark_internal::<BSS1, BSS2>(&mut group, num_cons);

group.finish();
}
}

#[derive(Clone, Debug, Default)]
struct NonTrivialCircuit<F: PrimeField> {
num_cons: usize,
Expand Down
57 changes: 57 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1397,6 +1397,63 @@ mod tests {
>();
}

type BatchedS<E, EE> = spartan::batched::BatchedRelaxedR1CSSNARK<E, EE>;
type BatchedSPrime<E, EE> = spartan::batched::BatchedRelaxedR1CSSNARK<E, EE>;

fn test_ivc_nontrivial_with_batched_compression_with<E1, EE1, EE2>()
where
E1: CurveCycleEquipped,
EE1: EvaluationEngineTrait<E1>,
EE2: EvaluationEngineTrait<Dual<E1>>,
// this is due to the reliance on Abomonation
<E1::Scalar as PrimeField>::Repr: Abomonation,
<<Dual<E1> as Engine>::Scalar as PrimeField>::Repr: Abomonation,
{
// this tests compatibility of the batched workflow with the non-batched one
test_ivc_nontrivial_with_some_compression_with::<E1, BatchedS<_, EE1>, BatchedS<_, EE2>>()
}

#[test]
fn test_ivc_nontrivial_with_batched_compression() {
test_ivc_nontrivial_with_batched_compression_with::<PallasEngine, EE<_>, EE<_>>();
test_ivc_nontrivial_with_batched_compression_with::<Bn256Engine, EE<_>, EE<_>>();
test_ivc_nontrivial_with_batched_compression_with::<Secp256k1Engine, EE<_>, EE<_>>();
test_ivc_nontrivial_with_batched_compression_with::<Bn256EngineZM, ZMPCS<Bn256, _>, EE<_>>();
test_ivc_nontrivial_with_batched_compression_with::<
Bn256EngineKZG,
provider::hyperkzg::EvaluationEngine<Bn256, _>,
EE<_>,
>();
}

fn test_ivc_nontrivial_with_batched_spark_compression_with<E1, EE1, EE2>()
where
E1: CurveCycleEquipped,
EE1: EvaluationEngineTrait<E1>,
EE2: EvaluationEngineTrait<Dual<E1>>,
// this is due to the reliance on Abomonation
<E1::Scalar as PrimeField>::Repr: Abomonation,
<<Dual<E1> as Engine>::Scalar as PrimeField>::Repr: Abomonation,
{
// this tests compatibility of the batched workflow with the non-batched one
test_ivc_nontrivial_with_some_compression_with::<E1, BatchedSPrime<_, EE1>, BatchedSPrime<_, EE2>>(
)
}

#[test]
fn test_ivc_nontrivial_with_batched_spark_compression() {
test_ivc_nontrivial_with_batched_spark_compression_with::<PallasEngine, EE<_>, EE<_>>();
test_ivc_nontrivial_with_batched_spark_compression_with::<Bn256Engine, EE<_>, EE<_>>();
test_ivc_nontrivial_with_batched_spark_compression_with::<Secp256k1Engine, EE<_>, EE<_>>();
test_ivc_nontrivial_with_batched_spark_compression_with::<Bn256EngineZM, ZMPCS<Bn256, _>, EE<_>>(
);
test_ivc_nontrivial_with_batched_spark_compression_with::<
Bn256EngineKZG,
provider::hyperkzg::EvaluationEngine<Bn256, _>,
EE<_>,
>();
}

fn test_ivc_nondet_with_compression_with<E1, EE1, EE2>()
where
E1: CurveCycleEquipped,
Expand Down
39 changes: 38 additions & 1 deletion src/spartan/batched.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use ff::Field;
use serde::{Deserialize, Serialize};

use core::slice;
use itertools::Itertools;
use once_cell::sync::OnceCell;
use rayon::prelude::*;
Expand All @@ -31,7 +32,7 @@ use crate::{
},
traits::{
evaluation::EvaluationEngineTrait,
snark::{BatchedRelaxedR1CSSNARKTrait, DigestHelperTrait},
snark::{BatchedRelaxedR1CSSNARKTrait, DigestHelperTrait, RelaxedR1CSSNARKTrait},
Engine, TranscriptEngineTrait,
},
zip_with, CommitmentKey,
Expand Down Expand Up @@ -588,3 +589,39 @@ impl<E: Engine, EE: EvaluationEngineTrait<E>> BatchedRelaxedR1CSSNARKTrait<E>
Ok(())
}
}

impl<E: Engine, EE: EvaluationEngineTrait<E>> RelaxedR1CSSNARKTrait<E>
for BatchedRelaxedR1CSSNARK<E, EE>
{
type ProverKey = ProverKey<E, EE>;

type VerifierKey = VerifierKey<E, EE>;

fn ck_floor() -> Box<dyn for<'a> Fn(&'a R1CSShape<E>) -> usize> {
<Self as BatchedRelaxedR1CSSNARKTrait<E>>::ck_floor()
}

fn setup(
ck: Arc<CommitmentKey<E>>,
S: &R1CSShape<E>,
) -> Result<(Self::ProverKey, Self::VerifierKey), NovaError> {
<Self as BatchedRelaxedR1CSSNARKTrait<E>>::setup(ck, vec![S])
}

fn prove(
ck: &CommitmentKey<E>,
pk: &Self::ProverKey,
S: &R1CSShape<E>,
U: &RelaxedR1CSInstance<E>,
W: &RelaxedR1CSWitness<E>,
) -> Result<Self, NovaError> {
let slice_U = slice::from_ref(U);
let slice_W = slice::from_ref(W);
<Self as BatchedRelaxedR1CSSNARKTrait<E>>::prove(ck, pk, vec![S], slice_U, slice_W)
}

fn verify(&self, vk: &Self::VerifierKey, U: &RelaxedR1CSInstance<E>) -> Result<(), NovaError> {
let slice = slice::from_ref(U);
<Self as BatchedRelaxedR1CSSNARKTrait<E>>::verify(self, vk, slice)
}
}
42 changes: 40 additions & 2 deletions src/spartan/batched_ppsnark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ use crate::{
traits::{
commitment::{CommitmentEngineTrait, CommitmentTrait, Len},
evaluation::EvaluationEngineTrait,
snark::{BatchedRelaxedR1CSSNARKTrait, DigestHelperTrait},
snark::{BatchedRelaxedR1CSSNARKTrait, DigestHelperTrait, RelaxedR1CSSNARKTrait},
Engine, TranscriptEngineTrait,
},
zip_with, zip_with_for_each, Commitment, CommitmentKey, CompressedCommitment,
};
use core::slice;
use ff::Field;
use itertools::{chain, Itertools as _};
use once_cell::sync::*;
Expand Down Expand Up @@ -145,7 +146,7 @@ impl<E: Engine, EE: EvaluationEngineTrait<E>> BatchedRelaxedR1CSSNARKTrait<E>
) -> Result<(Self::ProverKey, Self::VerifierKey), NovaError> {
for s in S.iter() {
// check the provided commitment key meets minimal requirements
if ck.length() < Self::ck_floor()(s) {
if ck.length() < <Self as BatchedRelaxedR1CSSNARKTrait<_>>::ck_floor()(s) {
// return Err(NovaError::InvalidCommitmentKeyLength);
return Err(NovaError::InternalError);
}
Expand Down Expand Up @@ -1338,3 +1339,40 @@ impl<E: Engine, EE: EvaluationEngineTrait<E>> BatchedRelaxedR1CSSNARK<E, EE> {
.collect()
}
}

impl<E: Engine, EE: EvaluationEngineTrait<E>> RelaxedR1CSSNARKTrait<E>
for BatchedRelaxedR1CSSNARK<E, EE>
{
type ProverKey = ProverKey<E, EE>;

type VerifierKey = VerifierKey<E, EE>;

fn ck_floor() -> Box<dyn for<'a> Fn(&'a R1CSShape<E>) -> usize> {
<Self as BatchedRelaxedR1CSSNARKTrait<E>>::ck_floor()
}

fn setup(
ck: Arc<CommitmentKey<E>>,
S: &R1CSShape<E>,
) -> Result<(Self::ProverKey, Self::VerifierKey), NovaError> {
<Self as BatchedRelaxedR1CSSNARKTrait<E>>::setup(ck, vec![S])
}

fn prove(
ck: &CommitmentKey<E>,
pk: &Self::ProverKey,
S: &R1CSShape<E>,
U: &RelaxedR1CSInstance<E>,
W: &RelaxedR1CSWitness<E>,
) -> Result<Self, NovaError> {
let slice_U = slice::from_ref(U);
let slice_W = slice::from_ref(W);

<Self as BatchedRelaxedR1CSSNARKTrait<E>>::prove(ck, pk, vec![S], slice_U, slice_W)
}

fn verify(&self, vk: &Self::VerifierKey, U: &RelaxedR1CSInstance<E>) -> Result<(), NovaError> {
let slice = slice::from_ref(U);
<Self as BatchedRelaxedR1CSSNARKTrait<E>>::verify(self, vk, slice)
}
}

0 comments on commit 4ae3a57

Please sign in to comment.