diff --git a/Cargo.toml b/Cargo.toml index 53d86f7..a709b65 100644 --- a/Cargo.toml +++ b/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" ] diff --git a/src/commitments.rs b/src/commitments.rs index e2cec07..e95f1f4 100644 --- a/src/commitments.rs +++ b/src/commitments.rs @@ -1,6 +1,6 @@ use super::{ errors::NovaError, - traits::{CompressedGroup, Group}, + traits::{AppendToTranscriptTrait, CompressedGroup, Group}, }; use core::{ fmt::Debug, @@ -80,10 +80,6 @@ impl CommitTrait for [G::Scalar] { } } -pub trait AppendToTranscriptTrait { - fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript); -} - impl AppendToTranscriptTrait for Commitment { fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) { transcript.append_message(label, self.comm.compress().as_bytes()); diff --git a/src/lib.rs b/src/lib.rs index 99a8508..905c837 100644 --- a/src/lib.rs +++ b/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 { @@ -59,6 +59,13 @@ impl StepSNARK { // append the protocol name to the transcript transcript.append_message(b"protocol-name", StepSNARK::::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,6 +98,7 @@ impl StepSNARK { /// if and only if `U1` and `U2` are satisfiable. pub fn verify( &self, + S: &R1CSShape, U1: &RelaxedR1CSInstance, U2: &R1CSInstance, transcript: &mut Transcript, @@ -98,6 +106,13 @@ impl StepSNARK { // append the protocol name to the transcript transcript.append_message(b"protocol-name", StepSNARK::::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 @@ mod tests { // 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 @@ mod tests { 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(); diff --git a/src/r1cs.rs b/src/r1cs.rs index 6ec70f3..5e03825 100644 --- a/src/r1cs.rs +++ b/src/r1cs.rs @@ -3,11 +3,14 @@ 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 { @@ -292,6 +295,46 @@ impl R1CSShape { } } +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +struct R1CSShapeSerialized { + num_cons: usize, + num_vars: usize, + num_io: usize, + A: Vec<(usize, usize, Vec)>, + B: Vec<(usize, usize, Vec)>, + C: Vec<(usize, usize, Vec)>, +} + +impl AppendToTranscriptTrait for R1CSShape { + 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 R1CSWitness { /// A method to create a witness object using a vector of scalars pub fn new(S: &R1CSShape, W: &[G::Scalar]) -> Result, NovaError> { @@ -326,6 +369,13 @@ impl R1CSInstance { } } +impl AppendToTranscriptTrait for R1CSInstance { + 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); + } +} + impl RelaxedR1CSWitness { /// Produces a default RelaxedR1CSWitness given an R1CSShape pub fn default(S: &R1CSShape) -> RelaxedR1CSWitness { @@ -427,3 +477,12 @@ impl RelaxedR1CSInstance { }) } } + +impl AppendToTranscriptTrait for RelaxedR1CSInstance { + 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); + } +} diff --git a/src/traits.rs b/src/traits.rs index b40e99a..4944fbf 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -68,6 +68,12 @@ pub trait CompressedGroup: Clone + Copy + Debug + Eq + Sized + Send + Sync + 'st 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 @@ pub trait StepCircuit { z: AllocatedNum, ) -> Result, SynthesisError>; } + +impl AppendToTranscriptTrait for F { + fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) { + transcript.append_message(label, self.to_repr().as_ref()); + } +} + +impl AppendToTranscriptTrait for [F] { + fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) { + for s in self { + s.append_to_transcript(label, transcript); + } + } +}