Browse Source

Make code generic over a TranscriptEngine (#139)

main
Srinath Setty 1 year ago
committed by GitHub
parent
commit
8faffd38ea
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 400 additions and 153 deletions
  1. +1
    -1
      Cargo.toml
  2. +3
    -0
      src/errors.rs
  3. +41
    -38
      src/provider/ipa_pc.rs
  4. +160
    -0
      src/provider/keccak.rs
  5. +1
    -0
      src/provider/mod.rs
  6. +8
    -11
      src/provider/pasta.rs
  7. +23
    -8
      src/provider/pedersen.rs
  8. +14
    -13
      src/r1cs.rs
  9. +65
    -34
      src/spartan/mod.rs
  10. +19
    -20
      src/spartan/sumcheck.rs
  11. +2
    -2
      src/traits/commitment.rs
  12. +2
    -3
      src/traits/evaluation.rs
  13. +61
    -23
      src/traits/mod.rs

+ 1
- 1
Cargo.toml

@ -13,7 +13,6 @@ keywords = ["zkSNARKs", "cryptography", "proofs"]
[dependencies]
bellperson = { version = "0.24", default-features = false }
ff = { version = "0.12.0", features = ["derive"] }
merlin = {version = "3.0.0", default-features = false }
digest = "0.8.1"
sha3 = "0.8.2"
rayon = "1.3.0"
@ -40,6 +39,7 @@ pasta-msm = { version = "0.1.0", package = "lurk-pasta-msm" }
[dev-dependencies]
criterion = "0.3.1"
rand = "0.8.4"
hex = "0.4.3"
[[bench]]
name = "recursive-snark"

+ 3
- 0
src/errors.rs

@ -41,4 +41,7 @@ pub enum NovaError {
/// returned when the step execution produces an output whose length differs from a previously declared arity
#[error("InvalidStepOutputLength")]
InvalidStepOutputLength,
/// returned when the transcript engine encounters an overflow of the round number
#[error("InternalTranscriptError")]
InternalTranscriptError,
}

+ 41
- 38
src/provider/ipa_pc.rs

@ -7,13 +7,12 @@ use crate::{
traits::{
commitment::{CommitmentEngineTrait, CommitmentGensTrait, CommitmentTrait},
evaluation::EvaluationEngineTrait,
AppendToTranscriptTrait, ChallengeTrait, Group,
AppendToTranscriptTrait, ChallengeTrait, Group, TranscriptEngineTrait,
},
Commitment, CommitmentGens, CompressedCommitment, CE,
};
use core::{cmp::max, iter};
use ff::Field;
use merlin::Transcript;
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
@ -58,7 +57,7 @@ where
fn prove_batch(
gens: &Self::EvaluationGens,
transcript: &mut Transcript,
transcript: &mut G::TE,
comms: &[Commitment<G>],
polys: &[Vec<G::Scalar>],
points: &[Vec<G::Scalar>],
@ -89,7 +88,7 @@ where
),
&InnerProductWitness::new(&polys[i]),
transcript,
);
)?;
nifs.push(n);
r_U = u;
r_W = w;
@ -103,7 +102,7 @@ where
/// A method to verify purported evaluations of a batch of polynomials
fn verify_batch(
gens: &Self::EvaluationGens,
transcript: &mut Transcript,
transcript: &mut G::TE,
comms: &[Commitment<G>],
points: &[Vec<G::Scalar>],
evals: &[G::Scalar],
@ -129,7 +128,7 @@ where
&evals[i],
),
transcript,
);
)?;
r_U = u;
num_vars = max(num_vars, points[i].len());
}
@ -219,9 +218,9 @@ impl NIFSForInnerProduct {
W1: &InnerProductWitness<G>,
U2: &InnerProductInstance<G>,
W2: &InnerProductWitness<G>,
transcript: &mut Transcript,
) -> (Self, InnerProductInstance<G>, InnerProductWitness<G>) {
transcript.append_message(b"protocol-name", Self::protocol_name());
transcript: &mut G::TE,
) -> Result<(Self, InnerProductInstance<G>, InnerProductWitness<G>), NovaError> {
transcript.absorb_bytes(b"protocol-name", Self::protocol_name());
// pad the instances and witness so they are of the same length
let U1 = U1.pad(max(U1.b_vec.len(), U2.b_vec.len()));
@ -230,21 +229,25 @@ impl NIFSForInnerProduct {
let W2 = W2.pad(max(U1.b_vec.len(), U2.b_vec.len()));
// add the two commitments and two public vectors to the transcript
// we do not need to add public vectors as their compressed versions were
// read from the transcript
U1.comm_a_vec
.append_to_transcript(b"U1_comm_a_vec", transcript);
U1.b_vec.append_to_transcript(b"U1_b_vec", transcript);
U2.comm_a_vec
.append_to_transcript(b"U2_comm_a_vec", transcript);
U2.b_vec.append_to_transcript(b"U2_b_vec", transcript);
// compute the cross-term
let cross_term = inner_product(&W1.a_vec, &U2.b_vec) + inner_product(&W2.a_vec, &U1.b_vec);
// add the cross-term to the transcript
cross_term.append_to_transcript(b"cross_term", transcript);
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
&cross_term,
b"cross_term",
transcript,
);
// obtain a random challenge
let r = G::Scalar::challenge(b"r", transcript);
let r = G::Scalar::challenge(b"r", transcript)?;
// fold the vectors and their inner product
let a_vec = W1
@ -270,36 +273,38 @@ impl NIFSForInnerProduct {
c,
};
(NIFSForInnerProduct { cross_term }, U, W)
Ok((NIFSForInnerProduct { cross_term }, U, W))
}
fn verify(
&self,
U1: &InnerProductInstance<G>,
U2: &InnerProductInstance<G>,
transcript: &mut Transcript,
) -> InnerProductInstance<G> {
transcript.append_message(b"protocol-name", Self::protocol_name());
transcript: &mut G::TE,
) -> Result<InnerProductInstance<G>, NovaError> {
transcript.absorb_bytes(b"protocol-name", Self::protocol_name());
// pad the instances so they are of the same length
let U1 = U1.pad(max(U1.b_vec.len(), U2.b_vec.len()));
let U2 = U2.pad(max(U1.b_vec.len(), U2.b_vec.len()));
// add the two commitments and two public vectors to the transcript
// we do not need to add public vectors as their compressed representation
// were derived from the transcript
U1.comm_a_vec
.append_to_transcript(b"U1_comm_a_vec", transcript);
U1.b_vec.append_to_transcript(b"U1_b_vec", transcript);
U2.comm_a_vec
.append_to_transcript(b"U2_comm_a_vec", transcript);
U2.b_vec.append_to_transcript(b"U2_b_vec", transcript);
// add the cross-term to the transcript
self
.cross_term
.append_to_transcript(b"cross_term", transcript);
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
&self.cross_term,
b"cross_term",
transcript,
);
// obtain a random challenge
let r = G::Scalar::challenge(b"r", transcript);
let r = G::Scalar::challenge(b"r", transcript)?;
// fold the vectors and their inner product
let b_vec = U1
@ -311,11 +316,11 @@ impl NIFSForInnerProduct {
let c = U1.c + r * r * U2.c + r * self.cross_term;
let comm_a_vec = U1.comm_a_vec + U2.comm_a_vec * r;
InnerProductInstance {
Ok(InnerProductInstance {
comm_a_vec,
b_vec,
c,
}
})
}
}
@ -343,27 +348,26 @@ where
gens_c: &CommitmentGens<G>,
U: &InnerProductInstance<G>,
W: &InnerProductWitness<G>,
transcript: &mut Transcript,
transcript: &mut G::TE,
) -> Result<Self, NovaError> {
transcript.append_message(b"protocol-name", Self::protocol_name());
transcript.absorb_bytes(b"protocol-name", Self::protocol_name());
if U.b_vec.len() != W.a_vec.len() {
return Err(NovaError::InvalidInputLength);
}
U.comm_a_vec.append_to_transcript(b"comm_a_vec", transcript);
U.b_vec.append_to_transcript(b"b_vec", transcript);
U.c.append_to_transcript(b"c", transcript);
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(&U.c, b"c", transcript);
// sample a random base for commiting to the inner product
let r = G::Scalar::challenge(b"r", transcript);
let r = G::Scalar::challenge(b"r", transcript)?;
let gens_c = gens_c.scale(&r);
// a closure that executes a step of the recursive inner product argument
let prove_inner = |a_vec: &[G::Scalar],
b_vec: &[G::Scalar],
gens: &CommitmentGens<G>,
transcript: &mut Transcript|
transcript: &mut G::TE|
-> Result<
(
CompressedCommitment<G>,
@ -402,7 +406,7 @@ where
L.append_to_transcript(b"L", transcript);
R.append_to_transcript(b"R", transcript);
let r = G::Scalar::challenge(b"challenge_r", transcript);
let r = G::Scalar::challenge(b"challenge_r", transcript)?;
let r_inverse = r.invert().unwrap();
// fold the left half and the right half
@ -456,9 +460,9 @@ where
gens_c: &CommitmentGens<G>,
n: usize,
U: &InnerProductInstance<G>,
transcript: &mut Transcript,
transcript: &mut G::TE,
) -> Result<(), NovaError> {
transcript.append_message(b"protocol-name", Self::protocol_name());
transcript.absorb_bytes(b"protocol-name", Self::protocol_name());
if U.b_vec.len() != n
|| n != (1 << self.L_vec.len())
|| self.L_vec.len() != self.R_vec.len()
@ -468,11 +472,10 @@ where
}
U.comm_a_vec.append_to_transcript(b"comm_a_vec", transcript);
U.b_vec.append_to_transcript(b"b_vec", transcript);
U.c.append_to_transcript(b"c", transcript);
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(&U.c, b"c", transcript);
// sample a random base for commiting to the inner product
let r = G::Scalar::challenge(b"r", transcript);
let r = G::Scalar::challenge(b"r", transcript)?;
let gens_c = gens_c.scale(&r);
let P = U.comm_a_vec + CE::<G>::commit(&gens_c, &[U.c]);
@ -511,7 +514,7 @@ where
self.R_vec[i].append_to_transcript(b"R", transcript);
G::Scalar::challenge(b"challenge_r", transcript)
})
.collect::<Vec<G::Scalar>>();
.collect::<Result<Vec<G::Scalar>, NovaError>>()?;
// precompute scalars necessary for verification
let r_square: Vec<G::Scalar> = (0..self.L_vec.len())

