arnaucube
arnaucube/hash-chain-sonobePublic Self-hosted repository
hash-chain-sonobe/src/naive_approach_poseidon_chain.rs
src/naive_approach_poseidon_chain.rsRaw
  1/// This example does the hash chain but in the naive approach: instead of using folding, it does a
  2/// big circuit containing n instantiations of the Poseidon constraints.
  3
  4#[cfg(test)]
  5mod tests {
  6    use ark_bn254::{Bn254, Fr};
  7
  8    use ark_groth16::Groth16;
  9    use ark_snark::SNARK;
 10
 11    use ark_ff::PrimeField;
 12
 13    use std::time::Instant;
 14
 15    use ark_crypto_primitives::sponge::{
 16        constraints::CryptographicSpongeVar,
 17        poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig, PoseidonSponge},
 18        CryptographicSponge,
 19    };
 20    use ark_r1cs_std::fields::fp::FpVar;
 21    use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget};
 22    use ark_relations::r1cs::{
 23        ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, SynthesisError,
 24    };
 25
 26    use folding_schemes::transcript::poseidon::poseidon_canonical_config;
 27
 28    /// Test circuit to be folded
 29    #[derive(Clone, Debug)]
 30    pub struct PoseidonChainCircuit<F: PrimeField, const N: usize, const HASHES_PER_STEP: usize> {
 31        z_0: Option<Vec<F>>,
 32        z_n: Option<Vec<F>>,
 33        config: PoseidonConfig<F>,
 34    }
 35    impl<F: PrimeField, const N: usize, const HASHES_PER_STEP: usize> ConstraintSynthesizer<F>
 36        for PoseidonChainCircuit<F, N, HASHES_PER_STEP>
 37    {
 38        fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> Result<(), SynthesisError> {
 39            let z_0 = Vec::<FpVar<F>>::new_witness(cs.clone(), || {
 40                Ok(self.z_0.unwrap_or(vec![F::zero()]))
 41            })?;
 42            let z_n =
 43                Vec::<FpVar<F>>::new_input(cs.clone(), || Ok(self.z_n.unwrap_or(vec![F::zero()])))?;
 44
 45            let mut sponge = PoseidonSpongeVar::<F>::new(cs.clone(), &self.config);
 46            let mut z_i: Vec<FpVar<F>> = z_0.clone();
 47            for _ in 0..N {
 48                for _ in 0..HASHES_PER_STEP {
 49                    sponge.absorb(&z_i)?;
 50                    z_i = sponge.squeeze_field_elements(1)?;
 51                }
 52            }
 53
 54            z_i.enforce_equal(&z_n)?;
 55            Ok(())
 56        }
 57    }
 58    // compute natively in rust the expected result
 59    fn rust_native_result(
 60        poseidon_config: &PoseidonConfig<Fr>,
 61        z_0: Vec<Fr>,
 62        n_steps: usize,
 63        hashes_per_step: usize,
 64    ) -> Vec<Fr> {
 65        let mut z_i: Vec<Fr> = z_0.clone();
 66        let mut sponge = PoseidonSponge::<Fr>::new(&poseidon_config);
 67        for _ in 0..n_steps {
 68            for _ in 0..hashes_per_step {
 69                sponge.absorb(&z_i);
 70                z_i = sponge.squeeze_field_elements(1);
 71            }
 72        }
 73        z_i.clone()
 74    }
 75
 76    #[test]
 77    fn full_flow() {
 78        // set how many iterations of the PoseidonChainCircuit circuit internal loop we want to
 79        // compute
 80        const N_STEPS: usize = 10;
 81        const HASHES_PER_STEP: usize = 400;
 82        println!("running the 'naive' PoseidonChainCircuit, with N_STEPS={}, HASHES_PER_STEP={}. Total hashes = {}", N_STEPS, HASHES_PER_STEP, N_STEPS* HASHES_PER_STEP);
 83
 84        let poseidon_config = poseidon_canonical_config::<Fr>();
 85
 86        // set the initial state
 87        // let z_0_aux: Vec<u32> = vec![0_u32; 32 * 8];
 88        let z_0_aux: Vec<u8> = vec![0_u8; 32];
 89        let z_0: Vec<Fr> = z_0_aux.iter().map(|v| Fr::from(*v)).collect::<Vec<Fr>>();
 90
 91        // run the N iterations 'natively' in rust to compute the expected `z_n`
 92        let z_n = rust_native_result(&poseidon_config, z_0.clone(), N_STEPS, HASHES_PER_STEP);
 93
 94        let circuit = PoseidonChainCircuit::<Fr, N_STEPS, HASHES_PER_STEP> {
 95            z_0: Some(z_0),
 96            z_n: Some(z_n.clone()),
 97            config: poseidon_config,
 98        };
 99
100        let cs = ConstraintSystem::<Fr>::new_ref();
101        circuit.clone().generate_constraints(cs.clone()).unwrap();
102        println!(
103            "number of constraints of the (naive) PoseidonChainCircuit with N_STEPS*HASHES_PER_STEP={} poseidon hashes in total: {} (num constraints)",
104            N_STEPS * HASHES_PER_STEP,
105            cs.num_constraints()
106        );
107
108        // now let's generate an actual Groth16 proof
109        let mut rng = rand::rngs::OsRng;
110        let (g16_pk, g16_vk) =
111            Groth16::<Bn254>::circuit_specific_setup(circuit.clone(), &mut rng).unwrap();
112
113        let start = Instant::now();
114        let proof = Groth16::<Bn254>::prove(&g16_pk, circuit.clone(), &mut rng).unwrap();
115        println!(
116            "Groth16 proof generation (for the naive PoseidonChainCircuit): {:?}",
117            start.elapsed()
118        );
119
120        let public_inputs = z_n;
121        let valid_proof = Groth16::<Bn254>::verify(&g16_vk, &public_inputs, &proof).unwrap();
122
123        assert!(valid_proof);
124
125        println!("finished running the 'naive' PoseidonChainCircuit, with N_STEPS={}, HASHES_PER_STEP={}. Total hashes = {}", N_STEPS, HASHES_PER_STEP, N_STEPS* HASHES_PER_STEP);
126    }
127}