Browse Source

Simplify TranscriptEngine usage (#148)

* simplify transcript engine usage

* update version
main
Srinath Setty 1 year ago
committed by GitHub
parent
commit
03710dbb97
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 163 additions and 191 deletions
  1. +1
    -1
      Cargo.toml
  2. +27
    -16
      src/provider/ipa_pc.rs
  3. +19
    -30
      src/provider/keccak.rs
  4. +14
    -11
      src/provider/pasta.rs
  5. +12
    -14
      src/provider/pedersen.rs
  6. +13
    -21
      src/r1cs.rs
  7. +24
    -40
      src/spartan/mod.rs
  8. +14
    -17
      src/spartan/sumcheck.rs
  9. +4
    -4
      src/traits/commitment.rs
  10. +35
    -37
      src/traits/mod.rs

+ 1
- 1
Cargo.toml

@ -1,6 +1,6 @@
[package] [package]
name = "nova-snark" name = "nova-snark"
version = "0.17.0"
version = "0.18.0"
authors = ["Srinath Setty <srinath@microsoft.com>"] authors = ["Srinath Setty <srinath@microsoft.com>"]
edition = "2021" edition = "2021"
description = "Recursive zkSNARKs without trusted setup" description = "Recursive zkSNARKs without trusted setup"

+ 27
- 16
src/provider/ipa_pc.rs

@ -7,7 +7,7 @@ use crate::{
traits::{ traits::{
commitment::{CommitmentEngineTrait, CommitmentKeyTrait, CommitmentTrait}, commitment::{CommitmentEngineTrait, CommitmentKeyTrait, CommitmentTrait},
evaluation::EvaluationEngineTrait, evaluation::EvaluationEngineTrait,
AppendToTranscriptTrait, ChallengeTrait, Group, TranscriptEngineTrait,
Group, TranscriptEngineTrait, TranscriptReprTrait,
}, },
Commitment, CommitmentKey, CompressedCommitment, CE, Commitment, CommitmentKey, CompressedCommitment, CE,
}; };
@ -139,6 +139,17 @@ impl InnerProductInstance {
} }
} }
impl<G: Group> TranscriptReprTrait<G> for InnerProductInstance<G> {
fn to_transcript_bytes(&self) -> Vec<u8> {
// we do not need to include self.b_vec as in our context it is produced from the transcript
[
self.comm_a_vec.to_transcript_bytes(),
self.c.to_transcript_bytes(),
]
.concat()
}
}
struct InnerProductWitness<G: Group> { struct InnerProductWitness<G: Group> {
a_vec: Vec<G::Scalar>, a_vec: Vec<G::Scalar>,
} }
@ -167,7 +178,7 @@ where
CommitmentKey<G>: CommitmentKeyExtTrait<G, CE = G::CE>, CommitmentKey<G>: CommitmentKeyExtTrait<G, CE = G::CE>,
{ {
fn protocol_name() -> &'static [u8] { fn protocol_name() -> &'static [u8] {
b"inner product argument"
b"IPA"
} }
fn prove( fn prove(
@ -177,17 +188,17 @@ where
W: &InnerProductWitness<G>, W: &InnerProductWitness<G>,
transcript: &mut G::TE, transcript: &mut G::TE,
) -> Result<Self, NovaError> { ) -> Result<Self, NovaError> {
transcript.absorb_bytes(b"protocol-name", Self::protocol_name());
transcript.dom_sep(Self::protocol_name());
if U.b_vec.len() != W.a_vec.len() { if U.b_vec.len() != W.a_vec.len() {
return Err(NovaError::InvalidInputLength); return Err(NovaError::InvalidInputLength);
} }
U.comm_a_vec.append_to_transcript(b"comm_a_vec", transcript);
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(&U.c, b"c", transcript);
// absorb the instance in the transcript
transcript.absorb(b"U", U);
// sample a random base for commiting to the inner product // sample a random base for commiting to the inner product
let r = G::Scalar::challenge(b"r", transcript)?;
let r = transcript.squeeze(b"r")?;
let ck_c = ck_c.scale(&r); let ck_c = ck_c.scale(&r);
// a closure that executes a step of the recursive inner product argument // a closure that executes a step of the recursive inner product argument
@ -230,10 +241,10 @@ where
) )
.compress(); .compress();
L.append_to_transcript(b"L", transcript);
R.append_to_transcript(b"R", transcript);
transcript.absorb(b"L", &L);
transcript.absorb(b"R", &R);
let r = G::Scalar::challenge(b"challenge_r", transcript)?;
let r = transcript.squeeze(b"r")?;
let r_inverse = r.invert().unwrap(); let r_inverse = r.invert().unwrap();
// fold the left half and the right half // fold the left half and the right half
@ -289,7 +300,7 @@ where
U: &InnerProductInstance<G>, U: &InnerProductInstance<G>,
transcript: &mut G::TE, transcript: &mut G::TE,
) -> Result<(), NovaError> { ) -> Result<(), NovaError> {
transcript.absorb_bytes(b"protocol-name", Self::protocol_name());
transcript.dom_sep(Self::protocol_name());
if U.b_vec.len() != n if U.b_vec.len() != n
|| n != (1 << self.L_vec.len()) || n != (1 << self.L_vec.len())
|| self.L_vec.len() != self.R_vec.len() || self.L_vec.len() != self.R_vec.len()
@ -298,11 +309,11 @@ where
return Err(NovaError::InvalidInputLength); return Err(NovaError::InvalidInputLength);
} }
U.comm_a_vec.append_to_transcript(b"comm_a_vec", transcript);
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(&U.c, b"c", transcript);
// absorb the instance in the transcript
transcript.absorb(b"U", U);
// sample a random base for commiting to the inner product // sample a random base for commiting to the inner product
let r = G::Scalar::challenge(b"r", transcript)?;
let r = transcript.squeeze(b"r")?;
let ck_c = ck_c.scale(&r); let ck_c = ck_c.scale(&r);
let P = U.comm_a_vec + CE::<G>::commit(&ck_c, &[U.c]); let P = U.comm_a_vec + CE::<G>::commit(&ck_c, &[U.c]);
@ -337,9 +348,9 @@ where
// compute a vector of public coins using self.L_vec and self.R_vec // compute a vector of public coins using self.L_vec and self.R_vec
let r = (0..self.L_vec.len()) let r = (0..self.L_vec.len())
.map(|i| { .map(|i| {
self.L_vec[i].append_to_transcript(b"L", transcript);
self.R_vec[i].append_to_transcript(b"R", transcript);
G::Scalar::challenge(b"challenge_r", transcript)
transcript.absorb(b"L", &self.L_vec[i]);
transcript.absorb(b"R", &self.R_vec[i]);
transcript.squeeze(b"r")
}) })
.collect::<Result<Vec<G::Scalar>, NovaError>>()?; .collect::<Result<Vec<G::Scalar>, NovaError>>()?;