+ 160
- 0
src/provider/keccak.rs

@ -0,0 +1,160 @@
//! This module provides an implementation of TranscriptEngineTrait using keccak256
use crate::traits::PrimeFieldExt;
use crate::{
errors::NovaError,
traits::{Group, TranscriptEngineTrait},
};
use core::marker::PhantomData;
use sha3::{Digest, Keccak256};
const PERSONA_TAG: &[u8] = b"NovaTranscript";
const DOM_SEP_TAG: &[u8] = b"NovaRound";
const KECCAK256_STATE_SIZE: usize = 64;
const KECCAK256_PREFIX_CHALLENGE_LO: u8 = 0;
const KECCAK256_PREFIX_CHALLENGE_HI: u8 = 1;
/// Provides an implementation of TranscriptEngine
#[derive(Debug, Clone)]
pub struct Keccak256Transcript<G: Group> {
round: u16,
state: [u8; KECCAK256_STATE_SIZE],
transcript: Vec<u8>,
_p: PhantomData<G>,
}
fn compute_updated_state(input: &[u8]) -> [u8; KECCAK256_STATE_SIZE] {
let input_lo = [input, &[KECCAK256_PREFIX_CHALLENGE_LO]].concat();
let input_hi = [input, &[KECCAK256_PREFIX_CHALLENGE_HI]].concat();
let mut hasher_lo = Keccak256::new();
let mut hasher_hi = Keccak256::new();
hasher_lo.input(&input_lo);
hasher_hi.input(&input_hi);
let output_lo = hasher_lo.result();
let output_hi = hasher_hi.result();
[output_lo, output_hi]
.concat()
.as_slice()
.try_into()
.unwrap()
}
impl<G: Group> TranscriptEngineTrait<G> for Keccak256Transcript<G> {
fn new(label: &'static [u8]) -> Self {
let input = [PERSONA_TAG, label].concat();
let output = compute_updated_state(&input);
Self {
round: 0u16,
state: output,
transcript: vec![],
_p: Default::default(),
}
}
fn squeeze_scalar(&mut self, label: &'static [u8]) -> Result<G::Scalar, NovaError> {
let input = [
DOM_SEP_TAG,
self.round.to_le_bytes().as_ref(),
self.state.as_ref(),
self.transcript.as_ref(),
label,
]
.concat();
let output = compute_updated_state(&input);
// update state
self.round = {
if let Some(v) = self.round.checked_add(1) {
v
} else {
return Err(NovaError::InternalTranscriptError);
}
};
self.state.copy_from_slice(&output);
self.transcript = vec![];
// squeeze out a challenge
Ok(G::Scalar::from_uniform(&output))
}
fn absorb_bytes(&mut self, label: &'static [u8], bytes: &[u8]) {
self.transcript.extend_from_slice(label);
self.transcript.extend_from_slice(bytes);
}
}
#[cfg(test)]
mod tests {
use crate::{
provider::keccak::Keccak256Transcript,
traits::{AppendToTranscriptTrait, ChallengeTrait, Group, TranscriptEngineTrait},
};
use ff::PrimeField;
use sha3::{Digest, Keccak256};
type G = pasta_curves::pallas::Point;
#[test]
fn test_keccak_transcript() {
let mut transcript = Keccak256Transcript::new(b"test");
// two scalars
let s1 = <G as Group>::Scalar::from(2u64);
let s2 = <G as Group>::Scalar::from(5u64);
// add the scalars to the transcript
<<G as Group>::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
&s1,
b"s1",
&mut transcript,
);
<<G as Group>::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
&s2,
b"s2",
&mut transcript,
);
// make a challenge
let c1 =
<<G as Group>::Scalar as ChallengeTrait<G>>::challenge(b"challenge_c1", &mut transcript)
.unwrap();
assert_eq!(
hex::encode(c1.to_repr().as_ref()),
"51648083af5387a04a7aa2aec789ee78fdabe45dc1391d270a38fcb576447c01"
);
// a scalar
let s3 = <G as Group>::Scalar::from(128u64);
// add the scalar to the transcript
<<G as Group>::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
&s3,
b"s3",
&mut transcript,
);
// make a challenge
let c2 =
<<G as Group>::Scalar as ChallengeTrait<G>>::challenge(b"challenge_c2", &mut transcript)
.unwrap();
assert_eq!(
hex::encode(c2.to_repr().as_ref()),
"9773f3349f7308153f6012e72b97fc304e48372bbd28bd122b37a8e46855d50f"
);
}
#[test]
fn test_keccak_example() {
let mut hasher = Keccak256::new();
hasher.input(0xffffffff_u32.to_le_bytes());
let output: [u8; 32] = hasher.result().try_into().unwrap();
assert_eq!(
hex::encode(output),
"29045a592007d0c246ef02c2223570da9522d0cf0f73282c79a1bc8f0bb2c238"
);
}
}

