mirror of
https://github.com/arnaucube/Nova.git
synced 2026-01-10 16:11:29 +01:00
Refactor circuit code (#37)
* update crate versions * refactor * small tweaks * run cargo fmt * fix comments * remove unused code * address clippy Co-authored-by: Srinath Setty <srinath@microsoft.com>
This commit is contained in:
657
src/circuit.rs
657
src/circuit.rs
@@ -1,36 +1,31 @@
|
|||||||
//! There are two Verification Circuits. Each of them is over a Pasta curve but
|
//! There are two Verification Circuits. The primary and the secondary.
|
||||||
//! only one of them executes the next step of the computation by applying the inner function F.
|
//! Each of them is over a Pasta curve but
|
||||||
//! There are also two running relaxed r1cs instances.
|
//! only the primary executes the next step of the computation.
|
||||||
//!
|
//! TODO: The base case is different for the primary and the secondary.
|
||||||
//! When we build a circuit we denote u1 the running relaxed r1cs instance of
|
//! We have two running instances. Each circuit takes as input 2 hashes: one for each
|
||||||
//! the circuit and u2 the running relaxed r1cs instance of the other circuit.
|
//! of the running instances. Each of these hashes is
|
||||||
//! The circuit takes as input two hashes h1 and h2.
|
//! H(params = H(shape, gens), i, z0, zi, U). Each circuit folds the last invocation of
|
||||||
//! If the circuit applies the inner function F, then
|
//! the other into the running instance
|
||||||
//! h1 = H(params = H(shape, gens), u2, i, z0, zi) and h2 = H(u1, i)
|
|
||||||
//! otherwise
|
|
||||||
//! h1 = H(u2, i) and h2 = H(params = H(shape, gens), u1, i, z0, zi)
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
commitments::Commitment,
|
commitments::Commitment,
|
||||||
gadgets::{
|
gadgets::{
|
||||||
ecc::AllocatedPoint,
|
ecc::AllocatedPoint,
|
||||||
utils::{
|
r1cs::{AllocatedR1CSInstance, AllocatedRelaxedR1CSInstance},
|
||||||
alloc_bignat_constant, alloc_num_equals, alloc_one, alloc_zero, conditionally_select,
|
utils::{alloc_num_equals, alloc_zero, conditionally_select, le_bits_to_num},
|
||||||
conditionally_select_bignat, le_bits_to_num,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
poseidon::{NovaPoseidonConstants, PoseidonROGadget},
|
poseidon::{NovaPoseidonConstants, PoseidonROGadget},
|
||||||
r1cs::RelaxedR1CSInstance,
|
r1cs::{R1CSInstance, RelaxedR1CSInstance},
|
||||||
traits::{Group, StepCircuit},
|
traits::{Group, StepCircuit},
|
||||||
};
|
};
|
||||||
use bellperson::{
|
use bellperson::{
|
||||||
gadgets::{boolean::Boolean, num::AllocatedNum, Assignment},
|
gadgets::{
|
||||||
|
boolean::{AllocatedBit, Boolean},
|
||||||
|
num::AllocatedNum,
|
||||||
|
Assignment,
|
||||||
|
},
|
||||||
Circuit, ConstraintSystem, SynthesisError,
|
Circuit, ConstraintSystem, SynthesisError,
|
||||||
};
|
};
|
||||||
use bellperson_nonnative::{
|
|
||||||
mp::bignat::BigNat,
|
|
||||||
util::{convert::f_to_nat, num::Num},
|
|
||||||
};
|
|
||||||
use ff::{Field, PrimeField, PrimeFieldBits};
|
use ff::{Field, PrimeField, PrimeFieldBits};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -53,15 +48,13 @@ pub struct NIFSVerifierCircuitInputs<G>
|
|||||||
where
|
where
|
||||||
G: Group,
|
G: Group,
|
||||||
{
|
{
|
||||||
h1: G::Base,
|
params: G::Base, // Hash(Shape of u2, Gens for u2). Needed for computing the challenge.
|
||||||
h2: G::Base,
|
|
||||||
u2: RelaxedR1CSInstance<G>,
|
|
||||||
i: G::Base,
|
i: G::Base,
|
||||||
z0: G::Base,
|
z0: G::Base,
|
||||||
zi: G::Base,
|
zi: G::Base,
|
||||||
params: G::Base, // Hash(Shape of u2, Gens for u2). Needed for computing the challenge.
|
U: RelaxedR1CSInstance<G>,
|
||||||
|
u: R1CSInstance<G>,
|
||||||
T: Commitment<G>,
|
T: Commitment<G>,
|
||||||
w: Commitment<G>, // The commitment to the witness of the fresh r1cs instance
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G> NIFSVerifierCircuitInputs<G>
|
impl<G> NIFSVerifierCircuitInputs<G>
|
||||||
@@ -71,26 +64,22 @@ where
|
|||||||
/// Create new inputs/witness for the verification circuit
|
/// Create new inputs/witness for the verification circuit
|
||||||
#[allow(dead_code, clippy::too_many_arguments)]
|
#[allow(dead_code, clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
h1: G::Base,
|
params: G::Base,
|
||||||
u2: RelaxedR1CSInstance<G>,
|
|
||||||
i: G::Base,
|
i: G::Base,
|
||||||
z0: G::Base,
|
z0: G::Base,
|
||||||
zi: G::Base,
|
zi: G::Base,
|
||||||
h2: G::Base,
|
U: RelaxedR1CSInstance<G>,
|
||||||
params: G::Base,
|
u: R1CSInstance<G>,
|
||||||
T: Commitment<G>,
|
T: Commitment<G>,
|
||||||
w: Commitment<G>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
h1,
|
params,
|
||||||
u2,
|
|
||||||
i,
|
i,
|
||||||
z0,
|
z0,
|
||||||
zi,
|
zi,
|
||||||
h2,
|
U,
|
||||||
params,
|
u,
|
||||||
T,
|
T,
|
||||||
w,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,7 +100,8 @@ where
|
|||||||
impl<G, SC> NIFSVerifierCircuit<G, SC>
|
impl<G, SC> NIFSVerifierCircuit<G, SC>
|
||||||
where
|
where
|
||||||
G: Group,
|
G: Group,
|
||||||
<G as Group>::Base: ff::PrimeField,
|
<G as Group>::Base: PrimeField + PrimeFieldBits,
|
||||||
|
<G as Group>::Scalar: PrimeField + PrimeFieldBits,
|
||||||
SC: StepCircuit<G::Base>,
|
SC: StepCircuit<G::Base>,
|
||||||
{
|
{
|
||||||
/// Create a new verification circuit for the input relaxed r1cs instances
|
/// Create a new verification circuit for the input relaxed r1cs instances
|
||||||
@@ -132,6 +122,122 @@ where
|
|||||||
poseidon_constants,
|
poseidon_constants,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allocate all witnesses and return
|
||||||
|
fn alloc_witness<CS: ConstraintSystem<<G as Group>::Base>>(
|
||||||
|
&self,
|
||||||
|
mut cs: CS,
|
||||||
|
) -> Result<
|
||||||
|
(
|
||||||
|
AllocatedNum<G::Base>,
|
||||||
|
AllocatedNum<G::Base>,
|
||||||
|
AllocatedNum<G::Base>,
|
||||||
|
AllocatedNum<G::Base>,
|
||||||
|
AllocatedRelaxedR1CSInstance<G>,
|
||||||
|
AllocatedR1CSInstance<G>,
|
||||||
|
AllocatedPoint<G::Base>,
|
||||||
|
),
|
||||||
|
SynthesisError,
|
||||||
|
> {
|
||||||
|
// Allocate the params
|
||||||
|
let params = AllocatedNum::alloc(cs.namespace(|| "params"), || Ok(self.inputs.get()?.params))?;
|
||||||
|
|
||||||
|
// Allocate i
|
||||||
|
let i = AllocatedNum::alloc(cs.namespace(|| "i"), || Ok(self.inputs.get()?.i))?;
|
||||||
|
|
||||||
|
// Allocate z0
|
||||||
|
let z_0 = AllocatedNum::alloc(cs.namespace(|| "z0"), || Ok(self.inputs.get()?.z0))?;
|
||||||
|
|
||||||
|
// Allocate zi
|
||||||
|
let z_i = AllocatedNum::alloc(cs.namespace(|| "zi"), || Ok(self.inputs.get()?.zi))?;
|
||||||
|
|
||||||
|
// Allocate the running instance
|
||||||
|
let U: AllocatedRelaxedR1CSInstance<G> = AllocatedRelaxedR1CSInstance::alloc(
|
||||||
|
cs.namespace(|| "Allocate U"),
|
||||||
|
self
|
||||||
|
.inputs
|
||||||
|
.get()
|
||||||
|
.map_or(None, |inputs| Some(inputs.U.clone())),
|
||||||
|
self.params.limb_width,
|
||||||
|
self.params.n_limbs,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Allocate the instance to be folded in
|
||||||
|
let u = AllocatedR1CSInstance::alloc(
|
||||||
|
cs.namespace(|| "allocate instance u to fold"),
|
||||||
|
self
|
||||||
|
.inputs
|
||||||
|
.get()
|
||||||
|
.map_or(None, |inputs| Some(inputs.u.clone())),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Allocate T
|
||||||
|
let T = AllocatedPoint::alloc(
|
||||||
|
cs.namespace(|| "allocate T"),
|
||||||
|
self
|
||||||
|
.inputs
|
||||||
|
.get()
|
||||||
|
.map_or(None, |inputs| Some(inputs.T.comm.to_coordinates())),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok((params, i, z_0, z_i, U, u, T))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Synthesizes base case and returns the new relaxed R1CSInstance
|
||||||
|
fn synthesize_base_case<CS: ConstraintSystem<<G as Group>::Base>>(
|
||||||
|
&self,
|
||||||
|
mut cs: CS,
|
||||||
|
) -> Result<AllocatedRelaxedR1CSInstance<G>, SynthesisError> {
|
||||||
|
let U_default: AllocatedRelaxedR1CSInstance<G> = AllocatedRelaxedR1CSInstance::default(
|
||||||
|
cs.namespace(|| "Allocate U_default"),
|
||||||
|
self.params.limb_width,
|
||||||
|
self.params.n_limbs,
|
||||||
|
)?;
|
||||||
|
Ok(U_default)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Synthesizes non base case and returns the new relaxed R1CSInstance
|
||||||
|
/// And a boolean indicating if all checks pass
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn synthesize_non_base_case<CS: ConstraintSystem<<G as Group>::Base>>(
|
||||||
|
&self,
|
||||||
|
mut cs: CS,
|
||||||
|
params: AllocatedNum<G::Base>,
|
||||||
|
i: AllocatedNum<G::Base>,
|
||||||
|
z_0: AllocatedNum<G::Base>,
|
||||||
|
z_i: AllocatedNum<G::Base>,
|
||||||
|
U: AllocatedRelaxedR1CSInstance<G>,
|
||||||
|
u: AllocatedR1CSInstance<G>,
|
||||||
|
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.poseidon_constants.clone());
|
||||||
|
ro.absorb(params);
|
||||||
|
ro.absorb(i);
|
||||||
|
ro.absorb(z_0);
|
||||||
|
ro.absorb(z_i);
|
||||||
|
let _ = U.absorb_in_ro(cs.namespace(|| "absorb U"), &mut ro)?;
|
||||||
|
|
||||||
|
let hash_bits = ro.get_hash(cs.namespace(|| "Input hash"))?;
|
||||||
|
let hash = le_bits_to_num(cs.namespace(|| "bits to hash"), hash_bits)?;
|
||||||
|
let check_pass = alloc_num_equals(
|
||||||
|
cs.namespace(|| "check consistency of u.X[0] with H(params, U, i, z0, zi)"),
|
||||||
|
u.X0.clone(),
|
||||||
|
hash,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Run NIFS Verifier
|
||||||
|
let U_fold = U.fold_with_r1cs(
|
||||||
|
cs.namespace(|| "compute fold of U and u"),
|
||||||
|
u,
|
||||||
|
T,
|
||||||
|
self.poseidon_constants.clone(),
|
||||||
|
self.params.limb_width,
|
||||||
|
self.params.n_limbs,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok((U_fold, check_pass))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G, SC> Circuit<<G as Group>::Base> for NIFSVerifierCircuit<G, SC>
|
impl<G, SC> Circuit<<G as Group>::Base> for NIFSVerifierCircuit<G, SC>
|
||||||
@@ -145,322 +251,54 @@ where
|
|||||||
self,
|
self,
|
||||||
cs: &mut CS,
|
cs: &mut CS,
|
||||||
) -> Result<(), SynthesisError> {
|
) -> Result<(), SynthesisError> {
|
||||||
/***********************************************************************/
|
// Allocate all witnesses
|
||||||
// Allocate h1
|
let (params, i, z_0, z_i, U, u, T) =
|
||||||
/***********************************************************************/
|
self.alloc_witness(cs.namespace(|| "allocate the circuit witness"))?;
|
||||||
|
|
||||||
let h1 = AllocatedNum::alloc(cs.namespace(|| "allocate h1"), || Ok(self.inputs.get()?.h1))?;
|
// Compute variable indicating if this is the base case
|
||||||
let h1_bn = BigNat::from_num(
|
|
||||||
cs.namespace(|| "allocate h1_bn"),
|
|
||||||
Num::from(h1.clone()),
|
|
||||||
self.params.limb_width,
|
|
||||||
self.params.n_limbs,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
// This circuit does not modify h2 but it outputs it.
|
|
||||||
// Allocate it and output it.
|
|
||||||
/***********************************************************************/
|
|
||||||
|
|
||||||
// Allocate h2 as a big number with 8 limbs
|
|
||||||
let h2 = AllocatedNum::alloc(cs.namespace(|| "allocate h2"), || Ok(self.inputs.get()?.h2))?;
|
|
||||||
let h2_bn = BigNat::from_num(
|
|
||||||
cs.namespace(|| "allocate h2_bn"),
|
|
||||||
Num::from(h2.clone()),
|
|
||||||
self.params.limb_width,
|
|
||||||
self.params.n_limbs,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let _ = h2.inputize(cs.namespace(|| "Output 1"))?;
|
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
// Allocate u2 by allocating W_r, E_r, u_r, X_r
|
|
||||||
/***********************************************************************/
|
|
||||||
|
|
||||||
// W_r = (x, y, infinity)
|
|
||||||
let W_r = AllocatedPoint::alloc(
|
|
||||||
cs.namespace(|| "allocate W_r"),
|
|
||||||
self
|
|
||||||
.inputs
|
|
||||||
.get()
|
|
||||||
.map_or(None, |inputs| Some(inputs.u2.comm_W.comm.to_coordinates())),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// E_r = (x, y, infinity)
|
|
||||||
let E_r = AllocatedPoint::alloc(
|
|
||||||
cs.namespace(|| "allocate E_r"),
|
|
||||||
self
|
|
||||||
.inputs
|
|
||||||
.get()
|
|
||||||
.map_or(None, |inputs| Some(inputs.u2.comm_E.comm.to_coordinates())),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// u_r << |G::Base| despite the fact that u_r is a scalar.
|
|
||||||
// So we parse all of its bytes as a G::Base element
|
|
||||||
let u_r = AllocatedNum::alloc(cs.namespace(|| "u_r"), || {
|
|
||||||
let u_bits = self.inputs.get()?.u2.u.to_le_bits();
|
|
||||||
let mut mult = G::Base::one();
|
|
||||||
let mut u = G::Base::zero();
|
|
||||||
for bit in u_bits {
|
|
||||||
if bit {
|
|
||||||
u += mult;
|
|
||||||
}
|
|
||||||
mult = mult + mult;
|
|
||||||
}
|
|
||||||
Ok(u)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// The running X is two items! the running h1 and the running h2
|
|
||||||
let Xr0 = BigNat::alloc_from_nat(
|
|
||||||
cs.namespace(|| "allocate X_r[0]"),
|
|
||||||
|| Ok(f_to_nat(&self.inputs.get()?.u2.X[0])),
|
|
||||||
self.params.limb_width,
|
|
||||||
self.params.n_limbs,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Analyze Xr0 as limbs to use later
|
|
||||||
let Xr0_bn = Xr0
|
|
||||||
.as_limbs::<CS>()
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, limb)| {
|
|
||||||
limb
|
|
||||||
.as_sapling_allocated_num(cs.namespace(|| format!("convert limb {} of X_r[0] to num", i)))
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<AllocatedNum<G::Base>>, _>>()?;
|
|
||||||
|
|
||||||
let Xr1 = BigNat::alloc_from_nat(
|
|
||||||
cs.namespace(|| "allocate X_r[1]"),
|
|
||||||
|| Ok(f_to_nat(&self.inputs.get()?.u2.X[1])),
|
|
||||||
self.params.limb_width,
|
|
||||||
self.params.n_limbs,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Analyze Xr1 as limbs to use later
|
|
||||||
let Xr1_bn = Xr1
|
|
||||||
.as_limbs::<CS>()
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, limb)| {
|
|
||||||
limb
|
|
||||||
.as_sapling_allocated_num(cs.namespace(|| format!("convert limb {} of X_r[1] to num", i)))
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<AllocatedNum<G::Base>>, _>>()?;
|
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
// Allocate i
|
|
||||||
/***********************************************************************/
|
|
||||||
|
|
||||||
let i = AllocatedNum::alloc(cs.namespace(|| "i"), || Ok(self.inputs.get()?.i))?;
|
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
// Allocate T
|
|
||||||
/***********************************************************************/
|
|
||||||
|
|
||||||
// T = (x, y, infinity)
|
|
||||||
|
|
||||||
let T = AllocatedPoint::alloc(
|
|
||||||
cs.namespace(|| "allocate T"),
|
|
||||||
self
|
|
||||||
.inputs
|
|
||||||
.get()
|
|
||||||
.map_or(None, |inputs| Some(inputs.T.comm.to_coordinates())),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
// Allocate params
|
|
||||||
/***********************************************************************/
|
|
||||||
|
|
||||||
let params = AllocatedNum::alloc(cs.namespace(|| "params"), || Ok(self.inputs.get()?.params))?;
|
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
// Allocate W
|
|
||||||
/***********************************************************************/
|
|
||||||
|
|
||||||
// W = (x, y, infinity)
|
|
||||||
let W = AllocatedPoint::alloc(
|
|
||||||
cs.namespace(|| "allocate W"),
|
|
||||||
self
|
|
||||||
.inputs
|
|
||||||
.get()
|
|
||||||
.map_or(None, |inputs| Some(inputs.w.comm.to_coordinates())),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
// U2' = default if i == 0, otherwise NIFS.V(pp, u_new, U, T)
|
|
||||||
/***********************************************************************/
|
|
||||||
|
|
||||||
// Allocate 0 and 1
|
|
||||||
let zero = alloc_zero(cs.namespace(|| "zero"))?;
|
let zero = alloc_zero(cs.namespace(|| "zero"))?;
|
||||||
let one = alloc_one(cs.namespace(|| "one"))?;
|
let is_base_case = alloc_num_equals(cs.namespace(|| "Check if base case"), i.clone(), zero)?; //TODO: maybe optimize this?
|
||||||
|
|
||||||
// Compute default values of U2':
|
// Synthesize the circuit for the base case and get the new running instance
|
||||||
// W_default and E_default are a commitment to zero
|
let Unew_base = self.synthesize_base_case(cs.namespace(|| "base case"))?;
|
||||||
let W_default = AllocatedPoint::new(zero.clone(), zero.clone(), one);
|
|
||||||
let E_default = W_default.clone();
|
|
||||||
|
|
||||||
// u_default = 0
|
// Synthesize the circuit for the non-base case and get the new running
|
||||||
let u_default = zero.clone();
|
// instance along with a boolean indicating if all checks have passed
|
||||||
|
let (Unew_non_base, check_non_base_pass) = self.synthesize_non_base_case(
|
||||||
// X_default = 0
|
cs.namespace(|| "synthesize non base case"),
|
||||||
let X0_default = BigNat::alloc_from_nat(
|
params.clone(),
|
||||||
cs.namespace(|| "allocate x_default[0]"),
|
i.clone(),
|
||||||
|| Ok(f_to_nat(&G::Scalar::zero())),
|
z_0.clone(),
|
||||||
self.params.limb_width,
|
z_i.clone(),
|
||||||
self.params.n_limbs,
|
U,
|
||||||
|
u.clone(),
|
||||||
|
T,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let X1_default = BigNat::alloc_from_nat(
|
// Either check_non_base_pass=true or we are in the base case
|
||||||
cs.namespace(|| "allocate x_default[1]"),
|
let should_be_false = AllocatedBit::nor(
|
||||||
|| Ok(f_to_nat(&G::Scalar::zero())),
|
cs.namespace(|| "check_non_base_pass nor base_case"),
|
||||||
self.params.limb_width,
|
&check_non_base_pass,
|
||||||
self.params.n_limbs,
|
&is_base_case,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Compute r:
|
|
||||||
|
|
||||||
let mut ro: PoseidonROGadget<G::Base> = PoseidonROGadget::new(self.poseidon_constants.clone());
|
|
||||||
|
|
||||||
ro.absorb(h1.clone());
|
|
||||||
ro.absorb(h2);
|
|
||||||
ro.absorb(W.x.clone());
|
|
||||||
ro.absorb(W.y.clone());
|
|
||||||
ro.absorb(W.is_infinity.clone());
|
|
||||||
ro.absorb(T.x.clone());
|
|
||||||
ro.absorb(T.y.clone());
|
|
||||||
ro.absorb(T.is_infinity.clone());
|
|
||||||
// absorb each of the limbs of X_r[0]
|
|
||||||
for limb in Xr0_bn.clone().into_iter() {
|
|
||||||
ro.absorb(limb);
|
|
||||||
}
|
|
||||||
|
|
||||||
// absorb each of the limbs of X_r[1]
|
|
||||||
for limb in Xr1_bn.clone().into_iter() {
|
|
||||||
ro.absorb(limb);
|
|
||||||
}
|
|
||||||
|
|
||||||
let r_bits = ro.get_challenge(cs.namespace(|| "r bits"))?;
|
|
||||||
let r = le_bits_to_num(cs.namespace(|| "r"), r_bits.clone())?;
|
|
||||||
|
|
||||||
// W_fold = W_r + r * W
|
|
||||||
let rW = W.scalar_mul(cs.namespace(|| "r * W"), r_bits.clone())?;
|
|
||||||
let W_fold = W_r.add(cs.namespace(|| "W_r + r * W"), &rW)?;
|
|
||||||
|
|
||||||
// E_fold = E_r + r * T
|
|
||||||
let rT = T.scalar_mul(cs.namespace(|| "r * T"), r_bits)?;
|
|
||||||
let E_fold = E_r.add(cs.namespace(|| "E_r + r * T"), &rT)?;
|
|
||||||
|
|
||||||
// u_fold = u_r + r
|
|
||||||
let u_fold = AllocatedNum::alloc(cs.namespace(|| "u_fold"), || {
|
|
||||||
Ok(*u_r.get_value().get()? + r.get_value().get()?)
|
|
||||||
})?;
|
|
||||||
cs.enforce(
|
cs.enforce(
|
||||||
|| "Check u_fold",
|
|| "check_non_base_pass nor base_case = false",
|
||||||
|
|lc| lc + should_be_false.get_variable(),
|
||||||
|
|lc| lc + CS::one(),
|
||||||
|lc| lc,
|
|lc| lc,
|
||||||
|lc| lc,
|
|
||||||
|lc| lc + u_fold.get_variable() - u_r.get_variable() - r.get_variable(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Fold the IO:
|
// Compute the U_new
|
||||||
// Analyze r into limbs
|
let Unew = Unew_base.conditionally_select(
|
||||||
let r_bn = BigNat::from_num(
|
cs.namespace(|| "compute U_new"),
|
||||||
cs.namespace(|| "allocate r_bn"),
|
Unew_non_base,
|
||||||
Num::from(r.clone()),
|
&Boolean::from(is_base_case.clone()),
|
||||||
self.params.limb_width,
|
|
||||||
self.params.n_limbs,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Allocate the order of the non-native field as a constant
|
|
||||||
let m_bn = alloc_bignat_constant(
|
|
||||||
cs.namespace(|| "alloc m"),
|
|
||||||
&G::get_order(),
|
|
||||||
self.params.limb_width,
|
|
||||||
self.params.n_limbs,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// First the fold h1 with X_r[0];
|
|
||||||
let (_, r_0) = h1_bn.mult_mod(cs.namespace(|| "r*h1"), &r_bn, &m_bn)?;
|
|
||||||
// add X_r[0]
|
|
||||||
let r_new_0 = Xr0.add::<CS>(&r_0)?;
|
|
||||||
// Now reduce
|
|
||||||
let Xr0_fold = r_new_0.red_mod(cs.namespace(|| "reduce folded X_r[0]"), &m_bn)?;
|
|
||||||
|
|
||||||
// First the fold h2 with X_r[1];
|
|
||||||
let (_, r_1) = h2_bn.mult_mod(cs.namespace(|| "r*h2"), &r_bn, &m_bn)?;
|
|
||||||
// add X_r[1]
|
|
||||||
let r_new_1 = Xr1.add::<CS>(&r_1)?;
|
|
||||||
// Now reduce
|
|
||||||
let Xr1_fold = r_new_1.red_mod(cs.namespace(|| "reduce folded X_r[1]"), &m_bn)?;
|
|
||||||
|
|
||||||
// Now select the default values if i == 0 otherwise the fold values
|
|
||||||
let is_base_case = Boolean::from(alloc_num_equals(
|
|
||||||
cs.namespace(|| "Check if base case"),
|
|
||||||
i.clone(),
|
|
||||||
zero,
|
|
||||||
)?);
|
|
||||||
|
|
||||||
let W_new = AllocatedPoint::conditionally_select(
|
|
||||||
cs.namespace(|| "W_new"),
|
|
||||||
&W_default,
|
|
||||||
&W_fold,
|
|
||||||
&is_base_case,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let E_new = AllocatedPoint::conditionally_select(
|
|
||||||
cs.namespace(|| "E_new"),
|
|
||||||
&E_default,
|
|
||||||
&E_fold,
|
|
||||||
&is_base_case,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let u_new = conditionally_select(cs.namespace(|| "u_new"), &u_default, &u_fold, &is_base_case)?;
|
|
||||||
|
|
||||||
let Xr0_new = conditionally_select_bignat(
|
|
||||||
cs.namespace(|| "X_r_new[0]"),
|
|
||||||
&X0_default,
|
|
||||||
&Xr0_fold,
|
|
||||||
&is_base_case,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Analyze Xr0_new as limbs to use later
|
|
||||||
let Xr0_new_bn = Xr0_new
|
|
||||||
.as_limbs::<CS>()
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, limb)| {
|
|
||||||
limb.as_sapling_allocated_num(
|
|
||||||
cs.namespace(|| format!("convert limb {} of X_r_new[0] to num", i)),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<AllocatedNum<G::Base>>, _>>()?;
|
|
||||||
|
|
||||||
let Xr1_new = conditionally_select_bignat(
|
|
||||||
cs.namespace(|| "X_r_new[1]"),
|
|
||||||
&X1_default,
|
|
||||||
&Xr1_fold,
|
|
||||||
&is_base_case,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Analyze Xr1_new as limbs to use later
|
|
||||||
let Xr1_new_bn = Xr1_new
|
|
||||||
.as_limbs::<CS>()
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, limb)| {
|
|
||||||
limb.as_sapling_allocated_num(
|
|
||||||
cs.namespace(|| format!("convert limb {} of X_r_new[1] to num", i)),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<AllocatedNum<G::Base>>, _>>()?;
|
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
// Compute i + 1
|
// Compute i + 1
|
||||||
/***********************************************************************/
|
|
||||||
|
|
||||||
let i_new = AllocatedNum::alloc(cs.namespace(|| "i + 1"), || {
|
let i_new = AllocatedNum::alloc(cs.namespace(|| "i + 1"), || {
|
||||||
Ok(*i.get_value().get()? + G::Base::one())
|
Ok(*i.get_value().get()? + G::Base::one())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
cs.enforce(
|
cs.enforce(
|
||||||
|| "check i + 1",
|
|| "check i + 1",
|
||||||
|lc| lc,
|
|lc| lc,
|
||||||
@@ -468,113 +306,32 @@ where
|
|||||||
|lc| lc + i_new.get_variable() - CS::one() - i.get_variable(),
|
|lc| lc + i_new.get_variable() - CS::one() - i.get_variable(),
|
||||||
);
|
);
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
// Allocate z0
|
|
||||||
/***********************************************************************/
|
|
||||||
|
|
||||||
let z_0 = AllocatedNum::alloc(cs.namespace(|| "z0"), || Ok(self.inputs.get()?.z0))?;
|
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
// Allocate zi
|
|
||||||
/***********************************************************************/
|
|
||||||
|
|
||||||
let z_i = AllocatedNum::alloc(cs.namespace(|| "zi"), || Ok(self.inputs.get()?.zi))?;
|
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
//Check that if i == 0, z0 = zi, that is (i == 0) AND (z0 != zi) = false
|
|
||||||
/***********************************************************************/
|
|
||||||
|
|
||||||
let z0_is_zi = Boolean::from(alloc_num_equals(
|
|
||||||
cs.namespace(|| "z0 = zi"),
|
|
||||||
z_0.clone(),
|
|
||||||
z_i.clone(),
|
|
||||||
)?);
|
|
||||||
|
|
||||||
cs.enforce(
|
|
||||||
|| "i == 0 and z0 != zi = false",
|
|
||||||
|_| is_base_case.lc(CS::one(), G::Base::one()),
|
|
||||||
|_| z0_is_zi.not().lc(CS::one(), G::Base::one()),
|
|
||||||
|lc| lc,
|
|
||||||
);
|
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
// Check that h1 = Hash(params, u2,i,z0,zi)
|
|
||||||
/***********************************************************************/
|
|
||||||
|
|
||||||
let mut h1_hash: PoseidonROGadget<G::Base> =
|
|
||||||
PoseidonROGadget::new(self.poseidon_constants.clone());
|
|
||||||
|
|
||||||
h1_hash.absorb(params.clone());
|
|
||||||
h1_hash.absorb(W_r.x);
|
|
||||||
h1_hash.absorb(W_r.y);
|
|
||||||
h1_hash.absorb(W_r.is_infinity);
|
|
||||||
h1_hash.absorb(E_r.x);
|
|
||||||
h1_hash.absorb(E_r.y);
|
|
||||||
h1_hash.absorb(E_r.is_infinity);
|
|
||||||
h1_hash.absorb(u_r.clone());
|
|
||||||
|
|
||||||
// absorb each of the limbs of X_r[0]
|
|
||||||
for limb in Xr0_bn.into_iter() {
|
|
||||||
h1_hash.absorb(limb);
|
|
||||||
}
|
|
||||||
|
|
||||||
// absorb each of the limbs of X_r[1]
|
|
||||||
for limb in Xr1_bn.into_iter() {
|
|
||||||
h1_hash.absorb(limb);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1_hash.absorb(i.clone());
|
|
||||||
h1_hash.absorb(z_0.clone());
|
|
||||||
h1_hash.absorb(z_i.clone());
|
|
||||||
|
|
||||||
let hash_bits = h1_hash.get_challenge(cs.namespace(|| "Input hash"))?; // TODO: use get_hash method
|
|
||||||
let hash = le_bits_to_num(cs.namespace(|| "bits to hash"), hash_bits)?;
|
|
||||||
|
|
||||||
cs.enforce(
|
|
||||||
|| "check h1",
|
|
||||||
|lc| lc,
|
|
||||||
|lc| lc,
|
|
||||||
|lc| lc + h1.get_variable() - hash.get_variable(),
|
|
||||||
);
|
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
// Compute z_{i+1}
|
// Compute z_{i+1}
|
||||||
/***********************************************************************/
|
let z_input = conditionally_select(
|
||||||
|
cs.namespace(|| "select input to F"),
|
||||||
|
&z_0,
|
||||||
|
&z_i,
|
||||||
|
&Boolean::from(is_base_case),
|
||||||
|
)?;
|
||||||
let z_next = self
|
let z_next = self
|
||||||
.step_circuit
|
.step_circuit
|
||||||
.synthesize(&mut cs.namespace(|| "F"), z_i)?;
|
.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, u2_new, i+1, z0, z_{i+1})
|
let mut ro: PoseidonROGadget<G::Base> = PoseidonROGadget::new(self.poseidon_constants);
|
||||||
/***********************************************************************/
|
ro.absorb(params);
|
||||||
|
ro.absorb(i_new.clone());
|
||||||
|
ro.absorb(z_0);
|
||||||
|
ro.absorb(z_next);
|
||||||
|
let _ = Unew.absorb_in_ro(cs.namespace(|| "absorb U_new"), &mut ro)?;
|
||||||
|
let hash_bits = ro.get_hash(cs.namespace(|| "output hash bits"))?;
|
||||||
|
let hash = le_bits_to_num(cs.namespace(|| "convert hash to num"), hash_bits)?;
|
||||||
|
|
||||||
h1_hash.flush_state();
|
// Outputs the computed hash and u.X[1] that corresponds to the hash of the other circuit
|
||||||
h1_hash.absorb(params);
|
let _ = hash.inputize(cs.namespace(|| "output new hash of this circuit"))?;
|
||||||
h1_hash.absorb(W_new.x.clone());
|
let _ = u
|
||||||
h1_hash.absorb(W_new.y.clone());
|
.X1
|
||||||
h1_hash.absorb(W_new.is_infinity);
|
.inputize(cs.namespace(|| "Output unmodified hash of the other circuit"))?;
|
||||||
h1_hash.absorb(E_new.x.clone());
|
|
||||||
h1_hash.absorb(E_new.y.clone());
|
|
||||||
h1_hash.absorb(E_new.is_infinity);
|
|
||||||
h1_hash.absorb(u_new);
|
|
||||||
|
|
||||||
// absorb each of the limbs of X_r_new[0]
|
|
||||||
for limb in Xr0_new_bn.into_iter() {
|
|
||||||
h1_hash.absorb(limb);
|
|
||||||
}
|
|
||||||
|
|
||||||
// absorb each of the limbs of X_r_new[1]
|
|
||||||
for limb in Xr1_new_bn.into_iter() {
|
|
||||||
h1_hash.absorb(limb);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1_hash.absorb(i_new.clone());
|
|
||||||
h1_hash.absorb(z_0);
|
|
||||||
h1_hash.absorb(z_next);
|
|
||||||
let h1_new_bits = h1_hash.get_challenge(cs.namespace(|| "h1_new bits"))?; // TODO: use get_hash method
|
|
||||||
let h1_new = le_bits_to_num(cs.namespace(|| "h1_new"), h1_new_bits)?;
|
|
||||||
let _ = h1_new.inputize(cs.namespace(|| "output h1_new"))?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -628,6 +385,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
poseidon_constants1.clone(),
|
poseidon_constants1.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// First create the shape
|
// First create the shape
|
||||||
let mut cs: ShapeCS<G1> = ShapeCS::new();
|
let mut cs: ShapeCS<G1> = ShapeCS::new();
|
||||||
let _ = circuit1.synthesize(&mut cs);
|
let _ = circuit1.synthesize(&mut cs);
|
||||||
@@ -658,27 +416,20 @@ mod tests {
|
|||||||
cs.num_constraints()
|
cs.num_constraints()
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: We need to hardwire default hash or give it as input
|
let zero = <<G2 as Group>::Base as Field>::zero();
|
||||||
let default_hash = <<G2 as Group>::Base as ff::PrimeField>::from_str_vartime(
|
let zero_fq = <<G2 as Group>::Scalar as Field>::zero();
|
||||||
"332553638888022689042501686561503049809",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let T = vec![<G2 as Group>::Scalar::zero()].commit(&gens2.gens_E);
|
let T = vec![<G2 as Group>::Scalar::zero()].commit(&gens2.gens_E);
|
||||||
let w = vec![<G2 as Group>::Scalar::zero()].commit(&gens2.gens_E);
|
let w = vec![<G2 as Group>::Scalar::zero()].commit(&gens2.gens_E);
|
||||||
|
|
||||||
// Now get an assignment
|
// Now get an assignment
|
||||||
let mut cs: SatisfyingAssignment<G1> = SatisfyingAssignment::new();
|
let mut cs: SatisfyingAssignment<G1> = SatisfyingAssignment::new();
|
||||||
let inputs: NIFSVerifierCircuitInputs<G2> = NIFSVerifierCircuitInputs::new(
|
let inputs: NIFSVerifierCircuitInputs<G2> = NIFSVerifierCircuitInputs::new(
|
||||||
default_hash,
|
<<G2 as Group>::Base as Field>::zero(), // TODO: provide real inputs
|
||||||
|
zero, // TODO: provide real inputs
|
||||||
|
zero, // TODO: provide real inputs
|
||||||
|
zero, // TODO: provide real inputs
|
||||||
RelaxedR1CSInstance::default(&gens2, &shape2),
|
RelaxedR1CSInstance::default(&gens2, &shape2),
|
||||||
<<G2 as Group>::Base as Field>::zero(), // TODO: provide real inputs
|
R1CSInstance::new(&shape2, &w, &[zero_fq, zero_fq]).unwrap(),
|
||||||
<<G2 as Group>::Base as Field>::zero(), // TODO: provide real inputs
|
T, // TODO: provide real inputs
|
||||||
<<G2 as Group>::Base as Field>::zero(), // TODO: provide real inputs
|
|
||||||
<<G2 as Group>::Base as Field>::zero(), // TODO: provide real inputs
|
|
||||||
<<G2 as Group>::Base as Field>::zero(), // TODO: provide real inputs
|
|
||||||
T, // TODO: provide real inputs
|
|
||||||
w,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let circuit: NIFSVerifierCircuit<G2, TestCircuit<<G2 as Group>::Base>> =
|
let circuit: NIFSVerifierCircuit<G2, TestCircuit<<G2 as Group>::Base>> =
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
pub mod ecc;
|
pub mod ecc;
|
||||||
|
pub mod r1cs;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|||||||
363
src/gadgets/r1cs.rs
Normal file
363
src/gadgets/r1cs.rs
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
use crate::{
|
||||||
|
gadgets::{
|
||||||
|
ecc::AllocatedPoint,
|
||||||
|
utils::{
|
||||||
|
alloc_bignat_constant, alloc_one, alloc_scalar_as_base, alloc_zero, conditionally_select,
|
||||||
|
conditionally_select_bignat, le_bits_to_num,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
poseidon::{NovaPoseidonConstants, PoseidonROGadget},
|
||||||
|
r1cs::{R1CSInstance, RelaxedR1CSInstance},
|
||||||
|
traits::Group,
|
||||||
|
};
|
||||||
|
use bellperson::{
|
||||||
|
gadgets::{boolean::Boolean, num::AllocatedNum, Assignment},
|
||||||
|
ConstraintSystem, SynthesisError,
|
||||||
|
};
|
||||||
|
use bellperson_nonnative::{
|
||||||
|
mp::bignat::BigNat,
|
||||||
|
util::{convert::f_to_nat, num::Num},
|
||||||
|
};
|
||||||
|
use ff::{Field, PrimeField, PrimeFieldBits};
|
||||||
|
|
||||||
|
/// An Allocated R1CS Instance
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AllocatedR1CSInstance<G>
|
||||||
|
where
|
||||||
|
G: Group,
|
||||||
|
{
|
||||||
|
pub(crate) W: AllocatedPoint<G::Base>,
|
||||||
|
pub(crate) X0: AllocatedNum<G::Base>,
|
||||||
|
pub(crate) X1: AllocatedNum<G::Base>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G> AllocatedR1CSInstance<G>
|
||||||
|
where
|
||||||
|
G: Group,
|
||||||
|
<G as Group>::Base: PrimeField + PrimeFieldBits,
|
||||||
|
<G as Group>::Scalar: PrimeFieldBits,
|
||||||
|
{
|
||||||
|
/// Takes the r1cs instance and creates a new allocated r1cs instance
|
||||||
|
pub fn alloc<CS: ConstraintSystem<<G as Group>::Base>>(
|
||||||
|
mut cs: CS,
|
||||||
|
u: Option<R1CSInstance<G>>,
|
||||||
|
) -> Result<Self, SynthesisError> {
|
||||||
|
// Check that the incoming instance has exactly 2 io
|
||||||
|
let W = AllocatedPoint::alloc(
|
||||||
|
cs.namespace(|| "allocate W"),
|
||||||
|
u.get()
|
||||||
|
.map_or(None, |u| Some(u.comm_W.comm.to_coordinates())),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let X0 = alloc_scalar_as_base::<G, _>(
|
||||||
|
cs.namespace(|| "allocate X[0]"),
|
||||||
|
u.get().map_or(None, |u| Some(u.X[0])),
|
||||||
|
)?;
|
||||||
|
let X1 = alloc_scalar_as_base::<G, _>(
|
||||||
|
cs.namespace(|| "allocate X[1]"),
|
||||||
|
u.get().map_or(None, |u| Some(u.X[1])),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(AllocatedR1CSInstance { W, X0, X1 })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn absorb_in_ro(&self, ro: &mut PoseidonROGadget<G::Base>) {
|
||||||
|
ro.absorb(self.W.x.clone());
|
||||||
|
ro.absorb(self.W.y.clone());
|
||||||
|
ro.absorb(self.W.is_infinity.clone());
|
||||||
|
ro.absorb(self.X0.clone());
|
||||||
|
ro.absorb(self.X1.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An Allocated Relaxed R1CS Instance
|
||||||
|
pub struct AllocatedRelaxedR1CSInstance<G>
|
||||||
|
where
|
||||||
|
G: Group,
|
||||||
|
<G as Group>::Base: PrimeField + PrimeFieldBits,
|
||||||
|
<G as Group>::Scalar: PrimeFieldBits,
|
||||||
|
{
|
||||||
|
pub(crate) W: AllocatedPoint<G::Base>,
|
||||||
|
pub(crate) E: AllocatedPoint<G::Base>,
|
||||||
|
pub(crate) u: AllocatedNum<G::Base>,
|
||||||
|
pub(crate) X0: BigNat<G::Base>,
|
||||||
|
pub(crate) X1: BigNat<G::Base>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G> AllocatedRelaxedR1CSInstance<G>
|
||||||
|
where
|
||||||
|
G: Group,
|
||||||
|
<G as Group>::Base: PrimeField + PrimeFieldBits,
|
||||||
|
<G as Group>::Scalar: PrimeFieldBits,
|
||||||
|
{
|
||||||
|
/// Allocates the given RelaxedR1CSInstance as a witness of the circuit
|
||||||
|
pub fn alloc<CS: ConstraintSystem<<G as Group>::Base>>(
|
||||||
|
mut cs: CS,
|
||||||
|
inst: Option<RelaxedR1CSInstance<G>>,
|
||||||
|
limb_width: usize,
|
||||||
|
n_limbs: usize,
|
||||||
|
) -> Result<Self, SynthesisError> {
|
||||||
|
let W = AllocatedPoint::alloc(
|
||||||
|
cs.namespace(|| "allocate W"),
|
||||||
|
inst
|
||||||
|
.get()
|
||||||
|
.map_or(None, |inst| Some(inst.comm_W.comm.to_coordinates())),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let E = AllocatedPoint::alloc(
|
||||||
|
cs.namespace(|| "allocate E"),
|
||||||
|
inst
|
||||||
|
.get()
|
||||||
|
.map_or(None, |inst| Some(inst.comm_E.comm.to_coordinates())),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// u << |G::Base| despite the fact that u is a scalar.
|
||||||
|
// So we parse all of its bytes as a G::Base element
|
||||||
|
let u = alloc_scalar_as_base::<G, _>(
|
||||||
|
cs.namespace(|| "allocate u"),
|
||||||
|
inst.get().map_or(None, |inst| Some(inst.u)),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let X0 = BigNat::alloc_from_nat(
|
||||||
|
cs.namespace(|| "allocate X[0]"),
|
||||||
|
|| Ok(f_to_nat(&inst.get()?.X[0])),
|
||||||
|
limb_width,
|
||||||
|
n_limbs,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let X1 = BigNat::alloc_from_nat(
|
||||||
|
cs.namespace(|| "allocate X[1]"),
|
||||||
|
|| Ok(f_to_nat(&inst.get()?.X[1])),
|
||||||
|
limb_width,
|
||||||
|
n_limbs,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(AllocatedRelaxedR1CSInstance { W, E, u, X0, X1 })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocates the hardcoded default RelaxedR1CSInstance in the circuit.
|
||||||
|
/// W = E = 0, u = 1, X0 = X1 = 0
|
||||||
|
pub fn default<CS: ConstraintSystem<<G as Group>::Base>>(
|
||||||
|
mut cs: CS,
|
||||||
|
limb_width: usize,
|
||||||
|
n_limbs: usize,
|
||||||
|
) -> Result<Self, SynthesisError> {
|
||||||
|
let zero = alloc_zero(cs.namespace(|| "zero"))?;
|
||||||
|
let one = alloc_one(cs.namespace(|| "one"))?;
|
||||||
|
|
||||||
|
let W_default = AllocatedPoint::new(zero.clone(), zero.clone(), one);
|
||||||
|
let E_default = W_default.clone();
|
||||||
|
|
||||||
|
let u_default = zero;
|
||||||
|
|
||||||
|
let X0_default = BigNat::alloc_from_nat(
|
||||||
|
cs.namespace(|| "allocate x_default[0]"),
|
||||||
|
|| Ok(f_to_nat(&G::Scalar::zero())),
|
||||||
|
limb_width,
|
||||||
|
n_limbs,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let X1_default = BigNat::alloc_from_nat(
|
||||||
|
cs.namespace(|| "allocate x_default[1]"),
|
||||||
|
|| Ok(f_to_nat(&G::Scalar::zero())),
|
||||||
|
limb_width,
|
||||||
|
n_limbs,
|
||||||
|
)?;
|
||||||
|
Ok(AllocatedRelaxedR1CSInstance {
|
||||||
|
W: W_default,
|
||||||
|
E: E_default,
|
||||||
|
u: u_default,
|
||||||
|
X0: X0_default,
|
||||||
|
X1: X1_default,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn absorb_in_ro<CS: ConstraintSystem<<G as Group>::Base>>(
|
||||||
|
&self,
|
||||||
|
mut cs: CS,
|
||||||
|
ro: &mut PoseidonROGadget<G::Base>,
|
||||||
|
) -> Result<(), SynthesisError> {
|
||||||
|
ro.absorb(self.W.x.clone());
|
||||||
|
ro.absorb(self.W.y.clone());
|
||||||
|
ro.absorb(self.W.is_infinity.clone());
|
||||||
|
ro.absorb(self.E.x.clone());
|
||||||
|
ro.absorb(self.E.y.clone());
|
||||||
|
ro.absorb(self.E.is_infinity.clone());
|
||||||
|
ro.absorb(self.u.clone());
|
||||||
|
|
||||||
|
// Analyze X0 as limbs
|
||||||
|
let X0_bn = self
|
||||||
|
.X0
|
||||||
|
.as_limbs::<CS>()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, limb)| {
|
||||||
|
limb
|
||||||
|
.as_sapling_allocated_num(cs.namespace(|| format!("convert limb {} of X_r[0] to num", i)))
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<AllocatedNum<G::Base>>, _>>()?;
|
||||||
|
|
||||||
|
// absorb each of the limbs of X[0]
|
||||||
|
for limb in X0_bn.into_iter() {
|
||||||
|
ro.absorb(limb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Analyze X1 as limbs
|
||||||
|
let X1_bn = self
|
||||||
|
.X1
|
||||||
|
.as_limbs::<CS>()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, limb)| {
|
||||||
|
limb
|
||||||
|
.as_sapling_allocated_num(cs.namespace(|| format!("convert limb {} of X_r[1] to num", i)))
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<AllocatedNum<G::Base>>, _>>()?;
|
||||||
|
|
||||||
|
// absorb each of the limbs of X[1]
|
||||||
|
for limb in X1_bn.into_iter() {
|
||||||
|
ro.absorb(limb);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Folds self with a relaxed r1cs instance and returns the result
|
||||||
|
pub fn fold_with_r1cs<CS: ConstraintSystem<<G as Group>::Base>>(
|
||||||
|
&self,
|
||||||
|
mut cs: CS,
|
||||||
|
u: AllocatedR1CSInstance<G>,
|
||||||
|
T: AllocatedPoint<G::Base>,
|
||||||
|
poseidon_constants: NovaPoseidonConstants<G::Base>,
|
||||||
|
limb_width: usize,
|
||||||
|
n_limbs: usize,
|
||||||
|
) -> Result<AllocatedRelaxedR1CSInstance<G>, SynthesisError> {
|
||||||
|
// Compute r:
|
||||||
|
let mut ro: PoseidonROGadget<G::Base> = PoseidonROGadget::new(poseidon_constants);
|
||||||
|
u.absorb_in_ro(&mut ro);
|
||||||
|
ro.absorb(T.x.clone());
|
||||||
|
ro.absorb(T.y.clone());
|
||||||
|
ro.absorb(T.is_infinity.clone());
|
||||||
|
let r_bits = ro.get_challenge(cs.namespace(|| "r bits"))?;
|
||||||
|
let r = le_bits_to_num(cs.namespace(|| "r"), r_bits.clone())?;
|
||||||
|
|
||||||
|
// W_fold = self.W + r * u.W
|
||||||
|
let rW = u.W.scalar_mul(cs.namespace(|| "r * u.W"), r_bits.clone())?;
|
||||||
|
let W_fold = self.W.add(cs.namespace(|| "self.W + r * u.W"), &rW)?;
|
||||||
|
|
||||||
|
// E_fold = self.E + r * T
|
||||||
|
let rT = T.scalar_mul(cs.namespace(|| "r * T"), r_bits)?;
|
||||||
|
let E_fold = self.E.add(cs.namespace(|| "self.E + r * T"), &rT)?;
|
||||||
|
|
||||||
|
// u_fold = u_r + r
|
||||||
|
let u_fold = AllocatedNum::alloc(cs.namespace(|| "u_fold"), || {
|
||||||
|
Ok(*self.u.get_value().get()? + r.get_value().get()?)
|
||||||
|
})?;
|
||||||
|
cs.enforce(
|
||||||
|
|| "Check u_fold",
|
||||||
|
|lc| lc,
|
||||||
|
|lc| lc,
|
||||||
|
|lc| lc + u_fold.get_variable() - self.u.get_variable() - r.get_variable(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Fold the IO:
|
||||||
|
// Analyze r into limbs
|
||||||
|
let r_bn = BigNat::from_num(
|
||||||
|
cs.namespace(|| "allocate r_bn"),
|
||||||
|
Num::from(r.clone()),
|
||||||
|
limb_width,
|
||||||
|
n_limbs,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Allocate the order of the non-native field as a constant
|
||||||
|
let m_bn = alloc_bignat_constant(
|
||||||
|
cs.namespace(|| "alloc m"),
|
||||||
|
&G::get_order(),
|
||||||
|
limb_width,
|
||||||
|
n_limbs,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Analyze X0 to bignat
|
||||||
|
let X0_bn = BigNat::from_num(
|
||||||
|
cs.namespace(|| "allocate X0_bn"),
|
||||||
|
Num::from(u.X0.clone()),
|
||||||
|
limb_width,
|
||||||
|
n_limbs,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Fold self.X[0] + r * X[0]
|
||||||
|
let (_, r_0) = X0_bn.mult_mod(cs.namespace(|| "r*X[0]"), &r_bn, &m_bn)?;
|
||||||
|
// add X_r[0]
|
||||||
|
let r_new_0 = self.X0.add::<CS>(&r_0)?;
|
||||||
|
// Now reduce
|
||||||
|
let X0_fold = r_new_0.red_mod(cs.namespace(|| "reduce folded X[0]"), &m_bn)?;
|
||||||
|
|
||||||
|
// Analyze X1 to bignat
|
||||||
|
let X1_bn = BigNat::from_num(
|
||||||
|
cs.namespace(|| "allocate X1_bn"),
|
||||||
|
Num::from(u.X1.clone()),
|
||||||
|
limb_width,
|
||||||
|
n_limbs,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Fold self.X[1] + r * X[1]
|
||||||
|
let (_, r_1) = X1_bn.mult_mod(cs.namespace(|| "r*X[1]"), &r_bn, &m_bn)?;
|
||||||
|
// add X_r[1]
|
||||||
|
let r_new_1 = self.X1.add::<CS>(&r_1)?;
|
||||||
|
// Now reduce
|
||||||
|
let X1_fold = r_new_1.red_mod(cs.namespace(|| "reduce folded X[1]"), &m_bn)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
W: W_fold,
|
||||||
|
E: E_fold,
|
||||||
|
u: u_fold,
|
||||||
|
X0: X0_fold,
|
||||||
|
X1: X1_fold,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If the condition is true then returns this otherwise it returns the other
|
||||||
|
pub fn conditionally_select<CS: ConstraintSystem<<G as Group>::Base>>(
|
||||||
|
&self,
|
||||||
|
mut cs: CS,
|
||||||
|
other: AllocatedRelaxedR1CSInstance<G>,
|
||||||
|
condition: &Boolean,
|
||||||
|
) -> Result<AllocatedRelaxedR1CSInstance<G>, SynthesisError> {
|
||||||
|
let W = AllocatedPoint::conditionally_select(
|
||||||
|
cs.namespace(|| "W = cond ? self.W : other.W"),
|
||||||
|
&self.W,
|
||||||
|
&other.W,
|
||||||
|
condition,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let E = AllocatedPoint::conditionally_select(
|
||||||
|
cs.namespace(|| "E = cond ? self.E : other.E"),
|
||||||
|
&self.E,
|
||||||
|
&other.E,
|
||||||
|
condition,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let u = conditionally_select(
|
||||||
|
cs.namespace(|| "u = cond ? self.u : other.u"),
|
||||||
|
&self.u,
|
||||||
|
&other.u,
|
||||||
|
condition,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let X0 = conditionally_select_bignat(
|
||||||
|
cs.namespace(|| "X[0] = cond ? self.X[0] : other.X[0]"),
|
||||||
|
&self.X0,
|
||||||
|
&other.X0,
|
||||||
|
condition,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let X1 = conditionally_select_bignat(
|
||||||
|
cs.namespace(|| "X[1] = cond ? self.X[1] : other.X[1]"),
|
||||||
|
&self.X1,
|
||||||
|
&other.X1,
|
||||||
|
condition,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(AllocatedRelaxedR1CSInstance { W, E, u, X0, X1 })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::traits::Group;
|
||||||
use bellperson::{
|
use bellperson::{
|
||||||
gadgets::{
|
gadgets::{
|
||||||
boolean::{AllocatedBit, Boolean},
|
boolean::{AllocatedBit, Boolean},
|
||||||
@@ -7,7 +8,7 @@ use bellperson::{
|
|||||||
ConstraintSystem, LinearCombination, SynthesisError,
|
ConstraintSystem, LinearCombination, SynthesisError,
|
||||||
};
|
};
|
||||||
use bellperson_nonnative::mp::bignat::{nat_to_limbs, BigNat};
|
use bellperson_nonnative::mp::bignat::{nat_to_limbs, BigNat};
|
||||||
use ff::{PrimeField, PrimeFieldBits};
|
use ff::{Field, PrimeField, PrimeFieldBits};
|
||||||
use rug::Integer;
|
use rug::Integer;
|
||||||
|
|
||||||
/// Gets as input the little indian representation of a number and spits out the number
|
/// Gets as input the little indian representation of a number and spits out the number
|
||||||
@@ -73,6 +74,30 @@ pub fn alloc_one<F: PrimeField, CS: ConstraintSystem<F>>(
|
|||||||
Ok(one)
|
Ok(one)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allocate a scalar as a base. Only to be used is the scalar fits in base!
|
||||||
|
pub fn alloc_scalar_as_base<G, CS>(
|
||||||
|
mut cs: CS,
|
||||||
|
input: Option<G::Scalar>,
|
||||||
|
) -> Result<AllocatedNum<G::Base>, SynthesisError>
|
||||||
|
where
|
||||||
|
G: Group,
|
||||||
|
<G as Group>::Scalar: PrimeFieldBits,
|
||||||
|
CS: ConstraintSystem<<G as Group>::Base>,
|
||||||
|
{
|
||||||
|
AllocatedNum::alloc(cs.namespace(|| "allocate scalar as base"), || {
|
||||||
|
let input_bits = input.get()?.clone().to_le_bits();
|
||||||
|
let mut mult = G::Base::one();
|
||||||
|
let mut val = G::Base::zero();
|
||||||
|
for bit in input_bits {
|
||||||
|
if bit {
|
||||||
|
val += mult;
|
||||||
|
}
|
||||||
|
mult = mult + mult;
|
||||||
|
}
|
||||||
|
Ok(val)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Allocate bignat a constant
|
/// Allocate bignat a constant
|
||||||
pub fn alloc_bignat_constant<F: PrimeField, CS: ConstraintSystem<F>>(
|
pub fn alloc_bignat_constant<F: PrimeField, CS: ConstraintSystem<F>>(
|
||||||
mut cs: CS,
|
mut cs: CS,
|
||||||
@@ -109,7 +134,6 @@ pub fn alloc_num_equals<F: PrimeField, CS: ConstraintSystem<F>>(
|
|||||||
) -> Result<AllocatedBit, SynthesisError> {
|
) -> Result<AllocatedBit, SynthesisError> {
|
||||||
// Allocate and constrain `r`: result boolean bit.
|
// Allocate and constrain `r`: result boolean bit.
|
||||||
// It equals `true` if `a` equals `b`, `false` otherwise
|
// It equals `true` if `a` equals `b`, `false` otherwise
|
||||||
|
|
||||||
let r_value = match (a.get_value(), b.get_value()) {
|
let r_value = match (a.get_value(), b.get_value()) {
|
||||||
(Some(a), Some(b)) => Some(a == b),
|
(Some(a), Some(b)) => Some(a == b),
|
||||||
_ => None,
|
_ => None,
|
||||||
@@ -147,7 +171,6 @@ pub fn alloc_num_equals<F: PrimeField, CS: ConstraintSystem<F>>(
|
|||||||
// Allocate `t = delta * delta_inv`
|
// Allocate `t = delta * delta_inv`
|
||||||
// If `delta` is non-zero (a != b), `t` will equal 1
|
// If `delta` is non-zero (a != b), `t` will equal 1
|
||||||
// If `delta` is zero (a == b), `t` cannot equal 1
|
// If `delta` is zero (a == b), `t` cannot equal 1
|
||||||
|
|
||||||
let t = AllocatedNum::alloc(cs.namespace(|| "t"), || {
|
let t = AllocatedNum::alloc(cs.namespace(|| "t"), || {
|
||||||
let mut tmp = *delta.get_value().get()?;
|
let mut tmp = *delta.get_value().get()?;
|
||||||
tmp.mul_assign(&(*delta_inv.get_value().get()?));
|
tmp.mul_assign(&(*delta_inv.get_value().get()?));
|
||||||
@@ -215,7 +238,6 @@ pub fn conditionally_select<F: PrimeField, CS: ConstraintSystem<F>>(
|
|||||||
|
|
||||||
// a * condition + b*(1-condition) = c ->
|
// a * condition + b*(1-condition) = c ->
|
||||||
// a * condition - b*condition = c - b
|
// a * condition - b*condition = c - b
|
||||||
|
|
||||||
cs.enforce(
|
cs.enforce(
|
||||||
|| "conditional select constraint",
|
|| "conditional select constraint",
|
||||||
|lc| lc + a.get_variable() - b.get_variable(),
|
|lc| lc + a.get_variable() - b.get_variable(),
|
||||||
@@ -278,7 +300,6 @@ pub fn conditionally_select2<F: PrimeField, CS: ConstraintSystem<F>>(
|
|||||||
|
|
||||||
// a * condition + b*(1-condition) = c ->
|
// a * condition + b*(1-condition) = c ->
|
||||||
// a * condition - b*condition = c - b
|
// a * condition - b*condition = c - b
|
||||||
|
|
||||||
cs.enforce(
|
cs.enforce(
|
||||||
|| "conditional select constraint",
|
|| "conditional select constraint",
|
||||||
|lc| lc + a.get_variable() - b.get_variable(),
|
|lc| lc + a.get_variable() - b.get_variable(),
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ impl<G: Group> StepSNARK<G> {
|
|||||||
NovaError,
|
NovaError,
|
||||||
> {
|
> {
|
||||||
// append the protocol name to the transcript
|
// append the protocol name to the transcript
|
||||||
//transcript.append_protocol_name(StepSNARK::protocol_name());
|
|
||||||
transcript.append_message(b"protocol-name", StepSNARK::<G>::protocol_name());
|
transcript.append_message(b"protocol-name", StepSNARK::<G>::protocol_name());
|
||||||
|
|
||||||
// compute a commitment to the cross-term
|
// compute a commitment to the cross-term
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use bellperson::{
|
|||||||
ConstraintSystem, SynthesisError,
|
ConstraintSystem, SynthesisError,
|
||||||
};
|
};
|
||||||
use ff::{PrimeField, PrimeFieldBits};
|
use ff::{PrimeField, PrimeFieldBits};
|
||||||
use generic_array::typenum::{U24, U25, U27, U31};
|
use generic_array::typenum::{U25, U27, U31, U8};
|
||||||
use neptune::{
|
use neptune::{
|
||||||
circuit::poseidon_hash,
|
circuit::poseidon_hash,
|
||||||
poseidon::{Poseidon, PoseidonConstants},
|
poseidon::{Poseidon, PoseidonConstants},
|
||||||
@@ -22,7 +22,7 @@ pub struct NovaPoseidonConstants<F>
|
|||||||
where
|
where
|
||||||
F: PrimeField,
|
F: PrimeField,
|
||||||
{
|
{
|
||||||
constants24: PoseidonConstants<F, U24>,
|
constants8: PoseidonConstants<F, U8>,
|
||||||
constants25: PoseidonConstants<F, U25>,
|
constants25: PoseidonConstants<F, U25>,
|
||||||
constants27: PoseidonConstants<F, U27>,
|
constants27: PoseidonConstants<F, U27>,
|
||||||
constants31: PoseidonConstants<F, U31>,
|
constants31: PoseidonConstants<F, U31>,
|
||||||
@@ -35,12 +35,12 @@ where
|
|||||||
{
|
{
|
||||||
/// Generate Poseidon constants for the arities that Nova uses
|
/// Generate Poseidon constants for the arities that Nova uses
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let constants24 = PoseidonConstants::<F, U24>::new_with_strength(Strength::Strengthened);
|
let constants8 = PoseidonConstants::<F, U8>::new_with_strength(Strength::Strengthened);
|
||||||
let constants25 = PoseidonConstants::<F, U25>::new_with_strength(Strength::Strengthened);
|
let constants25 = PoseidonConstants::<F, U25>::new_with_strength(Strength::Strengthened);
|
||||||
let constants27 = PoseidonConstants::<F, U27>::new_with_strength(Strength::Strengthened);
|
let constants27 = PoseidonConstants::<F, U27>::new_with_strength(Strength::Strengthened);
|
||||||
let constants31 = PoseidonConstants::<F, U31>::new_with_strength(Strength::Strengthened);
|
let constants31 = PoseidonConstants::<F, U31>::new_with_strength(Strength::Strengthened);
|
||||||
Self {
|
Self {
|
||||||
constants24,
|
constants8,
|
||||||
constants25,
|
constants25,
|
||||||
constants27,
|
constants27,
|
||||||
constants31,
|
constants31,
|
||||||
@@ -71,12 +71,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
/// Flush the state of the RO
|
|
||||||
pub fn flush_state(&mut self) {
|
|
||||||
self.state = Vec::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Absorb a new number into the state of the oracle
|
/// Absorb a new number into the state of the oracle
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn absorb(&mut self, e: Scalar) {
|
pub fn absorb(&mut self, e: Scalar) {
|
||||||
@@ -85,8 +79,8 @@ where
|
|||||||
|
|
||||||
fn hash_inner(&mut self) -> Scalar {
|
fn hash_inner(&mut self) -> Scalar {
|
||||||
match self.state.len() {
|
match self.state.len() {
|
||||||
24 => {
|
8 => {
|
||||||
Poseidon::<Scalar, U24>::new_with_preimage(&self.state, &self.constants.constants24).hash()
|
Poseidon::<Scalar, U8>::new_with_preimage(&self.state, &self.constants.constants8).hash()
|
||||||
}
|
}
|
||||||
25 => {
|
25 => {
|
||||||
Poseidon::<Scalar, U25>::new_with_preimage(&self.state, &self.constants.constants25).hash()
|
Poseidon::<Scalar, U25>::new_with_preimage(&self.state, &self.constants.constants25).hash()
|
||||||
@@ -160,11 +154,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flush the state of the RO
|
|
||||||
pub fn flush_state(&mut self) {
|
|
||||||
self.state = Vec::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Absorb a new number into the state of the oracle
|
/// Absorb a new number into the state of the oracle
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn absorb(&mut self, e: AllocatedNum<Scalar>) {
|
pub fn absorb(&mut self, e: AllocatedNum<Scalar>) {
|
||||||
@@ -176,10 +165,10 @@ where
|
|||||||
CS: ConstraintSystem<Scalar>,
|
CS: ConstraintSystem<Scalar>,
|
||||||
{
|
{
|
||||||
let out = match self.state.len() {
|
let out = match self.state.len() {
|
||||||
24 => poseidon_hash(
|
8 => poseidon_hash(
|
||||||
cs.namespace(|| "Posideon hash"),
|
cs.namespace(|| "Posideon hash"),
|
||||||
self.state.clone(),
|
self.state.clone(),
|
||||||
&self.constants.constants24,
|
&self.constants.constants8,
|
||||||
)?,
|
)?,
|
||||||
25 => poseidon_hash(
|
25 => poseidon_hash(
|
||||||
cs.namespace(|| "Poseidon hash"),
|
cs.namespace(|| "Poseidon hash"),
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ pub struct R1CSWitness<G: Group> {
|
|||||||
/// A type that holds an R1CS instance
|
/// A type that holds an R1CS instance
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct R1CSInstance<G: Group> {
|
pub struct R1CSInstance<G: Group> {
|
||||||
comm_W: Commitment<G>,
|
pub(crate) comm_W: Commitment<G>,
|
||||||
X: Vec<G::Scalar>,
|
pub(crate) X: Vec<G::Scalar>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type that holds a witness for a given Relaxed R1CS instance
|
/// A type that holds a witness for a given Relaxed R1CS instance
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use nova_snark::bellperson::{
|
|||||||
fn synthesize_alloc_bit<Fr: PrimeField, CS: ConstraintSystem<Fr>>(
|
fn synthesize_alloc_bit<Fr: PrimeField, CS: ConstraintSystem<Fr>>(
|
||||||
cs: &mut CS,
|
cs: &mut CS,
|
||||||
) -> Result<(), SynthesisError> {
|
) -> Result<(), SynthesisError> {
|
||||||
//get two bits as input and check that they are indeed bits
|
// get two bits as input and check that they are indeed bits
|
||||||
let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::one()))?;
|
let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::one()))?;
|
||||||
let _ = a.inputize(cs.namespace(|| "a is input"));
|
let _ = a.inputize(cs.namespace(|| "a is input"));
|
||||||
cs.enforce(
|
cs.enforce(
|
||||||
@@ -33,18 +33,18 @@ fn synthesize_alloc_bit<Fr: PrimeField, CS: ConstraintSystem<Fr>>(
|
|||||||
fn test_alloc_bit() {
|
fn test_alloc_bit() {
|
||||||
type G = pasta_curves::pallas::Point;
|
type G = pasta_curves::pallas::Point;
|
||||||
|
|
||||||
//First create the shape
|
// First create the shape
|
||||||
let mut cs: ShapeCS<G> = ShapeCS::new();
|
let mut cs: ShapeCS<G> = ShapeCS::new();
|
||||||
let _ = synthesize_alloc_bit(&mut cs);
|
let _ = synthesize_alloc_bit(&mut cs);
|
||||||
let shape = cs.r1cs_shape();
|
let shape = cs.r1cs_shape();
|
||||||
let gens = cs.r1cs_gens();
|
let gens = cs.r1cs_gens();
|
||||||
println!("Mult mod constraint no: {}", cs.num_constraints());
|
println!("Mult mod constraint no: {}", cs.num_constraints());
|
||||||
|
|
||||||
//Now get the assignment
|
// Now get the assignment
|
||||||
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
|
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
|
||||||
let _ = synthesize_alloc_bit(&mut cs);
|
let _ = synthesize_alloc_bit(&mut cs);
|
||||||
let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
|
let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
|
||||||
|
|
||||||
//Make sure that this is satisfiable
|
// Make sure that this is satisfiable
|
||||||
assert!(shape.is_sat(&gens, &inst, &witness).is_ok());
|
assert!(shape.is_sat(&gens, &inst, &witness).is_ok());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ fn synthesize_add_mod<Fr: PrimeField, CS: ConstraintSystem<Fr>>(
|
|||||||
fn test_mult_mod() {
|
fn test_mult_mod() {
|
||||||
type G = pasta_curves::pallas::Point;
|
type G = pasta_curves::pallas::Point;
|
||||||
|
|
||||||
//Set the inputs
|
// Set the inputs
|
||||||
let a_val = Integer::from_str_radix(
|
let a_val = Integer::from_str_radix(
|
||||||
"11572336752428856981970994795408771577024165681374400871001196932361466228192",
|
"11572336752428856981970994795408771577024165681374400871001196932361466228192",
|
||||||
10,
|
10,
|
||||||
@@ -196,19 +196,19 @@ fn test_mult_mod() {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
//First create the shape
|
// First create the shape
|
||||||
let mut cs: ShapeCS<G> = ShapeCS::new();
|
let mut cs: ShapeCS<G> = ShapeCS::new();
|
||||||
let _ = synthesize_mult_mod(&mut cs, &a_val, &b_val, &m_val, &q_val, &r_val, 32, 8);
|
let _ = synthesize_mult_mod(&mut cs, &a_val, &b_val, &m_val, &q_val, &r_val, 32, 8);
|
||||||
let shape = cs.r1cs_shape();
|
let shape = cs.r1cs_shape();
|
||||||
let gens = cs.r1cs_gens();
|
let gens = cs.r1cs_gens();
|
||||||
println!("Mult mod constraint no: {}", cs.num_constraints());
|
println!("Mult mod constraint no: {}", cs.num_constraints());
|
||||||
|
|
||||||
//Now get the assignment
|
// Now get the assignment
|
||||||
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
|
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
|
||||||
let _ = synthesize_mult_mod(&mut cs, &a_val, &b_val, &m_val, &q_val, &r_val, 32, 8);
|
let _ = synthesize_mult_mod(&mut cs, &a_val, &b_val, &m_val, &q_val, &r_val, 32, 8);
|
||||||
let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
|
let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
|
||||||
|
|
||||||
//Make sure that this is satisfiable
|
// Make sure that this is satisfiable
|
||||||
assert!(shape.is_sat(&gens, &inst, &witness).is_ok());
|
assert!(shape.is_sat(&gens, &inst, &witness).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +216,7 @@ fn test_mult_mod() {
|
|||||||
fn test_add() {
|
fn test_add() {
|
||||||
type G = pasta_curves::pallas::Point;
|
type G = pasta_curves::pallas::Point;
|
||||||
|
|
||||||
//Set the inputs
|
// Set the inputs
|
||||||
let a_val = Integer::from_str_radix(
|
let a_val = Integer::from_str_radix(
|
||||||
"11572336752428856981970994795408771577024165681374400871001196932361466228192",
|
"11572336752428856981970994795408771577024165681374400871001196932361466228192",
|
||||||
10,
|
10,
|
||||||
@@ -229,19 +229,19 @@ fn test_add() {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
//First create the shape
|
// First create the shape
|
||||||
let mut cs: ShapeCS<G> = ShapeCS::new();
|
let mut cs: ShapeCS<G> = ShapeCS::new();
|
||||||
let _ = synthesize_add(&mut cs, &a_val, &b_val, &c_val, 32, 8);
|
let _ = synthesize_add(&mut cs, &a_val, &b_val, &c_val, 32, 8);
|
||||||
let shape = cs.r1cs_shape();
|
let shape = cs.r1cs_shape();
|
||||||
let gens = cs.r1cs_gens();
|
let gens = cs.r1cs_gens();
|
||||||
println!("Add mod constraint no: {}", cs.num_constraints());
|
println!("Add mod constraint no: {}", cs.num_constraints());
|
||||||
|
|
||||||
//Now get the assignment
|
// Now get the assignment
|
||||||
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
|
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
|
||||||
let _ = synthesize_add(&mut cs, &a_val, &b_val, &c_val, 32, 8);
|
let _ = synthesize_add(&mut cs, &a_val, &b_val, &c_val, 32, 8);
|
||||||
let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
|
let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
|
||||||
|
|
||||||
//Make sure that this is satisfiable
|
// Make sure that this is satisfiable
|
||||||
assert!(shape.is_sat(&gens, &inst, &witness).is_ok());
|
assert!(shape.is_sat(&gens, &inst, &witness).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,7 +249,7 @@ fn test_add() {
|
|||||||
fn test_add_mod() {
|
fn test_add_mod() {
|
||||||
type G = pasta_curves::pallas::Point;
|
type G = pasta_curves::pallas::Point;
|
||||||
|
|
||||||
//Set the inputs
|
// Set the inputs
|
||||||
let a_val = Integer::from_str_radix(
|
let a_val = Integer::from_str_radix(
|
||||||
"11572336752428856981970994795408771577024165681374400871001196932361466228192",
|
"11572336752428856981970994795408771577024165681374400871001196932361466228192",
|
||||||
10,
|
10,
|
||||||
@@ -267,19 +267,19 @@ fn test_add_mod() {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
//First create the shape
|
// First create the shape
|
||||||
let mut cs: ShapeCS<G> = ShapeCS::new();
|
let mut cs: ShapeCS<G> = ShapeCS::new();
|
||||||
let _ = synthesize_add_mod(&mut cs, &a_val, &b_val, &c_val, &m_val, 32, 8);
|
let _ = synthesize_add_mod(&mut cs, &a_val, &b_val, &c_val, &m_val, 32, 8);
|
||||||
let shape = cs.r1cs_shape();
|
let shape = cs.r1cs_shape();
|
||||||
let gens = cs.r1cs_gens();
|
let gens = cs.r1cs_gens();
|
||||||
println!("Add mod constraint no: {}", cs.num_constraints());
|
println!("Add mod constraint no: {}", cs.num_constraints());
|
||||||
|
|
||||||
//Now get the assignment
|
// Now get the assignment
|
||||||
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
|
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
|
||||||
let _ = synthesize_add_mod(&mut cs, &a_val, &b_val, &c_val, &m_val, 32, 8);
|
let _ = synthesize_add_mod(&mut cs, &a_val, &b_val, &c_val, &m_val, 32, 8);
|
||||||
let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
|
let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
|
||||||
|
|
||||||
//Make sure that this is satisfiable
|
// Make sure that this is satisfiable
|
||||||
assert!(shape.is_sat(&gens, &inst, &witness).is_ok());
|
assert!(shape.is_sat(&gens, &inst, &witness).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,21 +287,21 @@ fn test_add_mod() {
|
|||||||
fn test_equal() {
|
fn test_equal() {
|
||||||
type G = pasta_curves::pallas::Point;
|
type G = pasta_curves::pallas::Point;
|
||||||
|
|
||||||
//Set the inputs
|
// Set the inputs
|
||||||
let a_val = Integer::from_str_radix("1157233675242885698197099479540877", 10).unwrap();
|
let a_val = Integer::from_str_radix("1157233675242885698197099479540877", 10).unwrap();
|
||||||
|
|
||||||
//First create the shape
|
// First create the shape
|
||||||
let mut cs: ShapeCS<G> = ShapeCS::new();
|
let mut cs: ShapeCS<G> = ShapeCS::new();
|
||||||
let _ = synthesize_is_equal(&mut cs, &a_val, 32, 8);
|
let _ = synthesize_is_equal(&mut cs, &a_val, 32, 8);
|
||||||
let shape = cs.r1cs_shape();
|
let shape = cs.r1cs_shape();
|
||||||
let gens = cs.r1cs_gens();
|
let gens = cs.r1cs_gens();
|
||||||
println!("Equal constraint no: {}", cs.num_constraints());
|
println!("Equal constraint no: {}", cs.num_constraints());
|
||||||
|
|
||||||
//Now get the assignment
|
// Now get the assignment
|
||||||
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
|
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
|
||||||
let _ = synthesize_is_equal(&mut cs, &a_val, 32, 8);
|
let _ = synthesize_is_equal(&mut cs, &a_val, 32, 8);
|
||||||
let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
|
let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
|
||||||
|
|
||||||
//Make sure that this is satisfiable
|
// Make sure that this is satisfiable
|
||||||
assert!(shape.is_sat(&gens, &inst, &witness).is_ok());
|
assert!(shape.is_sat(&gens, &inst, &witness).is_ok());
|
||||||
}
|
}
|
||||||
|
|||||||
12
tests/num.rs
12
tests/num.rs
@@ -42,18 +42,18 @@ fn synthesize_use_cs_one_after_inputize<Fr: PrimeField, CS: ConstraintSystem<Fr>
|
|||||||
fn test_use_cs_one() {
|
fn test_use_cs_one() {
|
||||||
type G = pasta_curves::pallas::Point;
|
type G = pasta_curves::pallas::Point;
|
||||||
|
|
||||||
//First create the shape
|
// First create the shape
|
||||||
let mut cs: ShapeCS<G> = ShapeCS::new();
|
let mut cs: ShapeCS<G> = ShapeCS::new();
|
||||||
let _ = synthesize_use_cs_one(&mut cs);
|
let _ = synthesize_use_cs_one(&mut cs);
|
||||||
let shape = cs.r1cs_shape();
|
let shape = cs.r1cs_shape();
|
||||||
let gens = cs.r1cs_gens();
|
let gens = cs.r1cs_gens();
|
||||||
|
|
||||||
//Now get the assignment
|
// Now get the assignment
|
||||||
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
|
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
|
||||||
let _ = synthesize_use_cs_one(&mut cs);
|
let _ = synthesize_use_cs_one(&mut cs);
|
||||||
let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
|
let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
|
||||||
|
|
||||||
//Make sure that this is satisfiable
|
// Make sure that this is satisfiable
|
||||||
assert!(shape.is_sat(&gens, &inst, &witness).is_ok());
|
assert!(shape.is_sat(&gens, &inst, &witness).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,17 +61,17 @@ fn test_use_cs_one() {
|
|||||||
fn test_use_cs_one_after_inputize() {
|
fn test_use_cs_one_after_inputize() {
|
||||||
type G = pasta_curves::pallas::Point;
|
type G = pasta_curves::pallas::Point;
|
||||||
|
|
||||||
//First create the shape
|
// First create the shape
|
||||||
let mut cs: ShapeCS<G> = ShapeCS::new();
|
let mut cs: ShapeCS<G> = ShapeCS::new();
|
||||||
let _ = synthesize_use_cs_one_after_inputize(&mut cs);
|
let _ = synthesize_use_cs_one_after_inputize(&mut cs);
|
||||||
let shape = cs.r1cs_shape();
|
let shape = cs.r1cs_shape();
|
||||||
let gens = cs.r1cs_gens();
|
let gens = cs.r1cs_gens();
|
||||||
|
|
||||||
//Now get the assignment
|
// Now get the assignment
|
||||||
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
|
let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
|
||||||
let _ = synthesize_use_cs_one_after_inputize(&mut cs);
|
let _ = synthesize_use_cs_one_after_inputize(&mut cs);
|
||||||
let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
|
let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
|
||||||
|
|
||||||
//Make sure that this is satisfiable
|
// Make sure that this is satisfiable
|
||||||
assert!(shape.is_sat(&gens, &inst, &witness).is_ok());
|
assert!(shape.is_sat(&gens, &inst, &witness).is_ok());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user