+ 19
- 30
src/provider/keccak.rs

@ -2,13 +2,13 @@
use crate::traits::PrimeFieldExt; use crate::traits::PrimeFieldExt;
use crate::{ use crate::{
errors::NovaError, errors::NovaError,
traits::{Group, TranscriptEngineTrait},
traits::{Group, TranscriptEngineTrait, TranscriptReprTrait},
}; };
use core::marker::PhantomData; use core::marker::PhantomData;
use sha3::{Digest, Keccak256}; use sha3::{Digest, Keccak256};
const PERSONA_TAG: &[u8] = b"NovaTranscript";
const DOM_SEP_TAG: &[u8] = b"NovaRound";
const PERSONA_TAG: &[u8] = b"NoTR";
const DOM_SEP_TAG: &[u8] = b"NoDS";
const KECCAK256_STATE_SIZE: usize = 64; const KECCAK256_STATE_SIZE: usize = 64;
const KECCAK256_PREFIX_CHALLENGE_LO: u8 = 0; const KECCAK256_PREFIX_CHALLENGE_LO: u8 = 0;
const KECCAK256_PREFIX_CHALLENGE_HI: u8 = 1; const KECCAK256_PREFIX_CHALLENGE_HI: u8 = 1;
@ -55,7 +55,7 @@ impl TranscriptEngineTrait for Keccak256Transcript {
} }
} }
fn squeeze_scalar(&mut self, label: &'static [u8]) -> Result<G::Scalar, NovaError> {
fn squeeze(&mut self, label: &'static [u8]) -> Result<G::Scalar, NovaError> {
let input = [ let input = [
DOM_SEP_TAG, DOM_SEP_TAG,
self.round.to_le_bytes().as_ref(), self.round.to_le_bytes().as_ref(),
@ -81,8 +81,13 @@ impl TranscriptEngineTrait for Keccak256Transcript {
Ok(G::Scalar::from_uniform(&output)) Ok(G::Scalar::from_uniform(&output))
} }
fn absorb_bytes(&mut self, label: &'static [u8], bytes: &[u8]) {
fn absorb<T: TranscriptReprTrait<G>>(&mut self, label: &'static [u8], o: &T) {
self.transcript.extend_from_slice(label); self.transcript.extend_from_slice(label);
self.transcript.extend_from_slice(&o.to_transcript_bytes());
}
fn dom_sep(&mut self, bytes: &'static [u8]) {
self.transcript.extend_from_slice(DOM_SEP_TAG);
self.transcript.extend_from_slice(bytes); self.transcript.extend_from_slice(bytes);
} }
} }
@ -91,7 +96,7 @@ impl TranscriptEngineTrait for Keccak256Transcript {
mod tests { mod tests {
use crate::{ use crate::{
provider::keccak::Keccak256Transcript, provider::keccak::Keccak256Transcript,
traits::{AppendToTranscriptTrait, ChallengeTrait, Group, TranscriptEngineTrait},
traits::{Group, TranscriptEngineTrait},
}; };
use ff::PrimeField; use ff::PrimeField;
use sha3::{Digest, Keccak256}; use sha3::{Digest, Keccak256};
@ -100,50 +105,34 @@ mod tests {
#[test] #[test]
fn test_keccak_transcript() { fn test_keccak_transcript() {
let mut transcript = Keccak256Transcript::new(b"test");
let mut transcript: Keccak256Transcript<G> = Keccak256Transcript::new(b"test");
// two scalars // two scalars
let s1 = <G as Group>::Scalar::from(2u64); let s1 = <G as Group>::Scalar::from(2u64);
let s2 = <G as Group>::Scalar::from(5u64); let s2 = <G as Group>::Scalar::from(5u64);
// add the scalars to the transcript // 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,
);
transcript.absorb(b"s1", &s1);
transcript.absorb(b"s2", &s2);
// make a challenge // make a challenge
let c1 =
<<G as Group>::Scalar as ChallengeTrait<G>>::challenge(b"challenge_c1", &mut transcript)
.unwrap();
let c1: <G as Group>::Scalar = transcript.squeeze(b"c1").unwrap();
assert_eq!( assert_eq!(
hex::encode(c1.to_repr().as_ref()), hex::encode(c1.to_repr().as_ref()),
"51648083af5387a04a7aa2aec789ee78fdabe45dc1391d270a38fcb576447c01"
"432d5811c8be3d44d47f52108a8749ae18482efd1a37b830f966456b5d75340c"
); );
// a scalar // a scalar
let s3 = <G as Group>::Scalar::from(128u64); let s3 = <G as Group>::Scalar::from(128u64);
// add the scalar to the transcript // add the scalar to the transcript
<<G as Group>::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
&s3,
b"s3",
&mut transcript,
);
transcript.absorb(b"s3", &s3);
// make a challenge // make a challenge
let c2 =
<<G as Group>::Scalar as ChallengeTrait<G>>::challenge(b"challenge_c2", &mut transcript)
.unwrap();
let c2: <G as Group>::Scalar = transcript.squeeze(b"c2").unwrap();
assert_eq!( assert_eq!(
hex::encode(c2.to_repr().as_ref()), hex::encode(c2.to_repr().as_ref()),
"9773f3349f7308153f6012e72b97fc304e48372bbd28bd122b37a8e46855d50f"
"65f7908d53abcd18f3b1d767456ef9009b91c7566a635e9ca7be26e21d4d7a10"
); );
} }

+ 14
- 11
src/provider/pasta.rs

@ -1,12 +1,11 @@
//! This module implements the Nova traits for pallas::Point, pallas::Scalar, vesta::Point, vesta::Scalar. //! This module implements the Nova traits for pallas::Point, pallas::Scalar, vesta::Point, vesta::Scalar.
use crate::{ use crate::{
errors::NovaError,
provider::{ provider::{
keccak::Keccak256Transcript, keccak::Keccak256Transcript,
pedersen::CommitmentEngine, pedersen::CommitmentEngine,
poseidon::{PoseidonRO, PoseidonROCircuit}, poseidon::{PoseidonRO, PoseidonROCircuit},
}, },
traits::{ChallengeTrait, CompressedGroup, Group, PrimeFieldExt, TranscriptEngineTrait},
traits::{CompressedGroup, Group, PrimeFieldExt, TranscriptReprTrait},
}; };
use digest::{ExtendableOutput, Input}; use digest::{ExtendableOutput, Input};
use ff::PrimeField; use ff::PrimeField;
@ -177,9 +176,11 @@ macro_rules! impl_traits {
let bytes_arr: [u8; 64] = bytes.try_into().unwrap(); let bytes_arr: [u8; 64] = bytes.try_into().unwrap();
$name::Scalar::from_bytes_wide(&bytes_arr) $name::Scalar::from_bytes_wide(&bytes_arr)
} }
}
fn to_bytes(&self) -> Vec<u8> {
self.to_repr().as_ref().to_vec()
impl<G: Group> TranscriptReprTrait<G> for $name_compressed {
fn to_transcript_bytes(&self) -> Vec<u8> {
self.repr.to_vec()
} }
} }
@ -189,17 +190,19 @@ macro_rules! impl_traits {
fn decompress(&self) -> Option<$name::Point> { fn decompress(&self) -> Option<$name::Point> {
Some($name_curve::from_bytes(&self.repr).unwrap()) Some($name_curve::from_bytes(&self.repr).unwrap())
} }
fn as_bytes(&self) -> Vec<u8> {
self.repr.to_vec()
}
} }
}; };
} }
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> TranscriptReprTrait<G> for pallas::Base {
fn to_transcript_bytes(&self) -> Vec<u8> {
self.to_repr().to_vec()
}
}
impl<G: Group> TranscriptReprTrait<G> for pallas::Scalar {
fn to_transcript_bytes(&self) -> Vec<u8> {
self.to_repr().to_vec()
} }
} }

