use crate::{
algorithms::crypto_hash::PoseidonCryptoHashGadget,
traits::alloc::AllocGadget,
CryptoHashGadget,
FpGadget,
PRFGadget,
};
use snarkvm_algorithms::{crypto_hash::PoseidonDefaultParametersField, prf::PoseidonPRF};
use snarkvm_fields::PrimeField;
use snarkvm_r1cs::{ConstraintSystem, SynthesisError};
use std::marker::PhantomData;
pub struct PoseidonPRFGadget<
F: PrimeField + PoseidonDefaultParametersField,
const RATE: usize,
const OPTIMIZED_FOR_WEIGHTS: bool,
>(PhantomData<F>);
impl<F: PrimeField + PoseidonDefaultParametersField, const RATE: usize, const OPTIMIZED_FOR_WEIGHTS: bool>
PRFGadget<PoseidonPRF<F, RATE, OPTIMIZED_FOR_WEIGHTS>, F> for PoseidonPRFGadget<F, RATE, OPTIMIZED_FOR_WEIGHTS>
{
type Input = Vec<FpGadget<F>>;
type Output = FpGadget<F>;
type Seed = FpGadget<F>;
fn check_evaluation_gadget<CS: ConstraintSystem<F>>(
mut cs: CS,
seed: &Self::Seed,
input: &Self::Input,
) -> Result<Self::Output, SynthesisError> {
let input_length = {
let mut buffer = input.len().to_le_bytes().to_vec();
buffer.resize((F::size_in_bits() + 7) / 8, 0u8);
F::from_bytes_le(&buffer)?
};
let input_length_gadget = FpGadget::<F>::alloc(cs.ns(|| "Allocate input length"), || Ok(&input_length))?;
let mut preimage = vec![seed.clone()];
preimage.push(input_length_gadget);
preimage.extend_from_slice(input.as_slice());
PoseidonCryptoHashGadget::<F, RATE, OPTIMIZED_FOR_WEIGHTS>::check_evaluation_gadget(
cs.ns(|| "Check Poseidon PRF evaluation"),
&preimage,
)
}
}
#[cfg(test)]
mod tests {
use crate::{
algorithms::prf::*,
traits::{algorithms::PRFGadget, alloc::AllocGadget, eq::EqGadget},
};
use snarkvm_algorithms::{prf::PoseidonPRF, traits::PRF};
use snarkvm_curves::bls12_377::Fr;
use snarkvm_r1cs::{ConstraintSystem, TestConstraintSystem};
use rand::{Rng, SeedableRng};
use rand_chacha::ChaChaRng;
#[test]
fn test_prf() {
let mut rng = ChaChaRng::seed_from_u64(1231275789u64);
let mut cs = TestConstraintSystem::<Fr>::new();
let seed = rng.gen();
let input = vec![rng.gen()];
let output = PoseidonPRF::<Fr, 4, false>::evaluate(&seed, &input).unwrap();
let seed_gadget =
<PoseidonPRFGadget<Fr, 4, false> as PRFGadget<_, Fr>>::Seed::alloc(&mut cs.ns(|| "seed"), || Ok(seed))
.unwrap();
let input_gadget =
<PoseidonPRFGadget<Fr, 4, false> as PRFGadget<_, Fr>>::Input::alloc(&mut cs.ns(|| "input"), || Ok(input))
.unwrap();
let expected_output_gadget =
<PoseidonPRFGadget<Fr, 4, false> as PRFGadget<_, Fr>>::Output::alloc(&mut cs.ns(|| "output"), || {
Ok(output)
})
.unwrap();
let candidate_output_gadget = PoseidonPRFGadget::<Fr, 4, false>::check_evaluation_gadget(
&mut cs.ns(|| "evaluate"),
&seed_gadget,
&input_gadget,
)
.unwrap();
candidate_output_gadget
.enforce_equal(&mut cs, &expected_output_gadget)
.unwrap();
if !cs.is_satisfied() {
println!("which is unsatisfied: {:?}", cs.which_is_unsatisfied().unwrap());
}
assert!(cs.is_satisfied());
}
}