use snarkvm_algorithms::DefaultCapacityAlgebraicSponge;
use snarkvm_fields::PrimeField;
use snarkvm_gadgets::{
bits::{Boolean, ToBitsBEGadget},
fields::{AllocatedFp, FpGadget},
integers::uint::UInt8,
nonnative::{
params::{get_params, OptimizationType},
AllocatedNonNativeFieldVar,
NonNativeFieldVar,
},
overhead,
traits::{alloc::AllocGadget, fields::FieldGadget, integers::Integer},
DefaultCapacityAlgebraicSpongeVar,
};
use snarkvm_r1cs::{ConstraintSystem, ConstraintVariable, LinearCombination, SynthesisError};
use crate::fiat_shamir::{traits::FiatShamirRngVar, FiatShamirAlgebraicSpongeRng};
use crate::{PhantomData, Vec};
#[derive(Clone)]
pub struct FiatShamirAlgebraicSpongeRngVar<
TargetField: PrimeField,
BaseField: PrimeField,
PS: DefaultCapacityAlgebraicSponge<BaseField, 6>,
S: DefaultCapacityAlgebraicSpongeVar<BaseField, PS, 6>,
> {
pub s: S,
#[doc(hidden)]
_target_field: PhantomData<TargetField>,
#[doc(hidden)]
_base_field: PhantomData<BaseField>,
#[doc(hidden)]
_sponge: PhantomData<PS>,
}
impl<
TargetField: PrimeField,
BaseField: PrimeField,
PS: DefaultCapacityAlgebraicSponge<BaseField, 6>,
S: DefaultCapacityAlgebraicSpongeVar<BaseField, PS, 6>,
> FiatShamirAlgebraicSpongeRngVar<TargetField, BaseField, PS, S>
{
pub fn compress_gadgets<CS: ConstraintSystem<BaseField>>(
mut cs: CS,
src_limbs: &[(FpGadget<BaseField>, BaseField)],
ty: OptimizationType,
) -> Result<Vec<FpGadget<BaseField>>, SynthesisError> {
let capacity = BaseField::size_in_bits() - 1;
let mut dest_limbs = Vec::<FpGadget<BaseField>>::new();
if src_limbs.is_empty() {
return Ok(vec![]);
}
let params = get_params(TargetField::size_in_bits(), BaseField::size_in_bits(), ty);
let adjustment_factor_lookup_table = {
let mut table = Vec::<BaseField>::new();
let mut cur = BaseField::one();
for _ in 1..=capacity {
table.push(cur);
cur.double_in_place();
}
table
};
let mut i: usize = 0;
let src_len = src_limbs.len();
while i < src_len {
let first = &src_limbs[i];
let second = if i + 1 < src_len { Some(&src_limbs[i + 1]) } else { None };
let first_max_bits_per_limb = params.bits_per_limb + overhead!(first.1 + BaseField::one());
let second_max_bits_per_limb = if second.is_some() {
params.bits_per_limb + overhead!(second.unwrap().1 + BaseField::one())
} else {
0
};
if second.is_some() && first_max_bits_per_limb + second_max_bits_per_limb <= capacity {
let adjustment_factor = &adjustment_factor_lookup_table[second_max_bits_per_limb];
let mut value = first.0.mul_by_constant(
cs.ns(|| format!("first_mul_by_constant_adjustment_factor_{}", i)),
adjustment_factor,
)?;
value = value.add(cs.ns(|| format!("value_add_second_{}", i)), &second.unwrap().0)?;
dest_limbs.push(value);
i += 2;
} else {
dest_limbs.push(first.0.clone());
i += 1;
}
}
Ok(dest_limbs)
}
pub fn push_gadgets_to_sponge<CS: ConstraintSystem<BaseField>>(
mut cs: CS,
sponge: &mut S,
src: &[NonNativeFieldVar<TargetField, BaseField>],
ty: OptimizationType,
) -> Result<(), SynthesisError> {
let mut src_limbs: Vec<(FpGadget<BaseField>, BaseField)> = Vec::new();
for (i, elem) in src.iter().enumerate() {
match elem {
NonNativeFieldVar::Constant(c) => {
let v = AllocatedNonNativeFieldVar::<TargetField, BaseField>::constant(
&mut cs.ns(|| format!("alloc_constant_{}", i)),
*c,
)?;
for limb in v.limbs {
let num_of_additions_over_normal_form =
if v.num_of_additions_over_normal_form == BaseField::zero() {
BaseField::one()
} else {
v.num_of_additions_over_normal_form
};
src_limbs.push((limb, num_of_additions_over_normal_form));
}
}
NonNativeFieldVar::Var(v) => {
for limb in v.limbs.clone() {
let num_of_additions_over_normal_form =
if v.num_of_additions_over_normal_form == BaseField::zero() {
BaseField::one()
} else {
v.num_of_additions_over_normal_form
};
src_limbs.push((limb, num_of_additions_over_normal_form));
}
}
}
}
let dest_limbs = Self::compress_gadgets(cs.ns(|| "compress_gadgets"), &src_limbs, ty)?;
sponge.absorb(cs.ns(|| "absorb"), dest_limbs.iter())?;
Ok(())
}
pub fn get_booleans_from_sponge<CS: ConstraintSystem<BaseField>>(
mut cs: CS,
sponge: &mut S,
num_bits: usize,
) -> Result<Vec<Boolean>, SynthesisError> {
let bits_per_element = BaseField::size_in_bits() - 1;
let num_elements = (num_bits + bits_per_element - 1) / bits_per_element;
let src_elements = sponge.squeeze_field_elements(cs.ns(|| "squeeze"), num_elements)?;
let mut dest_bits = Vec::<Boolean>::new();
for (i, elem) in src_elements.iter().enumerate() {
let elem_bits = elem.to_bits_be(cs.ns(|| format!("elem_to_bits_{}", i)))?;
dest_bits.extend_from_slice(&elem_bits[1..]); }
Ok(dest_bits)
}
pub fn get_gadgets_from_sponge<CS: ConstraintSystem<BaseField>>(
cs: CS,
sponge: &mut S,
num_elements: usize,
outputs_short_elements: bool,
) -> Result<Vec<NonNativeFieldVar<TargetField, BaseField>>, SynthesisError> {
let (dest_gadgets, _) =
Self::get_gadgets_and_bits_from_sponge(cs, sponge, num_elements, outputs_short_elements)?;
Ok(dest_gadgets)
}
#[allow(clippy::type_complexity)]
pub fn get_gadgets_and_bits_from_sponge<CS: ConstraintSystem<BaseField>>(
mut cs: CS,
sponge: &mut S,
num_elements: usize,
outputs_short_elements: bool,
) -> Result<(Vec<NonNativeFieldVar<TargetField, BaseField>>, Vec<Vec<Boolean>>), SynthesisError> {
let optimization_type = OptimizationType::Weight;
let params = get_params(
TargetField::size_in_bits(),
BaseField::size_in_bits(),
optimization_type,
);
let num_bits_per_nonnative = if outputs_short_elements {
128
} else {
TargetField::size_in_bits() - 1 };
let bits = Self::get_booleans_from_sponge(
cs.ns(|| "get_booleans_from_sponge"),
sponge,
num_bits_per_nonnative * num_elements,
)?;
let mut lookup_table = Vec::<Vec<BaseField>>::new();
let mut cur = TargetField::one();
for _ in 0..num_bits_per_nonnative {
let repr = AllocatedNonNativeFieldVar::<TargetField, BaseField>::get_limbs_representations(
&cur,
optimization_type,
)?;
lookup_table.push(repr);
cur.double_in_place();
}
let mut dest_gadgets = Vec::<NonNativeFieldVar<TargetField, BaseField>>::new();
let mut dest_bits = Vec::<Vec<Boolean>>::new();
bits.chunks_exact(num_bits_per_nonnative)
.enumerate()
.for_each(|(i, per_nonnative_bits)| {
let mut val = vec![BaseField::zero(); params.num_limbs];
let mut lc = vec![LinearCombination::<BaseField>::zero(); params.num_limbs];
let mut per_nonnative_bits_le = per_nonnative_bits.to_vec();
per_nonnative_bits_le.reverse();
dest_bits.push(per_nonnative_bits_le.clone());
for (j, bit) in per_nonnative_bits_le.iter().enumerate() {
if bit.get_value().unwrap_or_default() {
for (k, val) in val.iter_mut().enumerate().take(params.num_limbs) {
*val += &lookup_table[j][k];
}
}
#[allow(clippy::needless_range_loop)]
for k in 0..params.num_limbs {
lc[k] = &lc[k] + bit.lc(CS::one(), BaseField::one()) * lookup_table[j][k];
}
}
let mut limbs = Vec::new();
for k in 0..params.num_limbs {
let gadget = AllocatedFp::alloc(cs.ns(|| format!("alloc_{}_{}", i, k)), || Ok(val[k])).unwrap();
match &gadget.variable {
ConstraintVariable::Var(var) => {
lc[k] = lc[k].clone() - (BaseField::one(), *var);
}
ConstraintVariable::LC(linear_combination) => {
lc[k] = &lc[k] - (BaseField::one(), linear_combination);
}
}
cs.enforce(
|| format!("enforce_constraint_{}_{}", i, k),
|lc| lc,
|lc| lc,
|_| lc[k].clone(),
);
limbs.push(FpGadget::<BaseField>::from(gadget));
}
dest_gadgets.push(NonNativeFieldVar::<TargetField, BaseField>::Var(
AllocatedNonNativeFieldVar::<TargetField, BaseField> {
limbs,
num_of_additions_over_normal_form: BaseField::zero(),
is_in_the_normal_form: true,
target_phantom: Default::default(),
},
));
});
Ok((dest_gadgets, dest_bits))
}
}
impl<
TargetField: PrimeField,
BaseField: PrimeField,
PS: DefaultCapacityAlgebraicSponge<BaseField, 6>,
S: DefaultCapacityAlgebraicSpongeVar<BaseField, PS, 6>,
> FiatShamirRngVar<TargetField, BaseField, FiatShamirAlgebraicSpongeRng<TargetField, BaseField, PS>>
for FiatShamirAlgebraicSpongeRngVar<TargetField, BaseField, PS, S>
{
fn new<CS: ConstraintSystem<BaseField>>(cs: CS) -> Self {
Self {
s: S::with_parameters(cs, &PS::sample_parameters()),
_target_field: PhantomData,
_base_field: PhantomData,
_sponge: PhantomData,
}
}
fn constant<CS: ConstraintSystem<BaseField>>(
cs: CS,
pfs: &FiatShamirAlgebraicSpongeRng<TargetField, BaseField, PS>,
) -> Self {
Self {
s: S::constant(cs, &pfs.s.clone()),
_target_field: PhantomData,
_base_field: PhantomData,
_sponge: PhantomData,
}
}
fn absorb_nonnative_field_elements<CS: ConstraintSystem<BaseField>>(
&mut self,
cs: CS,
elems: &[NonNativeFieldVar<TargetField, BaseField>],
ty: OptimizationType,
) -> Result<(), SynthesisError> {
Self::push_gadgets_to_sponge(cs, &mut self.s, elems, ty)
}
fn absorb_native_field_elements<CS: ConstraintSystem<BaseField>>(
&mut self,
cs: CS,
elems: &[FpGadget<BaseField>],
) -> Result<(), SynthesisError> {
self.s.absorb(cs, elems.iter())?;
Ok(())
}
fn absorb_bytes<CS: ConstraintSystem<BaseField>>(
&mut self,
mut cs: CS,
elems: &[UInt8],
) -> Result<(), SynthesisError> {
let capacity = BaseField::size_in_bits() - 1;
let mut bits = Vec::<Boolean>::new();
for elem in elems.iter() {
let mut bits_le = elem.to_bits_le(); bits_le.reverse();
bits.extend_from_slice(&bits_le);
}
let mut adjustment_factors = Vec::<BaseField>::new();
let mut cur = BaseField::one();
for _ in 0..capacity {
adjustment_factors.push(cur);
cur.double_in_place();
}
let mut gadgets = Vec::<FpGadget<BaseField>>::new();
for (i, elem_bits) in bits.chunks(capacity).enumerate() {
let mut elem = BaseField::zero();
let mut lc = LinearCombination::zero();
for (bit, adjustment_factor) in elem_bits.iter().rev().zip(adjustment_factors.iter()) {
if bit.get_value().unwrap_or_default() {
elem += adjustment_factor;
}
lc = &lc + bit.lc(CS::one(), BaseField::one()) * *adjustment_factor;
}
let gadget = AllocatedFp::alloc(cs.ns(|| format!("alloc_{}", i)), || Ok(elem))?;
match &gadget.variable {
ConstraintVariable::Var(var) => {
lc = lc.clone() - (BaseField::one(), *var);
}
ConstraintVariable::LC(linear_combination) => {
lc = &lc - (BaseField::one(), linear_combination);
}
}
gadgets.push(FpGadget::from(gadget));
cs.enforce(|| format!("enforce_constraint_{}", i), |lc| lc, |lc| lc, |_| lc);
}
self.s.absorb(cs.ns(|| "absorb"), gadgets.iter())
}
fn squeeze_native_field_elements<CS: ConstraintSystem<BaseField>>(
&mut self,
cs: CS,
num: usize,
) -> Result<Vec<FpGadget<BaseField>>, SynthesisError> {
self.s.squeeze_field_elements(cs, num)
}
fn squeeze_field_elements<CS: ConstraintSystem<BaseField>>(
&mut self,
cs: CS,
num: usize,
) -> Result<Vec<NonNativeFieldVar<TargetField, BaseField>>, SynthesisError> {
Self::get_gadgets_from_sponge(cs, &mut self.s, num, false)
}
#[allow(clippy::type_complexity)]
fn squeeze_field_elements_and_bits<CS: ConstraintSystem<BaseField>>(
&mut self,
cs: CS,
num: usize,
) -> Result<(Vec<NonNativeFieldVar<TargetField, BaseField>>, Vec<Vec<Boolean>>), SynthesisError> {
Self::get_gadgets_and_bits_from_sponge(cs, &mut self.s, num, false)
}
fn squeeze_128_bits_field_elements<CS: ConstraintSystem<BaseField>>(
&mut self,
cs: CS,
num: usize,
) -> Result<Vec<NonNativeFieldVar<TargetField, BaseField>>, SynthesisError> {
Self::get_gadgets_from_sponge(cs, &mut self.s, num, true)
}
#[allow(clippy::type_complexity)]
fn squeeze_128_bits_field_elements_and_bits<CS: ConstraintSystem<BaseField>>(
&mut self,
cs: CS,
num: usize,
) -> Result<(Vec<NonNativeFieldVar<TargetField, BaseField>>, Vec<Vec<Boolean>>), SynthesisError> {
Self::get_gadgets_and_bits_from_sponge(cs, &mut self.s, num, true)
}
}
#[cfg(test)]
mod tests {
use rand::Rng;
use rand_chacha::ChaChaRng;
use rand_core::SeedableRng;
use snarkvm_curves::bls12_377::Fq;
use snarkvm_fields::One;
use snarkvm_gadgets::{bits::ToBitsLEGadget, traits::eq::EqGadget};
use snarkvm_r1cs::TestConstraintSystem;
use snarkvm_utilities::rand::UniformRand;
use crate::fiat_shamir::{traits::FiatShamirRng, PoseidonSpongeGadget};
use snarkvm_algorithms::crypto_hash::PoseidonSponge;
use super::*;
type PS = PoseidonSponge<Fq, 6, 1>;
type PSGadget = PoseidonSpongeGadget<Fq, 6, 1>;
type FS = FiatShamirAlgebraicSpongeRng<Fq, Fq, PS>;
type FSGadget = FiatShamirAlgebraicSpongeRngVar<Fq, Fq, PS, PSGadget>;
const MAX_ELEMENTS: usize = 50;
const MAX_ELEMENT_SIZE: usize = 100;
const ITERATIONS: usize = 50;
const NUM_ABSORBED_RAND_FIELD_ELEMS: usize = 10;
const NUM_ABSORBED_RAND_BYTE_ELEMS: usize = 10;
const SIZE_ABSORBED_BYTE_ELEM: usize = 64;
const NUM_SQUEEZED_FIELD_ELEMS: usize = 10;
const NUM_SQUEEZED_SHORT_FIELD_ELEMS: usize = 10;
#[test]
fn test_poseidon() {
let rng = &mut ChaChaRng::seed_from_u64(123456789u64);
let mut absorbed_rand_field_elems = Vec::new();
for _ in 0..NUM_ABSORBED_RAND_FIELD_ELEMS {
absorbed_rand_field_elems.push(Fq::rand(rng));
}
let mut absorbed_rand_byte_elems = Vec::<Vec<u8>>::new();
for _ in 0..NUM_ABSORBED_RAND_BYTE_ELEMS {
absorbed_rand_byte_elems.push((0..SIZE_ABSORBED_BYTE_ELEM).map(|_| u8::rand(rng)).collect());
}
let mut fs_rng = FS::new();
fs_rng.absorb_nonnative_field_elements(&absorbed_rand_field_elems, OptimizationType::Weight);
for absorbed_rand_byte_elem in &absorbed_rand_byte_elems {
fs_rng.absorb_bytes(absorbed_rand_byte_elem);
}
let squeezed_fields_elems = fs_rng
.squeeze_nonnative_field_elements(NUM_SQUEEZED_FIELD_ELEMS, OptimizationType::Weight)
.unwrap();
let squeezed_short_fields_elems = fs_rng
.squeeze_128_bits_nonnative_field_elements(NUM_SQUEEZED_SHORT_FIELD_ELEMS)
.unwrap();
let mut cs = TestConstraintSystem::<Fq>::new();
let mut fs_rng_gadget = FSGadget::new(cs.ns(|| "new"));
let mut absorbed_rand_field_elems_gadgets = Vec::new();
for (i, absorbed_rand_field_elem) in absorbed_rand_field_elems.iter().enumerate() {
absorbed_rand_field_elems_gadgets.push(
NonNativeFieldVar::alloc(cs.ns(|| format!("alloc_constant_nonnative_field_var_{}", i)), || {
Ok(absorbed_rand_field_elem)
})
.unwrap(),
);
}
fs_rng_gadget
.absorb_nonnative_field_elements(
cs.ns(|| "absorb_nonnative_fe"),
&absorbed_rand_field_elems_gadgets,
OptimizationType::Weight,
)
.unwrap();
let mut absorbed_rand_byte_elems_gadgets = Vec::<Vec<UInt8>>::new();
for absorbed_rand_byte_elem in absorbed_rand_byte_elems.iter() {
let mut byte_gadget = Vec::<UInt8>::new();
for byte in absorbed_rand_byte_elem.iter() {
byte_gadget.push(UInt8::constant(*byte));
}
absorbed_rand_byte_elems_gadgets.push(byte_gadget);
}
for (i, absorbed_rand_byte_elems_gadget) in absorbed_rand_byte_elems_gadgets.iter().enumerate() {
fs_rng_gadget
.absorb_bytes(cs.ns(|| format!("absorb_bytes_{}", i)), absorbed_rand_byte_elems_gadget)
.unwrap();
}
let squeezed_fields_elems_gadgets = fs_rng_gadget
.squeeze_field_elements(cs.ns(|| "squeeze_fe"), NUM_SQUEEZED_FIELD_ELEMS)
.unwrap();
let squeezed_short_fields_elems_gadgets = fs_rng_gadget
.squeeze_128_bits_field_elements(cs.ns(|| "squeeze_128_bits_fe"), NUM_SQUEEZED_SHORT_FIELD_ELEMS)
.unwrap();
for (i, (left, right)) in squeezed_fields_elems
.iter()
.zip(squeezed_fields_elems_gadgets.iter())
.enumerate()
{
assert_eq!(
left.to_repr(),
right.value().unwrap().to_repr(),
"{}: left = {:?}, right = {:?}",
i,
left.to_repr(),
right.value().unwrap().to_repr()
);
}
for (i, (left, right)) in squeezed_short_fields_elems
.iter()
.zip(squeezed_short_fields_elems_gadgets.iter())
.enumerate()
{
assert!(
left.to_repr().eq(&right.value().unwrap().to_repr()),
"{}: left = {:?}, right = {:?}",
i,
left.to_repr(),
right.value().unwrap().to_repr()
);
}
if !cs.is_satisfied() {
println!("\n=========================================================");
println!("\nUnsatisfied constraints:");
println!("\n{:?}", cs.which_is_unsatisfied().unwrap());
println!("\n=========================================================");
}
assert!(cs.is_satisfied());
}
#[test]
fn test_compress_gadgets_weight_optimized() {
let mut rng = ChaChaRng::seed_from_u64(123456789u64);
for i in 0..ITERATIONS {
let mut cs = TestConstraintSystem::<Fq>::new();
let num_elements: usize = rng.gen_range(0..MAX_ELEMENT_SIZE);
let elements: Vec<_> = (0..num_elements).map(|_| Fq::rand(&mut rng)).collect();
let mut element_limbs = Vec::<(Fq, Fq)>::new();
let mut element_limb_gadgets = Vec::<(FpGadget<Fq>, Fq)>::new();
for (j, elem) in elements.iter().enumerate() {
let limbs =
AllocatedNonNativeFieldVar::<Fq, Fq>::get_limbs_representations(elem, OptimizationType::Weight)
.unwrap();
for (k, limb) in limbs.iter().enumerate() {
let allocated_limb =
FpGadget::alloc(cs.ns(|| format!("alloc_limb_{}_{}_{}", i, j, k)), || Ok(limb)).unwrap();
element_limbs.push((*limb, Fq::one()));
element_limb_gadgets.push((allocated_limb, Fq::one()));
}
}
let compressed_elements = FS::compress_elements(&element_limbs, OptimizationType::Weight);
let compressed_element_gadgets = FSGadget::compress_gadgets(
cs.ns(|| "compress_elements"),
&element_limb_gadgets,
OptimizationType::Weight,
)
.unwrap();
for (j, (gadget, element)) in compressed_element_gadgets.iter().zip(compressed_elements).enumerate() {
let alloc_element =
FpGadget::alloc(cs.ns(|| format!("alloc_field_{}_{}", i, j)), || Ok(element)).unwrap();
gadget
.enforce_equal(cs.ns(|| format!("enforce_equal_element_{}_{}", i, j)), &alloc_element)
.unwrap();
}
}
}
#[test]
fn test_compress_gadgets_constraint_optimized() {
let mut rng = ChaChaRng::seed_from_u64(123456789u64);
for i in 0..ITERATIONS {
let mut cs = TestConstraintSystem::<Fq>::new();
let num_elements: usize = rng.gen_range(0..MAX_ELEMENT_SIZE);
let elements: Vec<_> = (0..num_elements).map(|_| Fq::rand(&mut rng)).collect();
let mut element_limbs = Vec::<(Fq, Fq)>::new();
let mut element_limb_gadgets = Vec::<(FpGadget<Fq>, Fq)>::new();
for (j, elem) in elements.iter().enumerate() {
let limbs = AllocatedNonNativeFieldVar::<Fq, Fq>::get_limbs_representations(
elem,
OptimizationType::Constraints,
)
.unwrap();
for (k, limb) in limbs.iter().enumerate() {
let allocated_limb =
FpGadget::alloc(cs.ns(|| format!("alloc_limb_{}_{}_{}", i, j, k)), || Ok(limb)).unwrap();
element_limbs.push((*limb, Fq::one()));
element_limb_gadgets.push((allocated_limb, Fq::one()));
}
}
let compressed_elements = FS::compress_elements(&element_limbs, OptimizationType::Constraints);
let compressed_element_gadgets = FSGadget::compress_gadgets(
cs.ns(|| "compress_elements"),
&element_limb_gadgets,
OptimizationType::Constraints,
)
.unwrap();
for (j, (gadget, element)) in compressed_element_gadgets.iter().zip(compressed_elements).enumerate() {
let alloc_element =
FpGadget::alloc(cs.ns(|| format!("alloc_field_{}_{}", i, j)), || Ok(element)).unwrap();
gadget
.enforce_equal(cs.ns(|| format!("enforce_equal_element_{}_{}", i, j)), &alloc_element)
.unwrap();
}
}
}
#[test]
fn test_get_booleans_from_sponge() {
let mut rng = ChaChaRng::seed_from_u64(123456789u64);
for i in 0..ITERATIONS {
let mut cs = TestConstraintSystem::<Fq>::new();
let mut fs_rng = FS::new();
let mut fs_rng_gadget = FSGadget::new(cs.ns(|| format!("new_fs_rng_gadget_{}", i)));
let num_bytes: usize = rng.gen_range(0..MAX_ELEMENT_SIZE);
let bytes: Vec<u8> = (0..num_bytes).map(|_| u8::rand(&mut rng)).collect();
let mut byte_gadgets = vec![];
for (j, byte) in bytes.iter().enumerate() {
let alloc_byte = UInt8::alloc(cs.ns(|| format!("alloc_byte_{}_{}", i, j)), || Ok(byte)).unwrap();
byte_gadgets.push(alloc_byte);
}
fs_rng.absorb_bytes(&bytes);
fs_rng_gadget
.absorb_bytes(cs.ns(|| format!("absorb_bytes{}", i)), &byte_gadgets)
.unwrap();
let num_bits = num_bytes * 8;
let bits = FS::get_bits_from_sponge(&mut fs_rng.s, num_bits);
let bit_gadgets = FSGadget::get_booleans_from_sponge(
cs.ns(|| format!("get_booleans_from_sponge_{}", i)),
&mut fs_rng_gadget.s,
num_bits,
)
.unwrap();
for (j, (bit_gadget, bit)) in bit_gadgets.iter().zip(bits).enumerate() {
let alloc_boolean =
Boolean::alloc(cs.ns(|| format!("alloc_boolean_result_{}_{}", i, j)), || Ok(bit)).unwrap();
bit_gadget
.enforce_equal(cs.ns(|| format!("enforce_equal_bit_{}_{}", i, j)), &alloc_boolean)
.unwrap();
}
}
}
#[test]
fn test_squeeze_native_field_elements() {
let mut rng = ChaChaRng::seed_from_u64(123456789u64);
for i in 0..ITERATIONS {
let mut cs = TestConstraintSystem::<Fq>::new();
let mut fs_rng = FS::new();
let mut fs_rng_gadget = FSGadget::new(cs.ns(|| format!("fs_rng_gadget_new{}", i)));
let num_elements: usize = rng.gen_range(0..MAX_ELEMENTS);
let elements: Vec<_> = (0..num_elements).map(|_| Fq::rand(&mut rng)).collect();
let mut element_gadgets = vec![];
for (j, element) in elements.iter().enumerate() {
let alloc_element =
FpGadget::alloc(cs.ns(|| format!("native_alloc_field_{}_{}", i, j)), || Ok(element)).unwrap();
element_gadgets.push(alloc_element);
}
fs_rng.absorb_native_field_elements(&elements);
fs_rng_gadget
.absorb_native_field_elements(cs.ns(|| format!("push_gadgets_to_sponge{}", i)), &element_gadgets)
.unwrap();
let squeeze_result = fs_rng.squeeze_native_field_elements(num_elements).unwrap();
let gadget_squeeze_result = fs_rng_gadget
.squeeze_native_field_elements(cs.ns(|| format!("squeeze_field_elements_{}", i)), num_elements)
.unwrap();
for (j, (gadget, element)) in gadget_squeeze_result.iter().zip(squeeze_result).enumerate() {
let alloc_element = FpGadget::alloc(cs.ns(|| format!("native_alloc_field_result{}_{}", i, j)), || {
Ok(element)
})
.unwrap();
gadget
.enforce_equal(cs.ns(|| format!("enforce_equal_element_{}_{}", i, j)), &alloc_element)
.unwrap();
}
}
}
#[test]
fn test_squeeze_nonnative_field_elements() {
let mut rng = ChaChaRng::seed_from_u64(123456789u64);
for i in 0..ITERATIONS {
let mut cs = TestConstraintSystem::<Fq>::new();
let mut fs_rng = FS::new();
let mut fs_rng_gadget = FSGadget::new(cs.ns(|| format!("fs_rng_gadget_new{}", i)));
let num_elements: usize = rng.gen_range(0..MAX_ELEMENTS);
let elements: Vec<_> = (0..num_elements).map(|_| Fq::rand(&mut rng)).collect();
let mut element_gadgets = vec![];
for (j, element) in elements.iter().enumerate() {
let alloc_element =
NonNativeFieldVar::alloc(cs.ns(|| format!("nonnative_alloc_field_{}_{}", i, j)), || Ok(element))
.unwrap();
element_gadgets.push(alloc_element);
}
fs_rng.absorb_nonnative_field_elements(&elements, OptimizationType::Constraints);
fs_rng_gadget
.absorb_nonnative_field_elements(
cs.ns(|| format!("push_gadgets_to_sponge{}", i)),
&element_gadgets,
OptimizationType::Constraints,
)
.unwrap();
let squeeze_result = fs_rng
.squeeze_nonnative_field_elements(num_elements, OptimizationType::Constraints)
.unwrap();
let gadget_squeeze_result = fs_rng_gadget
.squeeze_field_elements(cs.ns(|| format!("squeeze_field_elements_{}", i)), num_elements)
.unwrap();
for (j, (gadget, element)) in gadget_squeeze_result.iter().zip(squeeze_result).enumerate() {
let alloc_element =
NonNativeFieldVar::alloc(cs.ns(|| format!("nonnative_alloc_field_result{}_{}", i, j)), || {
Ok(element)
})
.unwrap();
gadget
.enforce_equal(cs.ns(|| format!("enforce_equal_element_{}_{}", i, j)), &alloc_element)
.unwrap();
}
}
}
#[test]
fn test_squeeze_field_elements_and_bits() {
let mut rng = ChaChaRng::seed_from_u64(123456789u64);
for i in 0..ITERATIONS {
let mut cs = TestConstraintSystem::<Fq>::new();
let mut fs_rng = FS::new();
let mut fs_rng_gadget = FSGadget::new(cs.ns(|| format!("fs_rng_gadget_new{}", i)));
let num_elements: usize = rng.gen_range(0..MAX_ELEMENTS);
let elements: Vec<_> = (0..num_elements).map(|_| Fq::rand(&mut rng)).collect();
let mut element_gadgets = vec![];
for (j, element) in elements.iter().enumerate() {
let alloc_element =
NonNativeFieldVar::alloc(cs.ns(|| format!("nonnative_alloc_field_{}_{}", i, j)), || Ok(element))
.unwrap();
element_gadgets.push(alloc_element);
}
fs_rng.absorb_nonnative_field_elements(&elements, OptimizationType::Constraints);
fs_rng_gadget
.absorb_nonnative_field_elements(
cs.ns(|| format!("push_gadgets_to_sponge{}", i)),
&element_gadgets,
OptimizationType::Constraints,
)
.unwrap();
let squeeze_result = fs_rng
.squeeze_nonnative_field_elements(num_elements, OptimizationType::Constraints)
.unwrap();
let (gadget_squeeze_result, gadget_squeeze_bits) = fs_rng_gadget
.squeeze_field_elements_and_bits(cs.ns(|| format!("squeeze_field_elements_{}", i)), num_elements)
.unwrap();
for (j, ((gadget, bits), element)) in gadget_squeeze_result
.iter()
.zip(gadget_squeeze_bits)
.zip(squeeze_result)
.enumerate()
{
let alloc_element =
NonNativeFieldVar::alloc(cs.ns(|| format!("nonnative_alloc_field_result{}_{}", i, j)), || {
Ok(element)
})
.unwrap();
gadget
.enforce_equal(cs.ns(|| format!("enforce_equal_element_{}_{}", i, j)), &alloc_element)
.unwrap();
let allocated_bits = alloc_element
.to_bits_le(cs.ns(|| format!("to_bits_le_{}_{}", i, j)))
.unwrap();
for (k, (allocated_bit, bit)) in allocated_bits.iter().zip(bits).enumerate() {
allocated_bit
.enforce_equal(cs.ns(|| format!("enforce_equal_bit_{}_{}_{}", i, j, k)), &bit)
.unwrap();
}
}
}
}
#[test]
fn test_squeeze_128_bits_field_elements() {
let mut rng = ChaChaRng::seed_from_u64(123456789u64);
for i in 0..ITERATIONS {
let mut cs = TestConstraintSystem::<Fq>::new();
let mut fs_rng = FS::new();
let mut fs_rng_gadget = FSGadget::new(cs.ns(|| format!("fs_rng_gadget_new{}", i)));
let num_elements: usize = rng.gen_range(0..MAX_ELEMENTS);
let elements: Vec<_> = (0..num_elements).map(|_| Fq::rand(&mut rng)).collect();
let mut element_gadgets = vec![];
for (j, element) in elements.iter().enumerate() {
let alloc_element =
NonNativeFieldVar::alloc(cs.ns(|| format!("nonnative_alloc_field_{}_{}", i, j)), || Ok(element))
.unwrap();
element_gadgets.push(alloc_element);
}
fs_rng.absorb_nonnative_field_elements(&elements, OptimizationType::Constraints);
fs_rng_gadget
.absorb_nonnative_field_elements(
cs.ns(|| format!("push_gadgets_to_sponge{}", i)),
&element_gadgets,
OptimizationType::Constraints,
)
.unwrap();
let squeeze_result = fs_rng.squeeze_128_bits_nonnative_field_elements(num_elements).unwrap();
let gadget_squeeze_result = fs_rng_gadget
.squeeze_128_bits_field_elements(
cs.ns(|| format!("squeeze_128_bit_field_elements_{}", i)),
num_elements,
)
.unwrap();
for (j, (gadget, element)) in gadget_squeeze_result.iter().zip(squeeze_result).enumerate() {
let alloc_element =
NonNativeFieldVar::alloc(cs.ns(|| format!("nonnative_alloc_field_result{}_{}", i, j)), || {
Ok(element)
})
.unwrap();
gadget
.enforce_equal(cs.ns(|| format!("enforce_equal_element_{}_{}", i, j)), &alloc_element)
.unwrap();
}
}
}
#[test]
fn test_squeeze_128_bits_field_elements_and_bits() {
let mut rng = ChaChaRng::seed_from_u64(123456789u64);
for i in 0..ITERATIONS {
let mut cs = TestConstraintSystem::<Fq>::new();
let mut fs_rng = FS::new();
let mut fs_rng_gadget = FSGadget::new(cs.ns(|| format!("fs_rng_gadget_new{}", i)));
let num_elements: usize = rng.gen_range(0..MAX_ELEMENTS);
let elements: Vec<_> = (0..num_elements).map(|_| Fq::rand(&mut rng)).collect();
let mut element_gadgets = vec![];
for (j, element) in elements.iter().enumerate() {
let alloc_element =
NonNativeFieldVar::alloc(cs.ns(|| format!("nonnative_alloc_field_{}_{}", i, j)), || Ok(element))
.unwrap();
element_gadgets.push(alloc_element);
}
fs_rng.absorb_nonnative_field_elements(&elements, OptimizationType::Constraints);
fs_rng_gadget
.absorb_nonnative_field_elements(
cs.ns(|| format!("push_gadgets_to_sponge{}", i)),
&element_gadgets,
OptimizationType::Constraints,
)
.unwrap();
let squeeze_result = fs_rng.squeeze_128_bits_nonnative_field_elements(num_elements).unwrap();
let (gadget_squeeze_result, gadget_squeeze_bits) = fs_rng_gadget
.squeeze_128_bits_field_elements_and_bits(
cs.ns(|| format!("squeeze_128_bit_field_elements_{}", i)),
num_elements,
)
.unwrap();
for (j, ((gadget, bits), element)) in gadget_squeeze_result
.iter()
.zip(gadget_squeeze_bits)
.zip(squeeze_result)
.enumerate()
{
let alloc_element =
NonNativeFieldVar::alloc(cs.ns(|| format!("nonnative_alloc_field_result{}_{}", i, j)), || {
Ok(element)
})
.unwrap();
gadget
.enforce_equal(cs.ns(|| format!("enforce_equal_element_{}_{}", i, j)), &alloc_element)
.unwrap();
let allocated_bits = alloc_element
.to_bits_le(cs.ns(|| format!("to_bits_le_{}_{}", i, j)))
.unwrap();
for (k, (allocated_bit, bit)) in allocated_bits.iter().zip(bits).enumerate() {
allocated_bit
.enforce_equal(cs.ns(|| format!("enforce_equal_bit_{}_{}_{}", i, j, k)), &bit)
.unwrap();
}
}
}
}
}