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