+ 12
- 14
src/provider/pedersen.rs

@ -3,8 +3,7 @@ use crate::{
errors::NovaError, errors::NovaError,
traits::{ traits::{
commitment::{CommitmentEngineTrait, CommitmentKeyTrait, CommitmentTrait}, commitment::{CommitmentEngineTrait, CommitmentKeyTrait, CommitmentTrait},
AbsorbInROTrait, AppendToTranscriptTrait, CompressedGroup, Group, ROTrait,
TranscriptEngineTrait,
AbsorbInROTrait, CompressedGroup, Group, ROTrait, TranscriptReprTrait,
}, },
}; };
use core::{ use core::{
@ -12,7 +11,7 @@ use core::{
marker::PhantomData, marker::PhantomData,
ops::{Add, AddAssign, Mul, MulAssign}, ops::{Add, AddAssign, Mul, MulAssign},
}; };
use ff::{Field, PrimeField};
use ff::Field;
use rayon::prelude::*; use rayon::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -89,17 +88,16 @@ impl Default for Commitment {
} }
} }
impl<G: Group> AppendToTranscriptTrait<G> for Commitment<G> {
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut G::TE) {
impl<G: Group> TranscriptReprTrait<G> for Commitment<G> {
fn to_transcript_bytes(&self) -> Vec<u8> {
let (x, y, is_infinity) = self.comm.to_coordinates(); let (x, y, is_infinity) = self.comm.to_coordinates();
let is_infinity_byte = if is_infinity { 0u8 } else { 1u8 }; 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],
[
x.to_transcript_bytes(),
y.to_transcript_bytes(),
[is_infinity_byte].to_vec(),
] ]
.concat();
transcript.absorb_bytes(label, &bytes);
.concat()
} }
} }
@ -116,9 +114,9 @@ impl AbsorbInROTrait for Commitment {
} }
} }
impl<G: Group> AppendToTranscriptTrait<G> for CompressedCommitment<G> {
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut G::TE) {
transcript.absorb_bytes(label, &self.comm.as_bytes());
impl<G: Group> TranscriptReprTrait<G> for CompressedCommitment<G> {
fn to_transcript_bytes(&self) -> Vec<u8> {
self.comm.to_transcript_bytes()
} }
} }

