Simplify TranscriptEngine usage (#148) * simplify transcript engine usage * update version
@@ -1,6 +1,6 @@
[package] name = "nova-snark" -version = "0.17.0" +version = "0.18.0" authors = ["Srinath Setty <srinath@microsoft.com>"] edition = "2021" description = "Recursive zkSNARKs without trusted setup"
@@ -7,7 +7,7 @@ spartan::polynomial::EqPolynomial,
traits::{ commitment::{CommitmentEngineTrait, CommitmentKeyTrait, CommitmentTrait}, evaluation::EvaluationEngineTrait, - AppendToTranscriptTrait, ChallengeTrait, Group, TranscriptEngineTrait, + Group, TranscriptEngineTrait, TranscriptReprTrait, }, Commitment, CommitmentKey, CompressedCommitment, CE, };@@ -139,6 +139,17 @@ }
} } +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> { a_vec: Vec<G::Scalar>, }@@ -167,7 +178,7 @@ G: Group,
CommitmentKey<G>: CommitmentKeyExtTrait<G, CE = G::CE>, { fn protocol_name() -> &'static [u8] { - b"inner product argument" + b"IPA" } fn prove(@@ -177,17 +188,17 @@ U: &InnerProductInstance<G>,
W: &InnerProductWitness<G>, transcript: &mut G::TE, ) -> 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() { 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 - let r = G::Scalar::challenge(b"r", transcript)?; + let r = transcript.squeeze(b"r")?; let ck_c = ck_c.scale(&r); // a closure that executes a step of the recursive inner product argument@@ -230,10 +241,10 @@ .collect::<Vec<G::Scalar>>(),
) .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(); // fold the left half and the right half@@ -289,7 +300,7 @@ n: usize,
U: &InnerProductInstance<G>, transcript: &mut G::TE, ) -> Result<(), NovaError> { - transcript.absorb_bytes(b"protocol-name", Self::protocol_name()); + transcript.dom_sep(Self::protocol_name()); if U.b_vec.len() != n || n != (1 << self.L_vec.len()) || self.L_vec.len() != self.R_vec.len()@@ -298,11 +309,11 @@ {
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 - let r = G::Scalar::challenge(b"r", transcript)?; + let r = transcript.squeeze(b"r")?; let ck_c = ck_c.scale(&r); let P = U.comm_a_vec + CE::<G>::commit(&ck_c, &[U.c]);@@ -337,9 +348,9 @@
// compute a vector of public coins using self.L_vec and self.R_vec let r = (0..self.L_vec.len()) .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>>()?;
@@ -2,13 +2,13 @@ //! This module provides an implementation of TranscriptEngineTrait using keccak256
use crate::traits::PrimeFieldExt; use crate::{ errors::NovaError, - traits::{Group, TranscriptEngineTrait}, + traits::{Group, TranscriptEngineTrait, TranscriptReprTrait}, }; use core::marker::PhantomData; 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_PREFIX_CHALLENGE_LO: u8 = 0; const KECCAK256_PREFIX_CHALLENGE_HI: u8 = 1;@@ -55,7 +55,7 @@ _p: Default::default(),
} } - 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 = [ DOM_SEP_TAG, self.round.to_le_bytes().as_ref(),@@ -81,8 +81,13 @@ // squeeze out a challenge
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(&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); } }@@ -91,7 +96,7 @@ #[cfg(test)]
mod tests { use crate::{ provider::keccak::Keccak256Transcript, - traits::{AppendToTranscriptTrait, ChallengeTrait, Group, TranscriptEngineTrait}, + traits::{Group, TranscriptEngineTrait}, }; use ff::PrimeField; use sha3::{Digest, Keccak256};@@ -100,50 +105,34 @@ type G = pasta_curves::pallas::Point;
#[test] fn test_keccak_transcript() { - let mut transcript = Keccak256Transcript::new(b"test"); + let mut transcript: Keccak256Transcript<G> = 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, - ); + transcript.absorb(b"s1", &s1); + transcript.absorb(b"s2", &s2); // 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!( hex::encode(c1.to_repr().as_ref()), - "51648083af5387a04a7aa2aec789ee78fdabe45dc1391d270a38fcb576447c01" + "432d5811c8be3d44d47f52108a8749ae18482efd1a37b830f966456b5d75340c" ); // 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, - ); + transcript.absorb(b"s3", &s3); // 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!( hex::encode(c2.to_repr().as_ref()), - "9773f3349f7308153f6012e72b97fc304e48372bbd28bd122b37a8e46855d50f" + "65f7908d53abcd18f3b1d767456ef9009b91c7566a635e9ca7be26e21d4d7a10" ); }
@@ -1,12 +1,11 @@
//! This module implements the Nova traits for pallas::Point, pallas::Scalar, vesta::Point, vesta::Scalar. use crate::{ - errors::NovaError, provider::{ keccak::Keccak256Transcript, pedersen::CommitmentEngine, poseidon::{PoseidonRO, PoseidonROCircuit}, }, - traits::{ChallengeTrait, CompressedGroup, Group, PrimeFieldExt, TranscriptEngineTrait}, + traits::{CompressedGroup, Group, PrimeFieldExt, TranscriptReprTrait}, }; use digest::{ExtendableOutput, Input}; use ff::PrimeField;@@ -177,9 +176,11 @@ fn from_uniform(bytes: &[u8]) -> Self {
let bytes_arr: [u8; 64] = bytes.try_into().unwrap(); $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 @@
fn decompress(&self) -> Option<$name::Point> { 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() } }
@@ -3,8 +3,7 @@ use crate::{
errors::NovaError, traits::{ commitment::{CommitmentEngineTrait, CommitmentKeyTrait, CommitmentTrait}, - AbsorbInROTrait, AppendToTranscriptTrait, CompressedGroup, Group, ROTrait, - TranscriptEngineTrait, + AbsorbInROTrait, CompressedGroup, Group, ROTrait, TranscriptReprTrait, }, }; use core::{@@ -12,7 +11,7 @@ fmt::Debug,
marker::PhantomData, ops::{Add, AddAssign, Mul, MulAssign}, }; -use ff::{Field, PrimeField}; +use ff::Field; use rayon::prelude::*; use serde::{Deserialize, Serialize};@@ -89,17 +88,16 @@ Commitment { comm: G::zero() }
} } -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 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<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() } }
@@ -9,7 +9,7 @@ utils::scalar_as_base,
}, traits::{ commitment::{CommitmentEngineTrait, CommitmentKeyTrait}, - AbsorbInROTrait, AppendToTranscriptTrait, Group, ROTrait, + AbsorbInROTrait, Group, ROTrait, TranscriptReprTrait, }, Commitment, CommitmentKey, CE, };@@ -435,13 +435,9 @@ }
} } -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() } }@@ -482,13 +478,6 @@ comm_W: *comm_W,
X: X.to_owned(), }) } - } -} - -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); } }@@ -623,12 +612,15 @@ })
} } -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() } }
@@ -7,8 +7,7 @@ use crate::{
errors::NovaError, r1cs::{R1CSShape, RelaxedR1CSInstance, RelaxedR1CSWitness}, traits::{ - evaluation::EvaluationEngineTrait, snark::RelaxedR1CSSNARKTrait, AppendToTranscriptTrait, - ChallengeTrait, Group, TranscriptEngineTrait, + evaluation::EvaluationEngineTrait, snark::RelaxedR1CSSNARKTrait, Group, TranscriptEngineTrait, }, CommitmentKey, };@@ -83,8 +82,8 @@ assert_eq!(pk.S.num_io.next_power_of_two(), pk.S.num_io);
assert!(pk.S.num_io < pk.S.num_vars); // 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 let mut z = concat(vec![W.W.clone(), vec![U.u], U.X.clone()]);@@ -96,7 +95,7 @@ );
// outer sum-check 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>>()?; let mut poly_tau = MultilinearPolynomial::new(EqPolynomial::new(tau).evals());@@ -134,15 +133,13 @@ // claims from the end of sum-check
let (claim_Az, claim_Bz): (G::Scalar, G::Scalar) = (claims_outer[1], claims_outer[2]); let claim_Cz = poly_Cz.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", - &mut transcript, + &[claim_Az, claim_Bz, claim_Cz, eval_E].as_slice(), ); // 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 poly_ABC = {@@ -213,11 +210,7 @@ &mut transcript,
)?; 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 // two claims: eval_W_prime =? W(rz) and eval_E_prime =? E(rz)@@ -225,7 +218,7 @@ // We can them combine the two into one: eval_W_prime + gamma * eval_E_prime =? (W + gamma*E)(rz),
// where gamma is a public challenge // Since commitments to W and E are homomorphic, the verifier can compute a commitment // 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 num_rounds_z = num_rounds_x;@@ -248,14 +241,10 @@ )?;
let eval_E_prime = claims_batch[1]; 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 - 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 poly = W .E@@ -285,8 +274,8 @@ fn verify(&self, vk: &Self::VerifierKey, U: &RelaxedR1CSInstance<G>) -> Result<(), NovaError> {
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); - 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) = ( (vk.S.num_cons as f64).log2() as usize,@@ -295,7 +284,7 @@ );
// outer sum-check 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>>()?; let (claim_outer_final, r_x) =@@ -312,19 +301,19 @@ if claim_outer_final != claim_outer_final_expected {
return Err(NovaError::InvalidSumcheckProof); } - <[G::Scalar] as AppendToTranscriptTrait<G>>::append_to_transcript( + transcript.absorb( + b"claims_outer", &[ self.claims_outer.0, self.claims_outer.1, self.claims_outer.2, self.eval_E, - ], - b"claims_outer", - &mut transcript, + ] + .as_slice(), ); // inner sum-check - let r = G::Scalar::challenge(b"r", &mut transcript)?; + let r = transcript.squeeze(b"r")?; let claim_inner_joint = self.claims_outer.0 + r * self.claims_outer.1 + r * r * self.claims_outer.2;@@ -380,13 +369,9 @@ return Err(NovaError::InvalidSumcheckProof);
} // 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 num_rounds_z = num_rounds_x; let (claim_batch_final, r_z) =@@ -405,14 +390,13 @@ if claim_batch_final != claim_batch_final_expected {
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", - &mut transcript, + &[self.eval_E_prime, self.eval_W_prime].as_slice(), ); // 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 eval = self.eval_E_prime + gamma * self.eval_W_prime;
@@ -2,7 +2,7 @@ #![allow(clippy::too_many_arguments)]
#![allow(clippy::type_complexity)] use super::polynomial::MultilinearPolynomial; use crate::errors::NovaError; -use crate::traits::{AppendToTranscriptTrait, ChallengeTrait, Group}; +use crate::traits::{Group, TranscriptEngineTrait, TranscriptReprTrait}; use core::marker::PhantomData; use ff::Field; use rayon::prelude::*;@@ -43,10 +43,10 @@ // decompress() call above already ensures that hods
debug_assert_eq!(poly.eval_at_zero() + poly.eval_at_one(), e); // 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)?; + let r_i = transcript.squeeze(b"c")?; r.push(r_i);@@ -98,10 +98,10 @@ UniPoly::from_evals(&evals)
}; // 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)?; + let r_i = transcript.squeeze(b"c")?; r.push(r_i); polys.push(poly.compress());@@ -172,10 +172,10 @@ UniPoly::from_evals(&evals)
}; // 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); polys.push(poly.compress());@@ -266,10 +266,10 @@ UniPoly::from_evals(&evals)
}; // 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)?; + let r_i = transcript.squeeze(b"c")?; r.push(r_i); polys.push(poly.compress());@@ -396,12 +396,9 @@ UniPoly { coeffs }
} } -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() } }
@@ -2,7 +2,7 @@ //! This module defines a collection of traits that define the behavior of a commitment engine
//! We require the commitment engine to provide a commitment to vectors with a single group element use crate::{ errors::NovaError, - traits::{AbsorbInROTrait, AppendToTranscriptTrait, Group}, + traits::{AbsorbInROTrait, Group, TranscriptReprTrait}, }; use core::{ fmt::Debug,@@ -65,10 +65,10 @@ + PartialEq
+ Eq + Send + Sync + + TranscriptReprTrait<G> + Serialize + for<'de> Deserialize<'de> + AbsorbInROTrait<G> - + AppendToTranscriptTrait<G> + CommitmentOps + CommitmentOpsOwned + ScalarMul<G::Scalar>@@ -80,9 +80,9 @@ + PartialEq
+ Eq + Send + Sync + + TranscriptReprTrait<G> + Serialize - + for<'de> Deserialize<'de> - + AppendToTranscriptTrait<G>; + + for<'de> Deserialize<'de>; /// Compresses self into a compressed commitment fn compress(&self) -> Self::CompressedCommitment;
@@ -34,15 +34,19 @@ + Serialize
+ for<'de> Deserialize<'de> { /// 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 type Scalar: PrimeField + PrimeFieldBits + PrimeFieldExt - + ChallengeTrait<Self> + Send + Sync + + TranscriptReprTrait<Self> + Serialize + for<'de> Deserialize<'de>;@@ -97,16 +101,23 @@ }
/// Represents a compressed version of a group element 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 type GroupElement: Group + Serialize + for<'de> Deserialize<'de>; /// Decompresses the compressed group element 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@@ -196,56 +207,43 @@ /// A helper trait for references implementing group scalar multiplication.
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> {} +/// 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 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>; + 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 pub trait PrimeFieldExt: PrimeField { /// Returns a scalar representing the bytes 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<_>>() .into_iter() .flatten() - .collect::<Vec<u8>>(); - transcript.absorb_bytes(label, &bytes); + .collect::<Vec<u8>>() } }