arnaucube
arnaucube/NovaPublic Self-hosted repository
generic traits for prime field elements and group elements
Srinath Setty srinath@microsoft.com
Tue, 31 Aug 2021 13:03:54 -0700
commit

732d937b09e8d286e5c72cade4c592d1a8b98f76

parent

06b1c4d415740f7832efd983004296ae111eea20

5 files changed, 356 insertions(+), 253 deletions(-)

jump to
M Cargo.toml → Cargo.toml

@@ -11,7 +11,6 @@ license-file = "LICENSE"

keywords = ["Recursive zkSNARKs", "cryptography", "proofs"] [dependencies] -curve25519-dalek = {version = "3.0.0", features = ["simd_backend"]} merlin = "2.0.0" rand = "0.7.3" digest = "0.8.1"

@@ -19,3 +18,7 @@ sha3 = "0.8.2"

rayon = "1.3.0" rand_core = { version = "0.5", default-features = false } itertools = "0.9.0" +subtle = "2.4" + +[dev-dependencies] +curve25519-dalek = {version = "3.0.0", features = ["simd_backend"]}
M src/commitments.rs → src/commitments.rs

@@ -1,56 +1,53 @@

use super::errors::NovaError; +use super::traits::{CompressedGroup, Group}; +use core::fmt::Debug; use core::ops::{Add, AddAssign, Mul, MulAssign}; -use curve25519_dalek::traits::VartimeMultiscalarMul; use digest::{ExtendableOutput, Input}; use merlin::Transcript; use sha3::Shake256; use std::io::Read; - -pub type Scalar = curve25519_dalek::scalar::Scalar; -type GroupElement = curve25519_dalek::ristretto::RistrettoPoint; -type CompressedGroup = curve25519_dalek::ristretto::CompressedRistretto; #[derive(Debug)] -pub struct CommitGens { - gens: Vec<GroupElement>, +pub struct CommitGens<G: Group> { + gens: Vec<G>, } -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Commitment { - comm: GroupElement, +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Commitment<G: Group> { + comm: G, } #[derive(Clone, Debug, PartialEq, Eq)] -pub struct CompressedCommitment { - comm: CompressedGroup, +pub struct CompressedCommitment<C: CompressedGroup> { + comm: C, } -impl CommitGens { +impl<G: Group> CommitGens<G> { pub fn new(label: &[u8], n: usize) -> Self { let mut shake = Shake256::default(); shake.input(label); let mut reader = shake.xof_result(); - let mut gens: Vec<GroupElement> = Vec::new(); + let mut gens: Vec<G> = Vec::new(); let mut uniform_bytes = [0u8; 64]; for _ in 0..n { reader.read_exact(&mut uniform_bytes).unwrap(); - gens.push(GroupElement::from_uniform_bytes(&uniform_bytes)); + gens.push(G::from_uniform_bytes(&uniform_bytes).unwrap()); } CommitGens { gens } } } -impl Commitment { - pub fn compress(&self) -> CompressedCommitment { +impl<G: Group> Commitment<G> { + pub fn compress(&self) -> CompressedCommitment<G::CompressedGroupElement> { CompressedCommitment { comm: self.comm.compress(), } } } -impl CompressedCommitment { - pub fn decompress(&self) -> Result<Commitment, NovaError> { +impl<C: CompressedGroup> CompressedCommitment<C> { + pub fn decompress(&self) -> Result<Commitment<C::GroupElement>, NovaError> { let comm = self.comm.decompress(); if comm.is_none() { return Err(NovaError::DecompressionError);