+ 13
- 21
src/r1cs.rs

@ -9,7 +9,7 @@ use crate::{
}, },
traits::{ traits::{
commitment::{CommitmentEngineTrait, CommitmentKeyTrait}, commitment::{CommitmentEngineTrait, CommitmentKeyTrait},
AbsorbInROTrait, AppendToTranscriptTrait, Group, ROTrait,
AbsorbInROTrait, Group, ROTrait, TranscriptReprTrait,
}, },
Commitment, CommitmentKey, CE, Commitment, CommitmentKey, CE,
}; };
@ -435,13 +435,9 @@ impl R1CSShape {
} }
} }
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,
);
impl<G: Group> TranscriptReprTrait<G> for R1CSShape<G> {
fn to_transcript_bytes(&self) -> Vec<u8> {
self.get_digest().to_transcript_bytes()
} }
} }
@ -485,13 +481,6 @@ impl R1CSInstance {
} }
} }
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);
<[G::Scalar] as AppendToTranscriptTrait<G>>::append_to_transcript(&self.X, b"X", transcript);
}
}
impl<G: Group> AbsorbInROTrait<G> for R1CSInstance<G> { impl<G: Group> AbsorbInROTrait<G> for R1CSInstance<G> {
fn absorb_in_ro(&self, ro: &mut G::RO) { fn absorb_in_ro(&self, ro: &mut G::RO) {
self.comm_W.absorb_in_ro(ro); self.comm_W.absorb_in_ro(ro);
@ -623,12 +612,15 @@ impl RelaxedR1CSInstance {
} }
} }
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);
<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);
impl<G: Group> TranscriptReprTrait<G> for RelaxedR1CSInstance<G> {
fn to_transcript_bytes(&self) -> Vec<u8> {
[
self.comm_W.to_transcript_bytes(),
self.comm_E.to_transcript_bytes(),
self.u.to_transcript_bytes(),
self.X.as_slice().to_transcript_bytes(),
]
.concat()
} }
} }

+ 24
- 40
src/spartan/mod.rs

