mirror of
https://github.com/arnaucube/Nova.git
synced 2026-01-11 16:41:28 +01:00
generic traits for prime field elements and group elements
This commit is contained in:
@@ -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, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct Commitment<G: Group> {
|
||||
comm: G,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Commitment {
|
||||
comm: GroupElement,
|
||||
pub struct CompressedCommitment<C: CompressedGroup> {
|
||||
comm: C,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CompressedCommitment {
|
||||
comm: CompressedGroup,
|
||||
}
|
||||
|
||||
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 @@ impl CompressedCommitment {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AppendToTranscriptTrait {
|
||||
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript);
|
||||
}
|
||||
|
||||
impl AppendToTranscriptTrait for CompressedCommitment {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
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<'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<G: Group> AddAssign<Commitment<G>> for Commitment<G> {
|
||||
fn add_assign(&mut self, rhs: 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<'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
|
||||
}
|
||||
}
|
||||
|
||||
define_add_assign_variants!(LHS = Commitment, RHS = Commitment);
|
||||
define_add_variants!(LHS = Commitment, RHS = Commitment, Output = Commitment);
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: Group> Add<Commitment<G>> for Commitment<G> {
|
||||
type Output = Commitment<G>;
|
||||
fn add(self, rhs: Commitment<G>) -> Commitment<G> {
|
||||
&self + &rhs
|
||||
}
|
||||
}
|
||||
|
||||
170
src/lib.rs
170
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 @@ impl StepSNARK {
|
||||
/// 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 @@ impl StepSNARK {
|
||||
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 @@ impl StepSNARK {
|
||||
/// 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 @@ impl StepSNARK {
|
||||
}
|
||||
|
||||
/// 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 @@ impl FinalSNARK {
|
||||
|
||||
#[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 @@ mod tests {
|
||||
// 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 @@ mod tests {
|
||||
// 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 @@ mod tests {
|
||||
};
|
||||
|
||||
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()
|
||||
|
||||
152
src/r1cs.rs
152
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 @@ impl R1CSGens {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ impl R1CSShape {
|
||||
|
||||
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 @@ impl R1CSShape {
|
||||
// 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 @@ impl R1CSShape {
|
||||
|
||||
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 @@ impl R1CSShape {
|
||||
|
||||
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 @@ impl R1CSShape {
|
||||
|
||||
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 @@ impl R1CSWitness {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ impl R1CSWitness {
|
||||
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 @@ impl R1CSInstance {
|
||||
|
||||
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 @@ impl R1CSInstance {
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
88
src/traits.rs
Normal file
88
src/traits.rs
Normal file
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user