arnaucube
arnaucube/NovaPublic Self-hosted repository
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
commit

0d53db18e36ee1f8bc927ec85ee1fd38dcd7a501

parent

485eb3fac64ed7319382e4841510a2d33d4d0464

5 files changed, 104 insertions(+), 11 deletions(-)

jump to
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); + } + } +}