@ -7,8 +7,7 @@ use crate::{
errors::NovaError, errors::NovaError,
r1cs::{R1CSShape, RelaxedR1CSInstance, RelaxedR1CSWitness}, r1cs::{R1CSShape, RelaxedR1CSInstance, RelaxedR1CSWitness},
traits::{ traits::{
evaluation::EvaluationEngineTrait, snark::RelaxedR1CSSNARKTrait, AppendToTranscriptTrait,
ChallengeTrait, Group, TranscriptEngineTrait,
evaluation::EvaluationEngineTrait, snark::RelaxedR1CSSNARKTrait, Group, TranscriptEngineTrait,
}, },
CommitmentKey, CommitmentKey,
}; };
@ -83,8 +82,8 @@ impl> RelaxedR1CSSNARKTrait
assert!(pk.S.num_io < pk.S.num_vars); assert!(pk.S.num_io < pk.S.num_vars);
// append the R1CSShape and RelaxedR1CSInstance to the transcript // append the R1CSShape and RelaxedR1CSInstance to the transcript
pk.S.append_to_transcript(b"S", &mut transcript);
U.append_to_transcript(b"U", &mut transcript);
transcript.absorb(b"S", &pk.S);
transcript.absorb(b"U", U);
// compute the full satisfying assignment by concatenating W.W, U.u, and U.X // compute the full satisfying assignment by concatenating W.W, U.u, and U.X
let mut z = concat(vec![W.W.clone(), vec![U.u], U.X.clone()]); let mut z = concat(vec![W.W.clone(), vec![U.u], U.X.clone()]);
@ -96,7 +95,7 @@ impl> RelaxedR1CSSNARKTrait
// outer sum-check // outer sum-check
let tau = (0..num_rounds_x) let tau = (0..num_rounds_x)
.map(|_i| G::Scalar::challenge(b"tau", &mut transcript))
.map(|_i| transcript.squeeze(b"t"))
.collect::<Result<Vec<G::Scalar>, NovaError>>()?; .collect::<Result<Vec<G::Scalar>, NovaError>>()?;
let mut poly_tau = MultilinearPolynomial::new(EqPolynomial::new(tau).evals()); let mut poly_tau = MultilinearPolynomial::new(EqPolynomial::new(tau).evals());
@ -134,15 +133,13 @@ impl> RelaxedR1CSSNARKTrait
let (claim_Az, claim_Bz): (G::Scalar, G::Scalar) = (claims_outer[1], claims_outer[2]); let (claim_Az, claim_Bz): (G::Scalar, G::Scalar) = (claims_outer[1], claims_outer[2]);
let claim_Cz = poly_Cz.evaluate(&r_x); let claim_Cz = poly_Cz.evaluate(&r_x);
let eval_E = MultilinearPolynomial::new(W.E.clone()).evaluate(&r_x); let eval_E = MultilinearPolynomial::new(W.E.clone()).evaluate(&r_x);
<[G::Scalar] as AppendToTranscriptTrait<G>>::append_to_transcript(
&[claim_Az, claim_Bz, claim_Cz, eval_E],
transcript.absorb(
b"claims_outer", b"claims_outer",
&mut transcript,
&[claim_Az, claim_Bz, claim_Cz, eval_E].as_slice(),
); );
// inner sum-check // inner sum-check
let r = G::Scalar::challenge(b"r", &mut transcript)?;
let r = transcript.squeeze(b"r")?;
let claim_inner_joint = claim_Az + r * claim_Bz + r * r * claim_Cz; let claim_inner_joint = claim_Az + r * claim_Bz + r * r * claim_Cz;
let poly_ABC = { let poly_ABC = {
@ -213,11 +210,7 @@ impl> RelaxedR1CSSNARKTrait
)?; )?;
let eval_W = MultilinearPolynomial::new(W.W.clone()).evaluate(&r_y[1..]); let eval_W = MultilinearPolynomial::new(W.W.clone()).evaluate(&r_y[1..]);
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
&eval_W,
b"eval_W",
&mut transcript,
);
transcript.absorb(b"eval_W", &eval_W);
// We will now reduce eval_W =? W(r_y[1..]) and eval_W =? E(r_x) into // We will now reduce eval_W =? W(r_y[1..]) and eval_W =? E(r_x) into
// two claims: eval_W_prime =? W(rz) and eval_E_prime =? E(rz) // two claims: eval_W_prime =? W(rz) and eval_E_prime =? E(rz)
@ -225,7 +218,7 @@ impl> RelaxedR1CSSNARKTrait
// where gamma is a public challenge // where gamma is a public challenge
// Since commitments to W and E are homomorphic, the verifier can compute a commitment // Since commitments to W and E are homomorphic, the verifier can compute a commitment
// to the batched polynomial. // to the batched polynomial.
let rho = G::Scalar::challenge(b"rho", &mut transcript)?;
let rho = transcript.squeeze(b"rho")?;
let claim_batch_joint = eval_E + rho * eval_W; let claim_batch_joint = eval_E + rho * eval_W;
let num_rounds_z = num_rounds_x; let num_rounds_z = num_rounds_x;
@ -248,14 +241,10 @@ impl> RelaxedR1CSSNARKTrait
let eval_E_prime = claims_batch[1]; let eval_E_prime = claims_batch[1];
let eval_W_prime = claims_batch[3]; let eval_W_prime = claims_batch[3];
<[G::Scalar] as AppendToTranscriptTrait<G>>::append_to_transcript(
&[eval_E_prime, eval_W_prime],
b"claims_batch",
&mut transcript,
);
transcript.absorb(b"claims_batch", &[eval_E_prime, eval_W_prime].as_slice());
// we now combine evaluation claims at the same point rz into one // we now combine evaluation claims at the same point rz into one
let gamma = G::Scalar::challenge(b"gamma", &mut transcript)?;
let gamma = transcript.squeeze(b"gamma")?;
let comm = U.comm_E + U.comm_W * gamma; let comm = U.comm_E + U.comm_W * gamma;
let poly = W let poly = W
.E .E
@ -285,8 +274,8 @@ impl> RelaxedR1CSSNARKTrait
let mut transcript = G::TE::new(b"RelaxedR1CSSNARK"); let mut transcript = G::TE::new(b"RelaxedR1CSSNARK");
// append the R1CSShape and RelaxedR1CSInstance to the transcript // append the R1CSShape and RelaxedR1CSInstance to the transcript
vk.S.append_to_transcript(b"S", &mut transcript);
U.append_to_transcript(b"U", &mut transcript);
transcript.absorb(b"S", &vk.S);
transcript.absorb(b"U", U);
let (num_rounds_x, num_rounds_y) = ( let (num_rounds_x, num_rounds_y) = (
(vk.S.num_cons as f64).log2() as usize, (vk.S.num_cons as f64).log2() as usize,
@ -295,7 +284,7 @@ impl> RelaxedR1CSSNARKTrait
// outer sum-check // outer sum-check
let tau = (0..num_rounds_x) let tau = (0..num_rounds_x)
.map(|_i| G::Scalar::challenge(b"tau", &mut transcript))
.map(|_i| transcript.squeeze(b"t"))
.collect::<Result<Vec<G::Scalar>, NovaError>>()?; .collect::<Result<Vec<G::Scalar>, NovaError>>()?;
let (claim_outer_final, r_x) = let (claim_outer_final, r_x) =
@ -312,19 +301,19 @@ impl> RelaxedR1CSSNARKTrait
return Err(NovaError::InvalidSumcheckProof); return Err(NovaError::InvalidSumcheckProof);
} }
<[G::Scalar] as AppendToTranscriptTrait<G>>::append_to_transcript(
transcript.absorb(
b"claims_outer",
&[ &[
self.claims_outer.0, self.claims_outer.0,
self.claims_outer.1, self.claims_outer.1,
self.claims_outer.2, self.claims_outer.2,
self.eval_E, self.eval_E,
],
b"claims_outer",
&mut transcript,
]
.as_slice(),
); );
// inner sum-check // inner sum-check
let r = G::Scalar::challenge(b"r", &mut transcript)?;
let r = transcript.squeeze(b"r")?;
let claim_inner_joint = let claim_inner_joint =
self.claims_outer.0 + r * self.claims_outer.1 + r * r * self.claims_outer.2; self.claims_outer.0 + r * self.claims_outer.1 + r * r * self.claims_outer.2;
@ -380,13 +369,9 @@ impl> RelaxedR1CSSNARKTrait
} }
// batch sum-check // batch sum-check
<G::Scalar as AppendToTranscriptTrait<G>>::append_to_transcript(
&self.eval_W,
b"eval_W",
&mut transcript,
);
transcript.absorb(b"eval_W", &self.eval_W);
let rho = G::Scalar::challenge(b"rho", &mut transcript)?;
let rho = transcript.squeeze(b"rho")?;
let claim_batch_joint = self.eval_E + rho * self.eval_W; let claim_batch_joint = self.eval_E + rho * self.eval_W;
let num_rounds_z = num_rounds_x; let num_rounds_z = num_rounds_x;
let (claim_batch_final, r_z) = let (claim_batch_final, r_z) =
@ -405,14 +390,13 @@ impl> RelaxedR1CSSNARKTrait
return Err(NovaError::InvalidSumcheckProof); return Err(NovaError::InvalidSumcheckProof);
} }
<[G::Scalar] as AppendToTranscriptTrait<G>>::append_to_transcript(
&[self.eval_E_prime, self.eval_W_prime],
transcript.absorb(
b"claims_batch", b"claims_batch",
&mut transcript,
&[self.eval_E_prime, self.eval_W_prime].as_slice(),
); );
// we now combine evaluation claims at the same point rz into one // we now combine evaluation claims at the same point rz into one
let gamma = G::Scalar::challenge(b"gamma", &mut transcript)?;
let gamma = transcript.squeeze(b"gamma")?;
let comm = U.comm_E + U.comm_W * gamma; let comm = U.comm_E + U.comm_W * gamma;
let eval = self.eval_E_prime + gamma * self.eval_W_prime; let eval = self.eval_E_prime + gamma * self.eval_W_prime;

