/ algorithms / src / snark / varuna / tests.rs
tests.rs
  1  // Copyright (c) 2025-2026 ACDC Network
  2  // This file is part of the alphavm library.
  3  //
  4  // Alpha Chain | Delta Chain Protocol
  5  // International Monetary Graphite.
  6  //
  7  // Derived from Aleo (https://aleo.org) and ProvableHQ (https://provable.com).
  8  // They built world-class ZK infrastructure. We installed the EASY button.
  9  // Their cryptography: elegant. Our modifications: bureaucracy-compatible.
 10  // Original brilliance: theirs. Robert's Rules: ours. Bugs: definitely ours.
 11  //
 12  // Original Aleo/ProvableHQ code subject to Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0
 13  // All modifications and new work: CC0 1.0 Universal Public Domain Dedication.
 14  // No rights reserved. No permission required. No warranty. No refunds.
 15  //
 16  // https://creativecommons.org/publicdomain/zero/1.0/
 17  // SPDX-License-Identifier: CC0-1.0
 18  
 19  #[cfg(any(test, feature = "test"))]
 20  mod varuna {
 21      use crate::{
 22          snark::varuna::{
 23              mode::SNARKMode,
 24              proof::proof_size,
 25              test_circuit::TestCircuit,
 26              AHPForR1CS,
 27              CircuitVerifyingKey,
 28              VarunaHidingMode,
 29              VarunaNonHidingMode,
 30              VarunaSNARK,
 31              VarunaVersion,
 32          },
 33          traits::{AlgebraicSponge, SNARK},
 34      };
 35  
 36      use std::collections::BTreeMap;
 37  
 38      use alphavm_curves::bls12_377::{Bls12_377, Fq, Fr};
 39      use alphavm_utilities::{
 40          rand::{TestRng, Uniform},
 41          CanonicalSerialize,
 42          ToBytes,
 43      };
 44  
 45      type FS = crate::crypto_hash::PoseidonSponge<Fq, 2, 1>;
 46  
 47      type VarunaSonicInst = VarunaSNARK<Bls12_377, FS, VarunaHidingMode>;
 48      type VarunaSonicPoSWInst = VarunaSNARK<Bls12_377, FS, VarunaNonHidingMode>;
 49  
 50      macro_rules! impl_varuna_test {
 51          ($test_struct: ident, $snark_inst: tt, $snark_mode: tt) => {
 52              struct $test_struct {}
 53              impl $test_struct {
 54                  pub(crate) fn test_circuit(num_constraints: usize, num_variables: usize, pk_size_expectation: usize, varuna_version: VarunaVersion, rng: &mut alphavm_utilities::rand::TestRng) {
 55                      let random = Fr::rand(rng);
 56  
 57                      let max_degree = AHPForR1CS::<Fr, $snark_mode>::max_degree(100, 25, 300).unwrap();
 58                      let universal_srs = $snark_inst::universal_setup(max_degree).unwrap();
 59                      let universal_prover = &universal_srs.to_universal_prover().unwrap();
 60                      let universal_verifier = &universal_srs.to_universal_verifier().unwrap();
 61                      let fs_parameters = FS::sample_parameters();
 62  
 63                      let wrong_varuna_version = match varuna_version {
 64                          VarunaVersion::V1 => VarunaVersion::V2,
 65                          VarunaVersion::V2 => VarunaVersion::V1,
 66                      };
 67  
 68                      for i in 0..5 {
 69                          let mul_depth = 1;
 70                          println!("running test with SM::ZK: {}, mul_depth: {}, num_constraints: {}, num_variables: {}, varuna_version: {:?}", $snark_mode::ZK, mul_depth + i, num_constraints + i, num_variables + i, varuna_version);
 71                          let (circ, public_inputs) = TestCircuit::gen_rand(mul_depth + i, num_constraints + i, num_variables + i, rng);
 72                          let mut fake_inputs = public_inputs.clone();
 73                          fake_inputs[public_inputs.len() - 1] = random;
 74  
 75                          let (index_pk, index_vk) = $snark_inst::circuit_setup(&universal_srs, &circ).unwrap();
 76                          println!("Called circuit setup");
 77  
 78                          let certificate = $snark_inst::prove_vk(universal_prover, &fs_parameters, &index_vk, &index_pk).unwrap();
 79                          assert!($snark_inst::verify_vk(universal_verifier, &fs_parameters, &circ, &index_vk, &certificate).unwrap());
 80                          println!("verified vk");
 81  
 82                          if i == 0 {
 83                              assert_eq!(pk_size_expectation, index_pk.to_bytes_le().unwrap().len(), "Update me if serialization has changed");
 84                          }
 85                          assert_eq!(664, index_vk.to_bytes_le().unwrap().len(), "Update me if serialization has changed");
 86  
 87                          let proof = $snark_inst::prove(universal_prover, &fs_parameters, &index_pk, varuna_version, &circ, rng).unwrap();
 88                          println!("Called prover");
 89  
 90                          assert!($snark_inst::verify(universal_verifier, &fs_parameters, &index_vk, varuna_version, public_inputs.clone(), &proof).unwrap());
 91                          println!("Called verifier");
 92                          eprintln!("\nShould not verify with fake inputs (i.e. verifier messages should print below):");
 93                          assert!(!$snark_inst::verify(universal_verifier, &fs_parameters, &index_vk, varuna_version, fake_inputs, &proof).unwrap());
 94                          eprintln!("\nShould not verify with wrong varuna version (i.e. verifier messages should print below):");
 95                          assert!(!$snark_inst::verify(universal_verifier, &fs_parameters, &index_vk, wrong_varuna_version, public_inputs, &proof).unwrap());
 96                      }
 97  
 98                      for circuit_batch_size in (0..4).map(|i| 2usize.pow(i)) {
 99                          for instance_batch_size in (0..4).map(|i| 2usize.pow(i)) {
100                              println!("running test with circuit_batch_size: {circuit_batch_size} and instance_batch_size: {instance_batch_size}");
101                              let mut constraints = BTreeMap::new();
102                              let mut inputs = BTreeMap::new();
103  
104                              for i in 0..circuit_batch_size {
105                                  let (circuit_batch, input_batch): (Vec<_>, Vec<_>) = (0..instance_batch_size)
106                                  .map(|_| {
107                                      let mul_depth = 2 + i;
108                                      let (circ, inputs) = TestCircuit::gen_rand(mul_depth, num_constraints + 100*i, num_variables, rng);
109                                      (circ, inputs)
110                                  })
111                                  .unzip();
112                                  let circuit_id = AHPForR1CS::<Fr, $snark_mode>::index(&circuit_batch[0]).unwrap().id;
113                                  constraints.insert(circuit_id, circuit_batch);
114                                  inputs.insert(circuit_id, input_batch);
115                              }
116                              let unique_instances = constraints.values().map(|instances| &instances[0]).collect::<Vec<_>>();
117  
118                              let index_keys =
119                                  $snark_inst::batch_circuit_setup(&universal_srs, unique_instances.as_slice()).unwrap();
120                              println!("Called circuit setup");
121  
122                              let mut pks_to_constraints = BTreeMap::new();
123                              let mut vks_to_inputs = BTreeMap::new();
124  
125                              for (index_pk, index_vk) in index_keys.iter() {
126                                  let certificate = $snark_inst::prove_vk(universal_prover, &fs_parameters, &index_vk, &index_pk).unwrap();
127                                  let circuits = constraints[&index_pk.circuit.id].as_slice();
128                                  assert!($snark_inst::verify_vk(universal_verifier, &fs_parameters, &circuits[0], &index_vk, &certificate).unwrap());
129                                  pks_to_constraints.insert(index_pk, circuits);
130                                  vks_to_inputs.insert(index_vk, inputs[&index_pk.circuit.id].as_slice());
131                              }
132                              println!("verified vks");
133  
134                              let proof =
135                                  $snark_inst::prove_batch(universal_prover, &fs_parameters, varuna_version, &pks_to_constraints, rng).unwrap();
136                              println!("Called prover");
137  
138                              if varuna_version == VarunaVersion::V2 {
139                                  let batch_sizes = proof.batch_sizes();
140                                  let mut proof_bytes = vec![];
141                                  proof.serialize_compressed(&mut proof_bytes).unwrap();
142                                  let actual_size = proof_size::<Bls12_377>(&batch_sizes, VarunaVersion::V2, $snark_mode::ZK).unwrap();
143                                  assert_eq!(proof_bytes.len(), actual_size);
144                                  println!("Compressed size is as expected ({actual_size} B)");
145                              }
146  
147                              assert!(
148                                  $snark_inst::verify_batch(universal_verifier, &fs_parameters, varuna_version, &vks_to_inputs, &proof).unwrap(),
149                                  "Batch verification failed with {instance_batch_size} instances and {circuit_batch_size} circuits for circuits: {constraints:?}"
150                              );
151                              println!("Called verifier");
152                              eprintln!("\nShould not verify with wrong inputs (i.e. verifier messages should print below):");
153                              let mut fake_instance_inputs = Vec::with_capacity(vks_to_inputs.len());
154                              for instance_input in vks_to_inputs.values() {
155                                  let mut fake_instance_input = Vec::with_capacity(instance_input.len());
156                                  for input in instance_input.iter() {
157                                      let mut fake_input = input.clone();
158                                      fake_input[input.len() - 1] = Fr::rand(rng);
159                                      fake_instance_input.push(fake_input);
160                                  }
161                                  fake_instance_inputs.push(fake_instance_input);
162                              }
163                              let mut vks_to_fake_inputs = BTreeMap::new();
164                              for (i, vk) in vks_to_inputs.keys().enumerate() {
165                                  vks_to_fake_inputs.insert(*vk, fake_instance_inputs[i].as_slice());
166                              }
167                              assert!(
168                                  !$snark_inst::verify_batch(
169                                      universal_verifier,
170                                      &fs_parameters,
171                                      varuna_version,
172                                      &vks_to_fake_inputs,
173                                      &proof,
174                                  )
175                                  .unwrap()
176                              );
177                              eprintln!("\nShould not verify with wrong varuna version (i.e. verifier messages should print below):");
178                              assert!(
179                                  !$snark_inst::verify_batch(
180                                      universal_verifier,
181                                      &fs_parameters,
182                                      wrong_varuna_version,
183                                      &vks_to_inputs,
184                                      &proof,
185                                  )
186                                  .unwrap()
187                              );
188                          }
189                      }
190                  }
191  
192                  pub(crate) fn test_serde_json(num_constraints: usize, num_variables: usize, rng: &mut TestRng) {
193                      use std::str::FromStr;
194  
195                      let max_degree = AHPForR1CS::<Fr, $snark_mode>::max_degree(100, 25, 300).unwrap();
196                      let universal_srs = $snark_inst::universal_setup(max_degree).unwrap();
197  
198                      let mul_depth = 1;
199                      let (circ, _) = TestCircuit::gen_rand(mul_depth, num_constraints, num_variables, rng);
200  
201                      let (_index_pk, index_vk) = $snark_inst::circuit_setup(&universal_srs, &circ).unwrap();
202                      println!("Called circuit setup");
203  
204                      // Serialize
205                      let expected_string = index_vk.to_string();
206                      let candidate_string = serde_json::to_string(&index_vk).unwrap();
207                      assert_eq!(
208                          expected_string,
209                          serde_json::Value::from_str(&candidate_string).unwrap().as_str().unwrap()
210                      );
211  
212                      // Deserialize
213                      assert_eq!(index_vk, CircuitVerifyingKey::from_str(&expected_string).unwrap());
214                      assert_eq!(index_vk, serde_json::from_str(&candidate_string).unwrap());
215                  }
216  
217                  pub(crate) fn test_bincode(num_constraints: usize, num_variables: usize, rng: &mut TestRng) {
218                      use alphavm_utilities::{FromBytes, ToBytes};
219  
220                      let max_degree = AHPForR1CS::<Fr, $snark_mode>::max_degree(100, 25, 300).unwrap();
221                      let universal_srs = $snark_inst::universal_setup(max_degree).unwrap();
222  
223                      let mul_depth = 1;
224                      let (circ, _) = TestCircuit::gen_rand(mul_depth, num_constraints, num_variables, rng);
225  
226                      let (_index_pk, index_vk) = $snark_inst::circuit_setup(&universal_srs, &circ).unwrap();
227                      println!("Called circuit setup");
228  
229                      // Serialize
230                      let expected_bytes = index_vk.to_bytes_le().unwrap();
231                      let candidate_bytes = bincode::serialize(&index_vk).unwrap();
232                      // NOTE(ACDC): bincode prepends 8-byte length prefix that ToBytes omits - tests use [8..] slice to match.
233                      assert_eq!(&expected_bytes[..], &candidate_bytes[8..]);
234  
235                      // Deserialize
236                      assert_eq!(index_vk, CircuitVerifyingKey::read_le(&expected_bytes[..]).unwrap());
237                      assert_eq!(index_vk, bincode::deserialize(&candidate_bytes[..]).unwrap());
238                  }
239              }
240          };
241      }
242  
243      impl_varuna_test!(SonicPCTest, VarunaSonicInst, VarunaHidingMode);
244      impl_varuna_test!(SonicPCPoswTest, VarunaSonicPoSWInst, VarunaNonHidingMode);
245  
246      #[test]
247      fn prove_and_verify_with_tall_matrix_big() {
248          let num_constraints = 100;
249          let num_variables = 25;
250          let pk_size_zk = 91971;
251          let pk_size_posw = 91633;
252          let mut rng = TestRng::default();
253  
254          SonicPCTest::test_circuit(num_constraints, num_variables, pk_size_zk, VarunaVersion::V1, &mut rng);
255          SonicPCPoswTest::test_circuit(num_constraints, num_variables, pk_size_posw, VarunaVersion::V1, &mut rng);
256  
257          SonicPCTest::test_circuit(num_constraints, num_variables, pk_size_zk, VarunaVersion::V2, &mut rng);
258          SonicPCPoswTest::test_circuit(num_constraints, num_variables, pk_size_posw, VarunaVersion::V2, &mut rng);
259  
260          SonicPCTest::test_serde_json(num_constraints, num_variables, &mut rng);
261          SonicPCPoswTest::test_serde_json(num_constraints, num_variables, &mut rng);
262  
263          SonicPCTest::test_bincode(num_constraints, num_variables, &mut rng);
264          SonicPCPoswTest::test_bincode(num_constraints, num_variables, &mut rng);
265      }
266  
267      #[test]
268      fn prove_and_verify_with_tall_matrix_small() {
269          let num_constraints = 26;
270          let num_variables = 25;
271          let pk_size_zk = 25428;
272          let pk_size_posw = 25090;
273          let mut rng = TestRng::default();
274  
275          SonicPCTest::test_circuit(num_constraints, num_variables, pk_size_zk, VarunaVersion::V1, &mut rng);
276          SonicPCPoswTest::test_circuit(num_constraints, num_variables, pk_size_posw, VarunaVersion::V1, &mut rng);
277  
278          SonicPCTest::test_circuit(num_constraints, num_variables, pk_size_zk, VarunaVersion::V2, &mut rng);
279          SonicPCPoswTest::test_circuit(num_constraints, num_variables, pk_size_posw, VarunaVersion::V2, &mut rng);
280  
281          SonicPCTest::test_serde_json(num_constraints, num_variables, &mut rng);
282          SonicPCPoswTest::test_serde_json(num_constraints, num_variables, &mut rng);
283  
284          SonicPCTest::test_bincode(num_constraints, num_variables, &mut rng);
285          SonicPCPoswTest::test_bincode(num_constraints, num_variables, &mut rng);
286      }
287  
288      #[test]
289      fn prove_and_verify_with_squat_matrix_big() {
290          let num_constraints = 25;
291          let num_variables = 100;
292          let pk_size_zk = 53523;
293          let pk_size_posw = 53185;
294          let mut rng = TestRng::default();
295  
296          SonicPCTest::test_circuit(num_constraints, num_variables, pk_size_zk, VarunaVersion::V1, &mut rng);
297          SonicPCPoswTest::test_circuit(num_constraints, num_variables, pk_size_posw, VarunaVersion::V1, &mut rng);
298  
299          SonicPCTest::test_circuit(num_constraints, num_variables, pk_size_zk, VarunaVersion::V2, &mut rng);
300          SonicPCPoswTest::test_circuit(num_constraints, num_variables, pk_size_posw, VarunaVersion::V2, &mut rng);
301  
302          SonicPCTest::test_serde_json(num_constraints, num_variables, &mut rng);
303          SonicPCPoswTest::test_serde_json(num_constraints, num_variables, &mut rng);
304  
305          SonicPCTest::test_bincode(num_constraints, num_variables, &mut rng);
306          SonicPCPoswTest::test_bincode(num_constraints, num_variables, &mut rng);
307      }
308  
309      #[test]
310      fn prove_and_verify_with_squat_matrix_small() {
311          let num_constraints = 25;
312          let num_variables = 26;
313          let pk_size_zk = 25284;
314          let pk_size_posw = 24946;
315          let mut rng = TestRng::default();
316  
317          SonicPCTest::test_circuit(num_constraints, num_variables, pk_size_zk, VarunaVersion::V1, &mut rng);
318          SonicPCPoswTest::test_circuit(num_constraints, num_variables, pk_size_posw, VarunaVersion::V1, &mut rng);
319  
320          SonicPCTest::test_circuit(num_constraints, num_variables, pk_size_zk, VarunaVersion::V2, &mut rng);
321          SonicPCPoswTest::test_circuit(num_constraints, num_variables, pk_size_posw, VarunaVersion::V2, &mut rng);
322  
323          SonicPCTest::test_serde_json(num_constraints, num_variables, &mut rng);
324          SonicPCPoswTest::test_serde_json(num_constraints, num_variables, &mut rng);
325  
326          SonicPCTest::test_bincode(num_constraints, num_variables, &mut rng);
327          SonicPCPoswTest::test_bincode(num_constraints, num_variables, &mut rng);
328      }
329  
330      #[test]
331      fn prove_and_verify_with_square_matrix() {
332          let num_constraints = 25;
333          let num_variables = 25;
334          let pk_size_zk = 25284;
335          let pk_size_posw = 24946;
336          let mut rng = TestRng::default();
337  
338          SonicPCTest::test_circuit(num_constraints, num_variables, pk_size_zk, VarunaVersion::V1, &mut rng);
339          SonicPCPoswTest::test_circuit(num_constraints, num_variables, pk_size_posw, VarunaVersion::V1, &mut rng);
340  
341          SonicPCTest::test_circuit(num_constraints, num_variables, pk_size_zk, VarunaVersion::V2, &mut rng);
342          SonicPCPoswTest::test_circuit(num_constraints, num_variables, pk_size_posw, VarunaVersion::V2, &mut rng);
343  
344          SonicPCTest::test_serde_json(num_constraints, num_variables, &mut rng);
345          SonicPCPoswTest::test_serde_json(num_constraints, num_variables, &mut rng);
346  
347          SonicPCTest::test_bincode(num_constraints, num_variables, &mut rng);
348          SonicPCPoswTest::test_bincode(num_constraints, num_variables, &mut rng);
349      }
350  }
351  
352  #[cfg(any(test, feature = "test"))]
353  mod varuna_hiding {
354      use crate::{
355          crypto_hash::PoseidonSponge,
356          snark::varuna::{
357              ahp::AHPForR1CS,
358              test_circuit::TestCircuit,
359              CircuitVerifyingKey,
360              VarunaHidingMode,
361              VarunaSNARK,
362              VarunaVersion,
363          },
364          traits::{AlgebraicSponge, SNARK},
365      };
366      use alphavm_curves::bls12_377::{Bls12_377, Fq, Fr};
367      use alphavm_utilities::{
368          rand::{TestRng, Uniform},
369          FromBytes,
370          ToBytes,
371      };
372  
373      use std::str::FromStr;
374  
375      type VarunaInst = VarunaSNARK<Bls12_377, FS, VarunaHidingMode>;
376      type FS = PoseidonSponge<Fq, 2, 1>;
377  
378      fn test_circuit_n_times(
379          num_constraints: usize,
380          num_variables: usize,
381          num_times: usize,
382          varuna_version: VarunaVersion,
383          rng: &mut TestRng,
384      ) {
385          let max_degree = AHPForR1CS::<Fr, VarunaHidingMode>::max_degree(100, 25, 300).unwrap();
386          let universal_srs = VarunaInst::universal_setup(max_degree).unwrap();
387          let universal_prover = &universal_srs.to_universal_prover().unwrap();
388          let universal_verifier = &universal_srs.to_universal_verifier().unwrap();
389          let fs_parameters = FS::sample_parameters();
390  
391          let wrong_varuna_version = match varuna_version {
392              VarunaVersion::V1 => VarunaVersion::V2,
393              VarunaVersion::V2 => VarunaVersion::V1,
394          };
395  
396          for _ in 0..num_times {
397              let mul_depth = 2;
398              let (circuit, public_inputs) = TestCircuit::gen_rand(mul_depth, num_constraints, num_variables, rng);
399              let mut fake_inputs = public_inputs.clone();
400              fake_inputs[public_inputs.len() - 1] = Fr::rand(rng);
401  
402              let (index_pk, index_vk) = VarunaInst::circuit_setup(&universal_srs, &circuit).unwrap();
403              println!("Called circuit setup");
404  
405              let proof =
406                  VarunaInst::prove(universal_prover, &fs_parameters, &index_pk, varuna_version, &circuit, rng).unwrap();
407              println!("Called prover");
408  
409              assert!(VarunaInst::verify(
410                  universal_verifier,
411                  &fs_parameters,
412                  &index_vk,
413                  varuna_version,
414                  public_inputs.clone(),
415                  &proof,
416              )
417              .unwrap());
418              println!("Called verifier");
419              eprintln!("\nShould not verify with fake inputs (i.e. verifier messages should print below):");
420              assert!(!VarunaInst::verify(
421                  universal_verifier,
422                  &fs_parameters,
423                  &index_vk,
424                  varuna_version,
425                  fake_inputs.clone(),
426                  &proof
427              )
428              .unwrap());
429              eprintln!("\nShould not verify with wrong varuna version (i.e. verifier messages should print below):");
430              assert!(!VarunaInst::verify(
431                  universal_verifier,
432                  &fs_parameters,
433                  &index_vk,
434                  wrong_varuna_version,
435                  public_inputs.clone(),
436                  &proof,
437              )
438              .unwrap());
439          }
440      }
441  
442      fn test_circuit(num_constraints: usize, num_variables: usize, varuna_version: VarunaVersion, rng: &mut TestRng) {
443          test_circuit_n_times(num_constraints, num_variables, 100, varuna_version, rng)
444      }
445  
446      fn test_serde_json(num_constraints: usize, num_variables: usize, rng: &mut TestRng) {
447          let max_degree = AHPForR1CS::<Fr, VarunaHidingMode>::max_degree(100, 25, 300).unwrap();
448          let universal_srs = VarunaInst::universal_setup(max_degree).unwrap();
449  
450          let mul_depth = 1;
451          let (circuit, _) = TestCircuit::gen_rand(mul_depth, num_constraints, num_variables, rng);
452  
453          let (_index_pk, index_vk) = VarunaInst::circuit_setup(&universal_srs, &circuit).unwrap();
454          println!("Called circuit setup");
455  
456          // Serialize
457          let expected_string = index_vk.to_string();
458          let candidate_string = serde_json::to_string(&index_vk).unwrap();
459          assert_eq!(expected_string, serde_json::Value::from_str(&candidate_string).unwrap().as_str().unwrap());
460  
461          // Deserialize
462          assert_eq!(index_vk, CircuitVerifyingKey::from_str(&expected_string).unwrap());
463          assert_eq!(index_vk, serde_json::from_str(&candidate_string).unwrap());
464      }
465  
466      fn test_bincode(num_constraints: usize, num_variables: usize, rng: &mut TestRng) {
467          let max_degree = AHPForR1CS::<Fr, VarunaHidingMode>::max_degree(100, 25, 300).unwrap();
468          let universal_srs = VarunaInst::universal_setup(max_degree).unwrap();
469  
470          let mul_depth = 1;
471          let (circuit, _) = TestCircuit::gen_rand(mul_depth, num_constraints, num_variables, rng);
472  
473          let (_index_pk, index_vk) = VarunaInst::circuit_setup(&universal_srs, &circuit).unwrap();
474          println!("Called circuit setup");
475  
476          // Serialize
477          let expected_bytes = index_vk.to_bytes_le().unwrap();
478          let candidate_bytes = bincode::serialize(&index_vk).unwrap();
479          // NOTE(ACDC): bincode prepends 8-byte length prefix that ToBytes omits.
480          // Tests use [8..] slice to skip prefix and match.
481          assert_eq!(&expected_bytes[..], &candidate_bytes[8..]);
482  
483          // Deserialize
484          assert_eq!(index_vk, CircuitVerifyingKey::read_le(&expected_bytes[..]).unwrap());
485          assert_eq!(index_vk, bincode::deserialize(&candidate_bytes[..]).unwrap());
486      }
487  
488      #[test]
489      fn prove_and_verify_with_tall_matrix_big() {
490          let num_constraints = 100;
491          let num_variables = 25;
492          let mut rng = TestRng::default();
493  
494          test_circuit(num_constraints, num_variables, VarunaVersion::V1, &mut rng);
495          test_circuit(num_constraints, num_variables, VarunaVersion::V2, &mut rng);
496          test_serde_json(num_constraints, num_variables, &mut rng);
497          test_bincode(num_constraints, num_variables, &mut rng);
498      }
499  
500      #[test]
501      fn prove_and_verify_with_tall_matrix_small() {
502          let num_constraints = 26;
503          let num_variables = 25;
504          let mut rng = TestRng::default();
505  
506          test_circuit(num_constraints, num_variables, VarunaVersion::V1, &mut rng);
507          test_circuit(num_constraints, num_variables, VarunaVersion::V2, &mut rng);
508          test_serde_json(num_constraints, num_variables, &mut rng);
509          test_bincode(num_constraints, num_variables, &mut rng);
510      }
511  
512      #[test]
513      fn prove_and_verify_with_squat_matrix_big() {
514          let num_constraints = 25;
515          let num_variables = 100;
516          let mut rng = TestRng::default();
517  
518          test_circuit(num_constraints, num_variables, VarunaVersion::V1, &mut rng);
519          test_circuit(num_constraints, num_variables, VarunaVersion::V2, &mut rng);
520          test_serde_json(num_constraints, num_variables, &mut rng);
521          test_bincode(num_constraints, num_variables, &mut rng);
522      }
523  
524      #[test]
525      fn prove_and_verify_with_squat_matrix_small() {
526          let num_constraints = 25;
527          let num_variables = 26;
528          let mut rng = TestRng::default();
529  
530          test_circuit(num_constraints, num_variables, VarunaVersion::V1, &mut rng);
531          test_circuit(num_constraints, num_variables, VarunaVersion::V2, &mut rng);
532          test_serde_json(num_constraints, num_variables, &mut rng);
533          test_bincode(num_constraints, num_variables, &mut rng);
534      }
535  
536      #[test]
537      fn prove_and_verify_with_square_matrix() {
538          let num_constraints = 25;
539          let num_variables = 25;
540          let mut rng = TestRng::default();
541  
542          test_circuit(num_constraints, num_variables, VarunaVersion::V1, &mut rng);
543          test_circuit(num_constraints, num_variables, VarunaVersion::V2, &mut rng);
544          test_serde_json(num_constraints, num_variables, &mut rng);
545          test_bincode(num_constraints, num_variables, &mut rng);
546      }
547  
548      #[test]
549      fn prove_and_verify_with_large_matrix() {
550          let num_constraints = 1 << 16;
551          let num_variables = 1 << 16;
552          let mut rng = TestRng::default();
553  
554          test_circuit_n_times(num_constraints, num_variables, 1, VarunaVersion::V1, &mut rng);
555          test_circuit_n_times(num_constraints, num_variables, 1, VarunaVersion::V2, &mut rng);
556      }
557  
558      #[test]
559      fn check_indexing() {
560          let rng = &mut TestRng::default();
561          let mul_depth = 2;
562          let num_constraints = 1 << 13;
563          let num_variables = 1 << 13;
564          let (circuit, public_inputs) = TestCircuit::gen_rand(mul_depth, num_constraints, num_variables, rng);
565  
566          let max_degree = AHPForR1CS::<Fr, VarunaHidingMode>::max_degree(100, 25, 300).unwrap();
567          let universal_srs = VarunaInst::universal_setup(max_degree).unwrap();
568          let universal_prover = &universal_srs.to_universal_prover().unwrap();
569          let universal_verifier = &universal_srs.to_universal_verifier().unwrap();
570          let fs_parameters = FS::sample_parameters();
571          for varuna_version in [VarunaVersion::V1, VarunaVersion::V2] {
572              let wrong_varuna_version = match varuna_version {
573                  VarunaVersion::V1 => VarunaVersion::V2,
574                  VarunaVersion::V2 => VarunaVersion::V1,
575              };
576              let (index_pk, index_vk) = VarunaInst::circuit_setup(&universal_srs, &circuit).unwrap();
577              println!("Called circuit setup");
578  
579              let proof =
580                  VarunaInst::prove(universal_prover, &fs_parameters, &index_pk, varuna_version, &circuit, rng).unwrap();
581              println!("Called prover");
582  
583              universal_srs.download_powers_for(0..2usize.pow(18)).unwrap();
584              let (new_pk, new_vk) = VarunaInst::circuit_setup(&universal_srs, &circuit).unwrap();
585              assert_eq!(index_pk, new_pk);
586              assert_eq!(index_vk, new_vk);
587              assert!(VarunaInst::verify(
588                  universal_verifier,
589                  &fs_parameters,
590                  &index_vk,
591                  varuna_version,
592                  public_inputs.clone(),
593                  &proof,
594              )
595              .unwrap());
596              assert!(VarunaInst::verify(
597                  universal_verifier,
598                  &fs_parameters,
599                  &new_vk,
600                  varuna_version,
601                  public_inputs.clone(),
602                  &proof
603              )
604              .unwrap());
605              assert!(!VarunaInst::verify(
606                  universal_verifier,
607                  &fs_parameters,
608                  &index_vk,
609                  wrong_varuna_version,
610                  public_inputs.clone(),
611                  &proof,
612              )
613              .unwrap());
614          }
615      }
616  
617      #[test]
618      fn test_srs_downloads() {
619          let rng = &mut TestRng::default();
620  
621          let max_degree = AHPForR1CS::<Fr, VarunaHidingMode>::max_degree(100, 25, 300).unwrap();
622          let universal_srs = VarunaInst::universal_setup(max_degree).unwrap();
623          let universal_prover = &universal_srs.to_universal_prover().unwrap();
624          let universal_verifier = &universal_srs.to_universal_verifier().unwrap();
625          let fs_parameters = FS::sample_parameters();
626          let varuna_version = VarunaVersion::V2;
627  
628          // Indexing, proving, and verifying for a circuit with 1 << 15 constraints and 1
629          // << 15 variables.
630          let mul_depth = 2;
631          let num_constraints = 2usize.pow(15) - 10;
632          let num_variables = 2usize.pow(15) - 10;
633          let (circuit1, public_inputs1) = TestCircuit::gen_rand(mul_depth, num_constraints, num_variables, rng);
634          let (pk1, vk1) = VarunaInst::circuit_setup(&universal_srs, &circuit1).unwrap();
635          println!("Called circuit setup");
636  
637          let proof1 = VarunaInst::prove(universal_prover, &fs_parameters, &pk1, varuna_version, &circuit1, rng).unwrap();
638          println!("Called prover");
639          assert!(VarunaInst::verify(
640              universal_verifier,
641              &fs_parameters,
642              &vk1,
643              varuna_version,
644              public_inputs1.clone(),
645              &proof1
646          )
647          .unwrap());
648  
649          /***************************************************************************
650           * * */
651  
652          // Indexing, proving, and verifying for a circuit with 1 << 19 constraints and 1
653          // << 19 variables.
654          let mul_depth = 2;
655          let num_constraints = 2usize.pow(19) - 10;
656          let num_variables = 2usize.pow(19) - 10;
657          let (circuit2, public_inputs2) = TestCircuit::gen_rand(mul_depth, num_constraints, num_variables, rng);
658          let (pk2, vk2) = VarunaInst::circuit_setup(&universal_srs, &circuit2).unwrap();
659          println!("Called circuit setup");
660  
661          let proof2 = VarunaInst::prove(universal_prover, &fs_parameters, &pk2, varuna_version, &circuit2, rng).unwrap();
662          println!("Called prover");
663          assert!(VarunaInst::verify(universal_verifier, &fs_parameters, &vk2, varuna_version, public_inputs2, &proof2)
664              .unwrap());
665          /***************************************************************************
666           * * */
667          assert!(VarunaInst::verify(universal_verifier, &fs_parameters, &vk1, varuna_version, public_inputs1, &proof1)
668              .unwrap());
669      }
670  }
671  
672  mod varuna_test_vectors {
673      use crate::{
674          fft::EvaluationDomain,
675          snark::varuna::{ahp::verifier, AHPForR1CS, TestCircuit, VarunaNonHidingMode, VarunaSNARK, VarunaVersion},
676          traits::snark::SNARK,
677      };
678      use alphavm_curves::bls12_377::{Bls12_377, Fq, Fr};
679      use alphavm_fields::One;
680      use std::{collections::BTreeMap, fs, ops::Deref, path::PathBuf, str::FromStr, sync::Arc};
681  
682      type FS = crate::crypto_hash::PoseidonSponge<Fq, 2, 1>;
683      type MM = VarunaNonHidingMode;
684      type VarunaSonicInst = VarunaSNARK<Bls12_377, FS, MM>;
685  
686      // Create the path for the `resources` folder.
687      fn resources_path(create_dir: bool) -> PathBuf {
688          let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
689          path.push("src");
690          path.push("snark");
691          path.push("varuna");
692          path.push("resources");
693  
694          // Create the `resources` folder, if it does not exist.
695          if !path.exists() {
696              if create_dir {
697                  fs::create_dir(&path).unwrap_or_else(|_| panic!("Failed to create resources folder: {path:?}"));
698              } else {
699                  panic!("Resources folder does not exist: {path:?}");
700              }
701          }
702  
703          path
704      }
705  
706      // Create the file path.
707      fn test_vector_path(folder: &str, file: &str, circuit: &str, create_dir: bool) -> PathBuf {
708          let mut path = resources_path(create_dir);
709  
710          // Construct the path where the test data lives.
711          path.push(circuit);
712          path.push(folder);
713  
714          // Create the test folder if it does not exist if specified, otherwise panic.
715          if !path.exists() {
716              if create_dir {
717                  fs::create_dir(&path).unwrap_or_else(|_| panic!("Failed to create resources folder: {path:?}"));
718              } else {
719                  panic!("Resources folder does not exist: {path:?}");
720              }
721          }
722  
723          // Construct the path for the test file.
724          path.push(file);
725          path.set_extension("txt");
726  
727          path
728      }
729  
730      // Loads the given `test_folder/test_file` and asserts the given `candidate`
731      // matches the expected values.
732      #[track_caller]
733      fn assert_test_vector_equality(test_folder: &str, test_file: &str, candidate: &str, circuit: &str) {
734          // Get the path to the test file.
735          let path = test_vector_path(test_folder, test_file, circuit, false);
736  
737          // Assert the test file is equal to the expected value.
738          expect_test::expect_file![path].assert_eq(candidate);
739      }
740  
741      // Create a test vector from a trusted revision of Varuna.
742      fn create_test_vector(folder: &str, file: &str, data: &str, circuit: &str) {
743          // Get the path to the test file.
744          let path = test_vector_path(folder, file, circuit, true);
745  
746          // Write the test vector to file.
747          fs::write(&path, data).unwrap_or_else(|_| panic!("Failed to write to file: {path:?}"));
748      }
749  
750      // Tests varuna against the test vectors in all circuits in the resources
751      // folder.
752      fn test_varuna_with_all_circuits(create_test_vectors: bool) {
753          let entries = fs::read_dir(resources_path(create_test_vectors)).expect("Failed to read resources folder");
754          entries.into_iter().for_each(|entry| {
755              let path = entry.unwrap().path();
756              if path.is_dir() {
757                  let circuit = path.file_name().unwrap().to_str().unwrap();
758                  test_circuit_with_test_vectors(create_test_vectors, circuit);
759              }
760          });
761      }
762  
763      // Test Varuna against test vectors for a specific circuit.
764      fn test_circuit_with_test_vectors(create_test_vectors: bool, circuit: &str) {
765          // Initialize the parts of the witness used in the multiplicative constraints.
766          let witness_path = format!("src/snark/varuna/resources/{circuit}/witness.input");
767          let instance_file = fs::read_to_string(witness_path).expect("Could not read the file");
768          let witness: Vec<u128> = serde_json::from_str(instance_file.lines().next().unwrap()).unwrap();
769          let (a, b) = (witness[0], witness[1]);
770  
771          // Initialize challenges from file.
772          let challenges_path = format!("src/snark/varuna/resources/{circuit}/challenges.input");
773          let challenges_file = fs::read_to_string(challenges_path).expect("Could not read the file");
774          let mut challenges = Vec::new();
775          for line in challenges_file.lines() {
776              challenges.push(line)
777          }
778          let (alpha, _eta_a, eta_b, eta_c, beta, delta_a, delta_b, delta_c, _gamma) = (
779              Fr::from_str(challenges[0]).unwrap(),
780              Fr::from_str(challenges[1]).unwrap(),
781              Fr::from_str(challenges[2]).unwrap(),
782              Fr::from_str(challenges[3]).unwrap(),
783              Fr::from_str(challenges[4]).unwrap(),
784              vec![Fr::from_str(challenges[5]).unwrap()],
785              vec![Fr::from_str(challenges[6]).unwrap()],
786              vec![Fr::from_str(challenges[7]).unwrap()],
787              Fr::from_str(challenges[8]).unwrap(),
788          );
789  
790          let circuit_combiner = Fr::one();
791          let instance_combiners = vec![Fr::one()];
792  
793          // Create sample circuit which corresponds to instance.input file.
794          let mul_depth = 3;
795          let num_constraints = 7;
796          let num_variables = 7;
797  
798          // Create a fixed seed rng that matches those the test vectors were generated
799          // with.
800          let rng = &mut alphavm_utilities::rand::TestRng::fixed(4730);
801          let max_degree =
802              AHPForR1CS::<Fr, MM>::max_degree(num_constraints, num_variables, num_variables * num_constraints).unwrap();
803          let universal_srs = VarunaSonicInst::universal_setup(max_degree).unwrap();
804          let (circ, _) =
805              TestCircuit::generate_circuit_with_fixed_witness(a, b, mul_depth, num_constraints, num_variables);
806          println!("Circuit: {circ:?}");
807          let (index_pk, _index_vk) = VarunaSonicInst::circuit_setup(&universal_srs, &circ).unwrap();
808          let mut keys_to_constraints = BTreeMap::new();
809          keys_to_constraints.insert(index_pk.circuit.deref(), std::slice::from_ref(&circ));
810  
811          // Begin the Varuna protocol execution.
812          let prover_state = AHPForR1CS::<_, MM>::init_prover(&keys_to_constraints, rng).unwrap();
813          let mut prover_state = AHPForR1CS::<_, MM>::prover_first_round(prover_state, rng).unwrap();
814          let first_round_oracles = Arc::new(prover_state.first_round_oracles.as_ref().unwrap());
815  
816          // Get private witness polynomial coefficients.
817          let (_, w_poly) = first_round_oracles.batches.iter().next().unwrap();
818          let w_lde = format!("{:?}", w_poly[0].0.coeffs().map(|(_, coeff)| coeff).collect::<Vec<_>>());
819          if create_test_vectors {
820              create_test_vector("polynomials", "w_lde", &w_lde, circuit);
821          }
822  
823          // Generate test vectors from assignments.
824          let assignments = AHPForR1CS::<_, MM>::calculate_assignments(&mut prover_state).unwrap();
825  
826          // Get full witness polynomial coefficients.
827          let (_, z_poly) = assignments.iter().next().unwrap();
828          let z_lde = format!("{:?}", z_poly[0].coeffs().iter().collect::<Vec<_>>());
829          if create_test_vectors {
830              create_test_vector("polynomials", "z_lde", &z_lde, circuit);
831          }
832  
833          let combiners = verifier::BatchCombiners::<Fr> { circuit_combiner, instance_combiners };
834          let first_round_batch_combiners = BTreeMap::from_iter([(index_pk.circuit.id, combiners)]);
835          let verifier_first_msg = verifier::FirstMessage::<Fr> { first_round_batch_combiners };
836  
837          let (second_oracles, prover_state) =
838              AHPForR1CS::<_, MM>::prover_second_round::<_>(&verifier_first_msg, prover_state, rng).unwrap();
839  
840          // Get round 2 rowcheck polynomial oracle coefficients.
841          let h_0 = format!("{:?}", second_oracles.h_0.coeffs().map(|(_, coeff)| coeff).collect::<Vec<_>>());
842          if create_test_vectors {
843              create_test_vector("polynomials", "h_0", &h_0, circuit);
844          }
845  
846          let verifier_second_msg = verifier::SecondMessage::<Fr> { alpha, eta_b: Some(eta_b), eta_c: Some(eta_c) };
847          let (_prover_third_message, third_oracles, prover_state) = AHPForR1CS::<_, MM>::prover_third_round(
848              &verifier_first_msg,
849              &verifier_second_msg,
850              &None,
851              prover_state,
852              rng,
853              VarunaVersion::V1,
854          )
855          .unwrap();
856  
857          // Get coefficients round 3 univariate rowcheck polynomial oracles.
858          let g_1 = format!("{:?}", third_oracles.g_1.coeffs().map(|(_, coeff)| coeff).collect::<Vec<_>>());
859          if create_test_vectors {
860              create_test_vector("polynomials", "g_1", &g_1, circuit);
861          }
862          let h_1 = format!("{:?}", third_oracles.h_1.coeffs().map(|(_, coeff)| coeff).collect::<Vec<_>>());
863          if create_test_vectors {
864              create_test_vector("polynomials", "h_1", &h_1, circuit);
865          }
866  
867          let verifier_third_msg = verifier::ThirdMessage::<Fr> { beta };
868          let (_prover_fourth_message, fourth_oracles, prover_state) =
869              AHPForR1CS::<_, MM>::prover_fourth_round(&verifier_second_msg, &verifier_third_msg, prover_state, rng)
870                  .unwrap();
871  
872          // Create round 4 rational sumcheck oracle polynomials.
873          let (_, gm_polys) = fourth_oracles.gs.iter().next().unwrap();
874          let g_a = format!("{:?}", gm_polys.g_a.coeffs().map(|(_, coeff)| coeff).collect::<Vec<_>>());
875          let g_b = format!("{:?}", gm_polys.g_b.coeffs().map(|(_, coeff)| coeff).collect::<Vec<_>>());
876          let g_c = format!("{:?}", gm_polys.g_b.coeffs().map(|(_, coeff)| coeff).collect::<Vec<_>>());
877          if create_test_vectors {
878              create_test_vector("polynomials", "g_a", &g_a, circuit);
879              create_test_vector("polynomials", "g_b", &g_b, circuit);
880              create_test_vector("polynomials", "g_c", &g_c, circuit);
881          }
882  
883          // Create the verifier's fourth message.
884          let verifier_fourth_msg = verifier::FourthMessage::<Fr> { delta_a, delta_b, delta_c };
885  
886          let mut public_inputs = BTreeMap::new();
887          let public_input = prover_state.public_inputs(&index_pk.circuit).unwrap();
888          public_inputs.insert(index_pk.circuit.id, public_input);
889          let non_zero_a_domain = EvaluationDomain::<Fr>::new(index_pk.circuit.index_info.num_non_zero_a).unwrap();
890          let non_zero_b_domain = EvaluationDomain::<Fr>::new(index_pk.circuit.index_info.num_non_zero_b).unwrap();
891          let non_zero_c_domain = EvaluationDomain::<Fr>::new(index_pk.circuit.index_info.num_non_zero_c).unwrap();
892          let variable_domain =
893              EvaluationDomain::<Fr>::new(index_pk.circuit.index_info.num_public_and_private_variables).unwrap();
894          let constraint_domain = EvaluationDomain::<Fr>::new(index_pk.circuit.index_info.num_constraints).unwrap();
895          let input_domain = EvaluationDomain::<Fr>::new(index_pk.circuit.index_info.num_public_inputs).unwrap();
896  
897          // Get constraint domain elements.
898          let mut constraint_domain_elements = Vec::with_capacity(constraint_domain.size());
899          for el in constraint_domain.elements() {
900              constraint_domain_elements.push(el);
901          }
902          if create_test_vectors {
903              create_test_vector("domain", "R", &format!("{constraint_domain_elements:?}"), circuit);
904          }
905  
906          // Get non_zero_domain elements.
907          let non_zero_domain = *[&non_zero_a_domain, &non_zero_b_domain, &non_zero_c_domain]
908              .iter()
909              .max_by_key(|domain| domain.size)
910              .unwrap();
911          let mut non_zero_domain_elements = Vec::with_capacity(non_zero_domain.size());
912          for el in non_zero_domain.elements() {
913              non_zero_domain_elements.push(el);
914          }
915          if create_test_vectors {
916              create_test_vector("domain", "K", &format!("{non_zero_domain_elements:?}"), circuit);
917          }
918  
919          // Get variable domain elements.
920          let mut variable_domain_elements = Vec::with_capacity(input_domain.size());
921          for el in variable_domain.elements() {
922              variable_domain_elements.push(el);
923          }
924          if create_test_vectors {
925              create_test_vector("domain", "C", &format!("{variable_domain_elements:?}"), circuit);
926          }
927  
928          let fifth_oracles = AHPForR1CS::<_, MM>::prover_fifth_round(verifier_fourth_msg, prover_state, rng).unwrap();
929  
930          // Get coefficients of final oracle polynomial from round 5.
931          let h_2 = format!("{:?}", fifth_oracles.h_2.coeffs().map(|(_, coeff)| coeff).collect::<Vec<_>>());
932          if create_test_vectors {
933              create_test_vector("polynomials", "h_2", &h_2, circuit);
934          }
935  
936          // Check the intermediate oracle polynomials against the test vectors.
937          assert_test_vector_equality("polynomials", "w_lde", &w_lde, circuit);
938          assert_test_vector_equality("polynomials", "z_lde", &z_lde, circuit);
939          assert_test_vector_equality("polynomials", "h_0", &h_0, circuit);
940          assert_test_vector_equality("polynomials", "h_1", &h_1, circuit);
941          assert_test_vector_equality("polynomials", "g_1", &g_1, circuit);
942          assert_test_vector_equality("polynomials", "h_2", &h_2, circuit);
943          assert_test_vector_equality("polynomials", "g_a", &g_a, circuit);
944          assert_test_vector_equality("polynomials", "g_b", &g_b, circuit);
945          assert_test_vector_equality("polynomials", "g_c", &g_c, circuit);
946  
947          // Check that the domains match the test vectors.
948          assert_test_vector_equality("domain", "R", &format!("{constraint_domain_elements:?}"), circuit);
949          assert_test_vector_equality("domain", "K", &format!("{non_zero_domain_elements:?}"), circuit);
950          assert_test_vector_equality("domain", "C", &format!("{variable_domain_elements:?}"), circuit);
951      }
952  
953      #[test]
954      fn test_varuna_with_prover_test_vectors() {
955          test_varuna_with_all_circuits(false);
956      }
957  }