+ 1
- 0
src/provider/mod.rs

@ -5,6 +5,7 @@
//! `EvaluationEngine` with an IPA-based polynomial evaluation argument
pub mod ipa_pc;
pub mod keccak;
pub mod pasta;
pub mod pedersen;
pub mod poseidon;

+ 8
- 11
src/provider/pasta.rs

@ -1,23 +1,21 @@
//! This module implements the Nova traits for pallas::Point, pallas::Scalar, vesta::Point, vesta::Scalar.
use crate::{
provider::{
keccak::Keccak256Transcript,
pedersen::CommitmentEngine,
poseidon::{PoseidonRO, PoseidonROCircuit},
},
traits::{ChallengeTrait, CompressedGroup, Group},
traits::{CompressedGroup, Group, PrimeFieldExt},
};
use digest::{ExtendableOutput, Input};
use ff::Field;
use merlin::Transcript;
use num_bigint::BigInt;
use num_traits::Num;
use pasta_curves::{
self,
arithmetic::{CurveAffine, CurveExt, Group as OtherGroup},
arithmetic::{CurveAffine, CurveExt, FieldExt, Group as OtherGroup},
group::{cofactor::CofactorCurveAffine, Curve, Group as AnotherGroup, GroupEncoding},
pallas, vesta, Ep, EpAffine, Eq, EqAffine,
};
use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng};
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use sha3::Shake256;
@ -64,6 +62,7 @@ macro_rules! impl_traits {
type PreprocessedGroupElement = $name::Affine;
type RO = PoseidonRO<Self::Base, Self::Scalar>;
type ROCircuit = PoseidonROCircuit<Self::Base>;
type TE = Keccak256Transcript<Self>;
type CE = CommitmentEngine<Self>;
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
@ -171,12 +170,10 @@ macro_rules! impl_traits {
}
}
impl ChallengeTrait for $name::Scalar {
fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
transcript.challenge_bytes(label, &mut key);
let mut rng = ChaCha20Rng::from_seed(key);
$name::Scalar::random(&mut rng)
impl PrimeFieldExt for $name::Scalar {
fn from_uniform(bytes: &[u8]) -> Self {
let bytes_arr: [u8; 64] = bytes.try_into().unwrap();
$name::Scalar::from_bytes_wide(&bytes_arr)
}
}

+ 23
- 8
src/provider/pedersen.rs

@ -6,6 +6,7 @@ use crate::{
CommitmentEngineTrait, CommitmentGensTrait, CommitmentTrait, CompressedCommitmentTrait,
},
AbsorbInROTrait, AppendToTranscriptTrait, CompressedGroup, Group, ROTrait,
TranscriptEngineTrait,
},
};
use core::{
@ -13,8 +14,7 @@ use core::{
marker::PhantomData,
ops::{Add, AddAssign, Mul, MulAssign},
};
use ff::Field;
use merlin::Transcript;
use ff::{Field, PrimeField};
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
@ -96,9 +96,17 @@ impl CompressedCommitmentTrait 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<G: Group> AppendToTranscriptTrait<G> for Commitment<G> {
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut G::TE) {
let (x, y, is_infinity) = self.comm.to_coordinates();
let is_infinity_byte = if is_infinity { 0u8 } else { 1u8 };
let bytes = [
x.to_repr().as_ref(),
y.to_repr().as_ref(),
&[is_infinity_byte],
]
.concat();
transcript.absorb_bytes(label, &bytes);
}
}
@ -115,9 +123,16 @@ impl AbsorbInROTrait for Commitment {
}
}
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<C: CompressedGroup> AppendToTranscriptTrait<C::GroupElement> for CompressedCommitment<C> {
fn append_to_transcript(
&self,
label: &'static [u8],
transcript: &mut <C::GroupElement as Group>::TE,
) {
let comm = self.decompress().unwrap();
<Commitment<C::GroupElement> as AppendToTranscriptTrait<C::GroupElement>>::append_to_transcript(
&comm, label, transcript,
);
}
}