+ 14
- 17
src/spartan/sumcheck.rs

@ -2,7 +2,7 @@
#![allow(clippy::type_complexity)] #![allow(clippy::type_complexity)]
use super::polynomial::MultilinearPolynomial; use super::polynomial::MultilinearPolynomial;
use crate::errors::NovaError; use crate::errors::NovaError;
use crate::traits::{AppendToTranscriptTrait, ChallengeTrait, Group};
use crate::traits::{Group, TranscriptEngineTrait, TranscriptReprTrait};
use core::marker::PhantomData; use core::marker::PhantomData;
use ff::Field; use ff::Field;
use rayon::prelude::*; use rayon::prelude::*;
@ -43,10 +43,10 @@ impl SumcheckProof {
debug_assert_eq!(poly.eval_at_zero() + poly.eval_at_one(), e); debug_assert_eq!(poly.eval_at_zero() + poly.eval_at_one(), e);
// append the prover's message to the transcript // append the prover's message to the transcript
poly.append_to_transcript(b"poly", transcript);
transcript.absorb(b"p", &poly);
//derive the verifier's challenge for the next round //derive the verifier's challenge for the next round
let r_i = G::Scalar::challenge(b"challenge", transcript)?;
let r_i = transcript.squeeze(b"c")?;
r.push(r_i); r.push(r_i);
@ -98,10 +98,10 @@ impl SumcheckProof {
}; };
// append the prover's message to the transcript // append the prover's message to the transcript
poly.append_to_transcript(b"poly", transcript);
transcript.absorb(b"p", &poly);
//derive the verifier's challenge for the next round //derive the verifier's challenge for the next round
let r_i = G::Scalar::challenge(b"challenge", transcript)?;
let r_i = transcript.squeeze(b"c")?;
r.push(r_i); r.push(r_i);
polys.push(poly.compress()); polys.push(poly.compress());
@ -172,10 +172,10 @@ impl SumcheckProof {
}; };
// append the prover's message to the transcript // append the prover's message to the transcript
poly.append_to_transcript(b"poly", transcript);
transcript.absorb(b"p", &poly);
//derive the verifier's challenge for the next round
let r_i = G::Scalar::challenge(b"challenge", transcript)?;
// derive the verifier's challenge for the next round
let r_i = transcript.squeeze(b"c")?;
r.push(r_i); r.push(r_i);
polys.push(poly.compress()); polys.push(poly.compress());
@ -266,10 +266,10 @@ impl SumcheckProof {
}; };
// append the prover's message to the transcript // append the prover's message to the transcript
poly.append_to_transcript(b"poly", transcript);
transcript.absorb(b"p", &poly);
//derive the verifier's challenge for the next round //derive the verifier's challenge for the next round
let r_i = G::Scalar::challenge(b"challenge", transcript)?;
let r_i = transcript.squeeze(b"c")?;
r.push(r_i); r.push(r_i);
polys.push(poly.compress()); polys.push(poly.compress());
@ -396,12 +396,9 @@ impl CompressedUniPoly {
} }
} }
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,
);
impl<G: Group> TranscriptReprTrait<G> for UniPoly<G> {
fn to_transcript_bytes(&self) -> Vec<u8> {
let coeffs = self.compress().coeffs_except_linear_term;
coeffs.as_slice().to_transcript_bytes()
} }
} }

