arnaucube
arnaucube/NovaPublic Self-hosted repository
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
Srinath Setty srinath@microsoft.com
Fri, 13 May 2022 10:38:43 +0530
commit

ccc6dc3a0462e5a8c82622bbbea59b0bd9e5cb67

parent

0d53db18e36ee1f8bc927ec85ee1fd38dcd7a501

4 files changed, 45 insertions(+), 14 deletions(-)

jump to
A src/constants.rs

@@ -0,0 +1,2 @@

+pub(crate) const NUM_CHALLENGE_BITS: usize = 128; +pub(crate) const NUM_HASH_BITS: usize = 250;
M src/lib.rs → src/lib.rs

@@ -3,10 +3,10 @@ #![allow(non_snake_case)]

#![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
M src/poseidon.rs → 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 @@ /// Compute a challenge by hashing the current state

#[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 @@

#[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<Scalar>, { 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<Scalar>, { let bits = self.hash_inner(cs.namespace(|| "hash"))?; - // Only keep 250 bits - Ok(bits[..250].into()) + Ok(bits[..NUM_HASH_BITS].into()) } }
M src/r1cs.rs → src/r1cs.rs

@@ -2,6 +2,7 @@ //! This module defines R1CS related types and a folding scheme for Relaxed R1CS

#![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<G: Group> {

@@ -328,10 +330,37 @@ .map(|(i, j, v)| (*i, *j, v.to_repr().as_ref().to_vec()))

.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); } }