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