mirror of
https://github.com/arnaucube/Nova.git
synced 2026-01-11 08:31:29 +01:00
integrate with neptune's sponge (#105)
* integrate with neptune's sponge * fix clippy warning * add checks to ensure at most one squeeze * add checks to ensure at most one squeeze
This commit is contained in:
@@ -24,7 +24,7 @@ itertools = "0.9.0"
|
|||||||
subtle = "2.4"
|
subtle = "2.4"
|
||||||
pasta_curves = { version = "0.4.0", features = ["repr-c"] }
|
pasta_curves = { version = "0.4.0", features = ["repr-c"] }
|
||||||
pasta-msm = "0.1.3"
|
pasta-msm = "0.1.3"
|
||||||
neptune = { version = "7.1", default-features = false }
|
neptune = { version = "7.2.0", default-features = false }
|
||||||
generic-array = "0.14.4"
|
generic-array = "0.14.4"
|
||||||
bellperson-nonnative = { version = "0.3.1", default-features = false, features = ["wasm"] }
|
bellperson-nonnative = { version = "0.3.1", default-features = false, features = ["wasm"] }
|
||||||
num-bigint = { version = "0.4", features = ["serde", "rand"] }
|
num-bigint = { version = "0.4", features = ["serde", "rand"] }
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
commitments::Commitment,
|
commitments::Commitment,
|
||||||
constants::NUM_HASH_BITS,
|
constants::{NUM_FE_FOR_HASH, NUM_HASH_BITS},
|
||||||
gadgets::{
|
gadgets::{
|
||||||
ecc::AllocatedPoint,
|
ecc::AllocatedPoint,
|
||||||
r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance},
|
r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance},
|
||||||
@@ -222,7 +222,7 @@ where
|
|||||||
T: AllocatedPoint<G::Base>,
|
T: AllocatedPoint<G::Base>,
|
||||||
) -> Result<(AllocatedRelaxedR1CSInstance<G>, AllocatedBit), SynthesisError> {
|
) -> Result<(AllocatedRelaxedR1CSInstance<G>, AllocatedBit), SynthesisError> {
|
||||||
// Check that u.x[0] = Hash(params, U, i, z0, zi)
|
// Check that u.x[0] = Hash(params, U, i, z0, zi)
|
||||||
let mut ro = G::ROCircuit::new(self.ro_consts.clone());
|
let mut ro = G::ROCircuit::new(self.ro_consts.clone(), NUM_FE_FOR_HASH);
|
||||||
ro.absorb(params.clone());
|
ro.absorb(params.clone());
|
||||||
ro.absorb(i);
|
ro.absorb(i);
|
||||||
ro.absorb(z_0);
|
ro.absorb(z_0);
|
||||||
@@ -329,7 +329,7 @@ where
|
|||||||
.synthesize(&mut cs.namespace(|| "F"), z_input)?;
|
.synthesize(&mut cs.namespace(|| "F"), z_input)?;
|
||||||
|
|
||||||
// Compute the new hash H(params, Unew, i+1, z0, z_{i+1})
|
// Compute the new hash H(params, Unew, i+1, z0, z_{i+1})
|
||||||
let mut ro = G::ROCircuit::new(self.ro_consts);
|
let mut ro = G::ROCircuit::new(self.ro_consts, NUM_FE_FOR_HASH);
|
||||||
ro.absorb(params);
|
ro.absorb(params);
|
||||||
ro.absorb(i_new.clone());
|
ro.absorb(i_new.clone());
|
||||||
ro.absorb(z_0);
|
ro.absorb(z_0);
|
||||||
@@ -361,7 +361,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_verification_circuit() {
|
fn test_recursive_circuit() {
|
||||||
// In the following we use 1 to refer to the primary, and 2 to refer to the secondary circuit
|
// 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 params1 = NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, true);
|
||||||
let params2 = NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, false);
|
let params2 = NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, false);
|
||||||
@@ -379,7 +379,7 @@ mod tests {
|
|||||||
let mut cs: ShapeCS<G1> = ShapeCS::new();
|
let mut cs: ShapeCS<G1> = ShapeCS::new();
|
||||||
let _ = circuit1.synthesize(&mut cs);
|
let _ = circuit1.synthesize(&mut cs);
|
||||||
let (shape1, gens1) = (cs.r1cs_shape(), cs.r1cs_gens());
|
let (shape1, gens1) = (cs.r1cs_shape(), cs.r1cs_gens());
|
||||||
assert_eq!(cs.num_constraints(), 20122);
|
assert_eq!(cs.num_constraints(), 19739);
|
||||||
|
|
||||||
// Initialize the shape and gens for the secondary
|
// Initialize the shape and gens for the secondary
|
||||||
let circuit2: NovaAugmentedCircuit<G1, TrivialTestCircuit<<G1 as Group>::Base>> =
|
let circuit2: NovaAugmentedCircuit<G1, TrivialTestCircuit<<G1 as Group>::Base>> =
|
||||||
@@ -392,7 +392,7 @@ mod tests {
|
|||||||
let mut cs: ShapeCS<G2> = ShapeCS::new();
|
let mut cs: ShapeCS<G2> = ShapeCS::new();
|
||||||
let _ = circuit2.synthesize(&mut cs);
|
let _ = circuit2.synthesize(&mut cs);
|
||||||
let (shape2, gens2) = (cs.r1cs_shape(), cs.r1cs_gens());
|
let (shape2, gens2) = (cs.r1cs_shape(), cs.r1cs_gens());
|
||||||
assert_eq!(cs.num_constraints(), 20654);
|
assert_eq!(cs.num_constraints(), 20271);
|
||||||
|
|
||||||
// Execute the base case for the primary
|
// Execute the base case for the primary
|
||||||
let zero1 = <<G2 as Group>::Base as Field>::zero();
|
let zero1 = <<G2 as Group>::Base as Field>::zero();
|
||||||
|
|||||||
@@ -2,3 +2,5 @@ pub(crate) const NUM_CHALLENGE_BITS: usize = 128;
|
|||||||
pub(crate) const NUM_HASH_BITS: usize = 250;
|
pub(crate) const NUM_HASH_BITS: usize = 250;
|
||||||
pub(crate) const BN_LIMB_WIDTH: usize = 64;
|
pub(crate) const BN_LIMB_WIDTH: usize = 64;
|
||||||
pub(crate) const BN_N_LIMBS: usize = 4;
|
pub(crate) const BN_N_LIMBS: usize = 4;
|
||||||
|
pub(crate) const NUM_FE_FOR_HASH: usize = 19;
|
||||||
|
pub(crate) const NUM_FE_FOR_RO: usize = 24;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! This module implements various gadgets necessary for folding R1CS types.
|
//! This module implements various gadgets necessary for folding R1CS types.
|
||||||
use crate::{
|
use crate::{
|
||||||
constants::NUM_CHALLENGE_BITS,
|
constants::{NUM_CHALLENGE_BITS, NUM_FE_FOR_RO},
|
||||||
gadgets::{
|
gadgets::{
|
||||||
ecc::AllocatedPoint,
|
ecc::AllocatedPoint,
|
||||||
utils::{
|
utils::{
|
||||||
@@ -268,7 +268,7 @@ where
|
|||||||
n_limbs: usize,
|
n_limbs: usize,
|
||||||
) -> Result<AllocatedRelaxedR1CSInstance<G>, SynthesisError> {
|
) -> Result<AllocatedRelaxedR1CSInstance<G>, SynthesisError> {
|
||||||
// Compute r:
|
// Compute r:
|
||||||
let mut ro = G::ROCircuit::new(ro_consts);
|
let mut ro = G::ROCircuit::new(ro_consts, NUM_FE_FOR_RO);
|
||||||
ro.absorb(params);
|
ro.absorb(params);
|
||||||
self.absorb_in_ro(cs.namespace(|| "absorb running instance"), &mut ro)?;
|
self.absorb_in_ro(cs.namespace(|| "absorb running instance"), &mut ro)?;
|
||||||
u.absorb_in_ro(&mut ro);
|
u.absorb_in_ro(&mut ro);
|
||||||
|
|||||||
11
src/lib.rs
11
src/lib.rs
@@ -26,8 +26,7 @@ use crate::bellperson::{
|
|||||||
};
|
};
|
||||||
use ::bellperson::{Circuit, ConstraintSystem};
|
use ::bellperson::{Circuit, ConstraintSystem};
|
||||||
use circuit::{NovaAugmentedCircuit, NovaAugmentedCircuitInputs, NovaAugmentedCircuitParams};
|
use circuit::{NovaAugmentedCircuit, NovaAugmentedCircuitInputs, NovaAugmentedCircuitParams};
|
||||||
use constants::NUM_HASH_BITS;
|
use constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_FE_FOR_HASH, NUM_HASH_BITS};
|
||||||
use constants::{BN_LIMB_WIDTH, BN_N_LIMBS};
|
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use errors::NovaError;
|
use errors::NovaError;
|
||||||
use ff::Field;
|
use ff::Field;
|
||||||
@@ -392,14 +391,14 @@ where
|
|||||||
|
|
||||||
// check if the output hashes in R1CS instances point to the right running instances
|
// check if the output hashes in R1CS instances point to the right running instances
|
||||||
let (hash_primary, hash_secondary) = {
|
let (hash_primary, hash_secondary) = {
|
||||||
let mut hasher = <G2 as Group>::RO::new(pp.ro_consts_secondary.clone());
|
let mut hasher = <G2 as Group>::RO::new(pp.ro_consts_secondary.clone(), NUM_FE_FOR_HASH);
|
||||||
hasher.absorb(scalar_as_base::<G2>(pp.r1cs_shape_secondary.get_digest()));
|
hasher.absorb(scalar_as_base::<G2>(pp.r1cs_shape_secondary.get_digest()));
|
||||||
hasher.absorb(G1::Scalar::from(num_steps as u64));
|
hasher.absorb(G1::Scalar::from(num_steps as u64));
|
||||||
hasher.absorb(z0_primary);
|
hasher.absorb(z0_primary);
|
||||||
hasher.absorb(self.zi_primary);
|
hasher.absorb(self.zi_primary);
|
||||||
self.r_U_secondary.absorb_in_ro(&mut hasher);
|
self.r_U_secondary.absorb_in_ro(&mut hasher);
|
||||||
|
|
||||||
let mut hasher2 = <G1 as Group>::RO::new(pp.ro_consts_primary.clone());
|
let mut hasher2 = <G1 as Group>::RO::new(pp.ro_consts_primary.clone(), NUM_FE_FOR_HASH);
|
||||||
hasher2.absorb(scalar_as_base::<G1>(pp.r1cs_shape_primary.get_digest()));
|
hasher2.absorb(scalar_as_base::<G1>(pp.r1cs_shape_primary.get_digest()));
|
||||||
hasher2.absorb(G2::Scalar::from(num_steps as u64));
|
hasher2.absorb(G2::Scalar::from(num_steps as u64));
|
||||||
hasher2.absorb(z0_secondary);
|
hasher2.absorb(z0_secondary);
|
||||||
@@ -607,14 +606,14 @@ where
|
|||||||
|
|
||||||
// check if the output hashes in R1CS instances point to the right running instances
|
// check if the output hashes in R1CS instances point to the right running instances
|
||||||
let (hash_primary, hash_secondary) = {
|
let (hash_primary, hash_secondary) = {
|
||||||
let mut hasher = <G2 as Group>::RO::new(pp.ro_consts_secondary.clone());
|
let mut hasher = <G2 as Group>::RO::new(pp.ro_consts_secondary.clone(), NUM_FE_FOR_HASH);
|
||||||
hasher.absorb(scalar_as_base::<G2>(pp.r1cs_shape_secondary.get_digest()));
|
hasher.absorb(scalar_as_base::<G2>(pp.r1cs_shape_secondary.get_digest()));
|
||||||
hasher.absorb(G1::Scalar::from(num_steps as u64));
|
hasher.absorb(G1::Scalar::from(num_steps as u64));
|
||||||
hasher.absorb(z0_primary);
|
hasher.absorb(z0_primary);
|
||||||
hasher.absorb(self.zn_primary);
|
hasher.absorb(self.zn_primary);
|
||||||
self.r_U_secondary.absorb_in_ro(&mut hasher);
|
self.r_U_secondary.absorb_in_ro(&mut hasher);
|
||||||
|
|
||||||
let mut hasher2 = <G1 as Group>::RO::new(pp.ro_consts_primary.clone());
|
let mut hasher2 = <G1 as Group>::RO::new(pp.ro_consts_primary.clone(), NUM_FE_FOR_HASH);
|
||||||
hasher2.absorb(scalar_as_base::<G1>(pp.r1cs_shape_primary.get_digest()));
|
hasher2.absorb(scalar_as_base::<G1>(pp.r1cs_shape_primary.get_digest()));
|
||||||
hasher2.absorb(G2::Scalar::from(num_steps as u64));
|
hasher2.absorb(G2::Scalar::from(num_steps as u64));
|
||||||
hasher2.absorb(z0_secondary);
|
hasher2.absorb(z0_secondary);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
commitments::CompressedCommitment,
|
commitments::CompressedCommitment,
|
||||||
constants::NUM_CHALLENGE_BITS,
|
constants::{NUM_CHALLENGE_BITS, NUM_FE_FOR_RO},
|
||||||
errors::NovaError,
|
errors::NovaError,
|
||||||
r1cs::{R1CSGens, R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness},
|
r1cs::{R1CSGens, R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness},
|
||||||
traits::{AbsorbInROTrait, Group, ROTrait},
|
traits::{AbsorbInROTrait, Group, ROTrait},
|
||||||
@@ -39,7 +39,7 @@ impl<G: Group> NIFS<G> {
|
|||||||
W2: &R1CSWitness<G>,
|
W2: &R1CSWitness<G>,
|
||||||
) -> Result<(NIFS<G>, (RelaxedR1CSInstance<G>, RelaxedR1CSWitness<G>)), NovaError> {
|
) -> Result<(NIFS<G>, (RelaxedR1CSInstance<G>, RelaxedR1CSWitness<G>)), NovaError> {
|
||||||
// initialize a new RO
|
// initialize a new RO
|
||||||
let mut ro = G::RO::new(ro_consts.clone());
|
let mut ro = G::RO::new(ro_consts.clone(), NUM_FE_FOR_RO);
|
||||||
|
|
||||||
// append S to the transcript
|
// append S to the transcript
|
||||||
S.absorb_in_ro(&mut ro);
|
S.absorb_in_ro(&mut ro);
|
||||||
@@ -86,7 +86,7 @@ impl<G: Group> NIFS<G> {
|
|||||||
U2: &R1CSInstance<G>,
|
U2: &R1CSInstance<G>,
|
||||||
) -> Result<RelaxedR1CSInstance<G>, NovaError> {
|
) -> Result<RelaxedR1CSInstance<G>, NovaError> {
|
||||||
// initialize a new RO
|
// initialize a new RO
|
||||||
let mut ro = G::RO::new(ro_consts.clone());
|
let mut ro = G::RO::new(ro_consts.clone(), NUM_FE_FOR_RO);
|
||||||
|
|
||||||
// append S to the transcript
|
// append S to the transcript
|
||||||
S.absorb_in_ro(&mut ro);
|
S.absorb_in_ro(&mut ro);
|
||||||
|
|||||||
132
src/poseidon.rs
132
src/poseidon.rs
@@ -9,36 +9,30 @@ use bellperson::{
|
|||||||
};
|
};
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use ff::{PrimeField, PrimeFieldBits};
|
use ff::{PrimeField, PrimeFieldBits};
|
||||||
use generic_array::typenum::{U19, U24};
|
use generic_array::typenum::U24;
|
||||||
use neptune::{
|
use neptune::{
|
||||||
circuit::poseidon_hash,
|
circuit2::Elt,
|
||||||
poseidon::{Poseidon, PoseidonConstants},
|
poseidon::PoseidonConstants,
|
||||||
|
sponge::{
|
||||||
|
api::{IOPattern, SpongeAPI, SpongeOp},
|
||||||
|
circuit::SpongeCircuit,
|
||||||
|
vanilla::{Mode::Simplex, Sponge, SpongeTrait},
|
||||||
|
},
|
||||||
Strength,
|
Strength,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// All Poseidon Constants that are used in Nova
|
/// All Poseidon Constants that are used in Nova
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct PoseidonConstantsCircuit<Scalar>
|
pub struct PoseidonConstantsCircuit<Scalar: PrimeField>(PoseidonConstants<Scalar, U24>);
|
||||||
where
|
|
||||||
Scalar: PrimeField,
|
|
||||||
{
|
|
||||||
constants19: PoseidonConstants<Scalar, U19>,
|
|
||||||
constants24: PoseidonConstants<Scalar, U24>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Scalar> ROConstantsTrait<Scalar> for PoseidonConstantsCircuit<Scalar>
|
impl<Scalar> ROConstantsTrait<Scalar> for PoseidonConstantsCircuit<Scalar>
|
||||||
where
|
where
|
||||||
Scalar: PrimeField + PrimeFieldBits,
|
Scalar: PrimeField + PrimeFieldBits,
|
||||||
{
|
{
|
||||||
/// Generate Poseidon constants for the arities that Nova uses
|
/// Generate Poseidon constants
|
||||||
#[allow(clippy::new_without_default)]
|
#[allow(clippy::new_without_default)]
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
let constants19 = PoseidonConstants::<Scalar, U19>::new_with_strength(Strength::Standard);
|
Self(Sponge::<Scalar, U24>::api_constants(Strength::Standard))
|
||||||
let constants24 = PoseidonConstants::<Scalar, U24>::new_with_strength(Strength::Standard);
|
|
||||||
Self {
|
|
||||||
constants19,
|
|
||||||
constants24,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,8 +44,9 @@ where
|
|||||||
{
|
{
|
||||||
// Internal State
|
// Internal State
|
||||||
state: Vec<Base>,
|
state: Vec<Base>,
|
||||||
// Constants for Poseidon
|
|
||||||
constants: PoseidonConstantsCircuit<Base>,
|
constants: PoseidonConstantsCircuit<Base>,
|
||||||
|
num_absorbs: usize,
|
||||||
|
squeezed: bool,
|
||||||
_p: PhantomData<Scalar>,
|
_p: PhantomData<Scalar>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,38 +57,43 @@ where
|
|||||||
{
|
{
|
||||||
type Constants = PoseidonConstantsCircuit<Base>;
|
type Constants = PoseidonConstantsCircuit<Base>;
|
||||||
|
|
||||||
fn new(constants: PoseidonConstantsCircuit<Base>) -> Self {
|
fn new(constants: PoseidonConstantsCircuit<Base>, num_absorbs: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: Vec::new(),
|
state: Vec::new(),
|
||||||
constants,
|
constants,
|
||||||
|
num_absorbs,
|
||||||
|
squeezed: false,
|
||||||
_p: PhantomData::default(),
|
_p: PhantomData::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Absorb a new number into the state of the oracle
|
/// Absorb a new number into the state of the oracle
|
||||||
fn absorb(&mut self, e: Base) {
|
fn absorb(&mut self, e: Base) {
|
||||||
|
assert!(!self.squeezed, "Cannot absorb after squeezing");
|
||||||
self.state.push(e);
|
self.state.push(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute a challenge by hashing the current state
|
/// Compute a challenge by hashing the current state
|
||||||
fn squeeze(&self, num_bits: usize) -> Scalar {
|
fn squeeze(&mut self, num_bits: usize) -> Scalar {
|
||||||
let hash = match self.state.len() {
|
// check if we have squeezed already
|
||||||
19 => {
|
assert!(!self.squeezed, "Cannot squeeze again after squeezing");
|
||||||
Poseidon::<Base, U19>::new_with_preimage(&self.state, &self.constants.constants19).hash()
|
self.squeezed = true;
|
||||||
}
|
|
||||||
24 => {
|
let mut sponge = Sponge::new_with_constants(&self.constants.0, Simplex);
|
||||||
Poseidon::<Base, U24>::new_with_preimage(&self.state, &self.constants.constants24).hash()
|
let acc = &mut ();
|
||||||
}
|
let parameter = IOPattern(vec![
|
||||||
_ => {
|
SpongeOp::Absorb(self.num_absorbs as u32),
|
||||||
panic!(
|
SpongeOp::Squeeze(1u32),
|
||||||
"Number of elements in the RO state does not match any of the arities used in Nova: {:?}",
|
]);
|
||||||
self.state.len()
|
|
||||||
);
|
sponge.start(parameter, Some(1u32), acc);
|
||||||
}
|
assert_eq!(self.num_absorbs, self.state.len());
|
||||||
};
|
SpongeAPI::absorb(&mut sponge, self.num_absorbs as u32, &self.state, acc);
|
||||||
|
let hash = SpongeAPI::squeeze(&mut sponge, 1, acc);
|
||||||
|
sponge.finish(acc).unwrap();
|
||||||
|
|
||||||
// Only return `num_bits`
|
// Only return `num_bits`
|
||||||
let bits = hash.to_le_bits();
|
let bits = hash[0].to_le_bits();
|
||||||
let mut res = Scalar::zero();
|
let mut res = Scalar::zero();
|
||||||
let mut coeff = Scalar::one();
|
let mut coeff = Scalar::one();
|
||||||
for bit in bits[0..num_bits].into_iter() {
|
for bit in bits[0..num_bits].into_iter() {
|
||||||
@@ -114,6 +114,8 @@ where
|
|||||||
// Internal state
|
// Internal state
|
||||||
state: Vec<AllocatedNum<Scalar>>,
|
state: Vec<AllocatedNum<Scalar>>,
|
||||||
constants: PoseidonConstantsCircuit<Scalar>,
|
constants: PoseidonConstantsCircuit<Scalar>,
|
||||||
|
num_absorbs: usize,
|
||||||
|
squeezed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Scalar> ROCircuitTrait<Scalar> for PoseidonROCircuit<Scalar>
|
impl<Scalar> ROCircuitTrait<Scalar> for PoseidonROCircuit<Scalar>
|
||||||
@@ -123,15 +125,18 @@ where
|
|||||||
type Constants = PoseidonConstantsCircuit<Scalar>;
|
type Constants = PoseidonConstantsCircuit<Scalar>;
|
||||||
|
|
||||||
/// Initialize the internal state and set the poseidon constants
|
/// Initialize the internal state and set the poseidon constants
|
||||||
fn new(constants: PoseidonConstantsCircuit<Scalar>) -> Self {
|
fn new(constants: PoseidonConstantsCircuit<Scalar>, num_absorbs: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: Vec::new(),
|
state: Vec::new(),
|
||||||
constants,
|
constants,
|
||||||
|
num_absorbs,
|
||||||
|
squeezed: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Absorb a new number into the state of the oracle
|
/// Absorb a new number into the state of the oracle
|
||||||
fn absorb(&mut self, e: AllocatedNum<Scalar>) {
|
fn absorb(&mut self, e: AllocatedNum<Scalar>) {
|
||||||
|
assert!(!self.squeezed, "Cannot absorb after squeezing");
|
||||||
self.state.push(e);
|
self.state.push(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,29 +149,41 @@ where
|
|||||||
where
|
where
|
||||||
CS: ConstraintSystem<Scalar>,
|
CS: ConstraintSystem<Scalar>,
|
||||||
{
|
{
|
||||||
let hash = match self.state.len() {
|
// check if we have squeezed already
|
||||||
19 => poseidon_hash(
|
assert!(!self.squeezed, "Cannot squeeze again after squeezing");
|
||||||
cs.namespace(|| "Poseidon hash"),
|
self.squeezed = true;
|
||||||
self.state.clone(),
|
let parameter = IOPattern(vec![
|
||||||
&self.constants.constants19,
|
SpongeOp::Absorb(self.num_absorbs as u32),
|
||||||
)?,
|
SpongeOp::Squeeze(1u32),
|
||||||
24 => poseidon_hash(
|
]);
|
||||||
cs.namespace(|| "Posideon hash"),
|
let mut ns = cs.namespace(|| "ns");
|
||||||
self.state.clone(),
|
|
||||||
&self.constants.constants24,
|
let hash = {
|
||||||
)?,
|
let mut sponge = SpongeCircuit::new_with_constants(&self.constants.0, Simplex);
|
||||||
_ => {
|
let acc = &mut ns;
|
||||||
panic!(
|
assert_eq!(self.num_absorbs, self.state.len());
|
||||||
"Number of elements in the RO state does not match any of the arities used in Nova: {}",
|
|
||||||
self.state.len()
|
sponge.start(parameter, Some(1u32), acc);
|
||||||
)
|
neptune::sponge::api::SpongeAPI::absorb(
|
||||||
}
|
&mut sponge,
|
||||||
|
self.num_absorbs as u32,
|
||||||
|
&(0..self.state.len())
|
||||||
|
.map(|i| Elt::Allocated(self.state[i].clone()))
|
||||||
|
.collect::<Vec<Elt<Scalar>>>(),
|
||||||
|
acc,
|
||||||
|
);
|
||||||
|
|
||||||
|
let output = neptune::sponge::api::SpongeAPI::squeeze(&mut sponge, 1, acc);
|
||||||
|
sponge.finish(acc).unwrap();
|
||||||
|
output
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let hash = Elt::ensure_allocated(&hash[0], &mut ns.namespace(|| "ensure allocated"), true)?;
|
||||||
|
|
||||||
// return the hash as a vector of bits, truncated
|
// return the hash as a vector of bits, truncated
|
||||||
Ok(
|
Ok(
|
||||||
hash
|
hash
|
||||||
.to_bits_le_strict(cs.namespace(|| "poseidon hash to boolean"))?
|
.to_bits_le_strict(ns.namespace(|| "poseidon hash to boolean"))?
|
||||||
.iter()
|
.iter()
|
||||||
.map(|boolean| match boolean {
|
.map(|boolean| match boolean {
|
||||||
Boolean::Is(ref x) => x.clone(),
|
Boolean::Is(ref x) => x.clone(),
|
||||||
@@ -196,10 +213,11 @@ mod tests {
|
|||||||
// Check that the number computed inside the circuit is equal to the number computed outside the circuit
|
// Check that the number computed inside the circuit is equal to the number computed outside the circuit
|
||||||
let mut csprng: OsRng = OsRng;
|
let mut csprng: OsRng = OsRng;
|
||||||
let constants = PoseidonConstantsCircuit::new();
|
let constants = PoseidonConstantsCircuit::new();
|
||||||
let mut ro: PoseidonRO<S, B> = PoseidonRO::new(constants.clone());
|
let num_absorbs = 32;
|
||||||
let mut ro_gadget: PoseidonROCircuit<S> = PoseidonROCircuit::new(constants);
|
let mut ro: PoseidonRO<S, B> = PoseidonRO::new(constants.clone(), num_absorbs);
|
||||||
|
let mut ro_gadget: PoseidonROCircuit<S> = PoseidonROCircuit::new(constants, num_absorbs);
|
||||||
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
|
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
|
||||||
for i in 0..19 {
|
for i in 0..num_absorbs {
|
||||||
let num = S::random(&mut csprng);
|
let num = S::random(&mut csprng);
|
||||||
ro.absorb(num);
|
ro.absorb(num);
|
||||||
let num_gadget =
|
let num_gadget =
|
||||||
|
|||||||
@@ -108,13 +108,13 @@ pub trait ROTrait<Base, Scalar> {
|
|||||||
type Constants: ROConstantsTrait<Base> + Clone + Send + Sync;
|
type Constants: ROConstantsTrait<Base> + Clone + Send + Sync;
|
||||||
|
|
||||||
/// Initializes the hash function
|
/// Initializes the hash function
|
||||||
fn new(constants: Self::Constants) -> Self;
|
fn new(constants: Self::Constants, num_absorbs: usize) -> Self;
|
||||||
|
|
||||||
/// Adds a scalar to the internal state
|
/// Adds a scalar to the internal state
|
||||||
fn absorb(&mut self, e: Base);
|
fn absorb(&mut self, e: Base);
|
||||||
|
|
||||||
/// Returns a challenge of `num_bits` by hashing the internal state
|
/// Returns a challenge of `num_bits` by hashing the internal state
|
||||||
fn squeeze(&self, num_bits: usize) -> Scalar;
|
fn squeeze(&mut 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
|
/// A helper trait that defines the behavior of a hash function that we use as an RO in the circuit model
|
||||||
@@ -123,7 +123,7 @@ pub trait ROCircuitTrait<Base: PrimeField> {
|
|||||||
type Constants: ROConstantsTrait<Base> + Clone + Send + Sync;
|
type Constants: ROConstantsTrait<Base> + Clone + Send + Sync;
|
||||||
|
|
||||||
/// Initializes the hash function
|
/// Initializes the hash function
|
||||||
fn new(constants: Self::Constants) -> Self;
|
fn new(constants: Self::Constants, num_absorbs: usize) -> Self;
|
||||||
|
|
||||||
/// Adds a scalar to the internal state
|
/// Adds a scalar to the internal state
|
||||||
fn absorb(&mut self, e: AllocatedNum<Base>);
|
fn absorb(&mut self, e: AllocatedNum<Base>);
|
||||||
|
|||||||
Reference in New Issue
Block a user