+ 14
- 13
src/r1cs.rs

@ -17,7 +17,6 @@ use core::cmp::max;
use ff::Field;
use flate2::{write::ZlibEncoder, Compression};
use itertools::concat;
use merlin::Transcript;
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use sha3::{Digest, Sha3_256};
@ -440,11 +439,13 @@ impl R1CSShape {
}
}
impl<G: Group> AppendToTranscriptTrait for R1CSShape<G> {
fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut Transcript) {
self
.get_digest()
.append_to_transcript(b"R1CSShape", transcript);
impl<G: Group> AppendToTranscriptTrait<G> for R1CSShape<G> {
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut G::TE) {
<<G as Group>::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
&self.get_digest(),
label,
transcript,
);
}
}
@ -488,10 +489,10 @@ impl R1CSInstance {
}
}
impl<G: Group> AppendToTranscriptTrait for R1CSInstance<G> {
fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut Transcript) {
impl<G: Group> AppendToTranscriptTrait<G> for R1CSInstance<G> {
fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut G::TE) {
self.comm_W.append_to_transcript(b"comm_W", transcript);
self.X.append_to_transcript(b"X", transcript);
<[G::Scalar] as AppendToTranscriptTrait<G>>::append_to_transcript(&self.X, b"X", transcript);
}
}
@ -629,12 +630,12 @@ impl RelaxedR1CSInstance {
}
}
impl<G: Group> AppendToTranscriptTrait for RelaxedR1CSInstance<G> {
fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut Transcript) {
impl<G: Group> AppendToTranscriptTrait<G> for RelaxedR1CSInstance<G> {
fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut G::TE) {
self.comm_W.append_to_transcript(b"comm_W", transcript);
self.comm_E.append_to_transcript(b"comm_E", transcript);
self.u.append_to_transcript(b"u", transcript);
self.X.append_to_transcript(b"X", transcript);
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(&self.u, b"u", transcript);
<[G::Scalar] as AppendToTranscriptTrait<G>>::append_to_transcript(&self.X, b"X", transcript);
}
}