@@ -61,161 +58,90 @@ })

} } -pub trait CommitTrait { - fn commit(&self, gens: &CommitGens) -> Commitment; +pub trait CommitTrait<G: Group> { + fn commit(&self, gens: &CommitGens<G>) -> Commitment<G>; } -impl CommitTrait for [Scalar] { - fn commit(&self, gens: &CommitGens) -> Commitment { +impl<G: Group> CommitTrait<G> for [G::Scalar] { + fn commit(&self, gens: &CommitGens<G>) -> Commitment<G> { assert_eq!(gens.gens.len(), self.len()); Commitment { - comm: GroupElement::vartime_multiscalar_mul(self, &gens.gens), + comm: G::vartime_multiscalar_mul(self, &gens.gens), } } } -pub trait ProofTranscriptTrait { - fn append_protocol_name(&mut self, protocol_name: &'static [u8]); - fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar; +pub trait AppendToTranscriptTrait { + fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript); } -impl ProofTranscriptTrait for Transcript { - fn append_protocol_name(&mut self, protocol_name: &'static [u8]) { - self.append_message(b"protocol-name", protocol_name); - } - - fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar { - let mut buf = [0u8; 64]; - self.challenge_bytes(label, &mut buf); - Scalar::from_bytes_mod_order_wide(&buf) +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()); } } -pub trait AppendToTranscriptTrait { - fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript); -} - -impl AppendToTranscriptTrait for CompressedCommitment { +impl<C: CompressedGroup> AppendToTranscriptTrait for CompressedCommitment<C> { fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) { transcript.append_message(label, self.comm.as_bytes()); } } -impl<'b> MulAssign<&'b Scalar> for Commitment { - fn mul_assign(&mut self, scalar: &'b Scalar) { - let result = (self as &Commitment).comm * scalar; +impl<'b, G: Group> MulAssign<&'b G::Scalar> for Commitment<G> { + fn mul_assign(&mut self, scalar: &'b G::Scalar) { + let result = (self as &Commitment<G>).comm * scalar; *self = Commitment { comm: result }; } } -impl<'a, 'b> Mul<&'b Scalar> for &'a Commitment { - type Output = Commitment; - fn mul(self, scalar: &'b Scalar) -> Commitment { +impl<G: Group> Mul<G::Scalar> for Commitment<G> { + type Output = Commitment<G>; + fn mul(self, scalar: G::Scalar) -> Commitment<G> { Commitment { comm: self.comm * scalar, } } } -impl<'a, 'b> Mul<&'b Commitment> for &'a Scalar { - type Output = Commitment; - - fn mul(self, comm: &'b Commitment) -> Commitment { - Commitment { - comm: self * comm.comm, - } - } -} - -macro_rules! define_mul_variants { - (LHS = $lhs:ty, RHS = $rhs:ty, Output = $out:ty) => { - impl<'b> Mul<&'b $rhs> for $lhs { - type Output = $out; - fn mul(self, rhs: &'b $rhs) -> $out { - &self * rhs - } - } - - impl<'a> Mul<$rhs> for &'a $lhs { - type Output = $out; - fn mul(self, rhs: $rhs) -> $out { - self * &rhs - } - } - - impl Mul<$rhs> for $lhs { - type Output = $out; - fn mul(self, rhs: $rhs) -> $out { - &self * &rhs - } - } - }; -} - -macro_rules! define_mul_assign_variants { - (LHS = $lhs:ty, RHS = $rhs:ty) => { - impl MulAssign<$rhs> for $lhs { - fn mul_assign(&mut self, rhs: $rhs) { - *self *= &rhs; - } - } - }; -} - -define_mul_assign_variants!(LHS = Commitment, RHS = Scalar); -define_mul_variants!(LHS = Commitment, RHS = Scalar, Output = Commitment); -define_mul_variants!(LHS = Scalar, RHS = Commitment, Output = Commitment); - -impl<'b> AddAssign<&'b Commitment> for Commitment { - fn add_assign(&mut self, other: &'b Commitment) { - let result = (self as &Commitment).comm + other.comm; +impl<'b, G: Group> AddAssign<&'b Commitment<G>> for Commitment<G> { + fn add_assign(&mut self, other: &'b Commitment<G>) { + let result = (self as &Commitment<G>).comm + other.comm; *self = Commitment { comm: result }; } } -impl<'a, 'b> Add<&'b Commitment> for &'a Commitment { - type Output = Commitment; - fn add(self, other: &'b Commitment) -> Commitment { +impl<'a, 'b, G: Group> Add<&'b Commitment<G>> for &'a Commitment<G> { + type Output = Commitment<G>; + fn add(self, other: &'b Commitment<G>) -> Commitment<G> { Commitment { comm: self.comm + other.comm, } } } -macro_rules! define_add_variants { - (LHS = $lhs:ty, RHS = $rhs:ty, Output = $out:ty) => { - impl<'b> Add<&'b $rhs> for $lhs { - type Output = $out; - fn add(self, rhs: &'b $rhs) -> $out { - &self + rhs - } - } +impl<G: Group> AddAssign<Commitment<G>> for Commitment<G> { + fn add_assign(&mut self, rhs: Commitment<G>) { + *self += &rhs; + } +} - impl<'a> Add<$rhs> for &'a $lhs { - type Output = $out; - fn add(self, rhs: $rhs) -> $out { - self + &rhs - } - } - - impl Add<$rhs> for $lhs { - type Output = $out; - fn add(self, rhs: $rhs) -> $out { - &self + &rhs - } - } - }; +impl<'b, G: Group> Add<&'b Commitment<G>> for Commitment<G> { + type Output = Commitment<G>; + fn add(self, rhs: &'b Commitment<G>) -> Commitment<G> { + &self + rhs + } } -macro_rules! define_add_assign_variants { - (LHS = $lhs:ty, RHS = $rhs:ty) => { - impl AddAssign<$rhs> for $lhs { - fn add_assign(&mut self, rhs: $rhs) { - *self += &rhs; - } - } - }; +impl<'a, G: Group> Add<Commitment<G>> for &'a Commitment<G> { + type Output = Commitment<G>; + fn add(self, rhs: Commitment<G>) -> Commitment<G> { + self + &rhs + } } -define_add_assign_variants!(LHS = Commitment, RHS = Commitment); -define_add_variants!(LHS = Commitment, RHS = Commitment, Output = Commitment); +impl<G: Group> Add<Commitment<G>> for Commitment<G> { + type Output = Commitment<G>; + fn add(self, rhs: Commitment<G>) -> Commitment<G> { + &self + &rhs + } +}
M src/lib.rs → src/lib.rs

@@ -1,33 +1,25 @@

+//! This library implements core components of Nova. #![allow(non_snake_case)] #![feature(test)] #![deny(missing_docs)] -#![feature(external_doc)] -#![doc(include = "../README.md")] - -extern crate core; -extern crate curve25519_dalek; -extern crate digest; -extern crate merlin; -extern crate rand; -extern crate rayon; -extern crate sha3; -extern crate test; mod commitments; mod errors; mod r1cs; +mod traits; -use commitments::{AppendToTranscriptTrait, CompressedCommitment, ProofTranscriptTrait}; +use commitments::{AppendToTranscriptTrait, Commitment}; use errors::NovaError; use merlin::Transcript; use r1cs::{R1CSGens, R1CSInstance, R1CSShape, R1CSWitness}; +use traits::{ChallengeTrait, Group}; /// A SNARK that holds the proof of a step of an incremental computation -pub struct StepSNARK { - comm_T: CompressedCommitment, +pub struct StepSNARK<G: Group> { + comm_T: Commitment<G>, } -impl StepSNARK { +impl<G: Group> StepSNARK<G> { fn protocol_name() -> &'static [u8] { b"NovaStepSNARK" }

@@ -38,16 +30,17 @@ /// and outputs a folded instance-witness tuple `(U, W)` of the same shape `shape`,

/// with the guarantee that the folded witness `W` satisfies the folded instance `U` /// if and only if `W1` satisfies `U1` and `W2` satisfies `U2`. pub fn prove( - gens: &R1CSGens, - S: &R1CSShape, - U1: &R1CSInstance, - W1: &R1CSWitness, - U2: &R1CSInstance, - W2: &R1CSWitness, + gens: &R1CSGens<G>, + S: &R1CSShape<G>, + U1: &R1CSInstance<G>, + W1: &R1CSWitness<G>, + U2: &R1CSInstance<G>, + W2: &R1CSWitness<G>, transcript: &mut Transcript, - ) -> Result<(StepSNARK, (R1CSInstance, R1CSWitness)), NovaError> { + ) -> Result<(StepSNARK<G>, (R1CSInstance<G>, R1CSWitness<G>)), NovaError> { // append the protocol name to the transcript - transcript.append_protocol_name(StepSNARK::protocol_name()); + //transcript.append_protocol_name(StepSNARK::protocol_name()); + transcript.append_message(b"protocol-name", StepSNARK::<G>::protocol_name()); // compute a commitment to the cross-term let (T, comm_T) = S.commit_T(gens, U1, W1, U2, W2)?;

@@ -56,10 +49,10 @@ // append `comm_T` to the transcript and obtain a challenge

comm_T.append_to_transcript(b"comm_T", transcript); // compute a challenge from the transcript - let r = transcript.challenge_scalar(b"r"); + let r = G::Scalar::challenge(b"r", transcript); // fold the instance using `r` and `comm_T` - let U = U1.fold(U2, &comm_T, &r)?; + let U = U1.fold(U2, &comm_T, &r); // fold the witness using `r` and `T` let W = W1.fold(W2, &T, &r)?;

@@ -75,21 +68,21 @@ /// with the guarantee that the folded instance `U`

/// if and only if `U1` and `U2` are satisfiable. pub fn verify( &self, - U1: &R1CSInstance, - U2: &R1CSInstance, + U1: &R1CSInstance<G>, + U2: &R1CSInstance<G>, transcript: &mut Transcript, - ) -> Result<R1CSInstance, NovaError> { + ) -> Result<R1CSInstance<G>, NovaError> { // append the protocol name to the transcript - transcript.append_protocol_name(StepSNARK::protocol_name()); + transcript.append_message(b"protocol-name", StepSNARK::<G>::protocol_name()); // append `comm_T` to the transcript and obtain a challenge self.comm_T.append_to_transcript(b"comm_T", transcript); // compute a challenge from the transcript - let r = transcript.challenge_scalar(b"r"); + let r = G::Scalar::challenge(b"r", transcript); // fold the instance using `r` and `comm_T` - let U = U1.fold(U2, &self.comm_T, &r)?; + let U = U1.fold(U2, &self.comm_T, &r); // return the folded instance Ok(U)

@@ -97,18 +90,23 @@ }

} /// A SNARK that holds the proof of the final step of an incremental computation -pub struct FinalSNARK { - W: R1CSWitness, +pub struct FinalSNARK<G: Group> { + W: R1CSWitness<G>, } -impl FinalSNARK { +impl<G: Group> FinalSNARK<G> { /// Produces a proof of a instance given its satisfying witness `W`. - pub fn prove(W: &R1CSWitness) -> Result<FinalSNARK, NovaError> { + pub fn prove(W: &R1CSWitness<G>) -> Result<FinalSNARK<G>, NovaError> { Ok(FinalSNARK { W: W.clone() }) } /// Verifies the proof of a folded instance `U` given its shape `S` public parameters `gens` - pub fn verify(&self, gens: &R1CSGens, S: &R1CSShape, U: &R1CSInstance) -> Result<(), NovaError> { + pub fn verify( + &self, + gens: &R1CSGens<G>, + S: &R1CSShape<G>, + U: &R1CSInstance<G>, + ) -> Result<(), NovaError> { // check that the witness is a valid witness to the folded instance `U` S.is_sat(gens, U, &self.W) }

@@ -116,13 +114,93 @@ }

#[cfg(test)] mod tests { - use super::commitments::Scalar; use super::*; - use rand::rngs::OsRng; + use crate::traits::{CompressedGroup, Group, PrimeField}; + use core::borrow::Borrow; + use curve25519_dalek::traits::MultiscalarMul; + use rand::{rngs::OsRng, CryptoRng, RngCore}; + + type S = curve25519_dalek::scalar::Scalar; + type G = curve25519_dalek::ristretto::RistrettoPoint; + type C = curve25519_dalek::ristretto::CompressedRistretto; + + impl Group for G { + type Scalar = S; + type CompressedGroupElement = C; + + fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> Self + where + I: IntoIterator, + I::Item: Borrow<Self::Scalar>, + J: IntoIterator, + J::Item: Borrow<Self>, + Self: Clone, + { + Self::multiscalar_mul(scalars, points) + } + + fn compress(&self) -> Self::CompressedGroupElement { + self.compress() + } + + fn from_uniform_bytes(bytes: &[u8]) -> Option<Self> { + if bytes.len() != 64 { + None + } else { + let mut arr = [0; 64]; + arr.copy_from_slice(&bytes[0..64]); + Some(Self::from_uniform_bytes(&arr)) + } + } + } + + impl PrimeField for S { + fn zero() -> Self { + S::zero() + } + + fn one() -> Self { + S::one() + } + + fn from_bytes_mod_order_wide(bytes: &[u8]) -> Option<Self> { + if bytes.len() != 64 { + None + } else { + let mut arr = [0; 64]; + arr.copy_from_slice(&bytes[0..64]); + Some(Self::from_bytes_mod_order_wide(&arr)) + } + } + + fn random(rng: &mut (impl RngCore + CryptoRng)) -> Self { + S::random(rng) + } + } + + impl CompressedGroup for C { + type GroupElement = G; + + fn decompress(&self) -> Option<Self::GroupElement> { + self.decompress() + } + + fn as_bytes(&self) -> &[u8] { + self.as_bytes() + } + } + + impl ChallengeTrait for S { + fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self { + let mut buf = [0u8; 64]; + transcript.challenge_bytes(label, &mut buf); + S::from_bytes_mod_order_wide(&buf) + } + } #[test] fn test_tiny_r1cs() { - let one = Scalar::one(); + let one = S::one(); let (num_cons, num_vars, num_inputs, A, B, C) = { let num_cons = 4; let num_vars = 4;

@@ -138,9 +216,9 @@ // Relaxed R1CS is a set of three sparse matrices (A B C), where there is a row for every

// constraint and a column for every entry in z = (vars, u, inputs) // An R1CS instance is satisfiable iff: // Az \circ Bz = u \cdot Cz + E, where z = (vars, 1, inputs) - let mut A: Vec<(usize, usize, Scalar)> = Vec::new(); - let mut B: Vec<(usize, usize, Scalar)> = Vec::new(); - let mut C: Vec<(usize, usize, Scalar)> = Vec::new(); + let mut A: Vec<(usize, usize, S)> = Vec::new(); + let mut B: Vec<(usize, usize, S)> = Vec::new(); + let mut C: Vec<(usize, usize, S)> = Vec::new(); // constraint 0 entries in (A,B,C) A.push((0, 0, one));

@@ -177,11 +255,11 @@

// generate generators let gens = R1CSGens::new(num_cons, num_vars); - let rand_inst_witness_generator = |gens: &R1CSGens| -> (R1CSInstance, R1CSWitness) { + let rand_inst_witness_generator = |gens: &R1CSGens<G>| -> (R1CSInstance<G>, R1CSWitness<G>) { // compute a satisfying (vars, X) tuple let (vars, X) = { let mut csprng: OsRng = OsRng; - let z0 = Scalar::random(&mut csprng); + let z0 = S::random(&mut csprng); let z1 = z0 * z0; // constraint 0 let z2 = z1 * z0; // constraint 1 let z3 = z2 + z0; // constraint 2

@@ -193,14 +271,14 @@ (vars, X)

}; let W = { - let E = vec![Scalar::zero(); num_cons]; // default E + let E = vec![S::zero(); num_cons]; // default E let res = R1CSWitness::new(&S, &vars, &E); assert!(res.is_ok()); res.unwrap() }; let U = { let (comm_W, comm_E) = W.commit(&gens); - let u = Scalar::one(); //default u + let u = S::one(); //default u let res = R1CSInstance::new(&S, &comm_W, &comm_E, &X, &u); assert!(res.is_ok()); res.unwrap()
M src/r1cs.rs → src/r1cs.rs

@@ -1,41 +1,41 @@

#![allow(clippy::type_complexity)] -use super::commitments::Scalar; -use super::commitments::{CommitGens, CommitTrait, Commitment, CompressedCommitment}; +use super::commitments::{CommitGens, CommitTrait, Commitment}; use super::errors::NovaError; +use super::traits::{Group, PrimeField}; use itertools::concat; use rayon::prelude::*; -pub struct R1CSGens { - gens_W: CommitGens, - gens_E: CommitGens, +pub struct R1CSGens<G: Group> { + gens_W: CommitGens<G>, + gens_E: CommitGens<G>, } #[derive(Debug)] -pub struct R1CSShape { +pub struct R1CSShape<G: Group> { num_cons: usize, num_vars: usize, num_inputs: usize, - A: Vec<(usize, usize, Scalar)>, - B: Vec<(usize, usize, Scalar)>, - C: Vec<(usize, usize, Scalar)>, + A: Vec<(usize, usize, G::Scalar)>, + B: Vec<(usize, usize, G::Scalar)>, + C: Vec<(usize, usize, G::Scalar)>, } #[derive(Clone, Debug)] -pub struct R1CSWitness { - W: Vec<Scalar>, - E: Vec<Scalar>, +pub struct R1CSWitness<G: Group> { + W: Vec<G::Scalar>, + E: Vec<G::Scalar>, } #[derive(Clone, Debug, PartialEq, Eq)] -pub struct R1CSInstance { - comm_W: Commitment, - comm_E: Commitment, - X: Vec<Scalar>, - u: Scalar, +pub struct R1CSInstance<G: Group> { + comm_W: Commitment<G>, + comm_E: Commitment<G>, + X: Vec<G::Scalar>, + u: G::Scalar, } -impl R1CSGens { - pub fn new(num_cons: usize, num_vars: usize) -> R1CSGens { +impl<G: Group> R1CSGens<G> { + pub fn new(num_cons: usize, num_vars: usize) -> R1CSGens<G> { // generators to commit to witness vector `W` let gens_W = CommitGens::new(b"gens_W", num_vars);

@@ -46,19 +46,19 @@ R1CSGens { gens_E, gens_W }

} } -impl R1CSShape { +impl<G: Group> R1CSShape<G> { pub fn new( num_cons: usize, num_vars: usize, num_inputs: usize, - A: &[(usize, usize, Scalar)], - B: &[(usize, usize, Scalar)], - C: &[(usize, usize, Scalar)], - ) -> Result<R1CSShape, NovaError> { + A: &[(usize, usize, G::Scalar)], + B: &[(usize, usize, G::Scalar)], + C: &[(usize, usize, G::Scalar)], + ) -> Result<R1CSShape<G>, NovaError> { let is_valid = |num_cons: usize, num_vars: usize, num_io: usize, - M: &[(usize, usize, Scalar)]| + M: &[(usize, usize, G::Scalar)]| -> Result<(), NovaError> { let res = (0..num_cons) .map(|i| {

@@ -100,8 +100,8 @@ }

fn multiply_vec( &self, - z: &[Scalar], - ) -> Result<(Vec<Scalar>, Vec<Scalar>, Vec<Scalar>), NovaError> { + z: &[G::Scalar], + ) -> Result<(Vec<G::Scalar>, Vec<G::Scalar>, Vec<G::Scalar>), NovaError> { if z.len() != self.num_inputs + self.num_vars + 1 { return Err(NovaError::InvalidWitnessLength); }

@@ -110,13 +110,13 @@ // computes a product between a sparse matrix `M` and a vector `z`

// This does not perform any validation of entries in M (e.g., if entries in `M` reference indexes outside the range of `z`) // This is safe since we know that `M` is valid let sparse_matrix_vec_product = - |M: &Vec<(usize, usize, Scalar)>, num_rows: usize, z: &[Scalar]| -> Vec<Scalar> { + |M: &Vec<(usize, usize, G::Scalar)>, num_rows: usize, z: &[G::Scalar]| -> Vec<G::Scalar> { (0..M.len()) .map(|i| { let (row, col, val) = M[i]; (row, val * z[col]) }) - .fold(vec![Scalar::zero(); num_rows], |mut Mz, (r, v)| { + .fold(vec![G::Scalar::zero(); num_rows], |mut Mz, (r, v)| { Mz[r] += v; Mz })

@@ -131,9 +131,9 @@ }

pub fn is_sat( &self, - gens: &R1CSGens, - U: &R1CSInstance, - W: &R1CSWitness, + gens: &R1CSGens<G>, + U: &R1CSInstance<G>, + W: &R1CSWitness<G>, ) -> Result<(), NovaError> { assert_eq!(W.W.len(), self.num_vars); assert_eq!(W.E.len(), self.num_cons);

@@ -177,12 +177,12 @@ }

pub fn commit_T( &self, - gens: &R1CSGens, - U1: &R1CSInstance, - W1: &R1CSWitness, - U2: &R1CSInstance, - W2: &R1CSWitness, - ) -> Result<(Vec<Scalar>, CompressedCommitment), NovaError> { + gens: &R1CSGens<G>, + U1: &R1CSInstance<G>, + W1: &R1CSWitness<G>, + U2: &R1CSInstance<G>, + W2: &R1CSWitness<G>, + ) -> Result<(Vec<G::Scalar>, Commitment<G>), NovaError> { let (AZ_1, BZ_1, CZ_1) = { let Z1 = concat(vec![W1.W.clone(), vec![U1.u], U1.X.clone()]); self.multiply_vec(&Z1)?

@@ -195,33 +195,37 @@ };

let AZ_1_circ_BZ_2 = (0..AZ_1.len()) .map(|i| AZ_1[i] * BZ_2[i]) - .collect::<Vec<Scalar>>(); + .collect::<Vec<G::Scalar>>(); let AZ_2_circ_BZ_1 = (0..AZ_2.len()) .map(|i| AZ_2[i] * BZ_1[i]) - .collect::<Vec<Scalar>>(); + .collect::<Vec<G::Scalar>>(); let u_1_cdot_CZ_2 = (0..CZ_2.len()) .map(|i| U1.u * CZ_2[i]) - .collect::<Vec<Scalar>>(); + .collect::<Vec<G::Scalar>>(); let u_2_cdot_CZ_1 = (0..CZ_1.len()) .map(|i| U2.u * CZ_1[i]) - .collect::<Vec<Scalar>>(); + .collect::<Vec<G::Scalar>>(); let T = AZ_1_circ_BZ_2 .par_iter() .zip(&AZ_2_circ_BZ_1) .zip(&u_1_cdot_CZ_2) .zip(&u_2_cdot_CZ_1) - .map(|(((a, b), c), d)| a + b - c - d) - .collect::<Vec<Scalar>>(); + .map(|(((a, b), c), d)| *a + *b - *c - *d) + .collect::<Vec<G::Scalar>>(); - let comm_T = T.commit(&gens.gens_E).compress(); + let comm_T = T.commit(&gens.gens_E); Ok((T, comm_T)) } } -impl R1CSWitness { - pub fn new(S: &R1CSShape, W: &[Scalar], E: &[Scalar]) -> Result<R1CSWitness, NovaError> { +impl<G: Group> R1CSWitness<G> { + pub fn new( + S: &R1CSShape<G>, + W: &[G::Scalar], + E: &[G::Scalar], + ) -> Result<R1CSWitness<G>, NovaError> { if S.num_vars != W.len() || S.num_cons != E.len() { Err(NovaError::InvalidWitnessLength) } else {

@@ -232,11 +236,16 @@ })

} } - pub fn commit(&self, gens: &R1CSGens) -> (Commitment, Commitment) { + pub fn commit(&self, gens: &R1CSGens<G>) -> (Commitment<G>, Commitment<G>) { (self.W.commit(&gens.gens_W), self.E.commit(&gens.gens_E)) } - pub fn fold(&self, W2: &R1CSWitness, T: &[Scalar], r: &Scalar) -> Result<R1CSWitness, NovaError> { + pub fn fold( + &self, + W2: &R1CSWitness<G>, + T: &[G::Scalar], + r: &G::Scalar, + ) -> Result<R1CSWitness<G>, NovaError> { let (W1, E1) = (&self.W, &self.E); let (W2, E2) = (&W2.W, &W2.E);

@@ -247,26 +256,26 @@

let W = W1 .par_iter() .zip(W2) - .map(|(a, b)| a + r * b) - .collect::<Vec<Scalar>>(); + .map(|(a, b)| *a + *r * *b) + .collect::<Vec<G::Scalar>>(); let E = E1 .par_iter() .zip(T) .zip(E2) - .map(|((a, b), c)| a + r * b + r * r * c) - .collect::<Vec<Scalar>>(); + .map(|((a, b), c)| *a + *r * *b + *r * *r * *c) + .collect::<Vec<G::Scalar>>(); Ok(R1CSWitness { W, E }) } } -impl R1CSInstance { +impl<G: Group> R1CSInstance<G> { pub fn new( - S: &R1CSShape, - comm_W: &Commitment, - comm_E: &Commitment, - X: &[Scalar], - u: &Scalar, - ) -> Result<R1CSInstance, NovaError> { + S: &R1CSShape<G>, + comm_W: &Commitment<G>, + comm_E: &Commitment<G>, + X: &[G::Scalar], + u: &G::Scalar, + ) -> Result<R1CSInstance<G>, NovaError> { if S.num_inputs != X.len() { Err(NovaError::InvalidInputLength) } else {

@@ -281,11 +290,10 @@ }

pub fn fold( &self, - U2: &R1CSInstance, - comm_T: &CompressedCommitment, - r: &Scalar, - ) -> Result<R1CSInstance, NovaError> { - let comm_T_unwrapped = comm_T.decompress()?; + U2: &R1CSInstance<G>, + comm_T: &Commitment<G>, + r: &G::Scalar, + ) -> R1CSInstance<G> { let (X1, u1, comm_W_1, comm_E_1) = (&self.X, &self.u, &self.comm_W.clone(), &self.comm_E.clone()); let (X2, u2, comm_W_2, comm_E_2) = (&U2.X, &U2.u, &U2.comm_W, &U2.comm_E);

@@ -294,17 +302,17 @@ // weighted sum of X

let X = X1 .par_iter() .zip(X2) - .map(|(a, b)| a + r * b) - .collect::<Vec<Scalar>>(); - let comm_W = comm_W_1 + r * comm_W_2; - let comm_E = comm_E_1 + r * comm_T_unwrapped + r * r * comm_E_2; - let u = u1 + r * u2; + .map(|(a, b)| *a + *r * *b) + .collect::<Vec<G::Scalar>>(); + let comm_W = comm_W_1 + *comm_W_2 * *r; + let comm_E = *comm_E_1 + *comm_T * *r + *comm_E_2 * *r * *r; + let u = *u1 + *r * *u2; - Ok(R1CSInstance { + R1CSInstance { comm_W, comm_E, X, u, - }) + } } }
A src/traits.rs

@@ -0,0 +1,88 @@

+use core::borrow::Borrow; +use core::fmt::Debug; +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use merlin::Transcript; +use rand::{CryptoRng, RngCore}; + +/// Represents an element of a prime field +pub trait PrimeField: + Sized + + Eq + + Copy + + Clone + + Default + + Send + + Sync + + Debug + + Add<Output = Self> + + Sub<Output = Self> + + Mul<Output = Self> + + Neg<Output = Self> + + for<'a> Add<&'a Self, Output = Self> + + for<'a> Mul<&'a Self, Output = Self> + + for<'a> Sub<&'a Self, Output = Self> + + AddAssign + + MulAssign + + SubAssign + + for<'a> AddAssign<&'a Self> + + for<'a> MulAssign<&'a Self> + + for<'a> SubAssign<&'a Self> +{ + /// returns the additive identity of the field + fn zero() -> Self; + + /// returns the multiplicative identity of the field + fn one() -> Self; + + /// converts the supplied bytes into an element of the field + fn from_bytes_mod_order_wide(bytes: &[u8]) -> Option<Self>; + + /// returns an uniformly random element from the finite field + fn random(rng: &mut (impl RngCore + CryptoRng)) -> Self; +} + +/// Represents an element of a group +pub trait Group: + Clone + + Copy + + Debug + + Eq + + Sized + + Mul<<Self as Group>::Scalar, Output = Self> + + MulAssign<<Self as Group>::Scalar> + + for<'a> MulAssign<&'a <Self as Group>::Scalar> + + for<'a> Mul<&'a <Self as Group>::Scalar, Output = Self> + + Add<Self, Output = Self> + + AddAssign<Self> + + for<'a> AddAssign<&'a Self> + + for<'a> Add<&'a Self, Output = Self> +{ + type Scalar: PrimeField + ChallengeTrait; + type CompressedGroupElement: CompressedGroup; + + fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> Self + where + I: IntoIterator, + I::Item: Borrow<Self::Scalar>, + J: IntoIterator, + J::Item: Borrow<Self>, + Self: Clone; + + fn compress(&self) -> Self::CompressedGroupElement; + + fn from_uniform_bytes(bytes: &[u8]) -> Option<Self>; +} + +/// Represents a compressed version of a group element +pub trait CompressedGroup: Clone + Copy + Debug + Eq + Sized + Send + Sync + 'static { + type GroupElement: Group; + + fn decompress(&self) -> Option<Self::GroupElement>; + + fn as_bytes(&self) -> &[u8]; +} + +/// A helper trait to generate challenges using a transcript object +pub trait ChallengeTrait { + fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self; +}