From ccc6dc3a0462e5a8c82622bbbea59b0bd9e5cb67 Mon Sep 17 00:00:00 2001 From: Srinath Setty Date: Fri, 13 May 2022 10:38:43 +0530 Subject: [PATCH] Truncate digest bits (#50) * apply a hash function before adding to transcript * truncate shape_digest into 250 bits * add missing file * fix clippy * cargo fmt --- src/constants.rs | 2 ++ src/lib.rs | 7 +++---- src/poseidon.rs | 19 ++++++++++--------- src/r1cs.rs | 31 ++++++++++++++++++++++++++++++- 4 files changed, 45 insertions(+), 14 deletions(-) create mode 100644 src/constants.rs diff --git a/src/constants.rs b/src/constants.rs new file mode 100644 index 0000000..d8601a6 --- /dev/null +++ b/src/constants.rs @@ -0,0 +1,2 @@ +pub(crate) const NUM_CHALLENGE_BITS: usize = 128; +pub(crate) const NUM_HASH_BITS: usize = 250; diff --git a/src/lib.rs b/src/lib.rs index 905c837..df7de20 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,10 +3,10 @@ #![allow(clippy::type_complexity)] #![deny(missing_docs)] -mod commitments; - pub mod bellperson; mod circuit; +mod commitments; +mod constants; pub mod errors; pub mod gadgets; pub mod pasta; @@ -14,14 +14,13 @@ mod poseidon; pub mod r1cs; pub mod traits; -use std::marker::PhantomData; - use commitments::CompressedCommitment; use errors::NovaError; use merlin::Transcript; use r1cs::{ R1CSGens, R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness, }; +use std::marker::PhantomData; use traits::{AppendToTranscriptTrait, ChallengeTrait, Group}; /// A SNARK that holds the proof of a step of an incremental computation diff --git a/src/poseidon.rs b/src/poseidon.rs index d745a95..7b208cc 100644 --- a/src/poseidon.rs +++ b/src/poseidon.rs @@ -1,5 +1,8 @@ //! Poseidon Constants and Poseidon-based RO used in Nova -use crate::traits::{HashFuncConstantsTrait, HashFuncTrait}; +use super::{ + constants::{NUM_CHALLENGE_BITS, NUM_HASH_BITS}, + traits::{HashFuncConstantsTrait, HashFuncTrait}, +}; use bellperson::{ gadgets::{ boolean::{AllocatedBit, Boolean}, @@ -102,11 +105,11 @@ where #[allow(dead_code)] fn get_challenge(&self) -> Scalar { let hash = self.hash_inner(); - // Only keep 128 bits + // Only keep NUM_CHALLENGE_BITS bits let bits = hash.to_le_bits(); let mut res = Scalar::zero(); let mut coeff = Scalar::one(); - for bit in bits[0..128].into_iter() { + for bit in bits[0..NUM_CHALLENGE_BITS].into_iter() { if *bit { res += coeff; } @@ -118,11 +121,11 @@ where #[allow(dead_code)] fn get_hash(&self) -> Scalar { let hash = self.hash_inner(); - // Only keep 250 bits + // Only keep NUM_HASH_BITS bits let bits = hash.to_le_bits(); let mut res = Scalar::zero(); let mut coeff = Scalar::one(); - for bit in bits[0..250].into_iter() { + for bit in bits[0..NUM_HASH_BITS].into_iter() { if *bit { res += coeff; } @@ -204,8 +207,7 @@ where CS: ConstraintSystem, { let bits = self.hash_inner(cs.namespace(|| "hash"))?; - // Only keep 128 bits - Ok(bits[..128].into()) + Ok(bits[..NUM_CHALLENGE_BITS].into()) } #[allow(dead_code)] @@ -214,8 +216,7 @@ where CS: ConstraintSystem, { let bits = self.hash_inner(cs.namespace(|| "hash"))?; - // Only keep 250 bits - Ok(bits[..250].into()) + Ok(bits[..NUM_HASH_BITS].into()) } } diff --git a/src/r1cs.rs b/src/r1cs.rs index 5e03825..3564127 100644 --- a/src/r1cs.rs +++ b/src/r1cs.rs @@ -2,6 +2,7 @@ #![allow(clippy::type_complexity)] use super::{ commitments::{CommitGens, CommitTrait, Commitment, CompressedCommitment}, + constants::NUM_HASH_BITS, errors::NovaError, traits::{AppendToTranscriptTrait, Group}, }; @@ -11,6 +12,7 @@ use itertools::concat; use merlin::Transcript; use rayon::prelude::*; use serde::{Deserialize, Serialize}; +use sha3::{Digest, Sha3_256}; /// Public parameters for a given R1CS pub struct R1CSGens { @@ -328,10 +330,37 @@ impl AppendToTranscriptTrait for R1CSShape { .collect(), }; + // obtain a vector of bytes representing the R1CS shape 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); + + // convert shape_bytes into a short digest + let mut hasher = Sha3_256::new(); + hasher.input(&shape_bytes); + let shape_digest = hasher.result(); + + let shape_digest_trun = { + // truncate the digest to 250 bits + let bv = (0..NUM_HASH_BITS).map(|i| { + let (byte_pos, bit_pos) = (i / 8, i % 8); + let bit = (shape_digest[byte_pos] >> bit_pos) & 1; + bit == 1 + }); + + // turn the bit vector into a scalar + let mut res = G::Scalar::zero(); + let mut coeff = G::Scalar::one(); + for bit in bv { + if bit { + res += coeff; + } + coeff += coeff; + } + res + }; + + shape_digest_trun.append_to_transcript(b"R1CSShape", transcript); } }