Browse Source

traits for a vector commitment engine and a polynomial evaluation engine (#136)

make spartan generic over the evaluation engine

update version

disable Wasm CI check
main
Srinath Setty 1 year ago
committed by GitHub
parent
commit
603c1e2a85
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 616 additions and 252 deletions
  1. +0
    -4
      .github/workflows/rust.yml
  2. +2
    -2
      Cargo.toml
  3. +4
    -2
      benches/compressed-snark.rs
  4. +5
    -2
      examples/minroot.rs
  5. +0
    -1
      examples/signature.rs
  6. +13
    -26
      src/circuit.rs
  7. +8
    -21
      src/gadgets/r1cs.rs
  8. +15
    -8
      src/lib.rs
  9. +8
    -4
      src/nifs.rs
  10. +202
    -57
      src/provider/ipa_pc.rs
  11. +10
    -0
      src/provider/mod.rs
  12. +5
    -1
      src/provider/pasta.rs
  13. +72
    -42
      src/provider/pedersen.rs
  14. +1
    -1
      src/provider/poseidon.rs
  15. +25
    -14
      src/r1cs.rs
  16. +35
    -62
      src/spartan/mod.rs
  17. +6
    -3
      src/spartan/polynomial.rs
  18. +1
    -1
      src/spartan/sumcheck.rs
  19. +149
    -0
      src/traits/commitment.rs
  20. +46
    -0
      src/traits/evaluation.rs
  21. +9
    -1
      src/traits/mod.rs

+ 0
- 4
.github/workflows/rust.yml

@ -17,12 +17,8 @@ jobs:
run: rustup component add rustfmt
- name: Install clippy
run: rustup component add clippy
- name: Install Wasm target
run: rustup target add wasm32-unknown-unknown
- name: Build
run: cargo build --verbose
- name: Wasm build
run: cargo build --target wasm32-unknown-unknown
- name: Build examples
run: cargo build --examples --verbose
- name: Run tests

+ 2
- 2
Cargo.toml

@ -1,6 +1,6 @@
[package]
name = "nova-snark"
version = "0.12.0"
version = "0.13.0"
authors = ["Srinath Setty <srinath@microsoft.com>"]
edition = "2021"
description = "Recursive zkSNARKs without trusted setup"
@ -13,7 +13,7 @@ keywords = ["zkSNARKs", "cryptography", "proofs"]
[dependencies]
bellperson = { version = "0.24", default-features = false }
ff = { version = "0.12.0", features = ["derive"] }
merlin = "2.0.0"
merlin = "3.0.0"
digest = "0.8.1"
sha3 = "0.8.2"
rayon = "1.3.0"

+ 4
- 2
benches/compressed-snark.rs

@ -15,8 +15,10 @@ use std::time::Duration;
type G1 = pasta_curves::pallas::Point;
type G2 = pasta_curves::vesta::Point;
type S1 = nova_snark::spartan_with_ipa_pc::RelaxedR1CSSNARK<G1>;
type S2 = nova_snark::spartan_with_ipa_pc::RelaxedR1CSSNARK<G2>;
type EE1 = nova_snark::provider::ipa_pc::EvaluationEngine<G1>;
type EE2 = nova_snark::provider::ipa_pc::EvaluationEngine<G2>;
type S1 = nova_snark::spartan::RelaxedR1CSSNARK<G1, EE1>;
type S2 = nova_snark::spartan::RelaxedR1CSSNARK<G2, EE2>;
type C1 = NonTrivialTestCircuit<<G1 as Group>::Scalar>;
type C2 = TrivialTestCircuit<<G2 as Group>::Scalar>;

+ 5
- 2
examples/minroot.rs

@ -257,8 +257,11 @@ fn main() {
// produce a compressed SNARK
println!("Generating a CompressedSNARK using Spartan with IPA-PC...");
let start = Instant::now();
type S1 = nova_snark::spartan_with_ipa_pc::RelaxedR1CSSNARK<G1>;
type S2 = nova_snark::spartan_with_ipa_pc::RelaxedR1CSSNARK<G2>;
type EE1 = nova_snark::provider::ipa_pc::EvaluationEngine<G1>;
type EE2 = nova_snark::provider::ipa_pc::EvaluationEngine<G2>;
type S1 = nova_snark::spartan::RelaxedR1CSSNARK<G1, EE1>;
type S2 = nova_snark::spartan::RelaxedR1CSSNARK<G2, EE2>;
let res = CompressedSNARK::<_, _, _, _, S1, S2>::prove(&pp, &recursive_snark);
println!(
"CompressedSNARK::prove: {:?}, took {:?}",

+ 0
- 1
examples/signature.rs

@ -182,7 +182,6 @@ pub fn synthesize_bits>(
bits: Option<Vec<bool>>,
) -> Result<Vec<AllocatedBit>, SynthesisError> {
(0..F::NUM_BITS)
.into_iter()
.map(|i| {
AllocatedBit::alloc(
cs.namespace(|| format!("bit {i}")),

+ 13
- 26
src/circuit.rs

@ -6,8 +6,7 @@
//! H(params = H(shape, gens), i, z0, zi, U). Each circuit folds the last invocation of
//! the other into the running instance
use super::{
commitments::Commitment,
use crate::{
constants::{NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS},
gadgets::{
ecc::AllocatedPoint,
@ -17,7 +16,10 @@ use super::{
},
},
r1cs::{R1CSInstance, RelaxedR1CSInstance},
traits::{circuit::StepCircuit, Group, ROCircuitTrait, ROConstantsCircuit},
traits::{
circuit::StepCircuit, commitment::CommitmentTrait, Group, ROCircuitTrait, ROConstantsCircuit,
},
Commitment,
};
use bellperson::{
gadgets::{
@ -59,10 +61,7 @@ pub struct NovaAugmentedCircuitInputs {
T: Option<Commitment<G>>,
}
impl<G> NovaAugmentedCircuitInputs<G>
where
G: Group,
{
impl<G: Group> NovaAugmentedCircuitInputs<G> {
/// Create new inputs/witness for the verification circuit
#[allow(clippy::too_many_arguments)]
pub fn new(
@ -88,22 +87,14 @@ where
/// The augmented circuit F' in Nova that includes a step circuit F
/// and the circuit for the verifier in Nova's non-interactive folding scheme
pub struct NovaAugmentedCircuit<G, SC>
where
G: Group,
SC: StepCircuit<G::Base>,
{
pub struct NovaAugmentedCircuit<G: Group, SC: StepCircuit<G::Base>> {
params: NovaAugmentedCircuitParams,
ro_consts: ROConstantsCircuit<G>,
inputs: Option<NovaAugmentedCircuitInputs<G>>,
step_circuit: SC, // The function that is applied for each step
}
impl<G, SC> NovaAugmentedCircuit<G, SC>
where
G: Group,
SC: StepCircuit<G::Base>,
{
impl<G: Group, SC: StepCircuit<G::Base>> NovaAugmentedCircuit<G, SC> {
/// Create a new verification circuit for the input relaxed r1cs instances
pub fn new(
params: NovaAugmentedCircuitParams,
@ -186,10 +177,7 @@ where
let T = AllocatedPoint::alloc(
cs.namespace(|| "allocate T"),
self.inputs.get().map_or(None, |inputs| {
inputs
.T
.get()
.map_or(None, |T| Some(T.comm.to_coordinates()))
inputs.T.get().map_or(None, |T| Some(T.to_coordinates()))
}),
)?;
@ -274,10 +262,8 @@ where
}
}
impl<G, SC> Circuit<<G as Group>::Base> for NovaAugmentedCircuit<G, SC>
where
G: Group,
SC: StepCircuit<G::Base>,
impl<G: Group, SC: StepCircuit<G::Base>> Circuit<<G as Group>::Base>
for NovaAugmentedCircuit<G, SC>
{
fn synthesize<CS: ConstraintSystem<<G as Group>::Base>>(
self,
@ -388,10 +374,11 @@ mod tests {
use crate::bellperson::{shape_cs::ShapeCS, solver::SatisfyingAssignment};
type G1 = pasta_curves::pallas::Point;
type G2 = pasta_curves::vesta::Point;
use crate::constants::{BN_LIMB_WIDTH, BN_N_LIMBS};
use crate::{
bellperson::r1cs::{NovaShape, NovaWitness},
poseidon::PoseidonConstantsCircuit,
provider::poseidon::PoseidonConstantsCircuit,
traits::{circuit::TrivialTestCircuit, ROConstantsTrait},
};

+ 8
- 21
src/gadgets/r1cs.rs

@ -13,7 +13,7 @@ use crate::{
},
},
r1cs::{R1CSInstance, RelaxedR1CSInstance},
traits::{Group, ROCircuitTrait, ROConstantsCircuit},
traits::{commitment::CommitmentTrait, Group, ROCircuitTrait, ROConstantsCircuit},
};
use bellperson::{
gadgets::{boolean::Boolean, num::AllocatedNum, Assignment},
@ -23,19 +23,13 @@ use ff::Field;
/// An Allocated R1CS Instance
#[derive(Clone)]
pub struct AllocatedR1CSInstance<G>
where
G: Group,
{
pub struct AllocatedR1CSInstance<G: Group> {
pub(crate) W: AllocatedPoint<G>,
pub(crate) X0: AllocatedNum<G::Base>,
pub(crate) X1: AllocatedNum<G::Base>,
}
impl<G> AllocatedR1CSInstance<G>
where
G: Group,
{
impl<G: Group> AllocatedR1CSInstance<G> {
/// Takes the r1cs instance and creates a new allocated r1cs instance
pub fn alloc<CS: ConstraintSystem<<G as Group>::Base>>(
mut cs: CS,
@ -44,8 +38,7 @@ where
// Check that the incoming instance has exactly 2 io
let W = AllocatedPoint::alloc(
cs.namespace(|| "allocate W"),
u.get()
.map_or(None, |u| Some(u.comm_W.comm.to_coordinates())),
u.get().map_or(None, |u| Some(u.comm_W.to_coordinates())),
)?;
let X0 = alloc_scalar_as_base::<G, _>(
@ -71,10 +64,7 @@ where
}
/// An Allocated Relaxed R1CS Instance
pub struct AllocatedRelaxedR1CSInstance<G>
where
G: Group,
{
pub struct AllocatedRelaxedR1CSInstance<G: Group> {
pub(crate) W: AllocatedPoint<G>,
pub(crate) E: AllocatedPoint<G>,
pub(crate) u: AllocatedNum<G::Base>,
@ -82,10 +72,7 @@ where
pub(crate) X1: BigNat<G::Base>,
}
impl<G> AllocatedRelaxedR1CSInstance<G>
where
G: Group,
{
impl<G: Group> AllocatedRelaxedR1CSInstance<G> {
/// Allocates the given RelaxedR1CSInstance as a witness of the circuit
pub fn alloc<CS: ConstraintSystem<<G as Group>::Base>>(
mut cs: CS,
@ -97,14 +84,14 @@ where
cs.namespace(|| "allocate W"),
inst
.get()
.map_or(None, |inst| Some(inst.comm_W.comm.to_coordinates())),
.map_or(None, |inst| Some(inst.comm_W.to_coordinates())),
)?;
let E = AllocatedPoint::alloc(
cs.namespace(|| "allocate E"),
inst
.get()
.map_or(None, |inst| Some(inst.comm_E.comm.to_coordinates())),
.map_or(None, |inst| Some(inst.comm_E.to_coordinates())),
)?;
// u << |G::Base| despite the fact that u is a scalar.

+ 15
- 8
src/lib.rs

@ -6,17 +6,15 @@
// private modules
mod bellperson;
mod circuit;
mod commitments;
mod constants;
mod nifs;
mod poseidon;
mod r1cs;
// public modules
pub mod errors;
pub mod gadgets;
pub mod pasta;
pub mod spartan_with_ipa_pc;
pub mod provider;
pub mod spartan;
pub mod traits;
use crate::bellperson::{
@ -37,8 +35,10 @@ use r1cs::{
};
use serde::{Deserialize, Serialize};
use traits::{
circuit::StepCircuit, snark::RelaxedR1CSSNARKTrait, AbsorbInROTrait, Group, ROConstants,
ROConstantsCircuit, ROConstantsTrait, ROTrait,
circuit::StepCircuit,
commitment::{CommitmentEngineTrait, CompressedCommitmentTrait},
snark::RelaxedR1CSSNARKTrait,
AbsorbInROTrait, Group, ROConstants, ROConstantsCircuit, ROConstantsTrait, ROTrait,
};
/// A type that holds public parameters of Nova
@ -717,13 +717,20 @@ where
}
}
type CommitmentGens<G> = <<G as traits::Group>::CE as CommitmentEngineTrait<G>>::CommitmentGens;
type Commitment<G> = <<G as Group>::CE as CommitmentEngineTrait<G>>::Commitment;
type CompressedCommitment<G> = <<G as Group>::CE as CommitmentEngineTrait<G>>::CompressedCommitment;
type CE<G> = <G as Group>::CE;
#[cfg(test)]
mod tests {
use super::*;
type G1 = pasta_curves::pallas::Point;
type G2 = pasta_curves::vesta::Point;
type S1 = spartan_with_ipa_pc::RelaxedR1CSSNARK<G1>;
type S2 = spartan_with_ipa_pc::RelaxedR1CSSNARK<G2>;
type EE1 = provider::ipa_pc::EvaluationEngine<G1>;
type EE2 = provider::ipa_pc::EvaluationEngine<G2>;
type S1 = spartan::RelaxedR1CSSNARK<G1, EE1>;
type S2 = spartan::RelaxedR1CSSNARK<G2, EE2>;
use ::bellperson::{gadgets::num::AllocatedNum, ConstraintSystem, SynthesisError};
use core::marker::PhantomData;
use ff::PrimeField;

+ 8
- 4
src/nifs.rs

@ -2,12 +2,15 @@
#![allow(non_snake_case)]
#![allow(clippy::type_complexity)]
use super::{
commitments::CompressedCommitment,
use crate::{
constants::{NUM_CHALLENGE_BITS, NUM_FE_FOR_RO},
errors::NovaError,
r1cs::{R1CSGens, R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness},
traits::{AbsorbInROTrait, Group, ROTrait},
traits::{
commitment::{CommitmentTrait, CompressedCommitmentTrait},
AbsorbInROTrait, Group, ROTrait,
},
CompressedCommitment,
};
use core::marker::PhantomData;
use serde::{Deserialize, Serialize};
@ -15,8 +18,9 @@ use serde::{Deserialize, Serialize};
/// A SNARK that holds the proof of a step of an incremental computation
#[allow(clippy::upper_case_acronyms)]
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(bound = "")]
pub struct NIFS<G: Group> {
pub(crate) comm_T: CompressedCommitment<G::CompressedGroupElement>,
pub(crate) comm_T: CompressedCommitment<G>,
_p: PhantomData<G>,
}

src/spartan_with_ipa_pc/ipa.rs → src/provider/ipa_pc.rs

@ -1,7 +1,15 @@
//! This module implements `EvaluationEngine` using an IPA-based polynomial commitment scheme
#![allow(clippy::too_many_arguments)]
use crate::commitments::{CommitGens, CommitTrait, Commitment, CompressedCommitment};
use crate::errors::NovaError;
use crate::traits::{AppendToTranscriptTrait, ChallengeTrait, Group};
use crate::{
errors::NovaError,
spartan::polynomial::EqPolynomial,
traits::{
commitment::{CommitmentEngineTrait, CommitmentGensTrait, CommitmentTrait},
evaluation::EvaluationEngineTrait,
AppendToTranscriptTrait, ChallengeTrait, Group,
},
Commitment, CommitmentGens, CompressedCommitment, CE,
};
use core::{cmp::max, iter};
use ff::Field;
use merlin::Transcript;
@ -9,7 +17,131 @@ use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
pub fn inner_product<T>(a: &[T], b: &[T]) -> T
/// Provides an implementation of generators for proving evaluations
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(bound = "")]
pub struct EvaluationGens<G: Group> {
gens_v: CommitmentGens<G>,
gens_s: CommitmentGens<G>,
}
/// Provides an implementation of a polynomial evaluation argument
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(bound = "")]
pub struct EvaluationArgument<G: Group> {
nifs: Vec<NIFSForInnerProduct<G>>,
ipa: InnerProductArgument<G>,
}
/// Provides an implementation of a polynomial evaluation engine using IPA
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EvaluationEngine<G: Group> {
_p: PhantomData<G>,
}
impl<G: Group> EvaluationEngineTrait<G> for EvaluationEngine<G> {
type CE = G::CE;
type EvaluationGens = EvaluationGens<G>;
type EvaluationArgument = EvaluationArgument<G>;
fn setup(gens: &<Self::CE as CommitmentEngineTrait<G>>::CommitmentGens) -> Self::EvaluationGens {
EvaluationGens {
gens_v: gens.clone(),
gens_s: CommitmentGens::<G>::new(b"ipa", 1),
}
}
fn prove_batch(
gens: &Self::EvaluationGens,
transcript: &mut Transcript,
comms: &[Commitment<G>],
polys: &[Vec<G::Scalar>],
points: &[Vec<G::Scalar>],
evals: &[G::Scalar],
) -> Result<Self::EvaluationArgument, NovaError> {
// sanity checks (these should never fail)
assert!(polys.len() >= 2);
assert_eq!(comms.len(), polys.len());
assert_eq!(comms.len(), points.len());
assert_eq!(comms.len(), evals.len());
let mut r_U = InnerProductInstance::new(
&comms[0],
&EqPolynomial::new(points[0].clone()).evals(),
&evals[0],
);
let mut r_W = InnerProductWitness::new(&polys[0]);
let mut nifs = Vec::new();
for i in 1..polys.len() {
let (n, u, w) = NIFSForInnerProduct::prove(
&r_U,
&r_W,
&InnerProductInstance::new(
&comms[i],
&EqPolynomial::new(points[i].clone()).evals(),
&evals[i],
),
&InnerProductWitness::new(&polys[i]),
transcript,
);
nifs.push(n);
r_U = u;
r_W = w;
}
let ipa = InnerProductArgument::prove(&gens.gens_v, &gens.gens_s, &r_U, &r_W, transcript)?;
Ok(EvaluationArgument { nifs, ipa })
}
/// A method to verify purported evaluations of a batch of polynomials
fn verify_batch(
gens: &Self::EvaluationGens,
transcript: &mut Transcript,
comms: &[<Self::CE as CommitmentEngineTrait<G>>::Commitment],
points: &[Vec<G::Scalar>],
evals: &[G::Scalar],
arg: &Self::EvaluationArgument,
) -> Result<(), NovaError> {
// sanity checks (these should never fail)
assert!(comms.len() >= 2);
assert_eq!(comms.len(), points.len());
assert_eq!(comms.len(), evals.len());
let mut r_U = InnerProductInstance::new(
&comms[0],
&EqPolynomial::new(points[0].clone()).evals(),
&evals[0],
);
let mut num_vars = points[0].len();
for i in 1..comms.len() {
let u = arg.nifs[i - 1].verify(
&r_U,
&InnerProductInstance::new(
&comms[i],
&EqPolynomial::new(points[i].clone()).evals(),
&evals[i],
),
transcript,
);
r_U = u;
num_vars = max(num_vars, points[i].len());
}
arg.ipa.verify(
&gens.gens_v,
&gens.gens_s,
(2_usize).pow(num_vars as u32),
&r_U,
transcript,
)?;
Ok(())
}
}
fn inner_product<T>(a: &[T], b: &[T]) -> T
where
T: Field + Send + Sync,
{
@ -29,7 +161,7 @@ pub struct InnerProductInstance {
}
impl<G: Group> InnerProductInstance<G> {
pub fn new(comm_a_vec: &Commitment<G>, b_vec: &[G::Scalar], c: &G::Scalar) -> Self {
fn new(comm_a_vec: &Commitment<G>, b_vec: &[G::Scalar], c: &G::Scalar) -> Self {
InnerProductInstance {
comm_a_vec: *comm_a_vec,
b_vec: b_vec.to_vec(),
@ -37,7 +169,7 @@ impl InnerProductInstance {
}
}
pub fn pad(&self, n: usize) -> InnerProductInstance<G> {
fn pad(&self, n: usize) -> InnerProductInstance<G> {
let mut b_vec = self.b_vec.clone();
b_vec.resize(n, G::Scalar::zero());
InnerProductInstance {
@ -48,18 +180,18 @@ impl InnerProductInstance {
}
}
pub struct InnerProductWitness<G: Group> {
struct InnerProductWitness<G: Group> {
a_vec: Vec<G::Scalar>,
}
impl<G: Group> InnerProductWitness<G> {
pub fn new(a_vec: &[G::Scalar]) -> Self {
fn new(a_vec: &[G::Scalar]) -> Self {
InnerProductWitness {
a_vec: a_vec.to_vec(),
}
}
pub fn pad(&self, n: usize) -> InnerProductWitness<G> {
fn pad(&self, n: usize) -> InnerProductWitness<G> {
let mut a_vec = self.a_vec.clone();
a_vec.resize(n, G::Scalar::zero());
InnerProductWitness { a_vec }
@ -67,17 +199,17 @@ impl InnerProductWitness {
}
/// A non-interactive folding scheme (NIFS) for inner product relations
#[derive(Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct NIFSForInnerProduct<G: Group> {
cross_term: G::Scalar,
}
impl<G: Group> NIFSForInnerProduct<G> {
pub fn protocol_name() -> &'static [u8] {
fn protocol_name() -> &'static [u8] {
b"NIFSForInnerProduct"
}
pub fn prove(
fn prove(
U1: &InnerProductInstance<G>,
W1: &InnerProductWitness<G>,
U2: &InnerProductInstance<G>,
@ -136,7 +268,7 @@ impl NIFSForInnerProduct {
(NIFSForInnerProduct { cross_term }, U, W)
}
pub fn verify(
fn verify(
&self,
U1: &InnerProductInstance<G>,
U2: &InnerProductInstance<G>,
@ -183,10 +315,11 @@ impl NIFSForInnerProduct {
}
/// An inner product argument
#[derive(Debug, Serialize, Deserialize)]
pub struct InnerProductArgument<G: Group> {
L_vec: Vec<CompressedCommitment<G::CompressedGroupElement>>,
R_vec: Vec<CompressedCommitment<G::CompressedGroupElement>>,
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(bound = "")]
struct InnerProductArgument<G: Group> {
L_vec: Vec<CompressedCommitment<G>>,
R_vec: Vec<CompressedCommitment<G>>,
a_hat: G::Scalar,
_p: PhantomData<G>,
}
@ -196,9 +329,9 @@ impl InnerProductArgument {
b"inner product argument"
}
pub fn prove(
gens: &CommitGens<G>,
gens_c: &CommitGens<G>,
fn prove(
gens: &CommitmentGens<G>,
gens_c: &CommitmentGens<G>,
U: &InnerProductInstance<G>,
W: &InnerProductWitness<G>,
transcript: &mut Transcript,
@ -220,15 +353,15 @@ impl InnerProductArgument {
// a closure that executes a step of the recursive inner product argument
let prove_inner = |a_vec: &[G::Scalar],
b_vec: &[G::Scalar],
gens: &CommitGens<G>,
gens: &CommitmentGens<G>,
transcript: &mut Transcript|
-> Result<
(
CompressedCommitment<G::CompressedGroupElement>,
CompressedCommitment<G::CompressedGroupElement>,
CompressedCommitment<G>,
CompressedCommitment<G>,
Vec<G::Scalar>,
Vec<G::Scalar>,
CommitGens<G>,
CommitmentGens<G>,
),
NovaError,
> {
@ -238,20 +371,24 @@ impl InnerProductArgument {
let c_L = inner_product(&a_vec[0..n / 2], &b_vec[n / 2..n]);
let c_R = inner_product(&a_vec[n / 2..n], &b_vec[0..n / 2]);
let L = a_vec[0..n / 2]
.iter()
.chain(iter::once(&c_L))
.copied()
.collect::<Vec<G::Scalar>>()
.commit(&gens_R.combine(&gens_c))
.compress();
let R = a_vec[n / 2..n]
.iter()
.chain(iter::once(&c_R))
.copied()
.collect::<Vec<G::Scalar>>()
.commit(&gens_L.combine(&gens_c))
.compress();
let L = CE::<G>::commit(
&gens_R.combine(&gens_c),
&a_vec[0..n / 2]
.iter()
.chain(iter::once(&c_L))
.copied()
.collect::<Vec<G::Scalar>>(),
)
.compress();
let R = CE::<G>::commit(
&gens_L.combine(&gens_c),
&a_vec[n / 2..n]
.iter()
.chain(iter::once(&c_R))
.copied()
.collect::<Vec<G::Scalar>>(),
)
.compress();
L.append_to_transcript(b"L", transcript);
R.append_to_transcript(b"R", transcript);
@ -278,8 +415,8 @@ impl InnerProductArgument {
};
// two vectors to hold the logarithmic number of group elements
let mut L_vec: Vec<CompressedCommitment<G::CompressedGroupElement>> = Vec::new();
let mut R_vec: Vec<CompressedCommitment<G::CompressedGroupElement>> = Vec::new();
let mut L_vec: Vec<CompressedCommitment<G>> = Vec::new();
let mut R_vec: Vec<CompressedCommitment<G>> = Vec::new();
// we create mutable copies of vectors and generators
let mut a_vec = W.a_vec.to_vec();
@ -304,10 +441,10 @@ impl InnerProductArgument {
})
}
pub fn verify(
fn verify(
&self,
gens: &CommitGens<G>,
gens_c: &CommitGens<G>,
gens: &CommitmentGens<G>,
gens_c: &CommitmentGens<G>,
n: usize,
U: &InnerProductInstance<G>,
transcript: &mut Transcript,
@ -329,7 +466,7 @@ impl InnerProductArgument {
let r = G::Scalar::challenge(b"r", transcript);
let gens_c = gens_c.scale(&r);
let P = U.comm_a_vec + [U.c].commit(&gens_c);
let P = U.comm_a_vec + CE::<G>::commit(&gens_c, &[U.c]);
let batch_invert = |v: &[G::Scalar]| -> Result<Vec<G::Scalar>, NovaError> {
let mut products = vec![G::Scalar::zero(); v.len()];
@ -396,29 +533,37 @@ impl InnerProductArgument {
};
let gens_hat = {
let c = s.commit(gens).compress();
CommitGens::reinterpret_commitments_as_gens(&[c])?
let c = CE::<G>::commit(gens, &s).compress();
CommitmentGens::<G>::reinterpret_commitments_as_gens(&[c])?
};
let b_hat = inner_product(&U.b_vec, &s);
let P_hat = {
let gens_folded = {
let gens_L = CommitGens::reinterpret_commitments_as_gens(&self.L_vec)?;
let gens_R = CommitGens::reinterpret_commitments_as_gens(&self.R_vec)?;
let gens_P = CommitGens::reinterpret_commitments_as_gens(&[P.compress()])?;
let gens_L = CommitmentGens::<G>::reinterpret_commitments_as_gens(&self.L_vec)?;
let gens_R = CommitmentGens::<G>::reinterpret_commitments_as_gens(&self.R_vec)?;
let gens_P = CommitmentGens::<G>::reinterpret_commitments_as_gens(&[P.compress()])?;
gens_L.combine(&gens_R).combine(&gens_P)
};
r_square
.iter()
.chain(r_inverse_square.iter())
.chain(iter::once(&G::Scalar::one()))
.copied()
.collect::<Vec<G::Scalar>>()
.commit(&gens_folded)
CE::<G>::commit(
&gens_folded,
&r_square
.iter()
.chain(r_inverse_square.iter())
.chain(iter::once(&G::Scalar::one()))
.copied()
.collect::<Vec<G::Scalar>>(),
)
};
if P_hat == [self.a_hat, self.a_hat * b_hat].commit(&gens_hat.combine(&gens_c)) {
if P_hat
== CE::<G>::commit(
&gens_hat.combine(&gens_c),
&[self.a_hat, self.a_hat * b_hat],
)
{
Ok(())
} else {
Err(NovaError::InvalidIPA)

+ 10
- 0
src/provider/mod.rs

@ -0,0 +1,10 @@
//! This module implements Nova's traits using the following configuration:
//! `CommitmentEngine` with Pedersen's commitments
//! `Group` with pasta curves
//! `RO` traits with Poseidon
//! `EvaluationEngine` with an IPA-based polynomial evaluation argument
pub mod ipa_pc;
pub mod pasta;
pub mod pedersen;
pub mod poseidon;

src/pasta.rs → src/provider/pasta.rs

@ -1,6 +1,9 @@
//! This module implements the Nova traits for pallas::Point, pallas::Scalar, vesta::Point, vesta::Scalar.
use crate::{
poseidon::{PoseidonRO, PoseidonROCircuit},
provider::{
pedersen::CommitmentEngine,
poseidon::{PoseidonRO, PoseidonROCircuit},
},
traits::{ChallengeTrait, CompressedGroup, Group},
};
use digest::{ExtendableOutput, Input};
@ -61,6 +64,7 @@ macro_rules! impl_traits {
type PreprocessedGroupElement = $name::Affine;
type RO = PoseidonRO<Self::Base, Self::Scalar>;
type ROCircuit = PoseidonROCircuit<Self::Base>;
type CE = CommitmentEngine<Self>;
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
fn vartime_multiscalar_mul(

src/commitments.rs → src/provider/pedersen.rs

@ -1,6 +1,12 @@
use super::{
//! This module provides an implementation of a commitment engine
use crate::{
errors::NovaError,
traits::{AbsorbInROTrait, AppendToTranscriptTrait, CompressedGroup, Group, ROTrait},
traits::{
commitment::{
CommitmentEngineTrait, CommitmentGensTrait, CommitmentTrait, CompressedCommitmentTrait,
},
AbsorbInROTrait, AppendToTranscriptTrait, CompressedGroup, Group, ROTrait,
},
};
use core::{
fmt::Debug,
@ -12,27 +18,33 @@ use merlin::Transcript;
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CommitGens<G: Group> {
/// A type that holds commitment generators
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CommitmentGens<G: Group> {
gens: Vec<G::PreprocessedGroupElement>,
_p: PhantomData<G>,
}
/// A type that holds a commitment
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(bound = "")]
pub struct Commitment<G: Group> {
pub(crate) comm: G,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
/// A type that holds a compressed commitment
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(bound = "")]
pub struct CompressedCommitment<C: CompressedGroup> {
comm: C,
}
impl<G: Group> CommitGens<G> {
pub fn new(label: &'static [u8], n: usize) -> Self {
CommitGens {
impl<G: Group> CommitmentGensTrait<G> for CommitmentGens<G> {
type Commitment = Commitment<G>;
type CompressedCommitment = CompressedCommitment<G::CompressedGroupElement>;
fn new(label: &'static [u8], n: usize) -> Self {
CommitmentGens {
gens: G::from_label(label, n.next_power_of_two()),
_p: Default::default(),
}
@ -42,76 +54,83 @@ impl CommitGens {
self.gens.len()
}
pub fn split_at(&self, n: usize) -> (CommitGens<G>, CommitGens<G>) {
fn commit(&self, v: &[G::Scalar]) -> Self::Commitment {
assert!(self.gens.len() >= v.len());
Commitment {
comm: G::vartime_multiscalar_mul(v, &self.gens[..v.len()]),
}
}
fn split_at(&self, n: usize) -> (CommitmentGens<G>, CommitmentGens<G>) {
(
CommitGens {
CommitmentGens {
gens: self.gens[0..n].to_vec(),
_p: Default::default(),
},
CommitGens {
CommitmentGens {
gens: self.gens[n..].to_vec(),
_p: Default::default(),
},
)
}
pub fn combine(&self, other: &CommitGens<G>) -> CommitGens<G> {
fn combine(&self, other: &CommitmentGens<G>) -> CommitmentGens<G> {
let gens = {
let mut c = self.gens.clone();
c.extend(other.gens.clone());
c
};
CommitGens {
CommitmentGens {
gens,
_p: Default::default(),
}
}
// combines the left and right halves of `self` using `w1` and `w2` as the weights
pub fn fold(&self, w1: &G::Scalar, w2: &G::Scalar) -> CommitGens<G> {
fn fold(&self, w1: &G::Scalar, w2: &G::Scalar) -> CommitmentGens<G> {
let w = vec![*w1, *w2];
let (L, R) = self.split_at(self.len() / 2);
let gens = (0..self.len() / 2)
.into_par_iter()
.map(|i| {
let gens = CommitGens::<G> {
let gens = CommitmentGens::<G> {
gens: [L.gens[i].clone(), R.gens[i].clone()].to_vec(),
_p: Default::default(),
};
w.commit(&gens).comm.preprocessed()
gens.commit(&w).comm.preprocessed()
})
.collect();
CommitGens {
CommitmentGens {
gens,
_p: Default::default(),
}
}
/// Scales each element in `self` by `r`
pub fn scale(&self, r: &G::Scalar) -> Self {
fn scale(&self, r: &G::Scalar) -> Self {
let gens_scaled = self
.gens
.clone()
.into_par_iter()
.map(|g| {
let gens = CommitGens::<G> {
let gens = CommitmentGens::<G> {
gens: vec![g],
_p: Default::default(),
};
[*r].commit(&gens).comm.preprocessed()
gens.commit(&[*r]).comm.preprocessed()
})
.collect();
CommitGens {
CommitmentGens {
gens: gens_scaled,
_p: Default::default(),
}
}
/// reinterprets a vector of commitments as a set of generators
pub fn reinterpret_commitments_as_gens(
fn reinterpret_commitments_as_gens(
c: &[CompressedCommitment<G::CompressedGroupElement>],
) -> Result<Self, NovaError> {
let d = (0..c.len())
@ -122,19 +141,25 @@ impl CommitGens {
.into_par_iter()
.map(|i| d[i].comm.preprocessed())
.collect();
Ok(CommitGens {
Ok(CommitmentGens {
gens,
_p: Default::default(),
})
}
}
impl<G: Group> Commitment<G> {
pub fn compress(&self) -> CompressedCommitment<G::CompressedGroupElement> {
impl<G: Group> CommitmentTrait<G> for Commitment<G> {
type CompressedCommitment = CompressedCommitment<G::CompressedGroupElement>;
fn compress(&self) -> CompressedCommitment<G::CompressedGroupElement> {
CompressedCommitment {
comm: self.comm.compress(),
}
}
fn to_coordinates(&self) -> (G::Base, G::Base, bool) {
self.comm.to_coordinates()
}
}
impl<G: Group> Default for Commitment<G> {
@ -143,8 +168,10 @@ impl Default for Commitment {
}
}
impl<C: CompressedGroup> CompressedCommitment<C> {
pub fn decompress(&self) -> Result<Commitment<C::GroupElement>, NovaError> {
impl<C: CompressedGroup> CompressedCommitmentTrait<C> for CompressedCommitment<C> {
type Commitment = Commitment<C::GroupElement>;
fn decompress(&self) -> Result<Self::Commitment, NovaError> {
let comm = self.comm.decompress();
if comm.is_none() {
return Err(NovaError::DecompressionError);
@ -155,19 +182,6 @@ impl CompressedCommitment {
}
}
pub trait CommitTrait<G: Group> {
fn commit(&self, gens: &CommitGens<G>) -> Commitment<G>;
}
impl<G: Group> CommitTrait<G> for [G::Scalar] {
fn commit(&self, gens: &CommitGens<G>) -> Commitment<G> {
assert!(gens.gens.len() >= self.len());
Commitment {
comm: G::vartime_multiscalar_mul(self, &gens.gens[..self.len()]),
}
}
}
impl<G: Group> AppendToTranscriptTrait for Commitment<G> {
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) {
transcript.append_message(label, self.comm.compress().as_bytes());
@ -193,8 +207,8 @@ impl AppendToTranscriptTrait for CompressedCommitment {
}
}
impl<'b, G: Group> MulAssign<&'b G::Scalar> for Commitment<G> {
fn mul_assign(&mut self, scalar: &'b G::Scalar) {
impl<G: Group> MulAssign<G::Scalar> for Commitment<G> {
fn mul_assign(&mut self, scalar: G::Scalar) {
let result = (self as &Commitment<G>).comm * scalar;
*self = Commitment { comm: result };
}
@ -272,3 +286,19 @@ macro_rules! define_add_assign_variants {
define_add_assign_variants!(G = Group, LHS = Commitment<G>, RHS = Commitment<G>);
define_add_variants!(G = Group, LHS = Commitment<G>, RHS = Commitment<G>, Output = Commitment<G>);
/// Provides a commitment engine
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CommitmentEngine<G: Group> {
_p: PhantomData<G>,
}
impl<G: Group> CommitmentEngineTrait<G> for CommitmentEngine<G> {
type CommitmentGens = CommitmentGens<G>;
type Commitment = Commitment<G>;
type CompressedCommitment = CompressedCommitment<G::CompressedGroupElement>;
fn commit(gens: &Self::CommitmentGens, v: &[G::Scalar]) -> Self::Commitment {
gens.commit(v)
}
}

src/poseidon.rs → src/provider/poseidon.rs

@ -1,5 +1,5 @@
//! Poseidon Constants and Poseidon-based RO used in Nova
use super::traits::{ROCircuitTrait, ROConstantsTrait, ROTrait};
use crate::traits::{ROCircuitTrait, ROConstantsTrait, ROTrait};
use bellperson::{
gadgets::{
boolean::{AllocatedBit, Boolean},

+ 25
- 14
src/r1cs.rs

@ -1,12 +1,17 @@
//! This module defines R1CS related types and a folding scheme for Relaxed R1CS
#![allow(clippy::type_complexity)]
use super::gadgets::nonnative::{bignat::nat_to_limbs, util::f_to_nat};
use super::{
commitments::{CommitGens, CommitTrait, Commitment},
use crate::{
constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_HASH_BITS},
errors::NovaError,
gadgets::utils::scalar_as_base,
traits::{AbsorbInROTrait, AppendToTranscriptTrait, Group, ROTrait},
gadgets::{
nonnative::{bignat::nat_to_limbs, util::f_to_nat},
utils::scalar_as_base,
},
traits::{
commitment::{CommitmentEngineTrait, CommitmentGensTrait},
AbsorbInROTrait, AppendToTranscriptTrait, Group, ROTrait,
},
Commitment, CommitmentGens, CE,
};
use core::cmp::max;
use ff::Field;
@ -21,7 +26,7 @@ use sha3::{Digest, Sha3_256};
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound = "")]
pub struct R1CSGens<G: Group> {
pub(crate) gens: CommitGens<G>,
pub(crate) gens: CommitmentGens<G>,
}
/// A type that holds the shape of the R1CS matrices
@ -71,7 +76,7 @@ impl R1CSGens {
/// Samples public parameters for the specified number of constraints and variables in an R1CS
pub fn new(num_cons: usize, num_vars: usize) -> R1CSGens<G> {
R1CSGens {
gens: CommitGens::new(b"gens", max(num_vars, num_cons)),
gens: CommitmentGens::<G>::new(b"gens", max(num_vars, num_cons)),
}
}
}
@ -202,7 +207,10 @@ impl R1CSShape {
// verify if comm_E and comm_W are commitments to E and W
let res_comm: bool = {
let (comm_W, comm_E) = rayon::join(|| W.W.commit(&gens.gens), || W.E.commit(&gens.gens));
let (comm_W, comm_E) = rayon::join(
|| CE::<G>::commit(&gens.gens, &W.W),
|| CE::<G>::commit(&gens.gens, &W.E),
);
U.comm_W == comm_W && U.comm_E == comm_E
};
@ -239,7 +247,7 @@ impl R1CSShape {
};
// verify if comm_W is a commitment to W
let res_comm: bool = U.comm_W == W.W.commit(&gens.gens);
let res_comm: bool = U.comm_W == gens.gens.commit(&W.W);
if res_eq && res_comm {
Ok(())
@ -293,7 +301,7 @@ impl R1CSShape {
.map(|(((a, b), c), d)| *a + *b - *c - *d)
.collect::<Vec<G::Scalar>>();
let comm_T = T.commit(&gens.gens);
let comm_T = gens.gens.commit(&T);
Ok((T, comm_T))
}
@ -458,7 +466,7 @@ impl R1CSWitness {
/// Commits to the witness using the supplied generators
pub fn commit(&self, gens: &R1CSGens<G>) -> Commitment<G> {
self.W.commit(&gens.gens)
gens.gens.commit(&self.W)
}
}
@ -515,7 +523,10 @@ impl RelaxedR1CSWitness {
/// Commits to the witness using the supplied generators
pub fn commit(&self, gens: &R1CSGens<G>) -> (Commitment<G>, Commitment<G>) {
(self.W.commit(&gens.gens), self.E.commit(&gens.gens))
(
CE::<G>::commit(&gens.gens, &self.W),
CE::<G>::commit(&gens.gens, &self.E),
)
}
/// Folds an incoming R1CSWitness into the current one
@ -566,7 +577,7 @@ impl RelaxedR1CSWitness {
impl<G: Group> RelaxedR1CSInstance<G> {
/// Produces a default RelaxedR1CSInstance given R1CSGens and R1CSShape
pub fn default(_gens: &R1CSGens<G>, S: &R1CSShape<G>) -> RelaxedR1CSInstance<G> {
let (comm_W, comm_E) = (Commitment::default(), Commitment::default());
let (comm_W, comm_E) = (Commitment::<G>::default(), Commitment::<G>::default());
RelaxedR1CSInstance {
comm_W,
comm_E,
@ -605,7 +616,7 @@ impl RelaxedR1CSInstance {
.zip(X2)
.map(|(a, b)| *a + *r * *b)
.collect::<Vec<G::Scalar>>();
let comm_W = comm_W_1 + comm_W_2 * r;
let comm_W = *comm_W_1 + *comm_W_2 * *r;
let comm_E = *comm_E_1 + *comm_T * *r;
let u = *u1 + *r;

src/spartan_with_ipa_pc/mod.rs → src/spartan/mod.rs

@ -1,21 +1,18 @@
//! This module implements RelaxedR1CSSNARKTrait using a Spartan variant
//! instantiated with an IPA-based polynomial commitment scheme
mod ipa;
mod polynomial;
//! This module implements RelaxedR1CSSNARKTrait using Spartan that is generic
//! over the polynomial commitment and evaluation argument (i.e., a PCS)
pub mod polynomial;
mod sumcheck;
use super::{
commitments::CommitGens,
use crate::{
errors::NovaError,
r1cs::{R1CSGens, R1CSShape, RelaxedR1CSInstance, RelaxedR1CSWitness},
traits::{
evaluation::EvaluationEngineTrait,
snark::{ProverKeyTrait, RelaxedR1CSSNARKTrait, VerifierKeyTrait},
AppendToTranscriptTrait, ChallengeTrait, Group,
},
};
use core::cmp::max;
use ff::Field;
use ipa::{InnerProductArgument, InnerProductInstance, InnerProductWitness, NIFSForInnerProduct};
use itertools::concat;
use merlin::Transcript;
use polynomial::{EqPolynomial, MultilinearPolynomial, SparsePolynomial};
@ -26,17 +23,15 @@ use sumcheck::SumcheckProof;
/// A type that represents the prover's key
#[derive(Serialize, Deserialize)]
#[serde(bound = "")]
pub struct ProverKey<G: Group> {
gens_r1cs: R1CSGens<G>,
gens_ipa: CommitGens<G>,
pub struct ProverKey<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> {
gens: EE::EvaluationGens,
S: R1CSShape<G>,
}
impl<G: Group> ProverKeyTrait<G> for ProverKey<G> {
impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> ProverKeyTrait<G> for ProverKey<G, EE> {
fn new(gens: &R1CSGens<G>, S: &R1CSShape<G>) -> Self {
ProverKey {
gens_r1cs: gens.clone(),
gens_ipa: CommitGens::new(b"ipa", 1),
gens: EE::setup(&gens.gens),
S: S.clone(),
}
}
@ -45,17 +40,17 @@ impl ProverKeyTrait for ProverKey {
/// A type that represents the verifier's key
#[derive(Serialize, Deserialize)]
#[serde(bound = "")]
pub struct VerifierKey<G: Group> {
gens_r1cs: R1CSGens<G>,
gens_ipa: CommitGens<G>,
pub struct VerifierKey<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> {
gens: EE::EvaluationGens,
S: R1CSShape<G>,
}
impl<G: Group> VerifierKeyTrait<G> for VerifierKey<G> {
impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> VerifierKeyTrait<G>
for VerifierKey<G, EE>
{
fn new(gens: &R1CSGens<G>, S: &R1CSShape<G>) -> Self {
VerifierKey {
gens_r1cs: gens.clone(),
gens_ipa: CommitGens::new(b"ipa", 1),
gens: EE::setup(&gens.gens),
S: S.clone(),
}
}
@ -66,19 +61,20 @@ impl VerifierKeyTrait for VerifierKey {
/// the commitment to a vector viewed as a polynomial commitment
#[derive(Serialize, Deserialize)]
#[serde(bound = "")]
pub struct RelaxedR1CSSNARK<G: Group> {
pub struct RelaxedR1CSSNARK<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> {
sc_proof_outer: SumcheckProof<G>,
claims_outer: (G::Scalar, G::Scalar, G::Scalar),
sc_proof_inner: SumcheckProof<G>,
eval_E: G::Scalar,
eval_W: G::Scalar,
nifs_ip: NIFSForInnerProduct<G>,
ipa: InnerProductArgument<G>,
eval_arg: EE::EvaluationArgument,
}
impl<G: Group> RelaxedR1CSSNARKTrait<G> for RelaxedR1CSSNARK<G> {
type ProverKey = ProverKey<G>;
type VerifierKey = VerifierKey<G>;
impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> RelaxedR1CSSNARKTrait<G>
for RelaxedR1CSSNARK<G, EE>
{
type ProverKey = ProverKey<G, EE>;
type VerifierKey = VerifierKey<G, EE>;
/// produces a succinct proof of satisfiability of a RelaxedR1CS instance
fn prove(
@ -88,8 +84,6 @@ impl RelaxedR1CSSNARKTrait for RelaxedR1CSSNARK {
) -> Result<Self, NovaError> {
let mut transcript = Transcript::new(b"RelaxedR1CSSNARK");
debug_assert!(pk.S.is_sat_relaxed(&pk.gens_r1cs, U, W).is_ok());
// sanity check that R1CSShape has certain size characteristics
assert_eq!(pk.S.num_cons.next_power_of_two(), pk.S.num_cons);
assert_eq!(pk.S.num_vars.next_power_of_two(), pk.S.num_vars);
@ -230,24 +224,13 @@ impl RelaxedR1CSSNARKTrait for RelaxedR1CSSNARK {
let eval_W = MultilinearPolynomial::new(W.W.clone()).evaluate(&r_y[1..]);
eval_W.append_to_transcript(b"eval_W", &mut transcript);
let (nifs_ip, r_U, r_W) = NIFSForInnerProduct::prove(
&InnerProductInstance::new(&U.comm_E, &EqPolynomial::new(r_x).evals(), &eval_E),
&InnerProductWitness::new(&W.E),
&InnerProductInstance::new(
&U.comm_W,
&EqPolynomial::new(r_y[1..].to_vec()).evals(),
&eval_W,
),
&InnerProductWitness::new(&W.W),
&mut transcript,
);
let ipa = InnerProductArgument::prove(
&pk.gens_r1cs.gens,
&pk.gens_ipa,
&r_U,
&r_W,
let eval_arg = EE::prove_batch(
&pk.gens,
&mut transcript,
&[U.comm_E, U.comm_W],
&[W.E.clone(), W.W.clone()],
&[r_x, r_y[1..].to_vec()],
&[eval_E, eval_W],
)?;
Ok(RelaxedR1CSSNARK {
@ -256,8 +239,7 @@ impl RelaxedR1CSSNARKTrait for RelaxedR1CSSNARK {
sc_proof_inner,
eval_W,
eval_E,
nifs_ip,
ipa,
eval_arg,
})
}
@ -366,22 +348,13 @@ impl RelaxedR1CSSNARKTrait for RelaxedR1CSSNARK {
// verify eval_W and eval_E
self.eval_W.append_to_transcript(b"eval_W", &mut transcript); //eval_E is already in the transcript
let r_U = self.nifs_ip.verify(
&InnerProductInstance::new(&U.comm_E, &EqPolynomial::new(r_x).evals(), &self.eval_E),
&InnerProductInstance::new(
&U.comm_W,
&EqPolynomial::new(r_y[1..].to_vec()).evals(),
&self.eval_W,
),
&mut transcript,
);
self.ipa.verify(
&vk.gens_r1cs.gens,
&vk.gens_ipa,
max(vk.S.num_vars, vk.S.num_cons),
&r_U,
EE::verify_batch(
&vk.gens,
&mut transcript,
&[U.comm_E, U.comm_W],
&[r_x, r_y[1..].to_vec()],
&[self.eval_E, self.eval_W],
&self.eval_arg,
)?;
Ok(())

src/spartan_with_ipa_pc/polynomial.rs → src/spartan/polynomial.rs

@ -1,16 +1,19 @@
//! This module defines basic types related to polynomials
use core::ops::Index;
use ff::PrimeField;
use rayon::prelude::*;
pub struct EqPolynomial<Scalar: PrimeField> {
pub(crate) struct EqPolynomial<Scalar: PrimeField> {
r: Vec<Scalar>,
}
impl<Scalar: PrimeField> EqPolynomial<Scalar> {
/// Creates a new polynomial from its succinct specification
pub fn new(r: Vec<Scalar>) -> Self {
EqPolynomial { r }
}
/// Evaluates the polynomial at the specified point
pub fn evaluate(&self, rx: &[Scalar]) -> Scalar {
assert_eq!(self.r.len(), rx.len());
(0..rx.len())
@ -43,7 +46,7 @@ impl EqPolynomial {
}
#[derive(Debug)]
pub struct MultilinearPolynomial<Scalar: PrimeField> {
pub(crate) struct MultilinearPolynomial<Scalar: PrimeField> {
num_vars: usize, // the number of variables in the multilinear polynomial
Z: Vec<Scalar>, // evaluations of the polynomial in all the 2^num_vars Boolean inputs
}
@ -105,7 +108,7 @@ impl Index for MultilinearPolynomial {
}
}
pub struct SparsePolynomial<Scalar: PrimeField> {
pub(crate) struct SparsePolynomial<Scalar: PrimeField> {
num_vars: usize,
Z: Vec<(usize, Scalar)>,
}

src/spartan_with_ipa_pc/sumcheck.rs → src/spartan/sumcheck.rs

@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
#[serde(bound = "")]
pub struct SumcheckProof<G: Group> {
pub(crate) struct SumcheckProof<G: Group> {
compressed_polys: Vec<CompressedUniPoly<G>>,
}

+ 149
- 0
src/traits/commitment.rs

@ -0,0 +1,149 @@
//! This module defines a collection of traits that define the behavior of a commitment engine
//! We require the commitment engine to provide a commitment to vectors with a single group element
use crate::{
errors::NovaError,
traits::{AbsorbInROTrait, AppendToTranscriptTrait, CompressedGroup, Group},
};
use core::{
fmt::Debug,
ops::{Add, AddAssign, Mul, MulAssign},
};
use serde::{Deserialize, Serialize};
/// This trait defines the behavior of commitment key
#[allow(clippy::len_without_is_empty)]
pub trait CommitmentGensTrait<G: Group>:
Clone + Debug + Send + Sync + Serialize + for<'de> Deserialize<'de>
{
/// Holds the type of the commitment that can be produced
type Commitment;
/// Holds the type of the compressed commitment
type CompressedCommitment;
/// Samples a new commitment key of a specified size
fn new(label: &'static [u8], n: usize) -> Self;
/// Returns the vector length that can be committed
fn len(&self) -> usize;
/// Commits to a vector using the commitment key
fn commit(&self, v: &[G::Scalar]) -> Self::Commitment;
/// Splits the commitment key into two pieces at a specified point
fn split_at(&self, n: usize) -> (Self, Self)
where
Self: Sized;
/// Combines two commitment keys into one
fn combine(&self, other: &Self) -> Self;
/// Folds the two commitment keys into one using the provided weights
fn fold(&self, w1: &G::Scalar, w2: &G::Scalar) -> Self;
/// Scales the commitment key using the provided scalar
fn scale(&self, r: &G::Scalar) -> Self;
/// Reinterprets commitments as commitment keys
fn reinterpret_commitments_as_gens(c: &[Self::CompressedCommitment]) -> Result<Self, NovaError>
where
Self: Sized;
}
/// Defines basic operations on commitments
pub trait CommitmentOps<Rhs = Self, Output = Self>:
Add<Rhs, Output = Output> + AddAssign<Rhs>
{
}
impl<T, Rhs, Output> CommitmentOps<Rhs, Output> for T where
T: Add<Rhs, Output = Output> + AddAssign<Rhs>
{
}
/// A helper trait for references with a commitment operation
pub trait CommitmentOpsOwned<Rhs = Self, Output = Self>:
for<'r> CommitmentOps<&'r Rhs, Output>
{
}
impl<T, Rhs, Output> CommitmentOpsOwned<Rhs, Output> for T where
T: for<'r> CommitmentOps<&'r Rhs, Output>
{
}
/// A helper trait for types implementing a multiplication of a commitment with a scalar
pub trait ScalarMul<Rhs, Output = Self>: Mul<Rhs, Output = Output> + MulAssign<Rhs> {}
impl<T, Rhs, Output> ScalarMul<Rhs, Output> for T where T: Mul<Rhs, Output = Output> + MulAssign<Rhs>
{}
/// This trait defines the behavior of the commitment
pub trait CommitmentTrait<G: Group>:
Clone
+ Copy
+ Debug
+ Default
+ PartialEq
+ Eq
+ Send
+ Sync
+ Serialize
+ for<'de> Deserialize<'de>
+ AbsorbInROTrait<G>
+ AppendToTranscriptTrait
+ CommitmentOps
+ CommitmentOpsOwned
+ ScalarMul<G::Scalar>
{
/// Holds the type of the compressed commitment
type CompressedCommitment;
/// Compresses self into a compressed commitment
fn compress(&self) -> Self::CompressedCommitment;
/// Returns the coordinate representation of the commitment
fn to_coordinates(&self) -> (G::Base, G::Base, bool);
}
/// This trait defines the behavior of a compressed commitment
pub trait CompressedCommitmentTrait<C: CompressedGroup>:
Clone
+ Debug
+ PartialEq
+ Eq
+ Send
+ Sync
+ Serialize
+ for<'de> Deserialize<'de>
+ AppendToTranscriptTrait
{
/// Holds the type of the commitment that can be decompressed into
type Commitment;
/// Decompresses self into a commitment
fn decompress(&self) -> Result<Self::Commitment, NovaError>;
}
/// A trait that ties different pieces of the commitment generation together
pub trait CommitmentEngineTrait<G: Group>:
Clone + Send + Sync + Serialize + for<'de> Deserialize<'de>
{
/// Holds the type of the commitment key
type CommitmentGens: CommitmentGensTrait<
G,
Commitment = Self::Commitment,
CompressedCommitment = Self::CompressedCommitment,
>;
/// Holds the type of the commitment
type Commitment: CommitmentTrait<G, CompressedCommitment = Self::CompressedCommitment>;
/// Holds the type of the compressed commitment
type CompressedCommitment: CompressedCommitmentTrait<
G::CompressedGroupElement,
Commitment = Self::Commitment,
>;
/// Commits to the provided vector using the provided generators
fn commit(gens: &Self::CommitmentGens, v: &[G::Scalar]) -> Self::Commitment;
}

+ 46
- 0
src/traits/evaluation.rs

@ -0,0 +1,46 @@
//! This module defines a collection of traits that define the behavior of a polynomial evaluation engine
//! A vector of size N is treated as a multilinear polynomial in \log{N} variables,
//! and a commitment provided by the commitment engine is treated as a multilinear polynomial commitment
use crate::{
errors::NovaError,
traits::{commitment::CommitmentEngineTrait, Group},
};
use merlin::Transcript;
use serde::{Deserialize, Serialize};
/// A trait that ties different pieces of the commitment evaluation together
pub trait EvaluationEngineTrait<G: Group>:
Clone + Send + Sync + Serialize + for<'de> Deserialize<'de>
{
/// A type that holds the associated commitment engine
type CE: CommitmentEngineTrait<G>;
/// A type that holds generators
type EvaluationGens: Clone + Send + Sync + Serialize + for<'de> Deserialize<'de>;
/// A type that holds the evaluation argument
type EvaluationArgument: Clone + Send + Sync + Serialize + for<'de> Deserialize<'de>;
/// A method to perform any additional setup needed to produce proofs of evaluations
fn setup(gens: &<Self::CE as CommitmentEngineTrait<G>>::CommitmentGens) -> Self::EvaluationGens;
/// A method to prove evaluations of a batch of polynomials
fn prove_batch(
gens: &Self::EvaluationGens,
transcript: &mut Transcript,
comm: &[<Self::CE as CommitmentEngineTrait<G>>::Commitment],
polys: &[Vec<G::Scalar>],
points: &[Vec<G::Scalar>],
evals: &[G::Scalar],
) -> Result<Self::EvaluationArgument, NovaError>;
/// A method to verify purported evaluations of a batch of polynomials
fn verify_batch(
gens: &Self::EvaluationGens,
transcript: &mut Transcript,
comm: &[<Self::CE as CommitmentEngineTrait<G>>::Commitment],
points: &[Vec<G::Scalar>],
evals: &[G::Scalar],
arg: &Self::EvaluationArgument,
) -> Result<(), NovaError>;
}

+ 9
- 1
src/traits/mod.rs

@ -12,6 +12,10 @@ use merlin::Transcript;
use num_bigint::BigInt;
use serde::{Deserialize, Serialize};
pub mod commitment;
use commitment::CommitmentEngineTrait;
/// Represents an element of a group
/// This is currently tailored for an elliptic curve group
pub trait Group:
@ -47,7 +51,7 @@ pub trait Group:
+ for<'de> Deserialize<'de>;
/// A type representing preprocessed group element
type PreprocessedGroupElement: Clone + Send + Sync + Serialize + for<'de> Deserialize<'de>;
type PreprocessedGroupElement: Clone + Debug + Send + Sync + Serialize + for<'de> Deserialize<'de>;
/// A type that represents a hash function that consumes elements
/// from the base field and squeezes out elements of the scalar field
@ -56,6 +60,9 @@ pub trait Group:
/// An alternate implementation of Self::RO in the circuit model
type ROCircuit: ROCircuitTrait<Self::Base> + Serialize + for<'de> Deserialize<'de>;
/// A type that defines a commitment engine over scalars in the group
type CE: CommitmentEngineTrait<Self> + Serialize + for<'de> Deserialize<'de>;
/// A method to compute a multiexponentation
fn vartime_multiscalar_mul(
scalars: &[Self::Scalar],
@ -212,4 +219,5 @@ impl AppendToTranscriptTrait for [F] {
}
pub mod circuit;
pub mod evaluation;
pub mod snark;

Loading…
Cancel
Save