Browse Source

Abstract the RO used in the circuit with traits (#84)

* cleanup RO usage inside the circuit: use traits

* Add a note

* rename types for clarity
main
Srinath Setty 2 years ago
committed by GitHub
parent
commit
6667d2f8b5
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 118 additions and 74 deletions
  1. +8
    -8
      src/circuit.rs
  2. +5
    -6
      src/gadgets/r1cs.rs
  3. +23
    -23
      src/lib.rs
  4. +3
    -3
      src/nifs.rs
  5. +5
    -3
      src/pasta.rs
  6. +37
    -30
      src/poseidon.rs
  7. +37
    -1
      src/traits.rs

+ 8
- 8
src/circuit.rs

@ -15,9 +15,8 @@ use super::{
alloc_num_equals, alloc_scalar_as_base, alloc_zero, conditionally_select, le_bits_to_num,
},
},
poseidon::{PoseidonROGadget, ROConstantsCircuit},
r1cs::{R1CSInstance, RelaxedR1CSInstance},
traits::{Group, StepCircuit},
traits::{Group, HashFuncCircuitTrait, HashFuncConstantsCircuit, StepCircuit},
};
use bellperson::{
gadgets::{
@ -91,7 +90,7 @@ where
SC: StepCircuit<G::Base>,
{
params: NIFSVerifierCircuitParams,
ro_consts: ROConstantsCircuit<G::Base>,
ro_consts: HashFuncConstantsCircuit<G>,
inputs: Option<NIFSVerifierCircuitInputs<G>>,
step_circuit: SC, // The function that is applied for each step
}
@ -106,7 +105,7 @@ where
params: NIFSVerifierCircuitParams,
inputs: Option<NIFSVerifierCircuitInputs<G>>,
step_circuit: SC,
ro_consts: ROConstantsCircuit<G::Base>,
ro_consts: HashFuncConstantsCircuit<G>,
) -> Self {
Self {
params,
@ -221,7 +220,7 @@ where
T: AllocatedPoint<G::Base>,
) -> Result<(AllocatedRelaxedR1CSInstance<G>, AllocatedBit), SynthesisError> {
// Check that u.x[0] = Hash(params, U, i, z0, zi)
let mut ro: PoseidonROGadget<G::Base> = PoseidonROGadget::new(self.ro_consts.clone());
let mut ro = G::HashFuncCircuit::new(self.ro_consts.clone());
ro.absorb(params.clone());
ro.absorb(i);
ro.absorb(z_0);
@ -328,7 +327,7 @@ where
.synthesize(&mut cs.namespace(|| "F"), z_input)?;
// Compute the new hash H(params, Unew, i+1, z0, z_{i+1})
let mut ro: PoseidonROGadget<G::Base> = PoseidonROGadget::new(self.ro_consts);
let mut ro = G::HashFuncCircuit::new(self.ro_consts);
ro.absorb(params);
ro.absorb(i_new.clone());
ro.absorb(z_0);
@ -356,6 +355,7 @@ mod tests {
use crate::constants::{BN_LIMB_WIDTH, BN_N_LIMBS};
use crate::{
bellperson::r1cs::{NovaShape, NovaWitness},
poseidon::PoseidonConstantsCircuit,
traits::HashFuncConstantsTrait,
};
use ff::PrimeField;
@ -388,8 +388,8 @@ mod tests {
// In the following we use 1 to refer to the primary, and 2 to refer to the secondary circuit
let params1 = NIFSVerifierCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, true);
let params2 = NIFSVerifierCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, false);
let ro_consts1: ROConstantsCircuit<<G2 as Group>::Base> = ROConstantsCircuit::new();
let ro_consts2: ROConstantsCircuit<<G1 as Group>::Base> = ROConstantsCircuit::new();
let ro_consts1: HashFuncConstantsCircuit<G2> = PoseidonConstantsCircuit::new();
let ro_consts2: HashFuncConstantsCircuit<G1> = PoseidonConstantsCircuit::new();
// Initialize the shape and gens for the primary
let circuit1: NIFSVerifierCircuit<G2, TestCircuit<<G2 as Group>::Base>> =

+ 5
- 6
src/gadgets/r1cs.rs

@ -7,9 +7,8 @@ use crate::{
conditionally_select_bignat, le_bits_to_num,
},
},
poseidon::{PoseidonROGadget, ROConstantsCircuit},
r1cs::{R1CSInstance, RelaxedR1CSInstance},
traits::Group,
traits::{Group, HashFuncCircuitTrait, HashFuncConstantsCircuit},
};
use bellperson::{
gadgets::{boolean::Boolean, num::AllocatedNum, Assignment},
@ -61,7 +60,7 @@ where
}
/// Absorb the provided instance in the RO
pub fn absorb_in_ro(&self, ro: &mut PoseidonROGadget<G::Base>) {
pub fn absorb_in_ro(&self, ro: &mut G::HashFuncCircuit) {
ro.absorb(self.W.x.clone());
ro.absorb(self.W.y.clone());
ro.absorb(self.W.is_infinity.clone());
@ -208,7 +207,7 @@ where
pub fn absorb_in_ro<CS: ConstraintSystem<<G as Group>::Base>>(
&self,
mut cs: CS,
ro: &mut PoseidonROGadget<G::Base>,
ro: &mut G::HashFuncCircuit,
) -> Result<(), SynthesisError> {
ro.absorb(self.W.x.clone());
ro.absorb(self.W.y.clone());
@ -263,12 +262,12 @@ where
params: AllocatedNum<G::Base>, // hash of R1CSShape of F'
u: AllocatedR1CSInstance<G>,
T: AllocatedPoint<G::Base>,
ro_consts: ROConstantsCircuit<G::Base>,
ro_consts: HashFuncConstantsCircuit<G>,
limb_width: usize,
n_limbs: usize,
) -> Result<AllocatedRelaxedR1CSInstance<G>, SynthesisError> {
// Compute r:
let mut ro: PoseidonROGadget<G::Base> = PoseidonROGadget::new(ro_consts);
let mut ro = G::HashFuncCircuit::new(ro_consts);
ro.absorb(params);
self.absorb_in_ro(cs.namespace(|| "absorb running instance"), &mut ro)?;
u.absorb_in_ro(&mut ro);

+ 23
- 23
src/lib.rs

@ -33,31 +33,30 @@ use errors::NovaError;
use ff::Field;
use gadgets::utils::scalar_as_base;
use nifs::NIFS;
use poseidon::ROConstantsCircuit; // TODO: make this a trait so we can use it without the concrete implementation
use r1cs::{
R1CSGens, R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness,
};
use snark::RelaxedR1CSSNARKTrait;
use traits::{AbsorbInROTrait, Group, HashFuncConstantsTrait, HashFuncTrait, StepCircuit};
type ROConstants<G> =
<<G as Group>::HashFunc as HashFuncTrait<<G as Group>::Base, <G as Group>::Scalar>>::Constants;
use traits::{
AbsorbInROTrait, Group, HashFuncConstants, HashFuncConstantsCircuit, HashFuncConstantsTrait,
HashFuncTrait, StepCircuit,
};
/// A type that holds public parameters of Nova
pub struct PublicParams<G1, G2, C1, C2>
where
G1: Group<Base = <G2 as Group>::Scalar>,
G2: Group<Base = <G1 as Group>::Scalar>,
C1: StepCircuit<G1::Scalar> + Clone,
C2: StepCircuit<G2::Scalar> + Clone,
C1: StepCircuit<G1::Scalar>,
C2: StepCircuit<G2::Scalar>,
{
ro_consts_primary: ROConstants<G1>,
ro_consts_circuit_primary: ROConstantsCircuit<<G2 as Group>::Base>,
ro_consts_primary: HashFuncConstants<G1>,
ro_consts_circuit_primary: HashFuncConstantsCircuit<G2>,
r1cs_gens_primary: R1CSGens<G1>,
r1cs_shape_primary: R1CSShape<G1>,
r1cs_shape_padded_primary: R1CSShape<G1>,
ro_consts_secondary: ROConstants<G2>,
ro_consts_circuit_secondary: ROConstantsCircuit<<G1 as Group>::Base>,
ro_consts_secondary: HashFuncConstants<G2>,
ro_consts_circuit_secondary: HashFuncConstantsCircuit<G1>,
r1cs_gens_secondary: R1CSGens<G2>,
r1cs_shape_secondary: R1CSShape<G2>,
r1cs_shape_padded_secondary: R1CSShape<G2>,
@ -71,21 +70,22 @@ impl PublicParams
where
G1: Group<Base = <G2 as Group>::Scalar>,
G2: Group<Base = <G1 as Group>::Scalar>,
C1: StepCircuit<G1::Scalar> + Clone,
C2: StepCircuit<G2::Scalar> + Clone,
C1: StepCircuit<G1::Scalar>,
C2: StepCircuit<G2::Scalar>,
{
/// Create a new `PublicParams`
pub fn setup(c_primary: C1, c_secondary: C2) -> Self {
let params_primary = NIFSVerifierCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, true);
let params_secondary = NIFSVerifierCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, false);
let ro_consts_primary: ROConstants<G1> = ROConstants::<G1>::new();
let ro_consts_secondary: ROConstants<G2> = ROConstants::<G2>::new();
let ro_consts_primary: HashFuncConstants<G1> = HashFuncConstants::<G1>::new();
let ro_consts_secondary: HashFuncConstants<G2> = HashFuncConstants::<G2>::new();
let ro_consts_circuit_primary: ROConstantsCircuit<<G2 as Group>::Base> =
ROConstantsCircuit::new();
let ro_consts_circuit_secondary: ROConstantsCircuit<<G1 as Group>::Base> =
ROConstantsCircuit::new();
// ro_consts_circuit_primart are parameterized by G2 because the type alias uses G2::Base = G1::Scalar
let ro_consts_circuit_primary: HashFuncConstantsCircuit<G2> =
HashFuncConstantsCircuit::<G2>::new();
let ro_consts_circuit_secondary: HashFuncConstantsCircuit<G1> =
HashFuncConstantsCircuit::<G1>::new();
// Initialize gens for the primary
let circuit_primary: NIFSVerifierCircuit<G2, C1> = NIFSVerifierCircuit::new(
@ -135,8 +135,8 @@ pub struct RecursiveSNARK
where
G1: Group<Base = <G2 as Group>::Scalar>,
G2: Group<Base = <G1 as Group>::Scalar>,
C1: StepCircuit<G1::Scalar> + Clone,
C2: StepCircuit<G2::Scalar> + Clone,
C1: StepCircuit<G1::Scalar>,
C2: StepCircuit<G2::Scalar>,
{
r_W_primary: RelaxedR1CSWitness<G1>,
r_U_primary: RelaxedR1CSInstance<G1>,
@ -156,8 +156,8 @@ impl RecursiveSNARK
where
G1: Group<Base = <G2 as Group>::Scalar>,
G2: Group<Base = <G1 as Group>::Scalar>,
C1: StepCircuit<G1::Scalar> + Clone,
C2: StepCircuit<G2::Scalar> + Clone,
C1: StepCircuit<G1::Scalar>,
C2: StepCircuit<G2::Scalar>,
{
/// Create a new `RecursiveSNARK`
pub fn prove(

+ 3
- 3
src/nifs.rs

@ -17,7 +17,7 @@ pub struct NIFS {
_p: PhantomData<G>,
}
type ROConstants<G> =
type HashFuncConstants<G> =
<<G as Group>::HashFunc as HashFuncTrait<<G as Group>::Base, <G as Group>::Scalar>>::Constants;
impl<G: Group> NIFS<G> {
@ -29,7 +29,7 @@ impl NIFS {
/// if and only if `W1` satisfies `U1` and `W2` satisfies `U2`.
pub fn prove(
gens: &R1CSGens<G>,
ro_consts: &ROConstants<G>,
ro_consts: &HashFuncConstants<G>,
S: &R1CSShape<G>,
U1: &RelaxedR1CSInstance<G>,
W1: &RelaxedR1CSWitness<G>,
@ -78,7 +78,7 @@ impl NIFS {
/// if and only if `U1` and `U2` are satisfiable.
pub fn verify(
&self,
ro_consts: &ROConstants<G>,
ro_consts: &HashFuncConstants<G>,
S: &R1CSShape<G>,
U1: &RelaxedR1CSInstance<G>,
U2: &R1CSInstance<G>,

+ 5
- 3
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::PoseidonRO,
poseidon::{PoseidonHashFunc, PoseidonHashFuncCircuit},
traits::{ChallengeTrait, CompressedGroup, Group},
};
use digest::{ExtendableOutput, Input};
@ -40,7 +40,8 @@ impl Group for pallas::Point {
type Scalar = pallas::Scalar;
type CompressedGroupElement = PallasCompressedElementWrapper;
type PreprocessedGroupElement = pallas::Affine;
type HashFunc = PoseidonRO<Self::Base, Self::Scalar>;
type HashFunc = PoseidonHashFunc<Self::Base, Self::Scalar>;
type HashFuncCircuit = PoseidonHashFuncCircuit<Self::Base>;
fn vartime_multiscalar_mul(
scalars: &[Self::Scalar],
@ -137,7 +138,8 @@ impl Group for vesta::Point {
type Scalar = vesta::Scalar;
type CompressedGroupElement = VestaCompressedElementWrapper;
type PreprocessedGroupElement = vesta::Affine;
type HashFunc = PoseidonRO<Self::Base, Self::Scalar>;
type HashFunc = PoseidonHashFunc<Self::Base, Self::Scalar>;
type HashFuncCircuit = PoseidonHashFuncCircuit<Self::Base>;
fn vartime_multiscalar_mul(
scalars: &[Self::Scalar],

+ 37
- 30
src/poseidon.rs

@ -1,7 +1,7 @@
//! Poseidon Constants and Poseidon-based RO used in Nova
use super::{
constants::{NUM_CHALLENGE_BITS, NUM_HASH_BITS},
traits::{HashFuncConstantsTrait, HashFuncTrait},
traits::{HashFuncCircuitTrait, HashFuncConstantsTrait, HashFuncTrait},
};
use bellperson::{
gadgets::{
@ -21,7 +21,7 @@ use neptune::{
/// All Poseidon Constants that are used in Nova
#[derive(Clone)]
pub struct ROConstantsCircuit<Scalar>
pub struct PoseidonConstantsCircuit<Scalar>
where
Scalar: PrimeField,
{
@ -29,7 +29,7 @@ where
constants32: PoseidonConstants<Scalar, U32>,
}
impl<Scalar> HashFuncConstantsTrait<Scalar> for ROConstantsCircuit<Scalar>
impl<Scalar> HashFuncConstantsTrait<Scalar> for PoseidonConstantsCircuit<Scalar>
where
Scalar: PrimeField + PrimeFieldBits,
{
@ -46,7 +46,7 @@ where
}
/// A Poseidon-based RO to use outside circuits
pub struct PoseidonRO<Base, Scalar>
pub struct PoseidonHashFunc<Base, Scalar>
where
Base: PrimeField + PrimeFieldBits,
Scalar: PrimeField + PrimeFieldBits,
@ -54,11 +54,11 @@ where
// Internal State
state: Vec<Base>,
// Constants for Poseidon
constants: ROConstantsCircuit<Base>,
constants: PoseidonConstantsCircuit<Base>,
_p: PhantomData<Scalar>,
}
impl<Base, Scalar> PoseidonRO<Base, Scalar>
impl<Base, Scalar> PoseidonHashFunc<Base, Scalar>
where
Base: PrimeField + PrimeFieldBits,
Scalar: PrimeField + PrimeFieldBits,
@ -81,14 +81,14 @@ where
}
}
impl<Base, Scalar> HashFuncTrait<Base, Scalar> for PoseidonRO<Base, Scalar>
impl<Base, Scalar> HashFuncTrait<Base, Scalar> for PoseidonHashFunc<Base, Scalar>
where
Base: PrimeField + PrimeFieldBits,
Scalar: PrimeField + PrimeFieldBits,
{
type Constants = ROConstantsCircuit<Base>;
type Constants = PoseidonConstantsCircuit<Base>;
fn new(constants: ROConstantsCircuit<Base>) -> Self {
fn new(constants: PoseidonConstantsCircuit<Base>) -> Self {
Self {
state: Vec::new(),
constants,
@ -134,32 +134,19 @@ where
}
/// A Poseidon-based RO gadget to use inside the verifier circuit.
pub struct PoseidonROGadget<Scalar>
pub struct PoseidonHashFuncCircuit<Scalar>
where
Scalar: PrimeField + PrimeFieldBits,
{
// Internal state
state: Vec<AllocatedNum<Scalar>>,
constants: ROConstantsCircuit<Scalar>,
constants: PoseidonConstantsCircuit<Scalar>,
}
impl<Scalar> PoseidonROGadget<Scalar>
impl<Scalar> PoseidonHashFuncCircuit<Scalar>
where
Scalar: PrimeField + PrimeFieldBits,
{
/// Initialize the internal state and set the poseidon constants
pub fn new(constants: ROConstantsCircuit<Scalar>) -> Self {
Self {
state: Vec::new(),
constants,
}
}
/// Absorb a new number into the state of the oracle
pub fn absorb(&mut self, e: AllocatedNum<Scalar>) {
self.state.push(e);
}
fn hash_inner<CS>(&mut self, mut cs: CS) -> Result<Vec<AllocatedBit>, SynthesisError>
where
CS: ConstraintSystem<Scalar>,
@ -195,9 +182,29 @@ where
.collect(),
)
}
}
impl<Scalar> HashFuncCircuitTrait<Scalar> for PoseidonHashFuncCircuit<Scalar>
where
Scalar: PrimeField + PrimeFieldBits,
{
type Constants = PoseidonConstantsCircuit<Scalar>;
/// Initialize the internal state and set the poseidon constants
fn new(constants: PoseidonConstantsCircuit<Scalar>) -> Self {
Self {
state: Vec::new(),
constants,
}
}
/// Absorb a new number into the state of the oracle
fn absorb(&mut self, e: AllocatedNum<Scalar>) {
self.state.push(e);
}
/// Compute a challenge by hashing the current state
pub fn get_challenge<CS>(&mut self, mut cs: CS) -> Result<Vec<AllocatedBit>, SynthesisError>
fn get_challenge<CS>(&mut self, mut cs: CS) -> Result<Vec<AllocatedBit>, SynthesisError>
where
CS: ConstraintSystem<Scalar>,
{
@ -205,7 +212,7 @@ where
Ok(bits[..NUM_CHALLENGE_BITS].into())
}
pub fn get_hash<CS>(&mut self, mut cs: CS) -> Result<Vec<AllocatedBit>, SynthesisError>
fn get_hash<CS>(&mut self, mut cs: CS) -> Result<Vec<AllocatedBit>, SynthesisError>
where
CS: ConstraintSystem<Scalar>,
{
@ -228,9 +235,9 @@ mod tests {
fn test_poseidon_ro() {
// Check that the number computed inside the circuit is equal to the number computed outside the circuit
let mut csprng: OsRng = OsRng;
let constants = ROConstantsCircuit::new();
let mut ro: PoseidonRO<S, B> = PoseidonRO::new(constants.clone());
let mut ro_gadget: PoseidonROGadget<S> = PoseidonROGadget::new(constants);
let constants = PoseidonConstantsCircuit::new();
let mut ro: PoseidonHashFunc<S, B> = PoseidonHashFunc::new(constants.clone());
let mut ro_gadget: PoseidonHashFuncCircuit<S> = PoseidonHashFuncCircuit::new(constants);
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
for i in 0..27 {
let num = S::random(&mut csprng);

+ 37
- 1
src/traits.rs

@ -1,5 +1,8 @@
//! This module defines various traits required by the users of the library to implement.
use bellperson::{gadgets::num::AllocatedNum, ConstraintSystem, SynthesisError};
use bellperson::{
gadgets::{boolean::AllocatedBit, num::AllocatedNum},
ConstraintSystem, SynthesisError,
};
use core::{
fmt::Debug,
ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign},
@ -38,6 +41,9 @@ pub trait Group:
/// from the base field and squeezes out elements of the scalar field
type HashFunc: HashFuncTrait<Self::Base, Self::Scalar>;
/// An alternate implementation of Self::HashFunc in the circuit model
type HashFuncCircuit: HashFuncCircuitTrait<Self::Base>;
/// A method to compute a multiexponentation
fn vartime_multiscalar_mul(
scalars: &[Self::Scalar],
@ -108,12 +114,42 @@ pub trait HashFuncTrait {
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
pub trait HashFuncCircuitTrait<Base: PrimeField> {
/// A type representing constants/parameters associated with the hash function
type Constants: HashFuncConstantsTrait<Base> + Clone + Send + Sync;
/// Initializes the hash function
fn new(constants: Self::Constants) -> Self;
/// Adds a scalar to the internal state
fn absorb(&mut self, e: AllocatedNum<Base>);
/// Returns a random challenge by hashing the internal state
fn get_challenge<CS>(&mut self, cs: CS) -> 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
CS: ConstraintSystem<Base>;
}
/// A helper trait that defines the constants associated with a hash function
pub trait HashFuncConstantsTrait<Base> {
/// produces constants/parameters associated with the hash function
fn new() -> Self;
}
/// An alias for constants associated with G::HashFunc
pub type HashFuncConstants<G> =
<<G as Group>::HashFunc as HashFuncTrait<<G as Group>::Base, <G as Group>::Scalar>>::Constants;
/// An alias for constants associated with G::HashFuncCircuit
pub type HashFuncConstantsCircuit<G> =
<<G as Group>::HashFuncCircuit as HashFuncCircuitTrait<<G as Group>::Base>>::Constants;
/// A helper trait for types with a group operation.
pub trait GroupOps<Rhs = Self, Output = Self>:
Add<Rhs, Output = Output> + Sub<Rhs, Output = Output> + AddAssign<Rhs> + SubAssign<Rhs>

Loading…
Cancel
Save