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 }