arnaucube
arnaucube/hash-chain-sonobePublic Self-hosted repository
hash-chain-sonobe/src/sha_chain_offchain.rs
src/sha_chain_offchain.rsRaw
  1///
  2/// This example performs the IVC:
  3/// - define the circuit to be folded
  4/// - fold the circuit with Nova+CycleFold's IVC
  5/// - verify the IVC proof
  6///
  7
  8#[cfg(test)]
  9mod tests {
 10    use ark_pallas::{constraints::GVar, Fr, Projective as G1};
 11    use ark_vesta::{constraints::GVar as GVar2, Projective as G2};
 12
 13    use ark_crypto_primitives::crh::sha256::{constraints::Sha256Gadget, digest::Digest, Sha256};
 14    use ark_ff::PrimeField;
 15    use ark_r1cs_std::fields::fp::FpVar;
 16    use ark_r1cs_std::{bits::uint8::UInt8, boolean::Boolean, ToBitsGadget, ToBytesGadget};
 17    use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
 18    use std::marker::PhantomData;
 19    use std::time::Instant;
 20
 21    use folding_schemes::{
 22        commitment::pedersen::Pedersen,
 23        folding::nova::{Nova, PreprocessorParam},
 24        frontend::FCircuit,
 25        transcript::poseidon::poseidon_canonical_config,
 26        Error, FoldingScheme,
 27    };
 28
 29    use crate::utils::tests::*;
 30
 31    /// Test circuit to be folded
 32    #[derive(Clone, Copy, Debug)]
 33    pub struct SHA256FoldStepCircuit<F: PrimeField, const HASHES_PER_STEP: usize> {
 34        _f: PhantomData<F>,
 35    }
 36    impl<F: PrimeField, const HASHES_PER_STEP: usize> FCircuit<F>
 37        for SHA256FoldStepCircuit<F, HASHES_PER_STEP>
 38    {
 39        type Params = ();
 40        fn new(_params: Self::Params) -> Result<Self, Error> {
 41            Ok(Self { _f: PhantomData })
 42        }
 43        fn state_len(&self) -> usize {
 44            32
 45        }
 46        fn external_inputs_len(&self) -> usize {
 47            0
 48        }
 49        fn step_native(
 50            &self,
 51            _i: usize,
 52            z_i: Vec<F>,
 53            _external_inputs: Vec<F>,
 54        ) -> Result<Vec<F>, Error> {
 55            let mut b = f_vec_to_bytes(z_i.to_vec());
 56
 57            for _ in 0..HASHES_PER_STEP {
 58                let mut sha256 = Sha256::default();
 59                sha256.update(b);
 60                b = sha256.finalize().to_vec();
 61            }
 62
 63            bytes_to_f_vec(b.to_vec()) // z_{i+1}
 64        }
 65        fn generate_step_constraints(
 66            &self,
 67            _cs: ConstraintSystemRef<F>,
 68            _i: usize,
 69            z_i: Vec<FpVar<F>>,
 70            _external_inputs: Vec<FpVar<F>>,
 71        ) -> Result<Vec<FpVar<F>>, SynthesisError> {
 72            let mut b: Vec<UInt8<F>> = z_i
 73                .iter()
 74                .map(|f| UInt8::<F>::from_bits_le(&f.to_bits_le().unwrap()[..8]))
 75                .collect::<Vec<_>>();
 76
 77            for _ in 0..HASHES_PER_STEP {
 78                let mut sha256_var = Sha256Gadget::default();
 79                sha256_var.update(&b).unwrap();
 80                b = sha256_var.finalize()?.to_bytes()?;
 81            }
 82
 83            let z_i1: Vec<FpVar<F>> = b
 84                .iter()
 85                .map(|e| {
 86                    let bits = e.to_bits_le().unwrap();
 87                    Boolean::<F>::le_bits_to_fp_var(&bits).unwrap()
 88                })
 89                .collect();
 90
 91            Ok(z_i1)
 92        }
 93    }
 94
 95    #[test]
 96    fn full_flow() {
 97        // set how many steps of folding we want to compute
 98        const N_STEPS: usize = 5;
 99        const HASHES_PER_STEP: usize = 20;
100        println!("running Nova folding scheme on SHA256FoldStepCircuit, with N_STEPS={}, HASHES_PER_STEP={}. Total hashes = {}", N_STEPS, HASHES_PER_STEP, N_STEPS* HASHES_PER_STEP);
101
102        // set the initial state
103        // let z_0_aux: Vec<u32> = vec![0_u32; 32 * 8];
104        let z_0_aux: Vec<u8> = vec![0_u8; 32];
105        let z_0: Vec<Fr> = z_0_aux.iter().map(|v| Fr::from(*v)).collect::<Vec<Fr>>();
106
107        let f_circuit = SHA256FoldStepCircuit::<Fr, HASHES_PER_STEP>::new(()).unwrap();
108
109        // ----------------
110        // Sanity check
111        // check that the f_circuit produces valid R1CS constraints
112        use ark_r1cs_std::alloc::AllocVar;
113        use ark_r1cs_std::fields::fp::FpVar;
114        use ark_r1cs_std::R1CSVar;
115        use ark_relations::r1cs::ConstraintSystem;
116        let cs = ConstraintSystem::<Fr>::new_ref();
117        let z_0_var = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(z_0.clone())).unwrap();
118        let z_1_var = f_circuit
119            .generate_step_constraints(cs.clone(), 1, z_0_var, vec![])
120            .unwrap();
121        // check z_1_var against the native z_1
122        let z_1_native = f_circuit.step_native(1, z_0.clone(), vec![]).unwrap();
123        assert_eq!(z_1_var.value().unwrap(), z_1_native);
124        // check that the constraint system is satisfied
125        assert!(cs.is_satisfied().unwrap());
126        println!(
127            "number of constraints of a single instantiation of the SHA256FoldStepCircuit: {}",
128            cs.num_constraints()
129        );
130        // ----------------
131
132        // define type aliases for the FoldingScheme (FS) and Decider (D), to avoid writting the
133        // whole type each time
134        pub type FS = Nova<
135            G1,
136            GVar,
137            G2,
138            GVar2,
139            SHA256FoldStepCircuit<Fr, HASHES_PER_STEP>,
140            Pedersen<G1>,
141            Pedersen<G2>,
142            false,
143        >;
144
145        let poseidon_config = poseidon_canonical_config::<Fr>();
146        let mut rng = rand::rngs::OsRng;
147
148        // prepare the Nova prover & verifier params
149        let nova_preprocess_params = PreprocessorParam::new(poseidon_config, f_circuit);
150        let start = Instant::now();
151        let nova_params = FS::preprocess(&mut rng, &nova_preprocess_params).unwrap();
152        println!("Nova params generated: {:?}", start.elapsed());
153
154        // initialize the folding scheme engine, in our case we use Nova
155        let mut nova = FS::init(&nova_params, f_circuit, z_0.clone()).unwrap();
156
157        // run n steps of the folding iteration
158        let start_full = Instant::now();
159        for _ in 0..N_STEPS {
160            let start = Instant::now();
161            nova.prove_step(rng, vec![], None).unwrap();
162            println!(
163                "Nova::prove_step (sha256) {}: {:?}",
164                nova.i,
165                start.elapsed()
166            );
167        }
168        println!(
169            "Nova's all {} steps time: {:?}",
170            N_STEPS,
171            start_full.elapsed()
172        );
173
174        // verify the last IVC proof
175        let ivc_proof = nova.ivc_proof();
176        FS::verify(
177            nova_params.1.clone(), // Nova's verifier params
178            ivc_proof,
179        )
180        .unwrap();
181    }
182}