diff --git a/Cargo.toml b/Cargo.toml index b1867d1..7425f68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nova-snark" -version = "0.7.1" +version = "0.7.2" authors = ["Srinath Setty "] edition = "2021" description = "Recursive zkSNARKs without trusted setup" diff --git a/benches/compressed-snark.rs b/benches/compressed-snark.rs index d86fb14..364786c 100644 --- a/benches/compressed-snark.rs +++ b/benches/compressed-snark.rs @@ -145,7 +145,7 @@ where Ok(y) } - fn compute(&self, z: &F) -> F { + fn output(&self, z: &F) -> F { let mut x = *z; let mut y = x; for _i in 0..self.num_cons { diff --git a/benches/recursive-snark.rs b/benches/recursive-snark.rs index 18bed2d..25511e8 100644 --- a/benches/recursive-snark.rs +++ b/benches/recursive-snark.rs @@ -146,7 +146,7 @@ where Ok(y) } - fn compute(&self, z: &F) -> F { + fn output(&self, z: &F) -> F { let mut x = *z; let mut y = x; for _i in 0..self.num_cons { diff --git a/examples/ecdsa/circuit.rs b/examples/ecdsa/circuit.rs index 94e859d..237ba40 100644 --- a/examples/ecdsa/circuit.rs +++ b/examples/ecdsa/circuit.rs @@ -289,7 +289,7 @@ where ) } - fn compute(&self, z: &F) -> F { + fn output(&self, z: &F) -> F { let z_hash = Poseidon::::new_with_preimage( &[ self.z_r.x, diff --git a/examples/minroot.rs b/examples/minroot.rs index 4643791..10655a7 100644 --- a/examples/minroot.rs +++ b/examples/minroot.rs @@ -148,7 +148,7 @@ where z_out } - fn compute(&self, z: &F) -> F { + fn output(&self, z: &F) -> F { // sanity check let z_hash = Poseidon::::new_with_preimage(&[self.seq[0].x_i, self.seq[0].y_i], &self.pc).hash(); diff --git a/src/circuit.rs b/src/circuit.rs index 9acc65d..0db2e41 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -8,6 +8,7 @@ use super::{ commitments::Commitment, + constants::NUM_HASH_BITS, gadgets::{ ecc::AllocatedPoint, r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance}, @@ -16,7 +17,7 @@ use super::{ }, }, r1cs::{R1CSInstance, RelaxedR1CSInstance}, - traits::{circuit::StepCircuit, Group, HashFuncCircuitTrait, HashFuncConstantsCircuit}, + traits::{circuit::StepCircuit, Group, ROCircuitTrait, ROConstantsCircuit}, }; use bellperson::{ gadgets::{ @@ -91,7 +92,7 @@ where SC: StepCircuit, { params: NovaAugmentedCircuitParams, - ro_consts: HashFuncConstantsCircuit, + ro_consts: ROConstantsCircuit, inputs: Option>, step_circuit: SC, // The function that is applied for each step } @@ -106,7 +107,7 @@ where params: NovaAugmentedCircuitParams, inputs: Option>, step_circuit: SC, - ro_consts: HashFuncConstantsCircuit, + ro_consts: ROConstantsCircuit, ) -> Self { Self { params, @@ -221,14 +222,14 @@ where T: AllocatedPoint, ) -> Result<(AllocatedRelaxedR1CSInstance, AllocatedBit), SynthesisError> { // Check that u.x[0] = Hash(params, U, i, z0, zi) - let mut ro = G::HashFuncCircuit::new(self.ro_consts.clone()); + let mut ro = G::ROCircuit::new(self.ro_consts.clone()); ro.absorb(params.clone()); ro.absorb(i); ro.absorb(z_0); ro.absorb(z_i); U.absorb_in_ro(cs.namespace(|| "absorb U"), &mut ro)?; - let hash_bits = ro.get_hash(cs.namespace(|| "Input hash"))?; + let hash_bits = ro.squeeze(cs.namespace(|| "Input hash"), NUM_HASH_BITS)?; let hash = le_bits_to_num(cs.namespace(|| "bits to hash"), hash_bits)?; let check_pass = alloc_num_equals( cs.namespace(|| "check consistency of u.X[0] with H(params, U, i, z0, zi)"), @@ -328,13 +329,13 @@ where .synthesize(&mut cs.namespace(|| "F"), z_input)?; // Compute the new hash H(params, Unew, i+1, z0, z_{i+1}) - let mut ro = G::HashFuncCircuit::new(self.ro_consts); + let mut ro = G::ROCircuit::new(self.ro_consts); ro.absorb(params); ro.absorb(i_new.clone()); ro.absorb(z_0); ro.absorb(z_next); Unew.absorb_in_ro(cs.namespace(|| "absorb U_new"), &mut ro)?; - let hash_bits = ro.get_hash(cs.namespace(|| "output hash bits"))?; + let hash_bits = ro.squeeze(cs.namespace(|| "output hash bits"), NUM_HASH_BITS)?; let hash = le_bits_to_num(cs.namespace(|| "convert hash to num"), hash_bits)?; // Outputs the computed hash and u.X[1] that corresponds to the hash of the other circuit @@ -356,7 +357,7 @@ mod tests { use crate::{ bellperson::r1cs::{NovaShape, NovaWitness}, poseidon::PoseidonConstantsCircuit, - traits::{circuit::TrivialTestCircuit, HashFuncConstantsTrait}, + traits::{circuit::TrivialTestCircuit, ROConstantsTrait}, }; #[test] @@ -364,8 +365,8 @@ mod tests { // In the following we use 1 to refer to the primary, and 2 to refer to the secondary circuit let params1 = NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, true); let params2 = NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, false); - let ro_consts1: HashFuncConstantsCircuit = PoseidonConstantsCircuit::new(); - let ro_consts2: HashFuncConstantsCircuit = PoseidonConstantsCircuit::new(); + let ro_consts1: ROConstantsCircuit = PoseidonConstantsCircuit::new(); + let ro_consts2: ROConstantsCircuit = PoseidonConstantsCircuit::new(); // Initialize the shape and gens for the primary let circuit1: NovaAugmentedCircuit::Base>> = diff --git a/src/commitments.rs b/src/commitments.rs index e97c082..ef13fc6 100644 --- a/src/commitments.rs +++ b/src/commitments.rs @@ -1,6 +1,6 @@ use super::{ errors::NovaError, - traits::{AbsorbInROTrait, AppendToTranscriptTrait, CompressedGroup, Group, HashFuncTrait}, + traits::{AbsorbInROTrait, AppendToTranscriptTrait, CompressedGroup, Group, ROTrait}, }; use core::{ fmt::Debug, @@ -166,7 +166,7 @@ impl AppendToTranscriptTrait for Commitment { } impl AbsorbInROTrait for Commitment { - fn absorb_in_ro(&self, ro: &mut G::HashFunc) { + fn absorb_in_ro(&self, ro: &mut G::RO) { let (x, y, is_infinity) = self.comm.to_coordinates(); ro.absorb(x); ro.absorb(y); diff --git a/src/gadgets/r1cs.rs b/src/gadgets/r1cs.rs index 33cc7d7..2fc7ff1 100644 --- a/src/gadgets/r1cs.rs +++ b/src/gadgets/r1cs.rs @@ -1,5 +1,6 @@ //! This module implements various gadgets necessary for folding R1CS types. use crate::{ + constants::NUM_CHALLENGE_BITS, gadgets::{ ecc::AllocatedPoint, utils::{ @@ -8,7 +9,7 @@ use crate::{ }, }, r1cs::{R1CSInstance, RelaxedR1CSInstance}, - traits::{Group, HashFuncCircuitTrait, HashFuncConstantsCircuit}, + traits::{Group, ROCircuitTrait, ROConstantsCircuit}, }; use bellperson::{ gadgets::{boolean::Boolean, num::AllocatedNum, Assignment}, @@ -60,7 +61,7 @@ where } /// Absorb the provided instance in the RO - pub fn absorb_in_ro(&self, ro: &mut G::HashFuncCircuit) { + pub fn absorb_in_ro(&self, ro: &mut G::ROCircuit) { ro.absorb(self.W.x.clone()); ro.absorb(self.W.y.clone()); ro.absorb(self.W.is_infinity.clone()); @@ -207,7 +208,7 @@ where pub fn absorb_in_ro::Base>>( &self, mut cs: CS, - ro: &mut G::HashFuncCircuit, + ro: &mut G::ROCircuit, ) -> Result<(), SynthesisError> { ro.absorb(self.W.x.clone()); ro.absorb(self.W.y.clone()); @@ -262,19 +263,19 @@ where params: AllocatedNum, // hash of R1CSShape of F' u: AllocatedR1CSInstance, T: AllocatedPoint, - ro_consts: HashFuncConstantsCircuit, + ro_consts: ROConstantsCircuit, limb_width: usize, n_limbs: usize, ) -> Result, SynthesisError> { // Compute r: - let mut ro = G::HashFuncCircuit::new(ro_consts); + let mut ro = G::ROCircuit::new(ro_consts); ro.absorb(params); self.absorb_in_ro(cs.namespace(|| "absorb running instance"), &mut ro)?; u.absorb_in_ro(&mut ro); ro.absorb(T.x.clone()); ro.absorb(T.y.clone()); ro.absorb(T.is_infinity.clone()); - let r_bits = ro.get_challenge(cs.namespace(|| "r bits"))?; + let r_bits = ro.squeeze(cs.namespace(|| "r bits"), NUM_CHALLENGE_BITS)?; let r = le_bits_to_num(cs.namespace(|| "r"), r_bits.clone())?; // W_fold = self.W + r * u.W diff --git a/src/lib.rs b/src/lib.rs index b09f75f..cbd5dea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,7 @@ use crate::bellperson::{ }; use ::bellperson::{Circuit, ConstraintSystem}; use circuit::{NovaAugmentedCircuit, NovaAugmentedCircuitInputs, NovaAugmentedCircuitParams}; +use constants::NUM_HASH_BITS; use constants::{BN_LIMB_WIDTH, BN_N_LIMBS}; use core::marker::PhantomData; use errors::NovaError; @@ -36,8 +37,8 @@ use r1cs::{ R1CSGens, R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness, }; use traits::{ - circuit::StepCircuit, snark::RelaxedR1CSSNARKTrait, AbsorbInROTrait, Group, HashFuncConstants, - HashFuncConstantsCircuit, HashFuncConstantsTrait, HashFuncTrait, + circuit::StepCircuit, snark::RelaxedR1CSSNARKTrait, AbsorbInROTrait, Group, ROConstants, + ROConstantsCircuit, ROConstantsTrait, ROTrait, }; /// A type that holds public parameters of Nova @@ -48,13 +49,13 @@ where C1: StepCircuit, C2: StepCircuit, { - ro_consts_primary: HashFuncConstants, - ro_consts_circuit_primary: HashFuncConstantsCircuit, + ro_consts_primary: ROConstants, + ro_consts_circuit_primary: ROConstantsCircuit, r1cs_gens_primary: R1CSGens, r1cs_shape_primary: R1CSShape, r1cs_shape_padded_primary: R1CSShape, - ro_consts_secondary: HashFuncConstants, - ro_consts_circuit_secondary: HashFuncConstantsCircuit, + ro_consts_secondary: ROConstants, + ro_consts_circuit_secondary: ROConstantsCircuit, r1cs_gens_secondary: R1CSGens, r1cs_shape_secondary: R1CSShape, r1cs_shape_padded_secondary: R1CSShape, @@ -78,14 +79,12 @@ where let augmented_circuit_params_secondary = NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, false); - let ro_consts_primary: HashFuncConstants = HashFuncConstants::::new(); - let ro_consts_secondary: HashFuncConstants = HashFuncConstants::::new(); + let ro_consts_primary: ROConstants = ROConstants::::new(); + let ro_consts_secondary: ROConstants = ROConstants::::new(); // ro_consts_circuit_primart are parameterized by G2 because the type alias uses G2::Base = G1::Scalar - let ro_consts_circuit_primary: HashFuncConstantsCircuit = - HashFuncConstantsCircuit::::new(); - let ro_consts_circuit_secondary: HashFuncConstantsCircuit = - HashFuncConstantsCircuit::::new(); + let ro_consts_circuit_primary: ROConstantsCircuit = ROConstantsCircuit::::new(); + let ro_consts_circuit_secondary: ROConstantsCircuit = ROConstantsCircuit::::new(); // Initialize gens for the primary let circuit_primary: NovaAugmentedCircuit = NovaAugmentedCircuit::new( @@ -245,8 +244,8 @@ where RelaxedR1CSInstance::::default(&pp.r1cs_gens_secondary, &pp.r1cs_shape_secondary); // Outputs of the two circuits thus far - let zi_primary = c_primary.compute(&z0_primary); - let zi_secondary = c_secondary.compute(&z0_secondary); + let zi_primary = c_primary.output(&z0_primary); + let zi_secondary = c_secondary.output(&z0_secondary); Ok(Self { r_W_primary, @@ -334,8 +333,8 @@ where .map_err(|_e| NovaError::UnSat)?; // update the running instances and witnesses - let zi_primary = c_primary.compute(&r_snark.zi_primary); - let zi_secondary = c_secondary.compute(&r_snark.zi_secondary); + let zi_primary = c_primary.output(&r_snark.zi_primary); + let zi_secondary = c_secondary.output(&r_snark.zi_secondary); Ok(Self { r_W_primary, @@ -385,21 +384,24 @@ where // check if the output hashes in R1CS instances point to the right running instances let (hash_primary, hash_secondary) = { - let mut hasher = ::HashFunc::new(pp.ro_consts_secondary.clone()); + let mut hasher = ::RO::new(pp.ro_consts_secondary.clone()); hasher.absorb(scalar_as_base::(pp.r1cs_shape_secondary.get_digest())); hasher.absorb(G1::Scalar::from(num_steps as u64)); hasher.absorb(z0_primary); hasher.absorb(self.zi_primary); self.r_U_secondary.absorb_in_ro(&mut hasher); - let mut hasher2 = ::HashFunc::new(pp.ro_consts_primary.clone()); + let mut hasher2 = ::RO::new(pp.ro_consts_primary.clone()); hasher2.absorb(scalar_as_base::(pp.r1cs_shape_primary.get_digest())); hasher2.absorb(G2::Scalar::from(num_steps as u64)); hasher2.absorb(z0_secondary); hasher2.absorb(self.zi_secondary); self.r_U_primary.absorb_in_ro(&mut hasher2); - (hasher.get_hash(), hasher2.get_hash()) + ( + hasher.squeeze(NUM_HASH_BITS), + hasher2.squeeze(NUM_HASH_BITS), + ) }; if hash_primary != scalar_as_base::(self.l_u_primary.X[1]) @@ -597,21 +599,24 @@ where // check if the output hashes in R1CS instances point to the right running instances let (hash_primary, hash_secondary) = { - let mut hasher = ::HashFunc::new(pp.ro_consts_secondary.clone()); + let mut hasher = ::RO::new(pp.ro_consts_secondary.clone()); hasher.absorb(scalar_as_base::(pp.r1cs_shape_secondary.get_digest())); hasher.absorb(G1::Scalar::from(num_steps as u64)); hasher.absorb(z0_primary); hasher.absorb(self.zn_primary); self.r_U_secondary.absorb_in_ro(&mut hasher); - let mut hasher2 = ::HashFunc::new(pp.ro_consts_primary.clone()); + let mut hasher2 = ::RO::new(pp.ro_consts_primary.clone()); hasher2.absorb(scalar_as_base::(pp.r1cs_shape_primary.get_digest())); hasher2.absorb(G2::Scalar::from(num_steps as u64)); hasher2.absorb(z0_secondary); hasher2.absorb(self.zn_secondary); self.r_U_primary.absorb_in_ro(&mut hasher2); - (hasher.get_hash(), hasher2.get_hash()) + ( + hasher.squeeze(NUM_HASH_BITS), + hasher2.squeeze(NUM_HASH_BITS), + ) }; if hash_primary != scalar_as_base::(self.l_u_primary.X[1]) @@ -709,7 +714,7 @@ mod tests { Ok(y) } - fn compute(&self, z: &F) -> F { + fn output(&self, z: &F) -> F { *z * *z * *z + z + F::from(5u64) } } @@ -816,7 +821,7 @@ mod tests { assert_eq!(zn_primary, ::Scalar::one()); let mut zn_secondary_direct = ::Scalar::zero(); for _i in 0..num_steps { - zn_secondary_direct = CubicCircuit::default().compute(&zn_secondary_direct); + zn_secondary_direct = CubicCircuit::default().output(&zn_secondary_direct); } assert_eq!(zn_secondary, zn_secondary_direct); assert_eq!(zn_secondary, ::Scalar::from(2460515u64)); @@ -878,7 +883,7 @@ mod tests { assert_eq!(zn_primary, ::Scalar::one()); let mut zn_secondary_direct = ::Scalar::zero(); for _i in 0..num_steps { - zn_secondary_direct = CubicCircuit::default().compute(&zn_secondary_direct); + zn_secondary_direct = CubicCircuit::default().output(&zn_secondary_direct); } assert_eq!(zn_secondary, zn_secondary_direct); assert_eq!(zn_secondary, ::Scalar::from(2460515u64)); @@ -960,7 +965,7 @@ mod tests { Ok(y) } - fn compute(&self, z: &F) -> F { + fn output(&self, z: &F) -> F { // sanity check let x = *z; let y_pow_5 = { diff --git a/src/nifs.rs b/src/nifs.rs index 55cdc04..8335df1 100644 --- a/src/nifs.rs +++ b/src/nifs.rs @@ -2,12 +2,13 @@ #![allow(non_snake_case)] #![allow(clippy::type_complexity)] -use super::commitments::CompressedCommitment; -use super::errors::NovaError; -use super::r1cs::{ - R1CSGens, R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness, +use super::{ + commitments::CompressedCommitment, + constants::NUM_CHALLENGE_BITS, + errors::NovaError, + r1cs::{R1CSGens, R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness}, + traits::{AbsorbInROTrait, Group, ROTrait}, }; -use super::traits::{AbsorbInROTrait, Group, HashFuncTrait}; use core::marker::PhantomData; /// A SNARK that holds the proof of a step of an incremental computation @@ -18,8 +19,8 @@ pub struct NIFS { _p: PhantomData, } -type HashFuncConstants = - <::HashFunc as HashFuncTrait<::Base, ::Scalar>>::Constants; +type ROConstants = + <::RO as ROTrait<::Base, ::Scalar>>::Constants; impl NIFS { /// Takes as input a Relaxed R1CS instance-witness tuple `(U1, W1)` and @@ -30,7 +31,7 @@ impl NIFS { /// if and only if `W1` satisfies `U1` and `W2` satisfies `U2`. pub fn prove( gens: &R1CSGens, - ro_consts: &HashFuncConstants, + ro_consts: &ROConstants, S: &R1CSShape, U1: &RelaxedR1CSInstance, W1: &RelaxedR1CSWitness, @@ -38,7 +39,7 @@ impl NIFS { W2: &R1CSWitness, ) -> Result<(NIFS, (RelaxedR1CSInstance, RelaxedR1CSWitness)), NovaError> { // initialize a new RO - let mut ro = G::HashFunc::new(ro_consts.clone()); + let mut ro = G::RO::new(ro_consts.clone()); // append S to the transcript S.absorb_in_ro(&mut ro); @@ -54,7 +55,7 @@ impl NIFS { comm_T.absorb_in_ro(&mut ro); // compute a challenge from the RO - let r = ro.get_challenge(); + let r = ro.squeeze(NUM_CHALLENGE_BITS); // fold the instance using `r` and `comm_T` let U = U1.fold(U2, &comm_T, &r)?; @@ -79,13 +80,13 @@ impl NIFS { /// if and only if `U1` and `U2` are satisfiable. pub fn verify( &self, - ro_consts: &HashFuncConstants, + ro_consts: &ROConstants, S: &R1CSShape, U1: &RelaxedR1CSInstance, U2: &R1CSInstance, ) -> Result, NovaError> { // initialize a new RO - let mut ro = G::HashFunc::new(ro_consts.clone()); + let mut ro = G::RO::new(ro_consts.clone()); // append S to the transcript S.absorb_in_ro(&mut ro); @@ -99,7 +100,7 @@ impl NIFS { comm_T.absorb_in_ro(&mut ro); // compute a challenge from the RO - let r = ro.get_challenge(); + let r = ro.squeeze(NUM_CHALLENGE_BITS); // fold the instance using `r` and `comm_T` let U = U1.fold(U2, &comm_T, &r)?; @@ -112,7 +113,7 @@ impl NIFS { #[cfg(test)] mod tests { use super::*; - use crate::traits::{Group, HashFuncConstantsTrait}; + use crate::traits::{Group, ROConstantsTrait}; use ::bellperson::{gadgets::num::AllocatedNum, ConstraintSystem, SynthesisError}; use ff::{Field, PrimeField}; use rand::rngs::OsRng; @@ -166,10 +167,8 @@ mod tests { let _ = synthesize_tiny_r1cs_bellperson(&mut cs, None); let shape = cs.r1cs_shape(); let gens = cs.r1cs_gens(); - let ro_consts = <::HashFunc as HashFuncTrait< - ::Base, - ::Scalar, - >>::Constants::new(); + let ro_consts = + <::RO as ROTrait<::Base, ::Scalar>>::Constants::new(); // Now get the instance and assignment for one instance let mut cs: SatisfyingAssignment = SatisfyingAssignment::new(); @@ -193,10 +192,7 @@ mod tests { fn execute_sequence( gens: &R1CSGens, - ro_consts: &<::HashFunc as HashFuncTrait< - ::Base, - ::Scalar, - >>::Constants, + ro_consts: &<::RO as ROTrait<::Base, ::Scalar>>::Constants, shape: &R1CSShape, U1: &R1CSInstance, W1: &R1CSWitness, @@ -304,10 +300,8 @@ mod tests { // generate generators and ro constants let gens = R1CSGens::new(num_cons, num_vars); - let ro_consts = <::HashFunc as HashFuncTrait< - ::Base, - ::Scalar, - >>::Constants::new(); + let ro_consts = + <::RO as ROTrait<::Base, ::Scalar>>::Constants::new(); let rand_inst_witness_generator = |gens: &R1CSGens, I: &S| -> (S, R1CSInstance, R1CSWitness) { diff --git a/src/pasta.rs b/src/pasta.rs index 9992354..89a0481 100644 --- a/src/pasta.rs +++ b/src/pasta.rs @@ -1,6 +1,6 @@ //! This module implements the Nova traits for pallas::Point, pallas::Scalar, vesta::Point, vesta::Scalar. use crate::{ - poseidon::{PoseidonHashFunc, PoseidonHashFuncCircuit}, + poseidon::{PoseidonRO, PoseidonROCircuit}, traits::{ChallengeTrait, CompressedGroup, Group}, }; use digest::{ExtendableOutput, Input}; @@ -40,8 +40,8 @@ impl Group for pallas::Point { type Scalar = pallas::Scalar; type CompressedGroupElement = PallasCompressedElementWrapper; type PreprocessedGroupElement = pallas::Affine; - type HashFunc = PoseidonHashFunc; - type HashFuncCircuit = PoseidonHashFuncCircuit; + type RO = PoseidonRO; + type ROCircuit = PoseidonROCircuit; fn vartime_multiscalar_mul( scalars: &[Self::Scalar], @@ -138,8 +138,8 @@ impl Group for vesta::Point { type Scalar = vesta::Scalar; type CompressedGroupElement = VestaCompressedElementWrapper; type PreprocessedGroupElement = vesta::Affine; - type HashFunc = PoseidonHashFunc; - type HashFuncCircuit = PoseidonHashFuncCircuit; + type RO = PoseidonRO; + type ROCircuit = PoseidonROCircuit; fn vartime_multiscalar_mul( scalars: &[Self::Scalar], diff --git a/src/poseidon.rs b/src/poseidon.rs index d389499..db116a0 100644 --- a/src/poseidon.rs +++ b/src/poseidon.rs @@ -1,8 +1,5 @@ //! Poseidon Constants and Poseidon-based RO used in Nova -use super::{ - constants::{NUM_CHALLENGE_BITS, NUM_HASH_BITS}, - traits::{HashFuncCircuitTrait, HashFuncConstantsTrait, HashFuncTrait}, -}; +use super::traits::{ROCircuitTrait, ROConstantsTrait, ROTrait}; use bellperson::{ gadgets::{ boolean::{AllocatedBit, Boolean}, @@ -29,7 +26,7 @@ where constants32: PoseidonConstants, } -impl HashFuncConstantsTrait for PoseidonConstantsCircuit +impl ROConstantsTrait for PoseidonConstantsCircuit where Scalar: PrimeField + PrimeFieldBits, { @@ -46,7 +43,7 @@ where } /// A Poseidon-based RO to use outside circuits -pub struct PoseidonHashFunc +pub struct PoseidonRO where Base: PrimeField + PrimeFieldBits, Scalar: PrimeField + PrimeFieldBits, @@ -58,30 +55,7 @@ where _p: PhantomData, } -impl PoseidonHashFunc -where - Base: PrimeField + PrimeFieldBits, - Scalar: PrimeField + PrimeFieldBits, -{ - fn hash_inner(&self) -> Base { - match self.state.len() { - 27 => { - Poseidon::::new_with_preimage(&self.state, &self.constants.constants27).hash() - } - 32 => { - Poseidon::::new_with_preimage(&self.state, &self.constants.constants32).hash() - } - _ => { - panic!( - "Number of elements in the RO state does not match any of the arities used in Nova: {:?}", - self.state.len() - ); - } - } - } -} - -impl HashFuncTrait for PoseidonHashFunc +impl ROTrait for PoseidonRO where Base: PrimeField + PrimeFieldBits, Scalar: PrimeField + PrimeFieldBits, @@ -102,28 +76,27 @@ where } /// Compute a challenge by hashing the current state - fn get_challenge(&self) -> Scalar { - let hash = self.hash_inner(); - // 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..NUM_CHALLENGE_BITS].into_iter() { - if *bit { - res += coeff; + fn squeeze(&self, num_bits: usize) -> Scalar { + let hash = match self.state.len() { + 27 => { + Poseidon::::new_with_preimage(&self.state, &self.constants.constants27).hash() } - coeff += coeff; - } - res - } + 32 => { + Poseidon::::new_with_preimage(&self.state, &self.constants.constants32).hash() + } + _ => { + panic!( + "Number of elements in the RO state does not match any of the arities used in Nova: {:?}", + self.state.len() + ); + } + }; - fn get_hash(&self) -> Scalar { - let hash = self.hash_inner(); - // Only keep NUM_HASH_BITS bits + // Only return `num_bits` let bits = hash.to_le_bits(); let mut res = Scalar::zero(); let mut coeff = Scalar::one(); - for bit in bits[0..NUM_HASH_BITS].into_iter() { + for bit in bits[0..num_bits].into_iter() { if *bit { res += coeff; } @@ -134,7 +107,7 @@ where } /// A Poseidon-based RO gadget to use inside the verifier circuit. -pub struct PoseidonHashFuncCircuit +pub struct PoseidonROCircuit where Scalar: PrimeField + PrimeFieldBits, { @@ -143,15 +116,35 @@ where constants: PoseidonConstantsCircuit, } -impl PoseidonHashFuncCircuit +impl ROCircuitTrait for PoseidonROCircuit where Scalar: PrimeField + PrimeFieldBits, { - fn hash_inner(&mut self, mut cs: CS) -> Result, SynthesisError> + type Constants = PoseidonConstantsCircuit; + + /// Initialize the internal state and set the poseidon constants + fn new(constants: PoseidonConstantsCircuit) -> Self { + Self { + state: Vec::new(), + constants, + } + } + + /// Absorb a new number into the state of the oracle + fn absorb(&mut self, e: AllocatedNum) { + self.state.push(e); + } + + /// Compute a challenge by hashing the current state + fn squeeze( + &mut self, + mut cs: CS, + num_bits: usize, + ) -> Result, SynthesisError> where CS: ConstraintSystem, { - let out = match self.state.len() { + let hash = match self.state.len() { 27 => poseidon_hash( cs.namespace(|| "Poseidon hash"), self.state.clone(), @@ -170,64 +163,31 @@ where } }; - // return the hash as a vector of bits + // return the hash as a vector of bits, truncated Ok( - out + hash .to_bits_le_strict(cs.namespace(|| "poseidon hash to boolean"))? .iter() .map(|boolean| match boolean { Boolean::Is(ref x) => x.clone(), _ => panic!("Wrong type of input. We should have never reached there"), }) - .collect(), + .collect::>()[..num_bits] + .into(), ) } } -impl HashFuncCircuitTrait for PoseidonHashFuncCircuit -where - Scalar: PrimeField + PrimeFieldBits, -{ - type Constants = PoseidonConstantsCircuit; - - /// Initialize the internal state and set the poseidon constants - fn new(constants: PoseidonConstantsCircuit) -> Self { - Self { - state: Vec::new(), - constants, - } - } - - /// Absorb a new number into the state of the oracle - fn absorb(&mut self, e: AllocatedNum) { - self.state.push(e); - } - - /// Compute a challenge by hashing the current state - fn get_challenge(&mut self, mut cs: CS) -> Result, SynthesisError> - where - CS: ConstraintSystem, - { - let bits = self.hash_inner(cs.namespace(|| "hash"))?; - Ok(bits[..NUM_CHALLENGE_BITS].into()) - } - - fn get_hash(&mut self, mut cs: CS) -> Result, SynthesisError> - where - CS: ConstraintSystem, - { - let bits = self.hash_inner(cs.namespace(|| "hash"))?; - Ok(bits[..NUM_HASH_BITS].into()) - } -} - #[cfg(test)] mod tests { use super::*; type S = pasta_curves::pallas::Scalar; type B = pasta_curves::vesta::Scalar; type G = pasta_curves::pallas::Point; - use crate::{bellperson::solver::SatisfyingAssignment, gadgets::utils::le_bits_to_num}; + use crate::{ + bellperson::solver::SatisfyingAssignment, constants::NUM_CHALLENGE_BITS, + gadgets::utils::le_bits_to_num, + }; use ff::Field; use rand::rngs::OsRng; @@ -236,8 +196,8 @@ mod tests { // Check that the number computed inside the circuit is equal to the number computed outside the circuit let mut csprng: OsRng = OsRng; let constants = PoseidonConstantsCircuit::new(); - let mut ro: PoseidonHashFunc = PoseidonHashFunc::new(constants.clone()); - let mut ro_gadget: PoseidonHashFuncCircuit = PoseidonHashFuncCircuit::new(constants); + let mut ro: PoseidonRO = PoseidonRO::new(constants.clone()); + let mut ro_gadget: PoseidonROCircuit = PoseidonROCircuit::new(constants); let mut cs: SatisfyingAssignment = SatisfyingAssignment::new(); for i in 0..27 { let num = S::random(&mut csprng); @@ -249,8 +209,8 @@ mod tests { .unwrap(); ro_gadget.absorb(num_gadget); } - let num = ro.get_challenge(); - let num2_bits = ro_gadget.get_challenge(&mut cs).unwrap(); + let num = ro.squeeze(NUM_CHALLENGE_BITS); + let num2_bits = ro_gadget.squeeze(&mut cs, NUM_CHALLENGE_BITS).unwrap(); let num2 = le_bits_to_num(&mut cs, num2_bits).unwrap(); assert_eq!(num.to_repr(), num2.get_value().unwrap().to_repr()); } diff --git a/src/r1cs.rs b/src/r1cs.rs index 69d497b..a099ab5 100644 --- a/src/r1cs.rs +++ b/src/r1cs.rs @@ -5,7 +5,7 @@ use super::{ constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_HASH_BITS}, errors::NovaError, gadgets::utils::scalar_as_base, - traits::{AbsorbInROTrait, AppendToTranscriptTrait, Group, HashFuncTrait}, + traits::{AbsorbInROTrait, AppendToTranscriptTrait, Group, ROTrait}, }; use bellperson_nonnative::{mp::bignat::nat_to_limbs, util::convert::f_to_nat}; use core::cmp::max; @@ -453,7 +453,7 @@ impl AppendToTranscriptTrait for R1CSShape { } impl AbsorbInROTrait for R1CSShape { - fn absorb_in_ro(&self, ro: &mut G::HashFunc) { + fn absorb_in_ro(&self, ro: &mut G::RO) { ro.absorb(scalar_as_base::(self.get_digest())); } } @@ -500,7 +500,7 @@ impl AppendToTranscriptTrait for R1CSInstance { } impl AbsorbInROTrait for R1CSInstance { - fn absorb_in_ro(&self, ro: &mut G::HashFunc) { + fn absorb_in_ro(&self, ro: &mut G::RO) { self.comm_W.absorb_in_ro(ro); for x in &self.X { ro.absorb(scalar_as_base::(*x)); @@ -640,7 +640,7 @@ impl AppendToTranscriptTrait for RelaxedR1CSInstance { } impl AbsorbInROTrait for RelaxedR1CSInstance { - fn absorb_in_ro(&self, ro: &mut G::HashFunc) { + fn absorb_in_ro(&self, ro: &mut G::RO) { self.comm_W.absorb_in_ro(ro); self.comm_E.absorb_in_ro(ro); ro.absorb(scalar_as_base::(self.u)); diff --git a/src/traits/circuit.rs b/src/traits/circuit.rs index 3afaccd..b5dde10 100644 --- a/src/traits/circuit.rs +++ b/src/traits/circuit.rs @@ -13,8 +13,8 @@ pub trait StepCircuit: Send + Sync + Clone { z: AllocatedNum, ) -> Result, SynthesisError>; - /// Execute the circuit for a computation step and return output - fn compute(&self, z: &F) -> F; + /// return the output of the step when provided with with the step's input + fn output(&self, z: &F) -> F; } /// A trivial step circuit that simply returns the input @@ -35,7 +35,7 @@ where Ok(z) } - fn compute(&self, z: &F) -> F { + fn output(&self, z: &F) -> F { *z } } diff --git a/src/traits/mod.rs b/src/traits/mod.rs index d8f79cf..3866bff 100644 --- a/src/traits/mod.rs +++ b/src/traits/mod.rs @@ -39,10 +39,10 @@ pub trait Group: /// A type that represents a hash function that consumes elements /// from the base field and squeezes out elements of the scalar field - type HashFunc: HashFuncTrait; + type RO: ROTrait; - /// An alternate implementation of Self::HashFunc in the circuit model - type HashFuncCircuit: HashFuncCircuitTrait; + /// An alternate implementation of Self::RO in the circuit model + type ROCircuit: ROCircuitTrait; /// A method to compute a multiexponentation fn vartime_multiscalar_mul( @@ -87,7 +87,7 @@ pub trait AppendToTranscriptTrait { /// A helper trait to absorb different objects in RO pub trait AbsorbInROTrait { /// Absorbs the value in the provided RO - fn absorb_in_ro(&self, ro: &mut G::HashFunc); + fn absorb_in_ro(&self, ro: &mut G::RO); } /// A helper trait to generate challenges using a transcript object @@ -97,9 +97,9 @@ pub trait ChallengeTrait { } /// A helper trait that defines the behavior of a hash function that we use as an RO -pub trait HashFuncTrait { +pub trait ROTrait { /// A type representing constants/parameters associated with the hash function - type Constants: HashFuncConstantsTrait + Clone + Send + Sync; + type Constants: ROConstantsTrait + Clone + Send + Sync; /// Initializes the hash function fn new(constants: Self::Constants) -> Self; @@ -107,17 +107,14 @@ pub trait HashFuncTrait { /// Adds a scalar to the internal state fn absorb(&mut self, e: Base); - /// Returns a random challenge by hashing the internal state - fn get_challenge(&self) -> Scalar; - - /// Returns a hash of the internal state - fn get_hash(&self) -> Scalar; + /// Returns a challenge of `num_bits` by hashing the internal state + fn squeeze(&self, num_bits: usize) -> Scalar; } /// A helper trait that defines the behavior of a hash function that we use as an RO in the circuit model -pub trait HashFuncCircuitTrait { +pub trait ROCircuitTrait { /// A type representing constants/parameters associated with the hash function - type Constants: HashFuncConstantsTrait + Clone + Send + Sync; + type Constants: ROConstantsTrait + Clone + Send + Sync; /// Initializes the hash function fn new(constants: Self::Constants) -> Self; @@ -125,30 +122,25 @@ pub trait HashFuncCircuitTrait { /// Adds a scalar to the internal state fn absorb(&mut self, e: AllocatedNum); - /// Returns a random challenge by hashing the internal state - fn get_challenge(&mut self, cs: CS) -> Result, SynthesisError> - where - CS: ConstraintSystem; - - /// Returns a hash of the internal state - fn get_hash(&mut self, cs: CS) -> Result, SynthesisError> + /// Returns a challenge of `num_bits` by hashing the internal state + fn squeeze(&mut self, cs: CS, num_bits: usize) -> Result, SynthesisError> where CS: ConstraintSystem; } /// A helper trait that defines the constants associated with a hash function -pub trait HashFuncConstantsTrait { +pub trait ROConstantsTrait { /// produces constants/parameters associated with the hash function fn new() -> Self; } -/// An alias for constants associated with G::HashFunc -pub type HashFuncConstants = - <::HashFunc as HashFuncTrait<::Base, ::Scalar>>::Constants; +/// An alias for constants associated with G::RO +pub type ROConstants = + <::RO as ROTrait<::Base, ::Scalar>>::Constants; -/// An alias for constants associated with G::HashFuncCircuit -pub type HashFuncConstantsCircuit = - <::HashFuncCircuit as HashFuncCircuitTrait<::Base>>::Constants; +/// An alias for constants associated with G::ROCircuit +pub type ROConstantsCircuit = + <::ROCircuit as ROCircuitTrait<::Base>>::Constants; /// A helper trait for types with a group operation. pub trait GroupOps: