implement transcript methods for various types (#49) * implement transcript methods for various types * address clippy * add shape to transcript
Srinath Setty srinath@microsoft.com
Thu, 12 May 2022 12:28:43 +0530
5 files changed,
104 insertions(+),
11 deletions(-)
M
Cargo.toml
→
Cargo.toml
@@ -27,6 +27,9 @@ neptune = "6.1"
generic-array = "0.14.4" bellperson-nonnative = { version = "0.2.1", default-features = false, features = ["wasm"] } rug = { version = "1.10", default-features = false, features = ["integer", "serde", "rand"] } +serde = { version = "1.0", features = ["derive"] } +bincode = "1.2.1" +flate2 = "1.0" [features] default = [ "bellperson/default", "bellperson-nonnative/default" ]
M
src/commitments.rs
→
src/commitments.rs
@@ -1,6 +1,6 @@
use super::{ errors::NovaError, - traits::{CompressedGroup, Group}, + traits::{AppendToTranscriptTrait, CompressedGroup, Group}, }; use core::{ fmt::Debug,@@ -78,10 +78,6 @@ Commitment {
comm: G::vartime_multiscalar_mul(self, &gens.gens[..self.len()]), } } -} - -pub trait AppendToTranscriptTrait { - fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript); } impl<G: Group> AppendToTranscriptTrait for Commitment<G> {
M
src/lib.rs
→
src/lib.rs
@@ -16,13 +16,13 @@ pub mod traits;
use std::marker::PhantomData; -use commitments::{AppendToTranscriptTrait, CompressedCommitment}; +use commitments::CompressedCommitment; use errors::NovaError; use merlin::Transcript; use r1cs::{ R1CSGens, R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness, }; -use traits::{ChallengeTrait, Group}; +use traits::{AppendToTranscriptTrait, ChallengeTrait, Group}; /// A SNARK that holds the proof of a step of an incremental computation pub struct StepSNARK<G: Group> {@@ -59,6 +59,13 @@ > {
// append the protocol name to the transcript transcript.append_message(b"protocol-name", StepSNARK::<G>::protocol_name()); + // append S to the transcript + S.append_to_transcript(b"S", transcript); + + // append U1 and U2 to transcript + U1.append_to_transcript(b"U1", transcript); + U2.append_to_transcript(b"U2", transcript); + // compute a commitment to the cross-term let (T, comm_T) = S.commit_T(gens, U1, W1, U2, W2)?;@@ -91,12 +98,20 @@ /// with the guarantee that the folded instance `U`
/// if and only if `U1` and `U2` are satisfiable. pub fn verify( &self, + S: &R1CSShape<G>, U1: &RelaxedR1CSInstance<G>, U2: &R1CSInstance<G>, transcript: &mut Transcript, ) -> Result<RelaxedR1CSInstance<G>, NovaError> { // append the protocol name to the transcript transcript.append_message(b"protocol-name", StepSNARK::<G>::protocol_name()); + + // append S to the transcript + S.append_to_transcript(b"S", transcript); + + // append U1 and U2 to transcript + U1.append_to_transcript(b"U1", transcript); + U2.append_to_transcript(b"U2", transcript); // append `comm_T` to the transcript and obtain a challenge self.comm_T.append_to_transcript(b"comm_T", transcript);@@ -232,7 +247,7 @@ let (step_snark, (_U, W)) = res.unwrap();
// verify the step SNARK with U1 as the first incoming instance let mut verifier_transcript = Transcript::new(b"StepSNARKExample"); - let res = step_snark.verify(&r_U, U1, &mut verifier_transcript); + let res = step_snark.verify(shape, &r_U, U1, &mut verifier_transcript); assert!(res.is_ok()); let U = res.unwrap();@@ -248,7 +263,7 @@ assert!(res.is_ok());
let (step_snark, (_U, W)) = res.unwrap(); // verify the step SNARK with U1 as the first incoming instance - let res = step_snark.verify(&r_U, U2, &mut verifier_transcript); + let res = step_snark.verify(shape, &r_U, U2, &mut verifier_transcript); assert!(res.is_ok()); let U = res.unwrap();
M
src/r1cs.rs
→
src/r1cs.rs
@@ -3,11 +3,14 @@ #![allow(clippy::type_complexity)]
use super::{ commitments::{CommitGens, CommitTrait, Commitment, CompressedCommitment}, errors::NovaError, - traits::Group, + traits::{AppendToTranscriptTrait, Group}, }; -use ff::Field; +use ff::{Field, PrimeField}; +use flate2::{write::ZlibEncoder, Compression}; use itertools::concat; +use merlin::Transcript; use rayon::prelude::*; +use serde::{Deserialize, Serialize}; /// Public parameters for a given R1CS pub struct R1CSGens<G: Group> {@@ -292,6 +295,46 @@ Ok((T, comm_T))
} } +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +struct R1CSShapeSerialized { + num_cons: usize, + num_vars: usize, + num_io: usize, + A: Vec<(usize, usize, Vec<u8>)>, + B: Vec<(usize, usize, Vec<u8>)>, + C: Vec<(usize, usize, Vec<u8>)>, +} + +impl<G: Group> AppendToTranscriptTrait for R1CSShape<G> { + fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut Transcript) { + let shape_serialized = R1CSShapeSerialized { + num_cons: self.num_cons, + num_vars: self.num_vars, + num_io: self.num_io, + A: self + .A + .iter() + .map(|(i, j, v)| (*i, *j, v.to_repr().as_ref().to_vec())) + .collect(), + B: self + .B + .iter() + .map(|(i, j, v)| (*i, *j, v.to_repr().as_ref().to_vec())) + .collect(), + C: self + .C + .iter() + .map(|(i, j, v)| (*i, *j, v.to_repr().as_ref().to_vec())) + .collect(), + }; + + let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default()); + bincode::serialize_into(&mut encoder, &shape_serialized).unwrap(); + let shape_bytes = encoder.finish().unwrap(); + transcript.append_message(b"R1CSShape", &shape_bytes); + } +} + impl<G: Group> R1CSWitness<G> { /// A method to create a witness object using a vector of scalars pub fn new(S: &R1CSShape<G>, W: &[G::Scalar]) -> Result<R1CSWitness<G>, NovaError> {@@ -323,6 +366,13 @@ comm_W: *comm_W,
X: X.to_owned(), }) } + } +} + +impl<G: Group> AppendToTranscriptTrait for R1CSInstance<G> { + fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut Transcript) { + self.comm_W.append_to_transcript(b"comm_W", transcript); + self.X.append_to_transcript(b"X", transcript); } }@@ -427,3 +477,12 @@ counter: self.counter + 1,
}) } } + +impl<G: Group> AppendToTranscriptTrait for RelaxedR1CSInstance<G> { + fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut Transcript) { + self.comm_W.append_to_transcript(b"comm_W", transcript); + self.comm_E.append_to_transcript(b"comm_E", transcript); + self.X.append_to_transcript(b"X", transcript); + self.u.append_to_transcript(b"u", transcript); + } +}
M
src/traits.rs
→
src/traits.rs
@@ -68,6 +68,12 @@ /// Returns a byte array representing the compressed group element
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 generate challenges using a transcript object pub trait ChallengeTrait { /// Returns a Scalar representing the challenge using the transcript@@ -133,3 +139,17 @@ cs: &mut CS,
z: AllocatedNum<F>, ) -> Result<AllocatedNum<F>, SynthesisError>; } + +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()); + } +} + +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); + } + } +}