arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/examples/fold_sha256.rs
examples/fold_sha256.rsRaw
  1#![allow(non_snake_case)]
  2#![allow(non_upper_case_globals)]
  3#![allow(non_camel_case_types)]
  4#![allow(clippy::upper_case_acronyms)]
  5
  6use ark_crypto_primitives::crh::{
  7    sha256::{
  8        constraints::{Sha256Gadget, UnitVar},
  9        Sha256,
 10    },
 11    CRHScheme, CRHSchemeGadget,
 12};
 13use ark_ff::{BigInteger, PrimeField, ToConstraintField};
 14use ark_r1cs_std::{fields::fp::FpVar, ToBytesGadget, ToConstraintFieldGadget};
 15use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
 16use core::marker::PhantomData;
 17use std::time::Instant;
 18
 19use ark_pallas::{constraints::GVar, Fr, Projective};
 20use ark_vesta::{constraints::GVar as GVar2, Projective as Projective2};
 21
 22use folding_schemes::commitment::pedersen::Pedersen;
 23use folding_schemes::folding::nova::{get_r1cs, Nova, ProverParams, VerifierParams};
 24use folding_schemes::frontend::FCircuit;
 25use folding_schemes::transcript::poseidon::poseidon_test_config;
 26use folding_schemes::{Error, FoldingScheme};
 27
 28/// This is the circuit that we want to fold, it implements the FCircuit trait.
 29/// The parameter z_i denotes the current state, and z_{i+1} denotes the next state which we get by
 30/// applying the step.
 31/// In this example we set z_i and z_{i+1} to be a single value, but the trait is made to support
 32/// arrays, so our state could be an array with different values.
 33#[derive(Clone, Copy, Debug)]
 34pub struct Sha256FCircuit<F: PrimeField> {
 35    _f: PhantomData<F>,
 36}
 37impl<F: PrimeField> FCircuit<F> for Sha256FCircuit<F> {
 38    type Params = ();
 39
 40    fn new(_params: Self::Params) -> Self {
 41        Self { _f: PhantomData }
 42    }
 43
 44    /// computes the next state values in place, assigning z_{i+1} into z_i, and computing the new
 45    /// z_{i+1}
 46    fn step_native(self, z_i: Vec<F>) -> Result<Vec<F>, Error> {
 47        let out_bytes = Sha256::evaluate(&(), z_i[0].into_bigint().to_bytes_le()).unwrap();
 48        let out: Vec<F> = out_bytes.to_field_elements().unwrap();
 49
 50        Ok(vec![out[0]])
 51    }
 52
 53    /// generates the constraints for the step of F for the given z_i
 54    fn generate_step_constraints(
 55        self,
 56        _cs: ConstraintSystemRef<F>,
 57        z_i: Vec<FpVar<F>>,
 58    ) -> Result<Vec<FpVar<F>>, SynthesisError> {
 59        let unit_var = UnitVar::default();
 60        let out_bytes = Sha256Gadget::evaluate(&unit_var, &z_i[0].to_bytes()?)?;
 61        let out = out_bytes.0.to_constraint_field()?;
 62        Ok(vec![out[0].clone()])
 63    }
 64}
 65
 66/// cargo test --example simple
 67#[cfg(test)]
 68pub mod tests {
 69    use super::*;
 70    use ark_r1cs_std::alloc::AllocVar;
 71    use ark_relations::r1cs::ConstraintSystem;
 72
 73    // test to check that the Sha256FCircuit computes the same values inside and outside the circuit
 74    #[test]
 75    fn test_sha256_f_circuit() {
 76        let cs = ConstraintSystem::<Fr>::new_ref();
 77
 78        let circuit = Sha256FCircuit::<Fr>::new(());
 79        let z_i = vec![Fr::from(1_u32)];
 80
 81        let z_i1 = circuit.step_native(z_i.clone()).unwrap();
 82
 83        let z_iVar = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(z_i)).unwrap();
 84        let computed_z_i1Var = circuit
 85            .generate_step_constraints(cs.clone(), z_iVar.clone())
 86            .unwrap();
 87        assert_eq!(computed_z_i1Var.value().unwrap(), z_i1);
 88    }
 89}
 90
 91// This method computes the Prover & Verifier parameters for the example. For a real world use case
 92// those parameters should be generated carefuly (both the PoseidonConfig and the PedersenParams)
 93#[allow(clippy::type_complexity)]
 94fn nova_setup<FC: FCircuit<Fr>>(
 95    F_circuit: FC,
 96) -> (
 97    ProverParams<Projective, Projective2, Pedersen<Projective>, Pedersen<Projective2>>,
 98    VerifierParams<Projective, Projective2>,
 99) {
100    let mut rng = ark_std::test_rng();
101    let poseidon_config = poseidon_test_config::<Fr>();
102
103    // get the CM & CF_CM len
104    let (r1cs, cf_r1cs) =
105        get_r1cs::<Projective, GVar, Projective2, GVar2, FC>(&poseidon_config, F_circuit).unwrap();
106    let cm_len = r1cs.A.n_rows;
107    let cf_cm_len = cf_r1cs.A.n_rows;
108
109    let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, cm_len);
110    let cf_pedersen_params = Pedersen::<Projective2>::new_params(&mut rng, cf_cm_len);
111
112    let prover_params =
113        ProverParams::<Projective, Projective2, Pedersen<Projective>, Pedersen<Projective2>> {
114            poseidon_config: poseidon_config.clone(),
115            cm_params: pedersen_params,
116            cf_cm_params: cf_pedersen_params,
117        };
118    let verifier_params = VerifierParams::<Projective, Projective2> {
119        poseidon_config: poseidon_config.clone(),
120        r1cs,
121        cf_r1cs,
122    };
123    (prover_params, verifier_params)
124}
125
126/// cargo run --release --example fold_sha256
127fn main() {
128    let num_steps = 10;
129    let initial_state = vec![Fr::from(1_u32)];
130
131    let F_circuit = Sha256FCircuit::<Fr>::new(());
132
133    println!("Prepare Nova ProverParams & VerifierParams");
134    let (prover_params, verifier_params) = nova_setup::<Sha256FCircuit<Fr>>(F_circuit);
135
136    /// The idea here is that eventually we could replace the next line chunk that defines the
137    /// `type NOVA = Nova<...>` by using another folding scheme that fulfills the `FoldingScheme`
138    /// trait, and the rest of our code would be working without needing to be updated.
139    type NOVA = Nova<
140        Projective,
141        GVar,
142        Projective2,
143        GVar2,
144        Sha256FCircuit<Fr>,
145        Pedersen<Projective>,
146        Pedersen<Projective2>,
147    >;
148
149    println!("Initialize FoldingScheme");
150    let mut folding_scheme = NOVA::init(&prover_params, F_circuit, initial_state.clone()).unwrap();
151
152    // compute a step of the IVC
153    for i in 0..num_steps {
154        let start = Instant::now();
155        folding_scheme.prove_step().unwrap();
156        println!("Nova::prove_step {}: {:?}", i, start.elapsed());
157    }
158
159    let (running_instance, incomming_instance, cyclefold_instance) = folding_scheme.instances();
160
161    println!("Run the Nova's IVC verifier");
162    NOVA::verify(
163        verifier_params,
164        initial_state,
165        folding_scheme.state(), // latest state
166        Fr::from(num_steps as u32),
167        running_instance,
168        incomming_instance,
169        cyclefold_instance,
170    )
171    .unwrap();
172}