mirror of
https://github.com/arnaucube/Nova.git
synced 2026-01-12 00:51:28 +01:00
Simplifications in Nova's RO (#98)
* rename methods for better clarity * rename * Bump version
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nova-snark"
|
name = "nova-snark"
|
||||||
version = "0.7.1"
|
version = "0.7.2"
|
||||||
authors = ["Srinath Setty <srinath@microsoft.com>"]
|
authors = ["Srinath Setty <srinath@microsoft.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Recursive zkSNARKs without trusted setup"
|
description = "Recursive zkSNARKs without trusted setup"
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ where
|
|||||||
Ok(y)
|
Ok(y)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute(&self, z: &F) -> F {
|
fn output(&self, z: &F) -> F {
|
||||||
let mut x = *z;
|
let mut x = *z;
|
||||||
let mut y = x;
|
let mut y = x;
|
||||||
for _i in 0..self.num_cons {
|
for _i in 0..self.num_cons {
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ where
|
|||||||
Ok(y)
|
Ok(y)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute(&self, z: &F) -> F {
|
fn output(&self, z: &F) -> F {
|
||||||
let mut x = *z;
|
let mut x = *z;
|
||||||
let mut y = x;
|
let mut y = x;
|
||||||
for _i in 0..self.num_cons {
|
for _i in 0..self.num_cons {
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ where
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute(&self, z: &F) -> F {
|
fn output(&self, z: &F) -> F {
|
||||||
let z_hash = Poseidon::<F, U8>::new_with_preimage(
|
let z_hash = Poseidon::<F, U8>::new_with_preimage(
|
||||||
&[
|
&[
|
||||||
self.z_r.x,
|
self.z_r.x,
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ where
|
|||||||
z_out
|
z_out
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute(&self, z: &F) -> F {
|
fn output(&self, z: &F) -> F {
|
||||||
// sanity check
|
// sanity check
|
||||||
let z_hash =
|
let z_hash =
|
||||||
Poseidon::<F, U2>::new_with_preimage(&[self.seq[0].x_i, self.seq[0].y_i], &self.pc).hash();
|
Poseidon::<F, U2>::new_with_preimage(&[self.seq[0].x_i, self.seq[0].y_i], &self.pc).hash();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
commitments::Commitment,
|
commitments::Commitment,
|
||||||
|
constants::NUM_HASH_BITS,
|
||||||
gadgets::{
|
gadgets::{
|
||||||
ecc::AllocatedPoint,
|
ecc::AllocatedPoint,
|
||||||
r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance},
|
r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance},
|
||||||
@@ -16,7 +17,7 @@ use super::{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
r1cs::{R1CSInstance, RelaxedR1CSInstance},
|
r1cs::{R1CSInstance, RelaxedR1CSInstance},
|
||||||
traits::{circuit::StepCircuit, Group, HashFuncCircuitTrait, HashFuncConstantsCircuit},
|
traits::{circuit::StepCircuit, Group, ROCircuitTrait, ROConstantsCircuit},
|
||||||
};
|
};
|
||||||
use bellperson::{
|
use bellperson::{
|
||||||
gadgets::{
|
gadgets::{
|
||||||
@@ -91,7 +92,7 @@ where
|
|||||||
SC: StepCircuit<G::Base>,
|
SC: StepCircuit<G::Base>,
|
||||||
{
|
{
|
||||||
params: NovaAugmentedCircuitParams,
|
params: NovaAugmentedCircuitParams,
|
||||||
ro_consts: HashFuncConstantsCircuit<G>,
|
ro_consts: ROConstantsCircuit<G>,
|
||||||
inputs: Option<NovaAugmentedCircuitInputs<G>>,
|
inputs: Option<NovaAugmentedCircuitInputs<G>>,
|
||||||
step_circuit: SC, // The function that is applied for each step
|
step_circuit: SC, // The function that is applied for each step
|
||||||
}
|
}
|
||||||
@@ -106,7 +107,7 @@ where
|
|||||||
params: NovaAugmentedCircuitParams,
|
params: NovaAugmentedCircuitParams,
|
||||||
inputs: Option<NovaAugmentedCircuitInputs<G>>,
|
inputs: Option<NovaAugmentedCircuitInputs<G>>,
|
||||||
step_circuit: SC,
|
step_circuit: SC,
|
||||||
ro_consts: HashFuncConstantsCircuit<G>,
|
ro_consts: ROConstantsCircuit<G>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
params,
|
params,
|
||||||
@@ -221,14 +222,14 @@ 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::HashFuncCircuit::new(self.ro_consts.clone());
|
let mut ro = G::ROCircuit::new(self.ro_consts.clone());
|
||||||
ro.absorb(params.clone());
|
ro.absorb(params.clone());
|
||||||
ro.absorb(i);
|
ro.absorb(i);
|
||||||
ro.absorb(z_0);
|
ro.absorb(z_0);
|
||||||
ro.absorb(z_i);
|
ro.absorb(z_i);
|
||||||
U.absorb_in_ro(cs.namespace(|| "absorb U"), &mut ro)?;
|
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 hash = le_bits_to_num(cs.namespace(|| "bits to hash"), hash_bits)?;
|
||||||
let check_pass = alloc_num_equals(
|
let check_pass = alloc_num_equals(
|
||||||
cs.namespace(|| "check consistency of u.X[0] with H(params, U, i, z0, zi)"),
|
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)?;
|
.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::HashFuncCircuit::new(self.ro_consts);
|
let mut ro = G::ROCircuit::new(self.ro_consts);
|
||||||
ro.absorb(params);
|
ro.absorb(params);
|
||||||
ro.absorb(i_new.clone());
|
ro.absorb(i_new.clone());
|
||||||
ro.absorb(z_0);
|
ro.absorb(z_0);
|
||||||
ro.absorb(z_next);
|
ro.absorb(z_next);
|
||||||
Unew.absorb_in_ro(cs.namespace(|| "absorb U_new"), &mut ro)?;
|
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)?;
|
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
|
// 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::{
|
use crate::{
|
||||||
bellperson::r1cs::{NovaShape, NovaWitness},
|
bellperson::r1cs::{NovaShape, NovaWitness},
|
||||||
poseidon::PoseidonConstantsCircuit,
|
poseidon::PoseidonConstantsCircuit,
|
||||||
traits::{circuit::TrivialTestCircuit, HashFuncConstantsTrait},
|
traits::{circuit::TrivialTestCircuit, ROConstantsTrait},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[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
|
// 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);
|
||||||
let ro_consts1: HashFuncConstantsCircuit<G2> = PoseidonConstantsCircuit::new();
|
let ro_consts1: ROConstantsCircuit<G2> = PoseidonConstantsCircuit::new();
|
||||||
let ro_consts2: HashFuncConstantsCircuit<G1> = PoseidonConstantsCircuit::new();
|
let ro_consts2: ROConstantsCircuit<G1> = PoseidonConstantsCircuit::new();
|
||||||
|
|
||||||
// Initialize the shape and gens for the primary
|
// Initialize the shape and gens for the primary
|
||||||
let circuit1: NovaAugmentedCircuit<G2, TrivialTestCircuit<<G2 as Group>::Base>> =
|
let circuit1: NovaAugmentedCircuit<G2, TrivialTestCircuit<<G2 as Group>::Base>> =
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::{
|
use super::{
|
||||||
errors::NovaError,
|
errors::NovaError,
|
||||||
traits::{AbsorbInROTrait, AppendToTranscriptTrait, CompressedGroup, Group, HashFuncTrait},
|
traits::{AbsorbInROTrait, AppendToTranscriptTrait, CompressedGroup, Group, ROTrait},
|
||||||
};
|
};
|
||||||
use core::{
|
use core::{
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
@@ -166,7 +166,7 @@ impl<G: Group> AppendToTranscriptTrait for Commitment<G> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<G: Group> AbsorbInROTrait<G> for Commitment<G> {
|
impl<G: Group> AbsorbInROTrait<G> for Commitment<G> {
|
||||||
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();
|
let (x, y, is_infinity) = self.comm.to_coordinates();
|
||||||
ro.absorb(x);
|
ro.absorb(x);
|
||||||
ro.absorb(y);
|
ro.absorb(y);
|
||||||
|
|||||||
@@ -1,5 +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,
|
||||||
gadgets::{
|
gadgets::{
|
||||||
ecc::AllocatedPoint,
|
ecc::AllocatedPoint,
|
||||||
utils::{
|
utils::{
|
||||||
@@ -8,7 +9,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
r1cs::{R1CSInstance, RelaxedR1CSInstance},
|
r1cs::{R1CSInstance, RelaxedR1CSInstance},
|
||||||
traits::{Group, HashFuncCircuitTrait, HashFuncConstantsCircuit},
|
traits::{Group, ROCircuitTrait, ROConstantsCircuit},
|
||||||
};
|
};
|
||||||
use bellperson::{
|
use bellperson::{
|
||||||
gadgets::{boolean::Boolean, num::AllocatedNum, Assignment},
|
gadgets::{boolean::Boolean, num::AllocatedNum, Assignment},
|
||||||
@@ -60,7 +61,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Absorb the provided instance in the RO
|
/// 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.x.clone());
|
||||||
ro.absorb(self.W.y.clone());
|
ro.absorb(self.W.y.clone());
|
||||||
ro.absorb(self.W.is_infinity.clone());
|
ro.absorb(self.W.is_infinity.clone());
|
||||||
@@ -207,7 +208,7 @@ where
|
|||||||
pub fn absorb_in_ro<CS: ConstraintSystem<<G as Group>::Base>>(
|
pub fn absorb_in_ro<CS: ConstraintSystem<<G as Group>::Base>>(
|
||||||
&self,
|
&self,
|
||||||
mut cs: CS,
|
mut cs: CS,
|
||||||
ro: &mut G::HashFuncCircuit,
|
ro: &mut G::ROCircuit,
|
||||||
) -> Result<(), SynthesisError> {
|
) -> Result<(), SynthesisError> {
|
||||||
ro.absorb(self.W.x.clone());
|
ro.absorb(self.W.x.clone());
|
||||||
ro.absorb(self.W.y.clone());
|
ro.absorb(self.W.y.clone());
|
||||||
@@ -262,19 +263,19 @@ where
|
|||||||
params: AllocatedNum<G::Base>, // hash of R1CSShape of F'
|
params: AllocatedNum<G::Base>, // hash of R1CSShape of F'
|
||||||
u: AllocatedR1CSInstance<G>,
|
u: AllocatedR1CSInstance<G>,
|
||||||
T: AllocatedPoint<G::Base>,
|
T: AllocatedPoint<G::Base>,
|
||||||
ro_consts: HashFuncConstantsCircuit<G>,
|
ro_consts: ROConstantsCircuit<G>,
|
||||||
limb_width: usize,
|
limb_width: usize,
|
||||||
n_limbs: usize,
|
n_limbs: usize,
|
||||||
) -> Result<AllocatedRelaxedR1CSInstance<G>, SynthesisError> {
|
) -> Result<AllocatedRelaxedR1CSInstance<G>, SynthesisError> {
|
||||||
// Compute r:
|
// Compute r:
|
||||||
let mut ro = G::HashFuncCircuit::new(ro_consts);
|
let mut ro = G::ROCircuit::new(ro_consts);
|
||||||
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);
|
||||||
ro.absorb(T.x.clone());
|
ro.absorb(T.x.clone());
|
||||||
ro.absorb(T.y.clone());
|
ro.absorb(T.y.clone());
|
||||||
ro.absorb(T.is_infinity.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())?;
|
let r = le_bits_to_num(cs.namespace(|| "r"), r_bits.clone())?;
|
||||||
|
|
||||||
// W_fold = self.W + r * u.W
|
// W_fold = self.W + r * u.W
|
||||||
|
|||||||
57
src/lib.rs
57
src/lib.rs
@@ -26,6 +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};
|
use constants::{BN_LIMB_WIDTH, BN_N_LIMBS};
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use errors::NovaError;
|
use errors::NovaError;
|
||||||
@@ -36,8 +37,8 @@ use r1cs::{
|
|||||||
R1CSGens, R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness,
|
R1CSGens, R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness,
|
||||||
};
|
};
|
||||||
use traits::{
|
use traits::{
|
||||||
circuit::StepCircuit, snark::RelaxedR1CSSNARKTrait, AbsorbInROTrait, Group, HashFuncConstants,
|
circuit::StepCircuit, snark::RelaxedR1CSSNARKTrait, AbsorbInROTrait, Group, ROConstants,
|
||||||
HashFuncConstantsCircuit, HashFuncConstantsTrait, HashFuncTrait,
|
ROConstantsCircuit, ROConstantsTrait, ROTrait,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A type that holds public parameters of Nova
|
/// A type that holds public parameters of Nova
|
||||||
@@ -48,13 +49,13 @@ where
|
|||||||
C1: StepCircuit<G1::Scalar>,
|
C1: StepCircuit<G1::Scalar>,
|
||||||
C2: StepCircuit<G2::Scalar>,
|
C2: StepCircuit<G2::Scalar>,
|
||||||
{
|
{
|
||||||
ro_consts_primary: HashFuncConstants<G1>,
|
ro_consts_primary: ROConstants<G1>,
|
||||||
ro_consts_circuit_primary: HashFuncConstantsCircuit<G2>,
|
ro_consts_circuit_primary: ROConstantsCircuit<G2>,
|
||||||
r1cs_gens_primary: R1CSGens<G1>,
|
r1cs_gens_primary: R1CSGens<G1>,
|
||||||
r1cs_shape_primary: R1CSShape<G1>,
|
r1cs_shape_primary: R1CSShape<G1>,
|
||||||
r1cs_shape_padded_primary: R1CSShape<G1>,
|
r1cs_shape_padded_primary: R1CSShape<G1>,
|
||||||
ro_consts_secondary: HashFuncConstants<G2>,
|
ro_consts_secondary: ROConstants<G2>,
|
||||||
ro_consts_circuit_secondary: HashFuncConstantsCircuit<G1>,
|
ro_consts_circuit_secondary: ROConstantsCircuit<G1>,
|
||||||
r1cs_gens_secondary: R1CSGens<G2>,
|
r1cs_gens_secondary: R1CSGens<G2>,
|
||||||
r1cs_shape_secondary: R1CSShape<G2>,
|
r1cs_shape_secondary: R1CSShape<G2>,
|
||||||
r1cs_shape_padded_secondary: R1CSShape<G2>,
|
r1cs_shape_padded_secondary: R1CSShape<G2>,
|
||||||
@@ -78,14 +79,12 @@ where
|
|||||||
let augmented_circuit_params_secondary =
|
let augmented_circuit_params_secondary =
|
||||||
NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, false);
|
NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, false);
|
||||||
|
|
||||||
let ro_consts_primary: HashFuncConstants<G1> = HashFuncConstants::<G1>::new();
|
let ro_consts_primary: ROConstants<G1> = ROConstants::<G1>::new();
|
||||||
let ro_consts_secondary: HashFuncConstants<G2> = HashFuncConstants::<G2>::new();
|
let ro_consts_secondary: ROConstants<G2> = ROConstants::<G2>::new();
|
||||||
|
|
||||||
// ro_consts_circuit_primart are parameterized by G2 because the type alias uses G2::Base = G1::Scalar
|
// ro_consts_circuit_primart are parameterized by G2 because the type alias uses G2::Base = G1::Scalar
|
||||||
let ro_consts_circuit_primary: HashFuncConstantsCircuit<G2> =
|
let ro_consts_circuit_primary: ROConstantsCircuit<G2> = ROConstantsCircuit::<G2>::new();
|
||||||
HashFuncConstantsCircuit::<G2>::new();
|
let ro_consts_circuit_secondary: ROConstantsCircuit<G1> = ROConstantsCircuit::<G1>::new();
|
||||||
let ro_consts_circuit_secondary: HashFuncConstantsCircuit<G1> =
|
|
||||||
HashFuncConstantsCircuit::<G1>::new();
|
|
||||||
|
|
||||||
// Initialize gens for the primary
|
// Initialize gens for the primary
|
||||||
let circuit_primary: NovaAugmentedCircuit<G2, C1> = NovaAugmentedCircuit::new(
|
let circuit_primary: NovaAugmentedCircuit<G2, C1> = NovaAugmentedCircuit::new(
|
||||||
@@ -245,8 +244,8 @@ where
|
|||||||
RelaxedR1CSInstance::<G2>::default(&pp.r1cs_gens_secondary, &pp.r1cs_shape_secondary);
|
RelaxedR1CSInstance::<G2>::default(&pp.r1cs_gens_secondary, &pp.r1cs_shape_secondary);
|
||||||
|
|
||||||
// Outputs of the two circuits thus far
|
// Outputs of the two circuits thus far
|
||||||
let zi_primary = c_primary.compute(&z0_primary);
|
let zi_primary = c_primary.output(&z0_primary);
|
||||||
let zi_secondary = c_secondary.compute(&z0_secondary);
|
let zi_secondary = c_secondary.output(&z0_secondary);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
r_W_primary,
|
r_W_primary,
|
||||||
@@ -334,8 +333,8 @@ where
|
|||||||
.map_err(|_e| NovaError::UnSat)?;
|
.map_err(|_e| NovaError::UnSat)?;
|
||||||
|
|
||||||
// update the running instances and witnesses
|
// update the running instances and witnesses
|
||||||
let zi_primary = c_primary.compute(&r_snark.zi_primary);
|
let zi_primary = c_primary.output(&r_snark.zi_primary);
|
||||||
let zi_secondary = c_secondary.compute(&r_snark.zi_secondary);
|
let zi_secondary = c_secondary.output(&r_snark.zi_secondary);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
r_W_primary,
|
r_W_primary,
|
||||||
@@ -385,21 +384,24 @@ 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>::HashFunc::new(pp.ro_consts_secondary.clone());
|
let mut hasher = <G2 as Group>::RO::new(pp.ro_consts_secondary.clone());
|
||||||
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>::HashFunc::new(pp.ro_consts_primary.clone());
|
let mut hasher2 = <G1 as Group>::RO::new(pp.ro_consts_primary.clone());
|
||||||
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);
|
||||||
hasher2.absorb(self.zi_secondary);
|
hasher2.absorb(self.zi_secondary);
|
||||||
self.r_U_primary.absorb_in_ro(&mut hasher2);
|
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::<G1>(self.l_u_primary.X[1])
|
if hash_primary != scalar_as_base::<G1>(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
|
// 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>::HashFunc::new(pp.ro_consts_secondary.clone());
|
let mut hasher = <G2 as Group>::RO::new(pp.ro_consts_secondary.clone());
|
||||||
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>::HashFunc::new(pp.ro_consts_primary.clone());
|
let mut hasher2 = <G1 as Group>::RO::new(pp.ro_consts_primary.clone());
|
||||||
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);
|
||||||
hasher2.absorb(self.zn_secondary);
|
hasher2.absorb(self.zn_secondary);
|
||||||
self.r_U_primary.absorb_in_ro(&mut hasher2);
|
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::<G1>(self.l_u_primary.X[1])
|
if hash_primary != scalar_as_base::<G1>(self.l_u_primary.X[1])
|
||||||
@@ -709,7 +714,7 @@ mod tests {
|
|||||||
Ok(y)
|
Ok(y)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute(&self, z: &F) -> F {
|
fn output(&self, z: &F) -> F {
|
||||||
*z * *z * *z + z + F::from(5u64)
|
*z * *z * *z + z + F::from(5u64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -816,7 +821,7 @@ mod tests {
|
|||||||
assert_eq!(zn_primary, <G1 as Group>::Scalar::one());
|
assert_eq!(zn_primary, <G1 as Group>::Scalar::one());
|
||||||
let mut zn_secondary_direct = <G2 as Group>::Scalar::zero();
|
let mut zn_secondary_direct = <G2 as Group>::Scalar::zero();
|
||||||
for _i in 0..num_steps {
|
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, zn_secondary_direct);
|
||||||
assert_eq!(zn_secondary, <G2 as Group>::Scalar::from(2460515u64));
|
assert_eq!(zn_secondary, <G2 as Group>::Scalar::from(2460515u64));
|
||||||
@@ -878,7 +883,7 @@ mod tests {
|
|||||||
assert_eq!(zn_primary, <G1 as Group>::Scalar::one());
|
assert_eq!(zn_primary, <G1 as Group>::Scalar::one());
|
||||||
let mut zn_secondary_direct = <G2 as Group>::Scalar::zero();
|
let mut zn_secondary_direct = <G2 as Group>::Scalar::zero();
|
||||||
for _i in 0..num_steps {
|
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, zn_secondary_direct);
|
||||||
assert_eq!(zn_secondary, <G2 as Group>::Scalar::from(2460515u64));
|
assert_eq!(zn_secondary, <G2 as Group>::Scalar::from(2460515u64));
|
||||||
@@ -960,7 +965,7 @@ mod tests {
|
|||||||
Ok(y)
|
Ok(y)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute(&self, z: &F) -> F {
|
fn output(&self, z: &F) -> F {
|
||||||
// sanity check
|
// sanity check
|
||||||
let x = *z;
|
let x = *z;
|
||||||
let y_pow_5 = {
|
let y_pow_5 = {
|
||||||
|
|||||||
46
src/nifs.rs
46
src/nifs.rs
@@ -2,12 +2,13 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
use super::commitments::CompressedCommitment;
|
use super::{
|
||||||
use super::errors::NovaError;
|
commitments::CompressedCommitment,
|
||||||
use super::r1cs::{
|
constants::NUM_CHALLENGE_BITS,
|
||||||
R1CSGens, R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness,
|
errors::NovaError,
|
||||||
|
r1cs::{R1CSGens, R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness},
|
||||||
|
traits::{AbsorbInROTrait, Group, ROTrait},
|
||||||
};
|
};
|
||||||
use super::traits::{AbsorbInROTrait, Group, HashFuncTrait};
|
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
/// A SNARK that holds the proof of a step of an incremental computation
|
/// A SNARK that holds the proof of a step of an incremental computation
|
||||||
@@ -18,8 +19,8 @@ pub struct NIFS<G: Group> {
|
|||||||
_p: PhantomData<G>,
|
_p: PhantomData<G>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type HashFuncConstants<G> =
|
type ROConstants<G> =
|
||||||
<<G as Group>::HashFunc as HashFuncTrait<<G as Group>::Base, <G as Group>::Scalar>>::Constants;
|
<<G as Group>::RO as ROTrait<<G as Group>::Base, <G as Group>::Scalar>>::Constants;
|
||||||
|
|
||||||
impl<G: Group> NIFS<G> {
|
impl<G: Group> NIFS<G> {
|
||||||
/// Takes as input a Relaxed R1CS instance-witness tuple `(U1, W1)` and
|
/// Takes as input a Relaxed R1CS instance-witness tuple `(U1, W1)` and
|
||||||
@@ -30,7 +31,7 @@ impl<G: Group> NIFS<G> {
|
|||||||
/// if and only if `W1` satisfies `U1` and `W2` satisfies `U2`.
|
/// if and only if `W1` satisfies `U1` and `W2` satisfies `U2`.
|
||||||
pub fn prove(
|
pub fn prove(
|
||||||
gens: &R1CSGens<G>,
|
gens: &R1CSGens<G>,
|
||||||
ro_consts: &HashFuncConstants<G>,
|
ro_consts: &ROConstants<G>,
|
||||||
S: &R1CSShape<G>,
|
S: &R1CSShape<G>,
|
||||||
U1: &RelaxedR1CSInstance<G>,
|
U1: &RelaxedR1CSInstance<G>,
|
||||||
W1: &RelaxedR1CSWitness<G>,
|
W1: &RelaxedR1CSWitness<G>,
|
||||||
@@ -38,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::HashFunc::new(ro_consts.clone());
|
let mut ro = G::RO::new(ro_consts.clone());
|
||||||
|
|
||||||
// append S to the transcript
|
// append S to the transcript
|
||||||
S.absorb_in_ro(&mut ro);
|
S.absorb_in_ro(&mut ro);
|
||||||
@@ -54,7 +55,7 @@ impl<G: Group> NIFS<G> {
|
|||||||
comm_T.absorb_in_ro(&mut ro);
|
comm_T.absorb_in_ro(&mut ro);
|
||||||
|
|
||||||
// compute a challenge from the 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`
|
// fold the instance using `r` and `comm_T`
|
||||||
let U = U1.fold(U2, &comm_T, &r)?;
|
let U = U1.fold(U2, &comm_T, &r)?;
|
||||||
@@ -79,13 +80,13 @@ impl<G: Group> NIFS<G> {
|
|||||||
/// if and only if `U1` and `U2` are satisfiable.
|
/// if and only if `U1` and `U2` are satisfiable.
|
||||||
pub fn verify(
|
pub fn verify(
|
||||||
&self,
|
&self,
|
||||||
ro_consts: &HashFuncConstants<G>,
|
ro_consts: &ROConstants<G>,
|
||||||
S: &R1CSShape<G>,
|
S: &R1CSShape<G>,
|
||||||
U1: &RelaxedR1CSInstance<G>,
|
U1: &RelaxedR1CSInstance<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::HashFunc::new(ro_consts.clone());
|
let mut ro = G::RO::new(ro_consts.clone());
|
||||||
|
|
||||||
// append S to the transcript
|
// append S to the transcript
|
||||||
S.absorb_in_ro(&mut ro);
|
S.absorb_in_ro(&mut ro);
|
||||||
@@ -99,7 +100,7 @@ impl<G: Group> NIFS<G> {
|
|||||||
comm_T.absorb_in_ro(&mut ro);
|
comm_T.absorb_in_ro(&mut ro);
|
||||||
|
|
||||||
// compute a challenge from the 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`
|
// fold the instance using `r` and `comm_T`
|
||||||
let U = U1.fold(U2, &comm_T, &r)?;
|
let U = U1.fold(U2, &comm_T, &r)?;
|
||||||
@@ -112,7 +113,7 @@ impl<G: Group> NIFS<G> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::traits::{Group, HashFuncConstantsTrait};
|
use crate::traits::{Group, ROConstantsTrait};
|
||||||
use ::bellperson::{gadgets::num::AllocatedNum, ConstraintSystem, SynthesisError};
|
use ::bellperson::{gadgets::num::AllocatedNum, ConstraintSystem, SynthesisError};
|
||||||
use ff::{Field, PrimeField};
|
use ff::{Field, PrimeField};
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
@@ -166,10 +167,8 @@ mod tests {
|
|||||||
let _ = synthesize_tiny_r1cs_bellperson(&mut cs, None);
|
let _ = synthesize_tiny_r1cs_bellperson(&mut cs, None);
|
||||||
let shape = cs.r1cs_shape();
|
let shape = cs.r1cs_shape();
|
||||||
let gens = cs.r1cs_gens();
|
let gens = cs.r1cs_gens();
|
||||||
let ro_consts = <<G as Group>::HashFunc as HashFuncTrait<
|
let ro_consts =
|
||||||
<G as Group>::Base,
|
<<G as Group>::RO as ROTrait<<G as Group>::Base, <G as Group>::Scalar>>::Constants::new();
|
||||||
<G as Group>::Scalar,
|
|
||||||
>>::Constants::new();
|
|
||||||
|
|
||||||
// Now get the instance and assignment for one instance
|
// Now get the instance and assignment for one instance
|
||||||
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
|
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
|
||||||
@@ -193,10 +192,7 @@ mod tests {
|
|||||||
|
|
||||||
fn execute_sequence(
|
fn execute_sequence(
|
||||||
gens: &R1CSGens<G>,
|
gens: &R1CSGens<G>,
|
||||||
ro_consts: &<<G as Group>::HashFunc as HashFuncTrait<
|
ro_consts: &<<G as Group>::RO as ROTrait<<G as Group>::Base, <G as Group>::Scalar>>::Constants,
|
||||||
<G as Group>::Base,
|
|
||||||
<G as Group>::Scalar,
|
|
||||||
>>::Constants,
|
|
||||||
shape: &R1CSShape<G>,
|
shape: &R1CSShape<G>,
|
||||||
U1: &R1CSInstance<G>,
|
U1: &R1CSInstance<G>,
|
||||||
W1: &R1CSWitness<G>,
|
W1: &R1CSWitness<G>,
|
||||||
@@ -304,10 +300,8 @@ mod tests {
|
|||||||
|
|
||||||
// generate generators and ro constants
|
// generate generators and ro constants
|
||||||
let gens = R1CSGens::new(num_cons, num_vars);
|
let gens = R1CSGens::new(num_cons, num_vars);
|
||||||
let ro_consts = <<G as Group>::HashFunc as HashFuncTrait<
|
let ro_consts =
|
||||||
<G as Group>::Base,
|
<<G as Group>::RO as ROTrait<<G as Group>::Base, <G as Group>::Scalar>>::Constants::new();
|
||||||
<G as Group>::Scalar,
|
|
||||||
>>::Constants::new();
|
|
||||||
|
|
||||||
let rand_inst_witness_generator =
|
let rand_inst_witness_generator =
|
||||||
|gens: &R1CSGens<G>, I: &S| -> (S, R1CSInstance<G>, R1CSWitness<G>) {
|
|gens: &R1CSGens<G>, I: &S| -> (S, R1CSInstance<G>, R1CSWitness<G>) {
|
||||||
|
|||||||
10
src/pasta.rs
10
src/pasta.rs
@@ -1,6 +1,6 @@
|
|||||||
//! This module implements the Nova traits for pallas::Point, pallas::Scalar, vesta::Point, vesta::Scalar.
|
//! This module implements the Nova traits for pallas::Point, pallas::Scalar, vesta::Point, vesta::Scalar.
|
||||||
use crate::{
|
use crate::{
|
||||||
poseidon::{PoseidonHashFunc, PoseidonHashFuncCircuit},
|
poseidon::{PoseidonRO, PoseidonROCircuit},
|
||||||
traits::{ChallengeTrait, CompressedGroup, Group},
|
traits::{ChallengeTrait, CompressedGroup, Group},
|
||||||
};
|
};
|
||||||
use digest::{ExtendableOutput, Input};
|
use digest::{ExtendableOutput, Input};
|
||||||
@@ -40,8 +40,8 @@ impl Group for pallas::Point {
|
|||||||
type Scalar = pallas::Scalar;
|
type Scalar = pallas::Scalar;
|
||||||
type CompressedGroupElement = PallasCompressedElementWrapper;
|
type CompressedGroupElement = PallasCompressedElementWrapper;
|
||||||
type PreprocessedGroupElement = pallas::Affine;
|
type PreprocessedGroupElement = pallas::Affine;
|
||||||
type HashFunc = PoseidonHashFunc<Self::Base, Self::Scalar>;
|
type RO = PoseidonRO<Self::Base, Self::Scalar>;
|
||||||
type HashFuncCircuit = PoseidonHashFuncCircuit<Self::Base>;
|
type ROCircuit = PoseidonROCircuit<Self::Base>;
|
||||||
|
|
||||||
fn vartime_multiscalar_mul(
|
fn vartime_multiscalar_mul(
|
||||||
scalars: &[Self::Scalar],
|
scalars: &[Self::Scalar],
|
||||||
@@ -138,8 +138,8 @@ impl Group for vesta::Point {
|
|||||||
type Scalar = vesta::Scalar;
|
type Scalar = vesta::Scalar;
|
||||||
type CompressedGroupElement = VestaCompressedElementWrapper;
|
type CompressedGroupElement = VestaCompressedElementWrapper;
|
||||||
type PreprocessedGroupElement = vesta::Affine;
|
type PreprocessedGroupElement = vesta::Affine;
|
||||||
type HashFunc = PoseidonHashFunc<Self::Base, Self::Scalar>;
|
type RO = PoseidonRO<Self::Base, Self::Scalar>;
|
||||||
type HashFuncCircuit = PoseidonHashFuncCircuit<Self::Base>;
|
type ROCircuit = PoseidonROCircuit<Self::Base>;
|
||||||
|
|
||||||
fn vartime_multiscalar_mul(
|
fn vartime_multiscalar_mul(
|
||||||
scalars: &[Self::Scalar],
|
scalars: &[Self::Scalar],
|
||||||
|
|||||||
164
src/poseidon.rs
164
src/poseidon.rs
@@ -1,8 +1,5 @@
|
|||||||
//! Poseidon Constants and Poseidon-based RO used in Nova
|
//! Poseidon Constants and Poseidon-based RO used in Nova
|
||||||
use super::{
|
use super::traits::{ROCircuitTrait, ROConstantsTrait, ROTrait};
|
||||||
constants::{NUM_CHALLENGE_BITS, NUM_HASH_BITS},
|
|
||||||
traits::{HashFuncCircuitTrait, HashFuncConstantsTrait, HashFuncTrait},
|
|
||||||
};
|
|
||||||
use bellperson::{
|
use bellperson::{
|
||||||
gadgets::{
|
gadgets::{
|
||||||
boolean::{AllocatedBit, Boolean},
|
boolean::{AllocatedBit, Boolean},
|
||||||
@@ -29,7 +26,7 @@ where
|
|||||||
constants32: PoseidonConstants<Scalar, U32>,
|
constants32: PoseidonConstants<Scalar, U32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Scalar> HashFuncConstantsTrait<Scalar> for PoseidonConstantsCircuit<Scalar>
|
impl<Scalar> ROConstantsTrait<Scalar> for PoseidonConstantsCircuit<Scalar>
|
||||||
where
|
where
|
||||||
Scalar: PrimeField + PrimeFieldBits,
|
Scalar: PrimeField + PrimeFieldBits,
|
||||||
{
|
{
|
||||||
@@ -46,7 +43,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A Poseidon-based RO to use outside circuits
|
/// A Poseidon-based RO to use outside circuits
|
||||||
pub struct PoseidonHashFunc<Base, Scalar>
|
pub struct PoseidonRO<Base, Scalar>
|
||||||
where
|
where
|
||||||
Base: PrimeField + PrimeFieldBits,
|
Base: PrimeField + PrimeFieldBits,
|
||||||
Scalar: PrimeField + PrimeFieldBits,
|
Scalar: PrimeField + PrimeFieldBits,
|
||||||
@@ -58,30 +55,7 @@ where
|
|||||||
_p: PhantomData<Scalar>,
|
_p: PhantomData<Scalar>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Base, Scalar> PoseidonHashFunc<Base, Scalar>
|
impl<Base, Scalar> ROTrait<Base, Scalar> for PoseidonRO<Base, Scalar>
|
||||||
where
|
|
||||||
Base: PrimeField + PrimeFieldBits,
|
|
||||||
Scalar: PrimeField + PrimeFieldBits,
|
|
||||||
{
|
|
||||||
fn hash_inner(&self) -> Base {
|
|
||||||
match self.state.len() {
|
|
||||||
27 => {
|
|
||||||
Poseidon::<Base, U27>::new_with_preimage(&self.state, &self.constants.constants27).hash()
|
|
||||||
}
|
|
||||||
32 => {
|
|
||||||
Poseidon::<Base, U32>::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<Base, Scalar> HashFuncTrait<Base, Scalar> for PoseidonHashFunc<Base, Scalar>
|
|
||||||
where
|
where
|
||||||
Base: PrimeField + PrimeFieldBits,
|
Base: PrimeField + PrimeFieldBits,
|
||||||
Scalar: PrimeField + PrimeFieldBits,
|
Scalar: PrimeField + PrimeFieldBits,
|
||||||
@@ -102,28 +76,27 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Compute a challenge by hashing the current state
|
/// Compute a challenge by hashing the current state
|
||||||
fn get_challenge(&self) -> Scalar {
|
fn squeeze(&self, num_bits: usize) -> Scalar {
|
||||||
let hash = self.hash_inner();
|
let hash = match self.state.len() {
|
||||||
// Only keep NUM_CHALLENGE_BITS bits
|
27 => {
|
||||||
let bits = hash.to_le_bits();
|
Poseidon::<Base, U27>::new_with_preimage(&self.state, &self.constants.constants27).hash()
|
||||||
let mut res = Scalar::zero();
|
|
||||||
let mut coeff = Scalar::one();
|
|
||||||
for bit in bits[0..NUM_CHALLENGE_BITS].into_iter() {
|
|
||||||
if *bit {
|
|
||||||
res += coeff;
|
|
||||||
}
|
}
|
||||||
coeff += coeff;
|
32 => {
|
||||||
|
Poseidon::<Base, U32>::new_with_preimage(&self.state, &self.constants.constants32).hash()
|
||||||
}
|
}
|
||||||
res
|
_ => {
|
||||||
|
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 {
|
// Only return `num_bits`
|
||||||
let hash = self.hash_inner();
|
|
||||||
// Only keep NUM_HASH_BITS bits
|
|
||||||
let bits = hash.to_le_bits();
|
let bits = hash.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_HASH_BITS].into_iter() {
|
for bit in bits[0..num_bits].into_iter() {
|
||||||
if *bit {
|
if *bit {
|
||||||
res += coeff;
|
res += coeff;
|
||||||
}
|
}
|
||||||
@@ -134,7 +107,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A Poseidon-based RO gadget to use inside the verifier circuit.
|
/// A Poseidon-based RO gadget to use inside the verifier circuit.
|
||||||
pub struct PoseidonHashFuncCircuit<Scalar>
|
pub struct PoseidonROCircuit<Scalar>
|
||||||
where
|
where
|
||||||
Scalar: PrimeField + PrimeFieldBits,
|
Scalar: PrimeField + PrimeFieldBits,
|
||||||
{
|
{
|
||||||
@@ -143,48 +116,7 @@ where
|
|||||||
constants: PoseidonConstantsCircuit<Scalar>,
|
constants: PoseidonConstantsCircuit<Scalar>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Scalar> PoseidonHashFuncCircuit<Scalar>
|
impl<Scalar> ROCircuitTrait<Scalar> for PoseidonROCircuit<Scalar>
|
||||||
where
|
|
||||||
Scalar: PrimeField + PrimeFieldBits,
|
|
||||||
{
|
|
||||||
fn hash_inner<CS>(&mut self, mut cs: CS) -> Result<Vec<AllocatedBit>, SynthesisError>
|
|
||||||
where
|
|
||||||
CS: ConstraintSystem<Scalar>,
|
|
||||||
{
|
|
||||||
let out = match self.state.len() {
|
|
||||||
27 => poseidon_hash(
|
|
||||||
cs.namespace(|| "Poseidon hash"),
|
|
||||||
self.state.clone(),
|
|
||||||
&self.constants.constants27,
|
|
||||||
)?,
|
|
||||||
32 => poseidon_hash(
|
|
||||||
cs.namespace(|| "Posideon hash"),
|
|
||||||
self.state.clone(),
|
|
||||||
&self.constants.constants32,
|
|
||||||
)?,
|
|
||||||
_ => {
|
|
||||||
panic!(
|
|
||||||
"Number of elements in the RO state does not match any of the arities used in Nova: {}",
|
|
||||||
self.state.len()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// return the hash as a vector of bits
|
|
||||||
Ok(
|
|
||||||
out
|
|
||||||
.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(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Scalar> HashFuncCircuitTrait<Scalar> for PoseidonHashFuncCircuit<Scalar>
|
|
||||||
where
|
where
|
||||||
Scalar: PrimeField + PrimeFieldBits,
|
Scalar: PrimeField + PrimeFieldBits,
|
||||||
{
|
{
|
||||||
@@ -204,20 +136,45 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Compute a challenge by hashing the current state
|
/// Compute a challenge by hashing the current state
|
||||||
fn get_challenge<CS>(&mut self, mut cs: CS) -> Result<Vec<AllocatedBit>, SynthesisError>
|
fn squeeze<CS>(
|
||||||
|
&mut self,
|
||||||
|
mut cs: CS,
|
||||||
|
num_bits: usize,
|
||||||
|
) -> Result<Vec<AllocatedBit>, SynthesisError>
|
||||||
where
|
where
|
||||||
CS: ConstraintSystem<Scalar>,
|
CS: ConstraintSystem<Scalar>,
|
||||||
{
|
{
|
||||||
let bits = self.hash_inner(cs.namespace(|| "hash"))?;
|
let hash = match self.state.len() {
|
||||||
Ok(bits[..NUM_CHALLENGE_BITS].into())
|
27 => poseidon_hash(
|
||||||
|
cs.namespace(|| "Poseidon hash"),
|
||||||
|
self.state.clone(),
|
||||||
|
&self.constants.constants27,
|
||||||
|
)?,
|
||||||
|
32 => poseidon_hash(
|
||||||
|
cs.namespace(|| "Posideon hash"),
|
||||||
|
self.state.clone(),
|
||||||
|
&self.constants.constants32,
|
||||||
|
)?,
|
||||||
|
_ => {
|
||||||
|
panic!(
|
||||||
|
"Number of elements in the RO state does not match any of the arities used in Nova: {}",
|
||||||
|
self.state.len()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
fn get_hash<CS>(&mut self, mut cs: CS) -> Result<Vec<AllocatedBit>, SynthesisError>
|
// return the hash as a vector of bits, truncated
|
||||||
where
|
Ok(
|
||||||
CS: ConstraintSystem<Scalar>,
|
hash
|
||||||
{
|
.to_bits_le_strict(cs.namespace(|| "poseidon hash to boolean"))?
|
||||||
let bits = self.hash_inner(cs.namespace(|| "hash"))?;
|
.iter()
|
||||||
Ok(bits[..NUM_HASH_BITS].into())
|
.map(|boolean| match boolean {
|
||||||
|
Boolean::Is(ref x) => x.clone(),
|
||||||
|
_ => panic!("Wrong type of input. We should have never reached there"),
|
||||||
|
})
|
||||||
|
.collect::<Vec<AllocatedBit>>()[..num_bits]
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +184,10 @@ mod tests {
|
|||||||
type S = pasta_curves::pallas::Scalar;
|
type S = pasta_curves::pallas::Scalar;
|
||||||
type B = pasta_curves::vesta::Scalar;
|
type B = pasta_curves::vesta::Scalar;
|
||||||
type G = pasta_curves::pallas::Point;
|
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 ff::Field;
|
||||||
use rand::rngs::OsRng;
|
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
|
// 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: PoseidonHashFunc<S, B> = PoseidonHashFunc::new(constants.clone());
|
let mut ro: PoseidonRO<S, B> = PoseidonRO::new(constants.clone());
|
||||||
let mut ro_gadget: PoseidonHashFuncCircuit<S> = PoseidonHashFuncCircuit::new(constants);
|
let mut ro_gadget: PoseidonROCircuit<S> = PoseidonROCircuit::new(constants);
|
||||||
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
|
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
|
||||||
for i in 0..27 {
|
for i in 0..27 {
|
||||||
let num = S::random(&mut csprng);
|
let num = S::random(&mut csprng);
|
||||||
@@ -249,8 +209,8 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
ro_gadget.absorb(num_gadget);
|
ro_gadget.absorb(num_gadget);
|
||||||
}
|
}
|
||||||
let num = ro.get_challenge();
|
let num = ro.squeeze(NUM_CHALLENGE_BITS);
|
||||||
let num2_bits = ro_gadget.get_challenge(&mut cs).unwrap();
|
let num2_bits = ro_gadget.squeeze(&mut cs, NUM_CHALLENGE_BITS).unwrap();
|
||||||
let num2 = le_bits_to_num(&mut cs, num2_bits).unwrap();
|
let num2 = le_bits_to_num(&mut cs, num2_bits).unwrap();
|
||||||
assert_eq!(num.to_repr(), num2.get_value().unwrap().to_repr());
|
assert_eq!(num.to_repr(), num2.get_value().unwrap().to_repr());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use super::{
|
|||||||
constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_HASH_BITS},
|
constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_HASH_BITS},
|
||||||
errors::NovaError,
|
errors::NovaError,
|
||||||
gadgets::utils::scalar_as_base,
|
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 bellperson_nonnative::{mp::bignat::nat_to_limbs, util::convert::f_to_nat};
|
||||||
use core::cmp::max;
|
use core::cmp::max;
|
||||||
@@ -453,7 +453,7 @@ impl<G: Group> AppendToTranscriptTrait for R1CSShape<G> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<G: Group> AbsorbInROTrait<G> for R1CSShape<G> {
|
impl<G: Group> AbsorbInROTrait<G> for R1CSShape<G> {
|
||||||
fn absorb_in_ro(&self, ro: &mut G::HashFunc) {
|
fn absorb_in_ro(&self, ro: &mut G::RO) {
|
||||||
ro.absorb(scalar_as_base::<G>(self.get_digest()));
|
ro.absorb(scalar_as_base::<G>(self.get_digest()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -500,7 +500,7 @@ impl<G: Group> AppendToTranscriptTrait for R1CSInstance<G> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<G: Group> AbsorbInROTrait<G> for R1CSInstance<G> {
|
impl<G: Group> AbsorbInROTrait<G> for R1CSInstance<G> {
|
||||||
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_W.absorb_in_ro(ro);
|
||||||
for x in &self.X {
|
for x in &self.X {
|
||||||
ro.absorb(scalar_as_base::<G>(*x));
|
ro.absorb(scalar_as_base::<G>(*x));
|
||||||
@@ -640,7 +640,7 @@ impl<G: Group> AppendToTranscriptTrait for RelaxedR1CSInstance<G> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<G: Group> AbsorbInROTrait<G> for RelaxedR1CSInstance<G> {
|
impl<G: Group> AbsorbInROTrait<G> for RelaxedR1CSInstance<G> {
|
||||||
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_W.absorb_in_ro(ro);
|
||||||
self.comm_E.absorb_in_ro(ro);
|
self.comm_E.absorb_in_ro(ro);
|
||||||
ro.absorb(scalar_as_base::<G>(self.u));
|
ro.absorb(scalar_as_base::<G>(self.u));
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ pub trait StepCircuit<F: PrimeField>: Send + Sync + Clone {
|
|||||||
z: AllocatedNum<F>,
|
z: AllocatedNum<F>,
|
||||||
) -> Result<AllocatedNum<F>, SynthesisError>;
|
) -> Result<AllocatedNum<F>, SynthesisError>;
|
||||||
|
|
||||||
/// Execute the circuit for a computation step and return output
|
/// return the output of the step when provided with with the step's input
|
||||||
fn compute(&self, z: &F) -> F;
|
fn output(&self, z: &F) -> F;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trivial step circuit that simply returns the input
|
/// A trivial step circuit that simply returns the input
|
||||||
@@ -35,7 +35,7 @@ where
|
|||||||
Ok(z)
|
Ok(z)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute(&self, z: &F) -> F {
|
fn output(&self, z: &F) -> F {
|
||||||
*z
|
*z
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,10 +39,10 @@ pub trait Group:
|
|||||||
|
|
||||||
/// A type that represents a hash function that consumes elements
|
/// A type that represents a hash function that consumes elements
|
||||||
/// from the base field and squeezes out elements of the scalar field
|
/// from the base field and squeezes out elements of the scalar field
|
||||||
type HashFunc: HashFuncTrait<Self::Base, Self::Scalar>;
|
type RO: ROTrait<Self::Base, Self::Scalar>;
|
||||||
|
|
||||||
/// An alternate implementation of Self::HashFunc in the circuit model
|
/// An alternate implementation of Self::RO in the circuit model
|
||||||
type HashFuncCircuit: HashFuncCircuitTrait<Self::Base>;
|
type ROCircuit: ROCircuitTrait<Self::Base>;
|
||||||
|
|
||||||
/// A method to compute a multiexponentation
|
/// A method to compute a multiexponentation
|
||||||
fn vartime_multiscalar_mul(
|
fn vartime_multiscalar_mul(
|
||||||
@@ -87,7 +87,7 @@ pub trait AppendToTranscriptTrait {
|
|||||||
/// A helper trait to absorb different objects in RO
|
/// A helper trait to absorb different objects in RO
|
||||||
pub trait AbsorbInROTrait<G: Group> {
|
pub trait AbsorbInROTrait<G: Group> {
|
||||||
/// Absorbs the value in the provided RO
|
/// 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
|
/// 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
|
/// A helper trait that defines the behavior of a hash function that we use as an RO
|
||||||
pub trait HashFuncTrait<Base, Scalar> {
|
pub trait ROTrait<Base, Scalar> {
|
||||||
/// A type representing constants/parameters associated with the hash function
|
/// A type representing constants/parameters associated with the hash function
|
||||||
type Constants: HashFuncConstantsTrait<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) -> Self;
|
||||||
@@ -107,17 +107,14 @@ pub trait HashFuncTrait<Base, Scalar> {
|
|||||||
/// 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 random challenge by hashing the internal state
|
/// Returns a challenge of `num_bits` by hashing the internal state
|
||||||
fn get_challenge(&self) -> Scalar;
|
fn squeeze(&self, num_bits: usize) -> Scalar;
|
||||||
|
|
||||||
/// Returns a hash of the internal state
|
|
||||||
fn get_hash(&self) -> 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
|
||||||
pub trait HashFuncCircuitTrait<Base: PrimeField> {
|
pub trait ROCircuitTrait<Base: PrimeField> {
|
||||||
/// A type representing constants/parameters associated with the hash function
|
/// A type representing constants/parameters associated with the hash function
|
||||||
type Constants: HashFuncConstantsTrait<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) -> Self;
|
||||||
@@ -125,30 +122,25 @@ pub trait HashFuncCircuitTrait<Base: PrimeField> {
|
|||||||
/// 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>);
|
||||||
|
|
||||||
/// Returns a random challenge by hashing the internal state
|
/// Returns a challenge of `num_bits` by hashing the internal state
|
||||||
fn get_challenge<CS>(&mut self, cs: CS) -> Result<Vec<AllocatedBit>, SynthesisError>
|
fn squeeze<CS>(&mut self, cs: CS, num_bits: usize) -> Result<Vec<AllocatedBit>, SynthesisError>
|
||||||
where
|
|
||||||
CS: ConstraintSystem<Base>;
|
|
||||||
|
|
||||||
/// Returns a hash of the internal state
|
|
||||||
fn get_hash<CS>(&mut self, cs: CS) -> Result<Vec<AllocatedBit>, SynthesisError>
|
|
||||||
where
|
where
|
||||||
CS: ConstraintSystem<Base>;
|
CS: ConstraintSystem<Base>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A helper trait that defines the constants associated with a hash function
|
/// A helper trait that defines the constants associated with a hash function
|
||||||
pub trait HashFuncConstantsTrait<Base> {
|
pub trait ROConstantsTrait<Base> {
|
||||||
/// produces constants/parameters associated with the hash function
|
/// produces constants/parameters associated with the hash function
|
||||||
fn new() -> Self;
|
fn new() -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An alias for constants associated with G::HashFunc
|
/// An alias for constants associated with G::RO
|
||||||
pub type HashFuncConstants<G> =
|
pub type ROConstants<G> =
|
||||||
<<G as Group>::HashFunc as HashFuncTrait<<G as Group>::Base, <G as Group>::Scalar>>::Constants;
|
<<G as Group>::RO as ROTrait<<G as Group>::Base, <G as Group>::Scalar>>::Constants;
|
||||||
|
|
||||||
/// An alias for constants associated with G::HashFuncCircuit
|
/// An alias for constants associated with G::ROCircuit
|
||||||
pub type HashFuncConstantsCircuit<G> =
|
pub type ROConstantsCircuit<G> =
|
||||||
<<G as Group>::HashFuncCircuit as HashFuncCircuitTrait<<G as Group>::Base>>::Constants;
|
<<G as Group>::ROCircuit as ROCircuitTrait<<G as Group>::Base>>::Constants;
|
||||||
|
|
||||||
/// A helper trait for types with a group operation.
|
/// A helper trait for types with a group operation.
|
||||||
pub trait GroupOps<Rhs = Self, Output = Self>:
|
pub trait GroupOps<Rhs = Self, Output = Self>:
|
||||||
|
|||||||
Reference in New Issue
Block a user