+ 4
- 4
src/traits/commitment.rs

@ -2,7 +2,7 @@
//! We require the commitment engine to provide a commitment to vectors with a single group element //! We require the commitment engine to provide a commitment to vectors with a single group element
use crate::{ use crate::{
errors::NovaError, errors::NovaError,
traits::{AbsorbInROTrait, AppendToTranscriptTrait, Group},
traits::{AbsorbInROTrait, Group, TranscriptReprTrait},
}; };
use core::{ use core::{
fmt::Debug, fmt::Debug,
@ -65,10 +65,10 @@ pub trait CommitmentTrait:
+ Eq + Eq
+ Send + Send
+ Sync + Sync
+ TranscriptReprTrait<G>
+ Serialize + Serialize
+ for<'de> Deserialize<'de> + for<'de> Deserialize<'de>
+ AbsorbInROTrait<G> + AbsorbInROTrait<G>
+ AppendToTranscriptTrait<G>
+ CommitmentOps + CommitmentOps
+ CommitmentOpsOwned + CommitmentOpsOwned
+ ScalarMul<G::Scalar> + ScalarMul<G::Scalar>
@ -80,9 +80,9 @@ pub trait CommitmentTrait:
+ Eq + Eq
+ Send + Send
+ Sync + Sync
+ TranscriptReprTrait<G>
+ Serialize + Serialize
+ for<'de> Deserialize<'de>
+ AppendToTranscriptTrait<G>;
+ for<'de> Deserialize<'de>;
/// Compresses self into a compressed commitment /// Compresses self into a compressed commitment
fn compress(&self) -> Self::CompressedCommitment; fn compress(&self) -> Self::CompressedCommitment;

+ 35
- 37
src/traits/mod.rs

@ -34,15 +34,19 @@ pub trait Group:
+ for<'de> Deserialize<'de> + for<'de> Deserialize<'de>
{ {
/// A type representing an element of the base field of the group /// A type representing an element of the base field of the group
type Base: PrimeField + PrimeFieldBits + Serialize + for<'de> Deserialize<'de>;
type Base: PrimeField
+ PrimeFieldBits
+ TranscriptReprTrait<Self>
+ Serialize
+ for<'de> Deserialize<'de>;
/// A type representing an element of the scalar field of the group /// A type representing an element of the scalar field of the group
type Scalar: PrimeField type Scalar: PrimeField
+ PrimeFieldBits + PrimeFieldBits
+ PrimeFieldExt + PrimeFieldExt
+ ChallengeTrait<Self>
+ Send + Send
+ Sync + Sync
+ TranscriptReprTrait<Self>
+ Serialize + Serialize
+ for<'de> Deserialize<'de>; + for<'de> Deserialize<'de>;
@ -97,16 +101,23 @@ pub trait Group:
/// Represents a compressed version of a group element /// Represents a compressed version of a group element
pub trait CompressedGroup: pub trait CompressedGroup:
Clone + Copy + Debug + Eq + Sized + Send + Sync + Serialize + for<'de> Deserialize<'de> + 'static
Clone
+ Copy
+ Debug
+ Eq
+ Sized
+ Send
+ Sync
+ TranscriptReprTrait<Self::GroupElement>
+ Serialize
+ for<'de> Deserialize<'de>
+ 'static
{ {
/// A type that holds the decompressed version of the compressed group element /// A type that holds the decompressed version of the compressed group element
type GroupElement: Group + Serialize + for<'de> Deserialize<'de>; type GroupElement: Group + Serialize + for<'de> Deserialize<'de>;
/// Decompresses the compressed group element /// Decompresses the compressed group element
fn decompress(&self) -> Option<Self::GroupElement>; fn decompress(&self) -> Option<Self::GroupElement>;
/// Returns a byte array representing the compressed group element
fn as_bytes(&self) -> Vec<u8>;
} }
/// A helper trait to absorb different objects in RO /// A helper trait to absorb different objects in RO
@ -196,56 +207,43 @@ impl ScalarMul for T where T: Mul
pub trait ScalarMulOwned<Rhs, Output = Self>: for<'r> ScalarMul<&'r Rhs, Output> {} 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<T, Rhs, Output> ScalarMulOwned<Rhs, Output> for T where T: for<'r> ScalarMul<&'r Rhs, Output> {}
/// This trait allows types to implement how they want to be added to TranscriptEngine
pub trait TranscriptReprTrait<G: Group>: Send + Sync {
/// returns a byte representation of self to be added to the transcript
fn to_transcript_bytes(&self) -> Vec<u8>;
}
/// This trait defines the behavior of a transcript engine compatible with Spartan /// This trait defines the behavior of a transcript engine compatible with Spartan
pub trait TranscriptEngineTrait<G: Group>: Send + Sync { pub trait TranscriptEngineTrait<G: Group>: Send + Sync {
/// initializes the transcript /// initializes the transcript
fn new(label: &'static [u8]) -> Self; fn new(label: &'static [u8]) -> Self;
/// returns a scalar element of the group as a challenge /// returns a scalar element of the group as a challenge
fn squeeze_scalar(&mut self, label: &'static [u8]) -> Result<G::Scalar, NovaError>;
fn squeeze(&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);
}
/// absorbs any type that implements TranscriptReprTrait under a label
fn absorb<T: TranscriptReprTrait<G>>(&mut self, label: &'static [u8], o: &T);
/// 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;
/// adds a domain separator
fn dom_sep(&mut self, bytes: &'static [u8]);
} }
/// Defines additional methods on PrimeField objects /// Defines additional methods on PrimeField objects
pub trait PrimeFieldExt: PrimeField { pub trait PrimeFieldExt: PrimeField {
/// Returns a scalar representing the bytes /// Returns a scalar representing the bytes
fn from_uniform(bytes: &[u8]) -> Self; fn from_uniform(bytes: &[u8]) -> Self;
/// Returns a vector of bytes representing the scalar
fn to_bytes(&self) -> Vec<u8>;
}
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, &<Self as PrimeFieldExt>::to_bytes(self));
}
} }
impl<G: Group<Scalar = F>, F: PrimeField + PrimeFieldExt> AppendToTranscriptTrait<G> for [F] {
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut G::TE) {
let bytes = (0..self.len())
.map(|i| <F as PrimeFieldExt>::to_bytes(&self[i]))
impl<G: Group<Scalar = F>, F: PrimeField + TranscriptReprTrait<G>> TranscriptReprTrait<G>
for &[F]
{
fn to_transcript_bytes(&self) -> Vec<u8> {
(0..self.len())
.map(|i| <F as TranscriptReprTrait<G>>::to_transcript_bytes(&self[i]))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.into_iter() .into_iter()
.flatten() .flatten()
.collect::<Vec<u8>>();
transcript.absorb_bytes(label, &bytes);
.collect::<Vec<u8>>()
} }
} }

Loading…
Cancel
Save