+ 65
- 34
src/spartan/mod.rs

@ -9,12 +9,11 @@ use crate::{
traits::{
evaluation::EvaluationEngineTrait,
snark::{ProverKeyTrait, RelaxedR1CSSNARKTrait, VerifierKeyTrait},
AppendToTranscriptTrait, ChallengeTrait, Group,
AppendToTranscriptTrait, ChallengeTrait, Group, TranscriptEngineTrait,
},
};
use ff::Field;
use itertools::concat;
use merlin::Transcript;
use polynomial::{EqPolynomial, MultilinearPolynomial, SparsePolynomial};
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
@ -82,7 +81,7 @@ impl> RelaxedR1CSSNARKTrait
U: &RelaxedR1CSInstance<G>,
W: &RelaxedR1CSWitness<G>,
) -> Result<Self, NovaError> {
let mut transcript = Transcript::new(b"RelaxedR1CSSNARK");
let mut transcript = G::TE::new(b"RelaxedR1CSSNARK");
// sanity check that R1CSShape has certain size characteristics
assert_eq!(pk.S.num_cons.next_power_of_two(), pk.S.num_cons);
@ -105,7 +104,7 @@ impl> RelaxedR1CSSNARKTrait
// outer sum-check
let tau = (0..num_rounds_x)
.map(|_i| G::Scalar::challenge(b"challenge_tau", &mut transcript))
.collect();
.collect::<Result<Vec<G::Scalar>, NovaError>>()?;
let mut poly_tau = MultilinearPolynomial::new(EqPolynomial::new(tau).evals());
let (mut poly_Az, mut poly_Bz, poly_Cz, mut poly_uCz_E) = {
@ -136,22 +135,39 @@ impl> RelaxedR1CSSNARKTrait
&mut poly_uCz_E,
comb_func_outer,
&mut transcript,
);
)?;
// claims from the end of sum-check
let (claim_Az, claim_Bz): (G::Scalar, G::Scalar) = (claims_outer[1], claims_outer[2]);
claim_Az.append_to_transcript(b"claim_Az", &mut transcript);
claim_Bz.append_to_transcript(b"claim_Bz", &mut transcript);
let claim_Cz = poly_Cz.evaluate(&r_x);
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
&claim_Az,
b"claim_Az",
&mut transcript,
);
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
&claim_Bz,
b"claim_Bz",
&mut transcript,
);
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
&claim_Cz,
b"claim_Cz",
&mut transcript,
);
let eval_E = MultilinearPolynomial::new(W.E.clone()).evaluate(&r_x);
claim_Cz.append_to_transcript(b"claim_Cz", &mut transcript);
eval_E.append_to_transcript(b"eval_E", &mut transcript);
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
&eval_E,
b"eval_E",
&mut transcript,
);
// inner sum-check
let r_A = G::Scalar::challenge(b"challenge_rA", &mut transcript);
let r_B = G::Scalar::challenge(b"challenge_rB", &mut transcript);
let r_C = G::Scalar::challenge(b"challenge_rC", &mut transcript);
let r_A = G::Scalar::challenge(b"challenge_rA", &mut transcript)?;
let r_B = G::Scalar::challenge(b"challenge_rB", &mut transcript)?;
let r_C = G::Scalar::challenge(b"challenge_rC", &mut transcript)?;
let claim_inner_joint = r_A * claim_Az + r_B * claim_Bz + r_C * claim_Cz;
let poly_ABC = {
@ -219,10 +235,14 @@ impl> RelaxedR1CSSNARKTrait
&mut MultilinearPolynomial::new(poly_z),
comb_func,
&mut transcript,
);
)?;
let eval_W = MultilinearPolynomial::new(W.W.clone()).evaluate(&r_y[1..]);
eval_W.append_to_transcript(b"eval_W", &mut transcript);
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
&eval_W,
b"eval_W",
&mut transcript,
);
let eval_arg = EE::prove_batch(
&pk.gens,
@ -245,7 +265,7 @@ impl> RelaxedR1CSSNARKTrait
/// verifies a proof of satisfiability of a RelaxedR1CS instance
fn verify(&self, vk: &Self::VerifierKey, U: &RelaxedR1CSInstance<G>) -> Result<(), NovaError> {
let mut transcript = Transcript::new(b"RelaxedR1CSSNARK");
let mut transcript = G::TE::new(b"RelaxedR1CSSNARK");
// append the R1CSShape and RelaxedR1CSInstance to the transcript
vk.S.append_to_transcript(b"S", &mut transcript);
@ -259,7 +279,7 @@ impl> RelaxedR1CSSNARKTrait
// outer sum-check
let tau = (0..num_rounds_x)
.map(|_i| G::Scalar::challenge(b"challenge_tau", &mut transcript))
.collect::<Vec<G::Scalar>>();
.collect::<Result<Vec<G::Scalar>, NovaError>>()?;
let (claim_outer_final, r_x) =
self
@ -275,24 +295,31 @@ impl> RelaxedR1CSSNARKTrait
return Err(NovaError::InvalidSumcheckProof);
}
self
.claims_outer
.0
.append_to_transcript(b"claim_Az", &mut transcript);
self
.claims_outer
.1
.append_to_transcript(b"claim_Bz", &mut transcript);
self
.claims_outer
.2
.append_to_transcript(b"claim_Cz", &mut transcript);
self.eval_E.append_to_transcript(b"eval_E", &mut transcript);
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
&self.claims_outer.0,
b"claim_Az",
&mut transcript,
);
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
&self.claims_outer.1,
b"claim_Bz",
&mut transcript,
);
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
&self.claims_outer.2,
b"claim_Cz",
&mut transcript,
);
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
&self.eval_E,
b"eval_E",
&mut transcript,
);
// inner sum-check
let r_A = G::Scalar::challenge(b"challenge_rA", &mut transcript);
let r_B = G::Scalar::challenge(b"challenge_rB", &mut transcript);
let r_C = G::Scalar::challenge(b"challenge_rC", &mut transcript);
let r_A = G::Scalar::challenge(b"challenge_rA", &mut transcript)?;
let r_B = G::Scalar::challenge(b"challenge_rB", &mut transcript)?;
let r_C = G::Scalar::challenge(b"challenge_rC", &mut transcript)?;
let claim_inner_joint =
r_A * self.claims_outer.0 + r_B * self.claims_outer.1 + r_C * self.claims_outer.2;
@ -346,7 +373,11 @@ impl> RelaxedR1CSSNARKTrait
}
// verify eval_W and eval_E
self.eval_W.append_to_transcript(b"eval_W", &mut transcript); //eval_E is already in the transcript
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
&self.eval_W,
b"eval_W",
&mut transcript,
); //eval_E is already in the transcript
EE::verify_batch(
&vk.gens,

+ 19
- 20
src/spartan/sumcheck.rs

@ -5,7 +5,6 @@ use crate::errors::NovaError;
use crate::traits::{AppendToTranscriptTrait, ChallengeTrait, Group};
use core::marker::PhantomData;
use ff::Field;
use merlin::Transcript;
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
@ -21,7 +20,7 @@ impl SumcheckProof {
claim: G::Scalar,
num_rounds: usize,
degree_bound: usize,
transcript: &mut Transcript,
transcript: &mut G::TE,
) -> Result<(G::Scalar, Vec<G::Scalar>), NovaError> {
let mut e = claim;
let mut r: Vec<G::Scalar> = Vec::new();
@ -48,7 +47,7 @@ impl SumcheckProof {
poly.append_to_transcript(b"poly", transcript);
//derive the verifier's challenge for the next round
let r_i = G::Scalar::challenge(b"challenge_nextround", transcript);
let r_i = G::Scalar::challenge(b"challenge_nextround", transcript)?;
r.push(r_i);
@ -65,8 +64,8 @@ impl SumcheckProof {
poly_A: &mut MultilinearPolynomial<G::Scalar>,
poly_B: &mut MultilinearPolynomial<G::Scalar>,
comb_func: F,
transcript: &mut Transcript,
) -> (Self, Vec<G::Scalar>, Vec<G::Scalar>)
transcript: &mut G::TE,
) -> Result<(Self, Vec<G::Scalar>, Vec<G::Scalar>), NovaError>
where
F: Fn(&G::Scalar, &G::Scalar) -> G::Scalar + Sync,
{
@ -103,7 +102,7 @@ impl SumcheckProof {
poly.append_to_transcript(b"poly", transcript);
//derive the verifier's challenge for the next round
let r_i = G::Scalar::challenge(b"challenge_nextround", transcript);
let r_i = G::Scalar::challenge(b"challenge_nextround", transcript)?;
r.push(r_i);
polys.push(poly.compress());
@ -115,13 +114,13 @@ impl SumcheckProof {
poly_B.bound_poly_var_top(&r_i);
}
(
Ok((
SumcheckProof {
compressed_polys: polys,
},
r,
vec![poly_A[0], poly_B[0]],
)
))
}
pub fn prove_cubic_with_additive_term<F>(
@ -132,8 +131,8 @@ impl SumcheckProof {
poly_C: &mut MultilinearPolynomial<G::Scalar>,
poly_D: &mut MultilinearPolynomial<G::Scalar>,
comb_func: F,
transcript: &mut Transcript,
) -> (Self, Vec<G::Scalar>, Vec<G::Scalar>)
transcript: &mut G::TE,
) -> Result<(Self, Vec<G::Scalar>, Vec<G::Scalar>), NovaError>
where
F: Fn(&G::Scalar, &G::Scalar, &G::Scalar, &G::Scalar) -> G::Scalar + Sync,
{
@ -195,7 +194,7 @@ impl SumcheckProof {
poly.append_to_transcript(b"poly", transcript);
//derive the verifier's challenge for the next round
let r_i = G::Scalar::challenge(b"challenge_nextround", transcript);
let r_i = G::Scalar::challenge(b"challenge_nextround", transcript)?;
r.push(r_i);
polys.push(poly.compress());
@ -209,13 +208,13 @@ impl SumcheckProof {
poly_D.bound_poly_var_top(&r_i);
}
(
Ok((
SumcheckProof {
compressed_polys: polys,
},
r,
vec![poly_A[0], poly_B[0], poly_C[0], poly_D[0]],
)
))
}
}
@ -322,12 +321,12 @@ impl CompressedUniPoly {
}
}
impl<G: Group> AppendToTranscriptTrait for UniPoly<G> {
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) {
transcript.append_message(label, b"UniPoly_begin");
for i in 0..self.coeffs.len() {
self.coeffs[i].append_to_transcript(b"coeff", transcript);
}
transcript.append_message(label, b"UniPoly_end");
impl<G: Group> AppendToTranscriptTrait<G> for UniPoly<G> {
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut G::TE) {
<[G::Scalar] as AppendToTranscriptTrait<G>>::append_to_transcript(
&self.coeffs,
label,
transcript,
);
}
}

+ 2
- 2
src/traits/commitment.rs

@ -71,7 +71,7 @@ pub trait CommitmentTrait:
+ Serialize
+ for<'de> Deserialize<'de>
+ AbsorbInROTrait<G>
+ AppendToTranscriptTrait
+ AppendToTranscriptTrait<G>
+ CommitmentOps
+ CommitmentOpsOwned
+ ScalarMul<G::Scalar>
@ -96,7 +96,7 @@ pub trait CompressedCommitmentTrait:
+ Sync
+ Serialize
+ for<'de> Deserialize<'de>
+ AppendToTranscriptTrait
+ AppendToTranscriptTrait<C::GroupElement>
{
/// Holds the type of the commitment that can be decompressed into
type Commitment;

+ 2
- 3
src/traits/evaluation.rs

@ -5,7 +5,6 @@ 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
@ -27,7 +26,7 @@ pub trait EvaluationEngineTrait:
/// A method to prove evaluations of a batch of polynomials
fn prove_batch(
gens: &Self::EvaluationGens,
transcript: &mut Transcript,
transcript: &mut G::TE,
comm: &[<Self::CE as CommitmentEngineTrait<G>>::Commitment],
polys: &[Vec<G::Scalar>],
points: &[Vec<G::Scalar>],
@ -37,7 +36,7 @@ pub trait EvaluationEngineTrait:
/// A method to verify purported evaluations of a batch of polynomials
fn verify_batch(
gens: &Self::EvaluationGens,
transcript: &mut Transcript,
transcript: &mut G::TE,
comm: &[<Self::CE as CommitmentEngineTrait<G>>::Commitment],
points: &[Vec<G::Scalar>],
evals: &[G::Scalar],

+ 61
- 23
src/traits/mod.rs

@ -1,4 +1,5 @@
//! This module defines various traits required by the users of the library to implement.
use crate::errors::NovaError;
use bellperson::{
gadgets::{boolean::AllocatedBit, num::AllocatedNum},
ConstraintSystem, SynthesisError,
@ -8,7 +9,6 @@ use core::{
ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign},
};
use ff::{PrimeField, PrimeFieldBits};
use merlin::Transcript;
use num_bigint::BigInt;
use serde::{Deserialize, Serialize};
@ -39,7 +39,8 @@ pub trait Group:
/// A type representing an element of the scalar field of the group
type Scalar: PrimeField
+ PrimeFieldBits
+ ChallengeTrait
+ PrimeFieldExt
+ ChallengeTrait<Self>
+ Send
+ Sync
+ Serialize
@ -53,13 +54,16 @@ pub trait Group:
/// A type representing preprocessed group element
type PreprocessedGroupElement: Clone + Debug + Send + Sync + Serialize + for<'de> Deserialize<'de>;
/// A type that represents a hash function that consumes elements
/// A type that represents a circuit-friendly sponge that consumes elements
/// from the base field and squeezes out elements of the scalar field
type RO: ROTrait<Self::Base, Self::Scalar> + Serialize + for<'de> Deserialize<'de>;
/// An alternate implementation of Self::RO in the circuit model
type ROCircuit: ROCircuitTrait<Self::Base> + Serialize + for<'de> Deserialize<'de>;
/// A type that provides a generic Fiat-Shamir transcript to be used when externalizing proofs
type TE: TranscriptEngineTrait<Self>;
/// A type that defines a commitment engine over scalars in the group
type CE: CommitmentEngineTrait<Self> + Serialize + for<'de> Deserialize<'de>;
@ -105,24 +109,12 @@ pub trait CompressedGroup:
fn as_bytes(&self) -> &[u8];
}
/// A helper trait to append different types to the transcript
pub trait AppendToTranscriptTrait {
/// appends the value to the transcript under the provided label
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript);
}
/// A helper trait to absorb different objects in RO
pub trait AbsorbInROTrait<G: Group> {
/// Absorbs the value in the provided RO
fn absorb_in_ro(&self, ro: &mut G::RO);
}
/// A helper trait to generate challenges using a transcript object
pub trait ChallengeTrait {
/// Returns a Scalar representing the challenge using the transcript
fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self;
}
/// A helper trait that defines the behavior of a hash function that we use as an RO
pub trait ROTrait<Base, Scalar> {
/// A type representing constants/parameters associated with the hash function
@ -204,17 +196,63 @@ impl ScalarMul for T where T: Mul
pub trait ScalarMulOwned<Rhs, Output = Self>: for<'r> ScalarMul<&'r Rhs, Output> {}
impl<T, Rhs, Output> ScalarMulOwned<Rhs, Output> for T where T: for<'r> ScalarMul<&'r Rhs, Output> {}
impl<F: PrimeField> AppendToTranscriptTrait for F {
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) {
transcript.append_message(label, self.to_repr().as_ref());
/// This trait defines the behavior of a transcript engine compatible with Spartan
pub trait TranscriptEngineTrait<G: Group>: Send + Sync {
/// initializes the transcript
fn new(label: &'static [u8]) -> Self;
/// returns a scalar element of the group as a challenge
fn squeeze_scalar(&mut self, label: &'static [u8]) -> Result<G::Scalar, NovaError>;
/// absorbs a label and a sequence of bytes
fn absorb_bytes(&mut self, label: &'static [u8], bytes: &[u8]);
}
/// A helper trait to append different types to the transcript
pub trait AppendToTranscriptTrait<G: Group> {
/// appends the value to the transcript under the provided label
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut G::TE);
}
/// A helper trait to generate challenges using a transcript object
pub trait ChallengeTrait<G: Group> {
/// Returns a challenge from the transcript
fn challenge(label: &'static [u8], transcript: &mut G::TE) -> Result<Self, NovaError>
where
Self: Sized;
}
/// Defines additional methods on PrimeField objects
pub trait PrimeFieldExt: PrimeField {
/// Returns a Scalar representing the bytes
fn from_uniform(bytes: &[u8]) -> Self;
/// Returns a byte representation
fn to_bytes(v: &[Self]) -> Vec<u8> {
(0..v.len())
.map(|i| v[i].to_repr().as_ref().to_vec())
.collect::<Vec<Vec<u8>>>()
.into_iter()
.flatten()
.collect::<Vec<u8>>()
}
}
impl<G: Group<Scalar = F>, F: PrimeField> ChallengeTrait<G> for F {
fn challenge(label: &'static [u8], transcript: &mut G::TE) -> Result<F, NovaError> {
transcript.squeeze_scalar(label)
}
}
impl<G: Group<Scalar = F>, F: PrimeField> AppendToTranscriptTrait<G> for F {
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut G::TE) {
transcript.absorb_bytes(label, self.to_repr().as_ref());
}
}
impl<F: PrimeField> AppendToTranscriptTrait for [F] {
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) {
for s in self {
s.append_to_transcript(label, transcript);
}
impl<G: Group<Scalar = F>, F: PrimeField + PrimeFieldExt> AppendToTranscriptTrait<G> for [F] {
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut G::TE) {
transcript.absorb_bytes(label, &<F as PrimeFieldExt>::to_bytes(self));
}
}

Loading…
Cancel
Save