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