diff --git a/Cargo.lock b/Cargo.lock index 1ad9d5d..25e2b11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,7 +85,7 @@ dependencies = [ "ark-serialize", "ark-std", "arrayvec", - "digest", + "digest 0.10.7", "educe", "itertools 0.13.0", "num-bigint", @@ -142,7 +142,7 @@ dependencies = [ "ark-serialize-derive", "ark-std", "arrayvec", - "digest", + "digest 0.10.7", "num-bigint", "rayon", ] @@ -165,7 +165,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" dependencies = [ "num-traits", - "rand", + "rand 0.8.5", "rayon", ] @@ -190,22 +190,13 @@ dependencies = [ "serde", ] -[[package]] -name = "blake2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" -dependencies = [ - "digest", -] - [[package]] name = "block-buffer" -version = "0.10.4" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" dependencies = [ - "generic-array", + "hybrid-array", ] [[package]] @@ -278,6 +269,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "criterion" version = "0.5.1" @@ -355,15 +361,33 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +dependencies = [ + "hybrid-array", +] + [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "crypto-common 0.1.7", +] + +[[package]] +name = "digest" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c" dependencies = [ "block-buffer", - "crypto-common", - "subtle", + "const-oid", + "crypto-common 0.2.1", ] [[package]] @@ -385,14 +409,13 @@ dependencies = [ "ark-serialize", "ark-std", "bincode", - "blake2", "criterion", - "digest", "dory-derive", - "rand", - "rand_core", + "rand 0.8.5", + "rand_core 0.6.4", "rayon", "serde", + "spongefish", "thiserror", "tracing", "tracing-subscriber", @@ -489,6 +512,15 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +[[package]] +name = "hybrid-array" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3944cf8cf766b40e2a1a333ee5e9b563f854d5fa49d6a8ca2764e97c6eddb214" +dependencies = [ + "typenum", +] + [[package]] name = "is-terminal" version = "0.4.17" @@ -518,6 +550,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.17" @@ -534,6 +575,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keccak" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa" +dependencies = [ + "cfg-if", + "cpufeatures", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -546,6 +597,15 @@ version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.29" @@ -607,6 +667,171 @@ version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" +[[package]] +name = "p3-challenger" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0b490c745a7d2adeeafff06411814c8078c432740162332b3cd71be0158a76" +dependencies = [ + "p3-field", + "p3-maybe-rayon", + "p3-monty-31", + "p3-symmetric", + "p3-util", + "tracing", +] + +[[package]] +name = "p3-dft" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55301e91544440254977108b85c32c09d7ea05f2f0dd61092a2825339906a4a7" +dependencies = [ + "itertools 0.14.0", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-util", + "spin", + "tracing", +] + +[[package]] +name = "p3-field" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85affca7fc983889f260655c4cf74163eebb94605f702e4b6809ead707cba54f" +dependencies = [ + "itertools 0.14.0", + "num-bigint", + "p3-maybe-rayon", + "p3-util", + "paste", + "rand 0.10.0", + "serde", + "tracing", +] + +[[package]] +name = "p3-koala-bear" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7369a8eb2a27b314338f6b4b77e6a701ad31f1deff25b75bd302fc08c924c9b3" +dependencies = [ + "p3-challenger", + "p3-field", + "p3-mds", + "p3-monty-31", + "p3-poseidon1", + "p3-poseidon2", + "p3-symmetric", + "rand 0.10.0", +] + +[[package]] +name = "p3-matrix" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53428126b009071563d1d07305a9de8be0d21de00b57d2475289ee32ffca6577" +dependencies = [ + "itertools 0.14.0", + "p3-field", + "p3-maybe-rayon", + "p3-util", + "rand 0.10.0", + "serde", + "tracing", +] + +[[package]] +name = "p3-maybe-rayon" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "082bf467011c06c768c579ec6eb9accb5e1e62108891634cc770396e917f978a" + +[[package]] +name = "p3-mds" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35209e6214102ea6ec6b8cb1b9c15a9b8e597a39f9173597c957f123bced81b3" +dependencies = [ + "p3-dft", + "p3-field", + "p3-symmetric", + "p3-util", + "rand 0.10.0", +] + +[[package]] +name = "p3-monty-31" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffa8c99ec50c035020bbf5457c6a729ba6a975719c1a8dd3f16421081e4f650c" +dependencies = [ + "itertools 0.14.0", + "num-bigint", + "p3-dft", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-mds", + "p3-poseidon1", + "p3-poseidon2", + "p3-symmetric", + "p3-util", + "paste", + "rand 0.10.0", + "serde", + "spin", + "tracing", +] + +[[package]] +name = "p3-poseidon1" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a018b618e3fa0aec8be933b1d8e404edd23f46991f6bf3f5c2f3f95e9413fe9" +dependencies = [ + "p3-field", + "p3-symmetric", + "rand 0.10.0", +] + +[[package]] +name = "p3-poseidon2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256a668a9ba916f8767552f13d0ba50d18968bc74a623bfdafa41e2970c944d0" +dependencies = [ + "p3-field", + "p3-mds", + "p3-symmetric", + "p3-util", + "rand 0.10.0", +] + +[[package]] +name = "p3-symmetric" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c60a71a1507c13611b0f2b0b6e83669fd5b76f8e3115bcbced5ccfdf3ca7807" +dependencies = [ + "itertools 0.14.0", + "p3-field", + "p3-util", + "serde", +] + +[[package]] +name = "p3-util" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8b766b9e9254bf3fa98d76e42cf8a5b30628c182dfd5272d270076ee12f0fc0" +dependencies = [ + "serde", + "transpose", +] + [[package]] name = "paste" version = "1.0.15" @@ -682,7 +907,16 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +dependencies = [ + "rand_core 0.10.0", ] [[package]] @@ -692,7 +926,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -704,6 +938,12 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_core" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" + [[package]] name = "rayon" version = "1.11.0" @@ -768,6 +1008,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.228" @@ -811,6 +1057,27 @@ dependencies = [ "zmij", ] +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.11.2", +] + +[[package]] +name = "sha3" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1" +dependencies = [ + "digest 0.11.2", + "keccak", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -827,10 +1094,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] -name = "subtle" -version = "2.6.1" +name = "spin" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spongefish" +version = "0.5.1" +source = "git+https://github.com/arkworks-rs/spongefish?tag=v0.5.1#715a277ef522472bb71b06037507e541d1070555" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "digest 0.11.2", + "p3-koala-bear", + "rand 0.8.5", + "sha2", + "sha3", + "zeroize", +] + +[[package]] +name = "strength_reduce" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" [[package]] name = "syn" @@ -939,6 +1231,16 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "transpose" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" +dependencies = [ + "num-integer", + "strength_reduce", +] + [[package]] name = "typenum" version = "1.19.0" diff --git a/Cargo.toml b/Cargo.toml index 9edbf11..0e7693d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,10 +42,9 @@ arkworks = [ "dep:ark-ff", "dep:ark-serialize", "dep:ark-std", - "dep:blake2", - "dep:digest", "dep:bincode", "dep:serde", + "dep:spongefish", ] parallel = ["dep:rayon", "ark-ec?/parallel", "ark-ff?/parallel"] cache = ["arkworks", "parallel"] @@ -63,10 +62,9 @@ ark-ec = { version = "0.5", optional = true } ark-ff = { version = "0.5", optional = true } ark-serialize = { version = "0.5", optional = true } ark-std = { version = "0.5", optional = true } -blake2 = { version = "0.10", optional = true } -digest = { version = "0.10", optional = true } bincode = { version = "1.3", optional = true } serde = { version = "1.0", optional = true, features = ["derive"] } +spongefish = { git = "https://github.com/arkworks-rs/spongefish", tag = "v0.5.1", optional = true, features = ["ark-ec", "sha3"] } rayon = { version = "1.10", optional = true } [dev-dependencies] diff --git a/benches/arkworks_proof.rs b/benches/arkworks_proof.rs index c5dc4fd..d83415c 100644 --- a/benches/arkworks_proof.rs +++ b/benches/arkworks_proof.rs @@ -13,7 +13,7 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; use dory_pcs::backends::arkworks::{ - ArkFr, ArkworksPolynomial, Blake2bTranscript, G1Routines, G2Routines, BN254, + dory_prover, dory_verifier, ArkFr, ArkworksPolynomial, G1Routines, G2Routines, BN254, }; use dory_pcs::mode::Transparent; use dory_pcs::primitives::arithmetic::Field; @@ -33,7 +33,6 @@ fn setup_benchmark_data() -> ( let (prover_setup, verifier_setup) = setup::(max_log_n); - // Initialize cache with setup generators for optimized pairings #[cfg(feature = "cache")] { if !dory_pcs::backends::arkworks::is_cached() { @@ -41,8 +40,7 @@ fn setup_benchmark_data() -> ( } } - // Create polynomial with 2^26 coefficients (nu=13, sigma=13) - let poly_size = 1 << 26; // 67,108,864 coefficients + let poly_size = 1 << 26; let num_vars = 26; let coefficients: Vec = (0..poly_size).map(|_| ArkFr::random()).collect(); let poly = ArkworksPolynomial::new(coefficients); @@ -74,22 +72,26 @@ fn bench_prove(c: &mut Criterion) { let nu = 13; let sigma = 13; - let (_, tier_1, commit_blind) = poly + let (tier_2, tier_1, commit_blind) = poly .commit::(nu, sigma, &prover_setup) .unwrap(); + let evaluation = poly.evaluate(&point); + c.bench_function("prove_2^26_coefficients", |b| { b.iter(|| { - let mut transcript = Blake2bTranscript::new(b"dory-bench"); + let mut prover = dory_prover(nu, sigma, false); prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( black_box(&poly), black_box(&point), + black_box(&tier_2), + black_box(&evaluation), black_box(tier_1.clone()), black_box(commit_blind), black_box(nu), black_box(sigma), black_box(&prover_setup), - black_box(&mut transcript), + black_box(&mut prover), ) .unwrap() }) @@ -105,33 +107,38 @@ fn bench_verify(c: &mut Criterion) { .commit::(nu, sigma, &prover_setup) .unwrap(); - let mut prover_transcript = Blake2bTranscript::new(b"dory-bench"); - let (proof, _) = prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( + let evaluation = poly.evaluate(&point); + + let mut prover = dory_prover(nu, sigma, false); + prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); - - let evaluation = poly.evaluate(&point); + let proof_bytes = prover.narg_string().to_vec(); c.bench_function("verify_2^26_coefficients", |b| { b.iter(|| { - let mut transcript = Blake2bTranscript::new(b"dory-bench"); - verify::<_, BN254, G1Routines, G2Routines, _>( + let mut verifier = dory_verifier(nu, sigma, false, &proof_bytes); + verify::<_, BN254, G1Routines, G2Routines, _, Transparent>( black_box(tier_2), black_box(evaluation), black_box(&point), - black_box(&proof), + black_box(nu), + black_box(sigma), black_box(verifier_setup.clone()), - black_box(&mut transcript), + black_box(&mut verifier), ) - .unwrap() + .unwrap(); + verifier.check_eof().unwrap(); }) }); } @@ -140,7 +147,6 @@ fn bench_end_to_end(c: &mut Criterion) { let max_log_n = 26; let (prover_setup, verifier_setup) = setup::(max_log_n); - // Initialize cache once #[cfg(feature = "cache")] { if !dory_pcs::backends::arkworks::is_cached() { @@ -152,47 +158,47 @@ fn bench_end_to_end(c: &mut Criterion) { b.iter(|| { let nu = 13; let sigma = 13; - let poly_size = 1 << 26; // 67,108,864 coefficients + let poly_size = 1 << 26; let num_vars = 26; - // Create polynomial let coefficients: Vec = (0..poly_size).map(|_| ArkFr::random()).collect(); let poly = ArkworksPolynomial::new(coefficients); - // Commit let (tier_2, tier_1, commit_blind) = poly .commit::(nu, sigma, &prover_setup) .unwrap(); - // Evaluate let point: Vec = (0..num_vars).map(|_| ArkFr::random()).collect(); let evaluation = poly.evaluate(&point); - // Prove - let mut prover_transcript = Blake2bTranscript::new(b"dory-bench"); - let (proof, _) = prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( + let mut prover = dory_prover(nu, sigma, false); + prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.narg_string().to_vec(); - // Verify - let mut verifier_transcript = Blake2bTranscript::new(b"dory-bench"); - verify::<_, BN254, G1Routines, G2Routines, _>( + let mut verifier = dory_verifier(nu, sigma, false, &proof_bytes); + verify::<_, BN254, G1Routines, G2Routines, _, Transparent>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup.clone(), - &mut verifier_transcript, + &mut verifier, ) .unwrap(); + verifier.check_eof().unwrap(); }) }); } diff --git a/examples/basic_e2e.rs b/examples/basic_e2e.rs index 146756c..0900607 100644 --- a/examples/basic_e2e.rs +++ b/examples/basic_e2e.rs @@ -6,7 +6,7 @@ //! Matrix dimensions: 16x16 (nu=4, sigma=4, total 256 coefficients) use dory_pcs::backends::arkworks::{ - ArkFr, ArkworksPolynomial, Blake2bTranscript, G1Routines, G2Routines, BN254, + dory_prover, dory_verifier, ArkFr, ArkworksPolynomial, G1Routines, G2Routines, BN254, }; use dory_pcs::primitives::arithmetic::Field; use dory_pcs::primitives::poly::Polynomial; @@ -31,27 +31,32 @@ fn main() -> Result<(), Box> { let point: Vec = (0..num_vars).map(|_| ArkFr::random()).collect(); let evaluation = poly.evaluate(&point); - let mut prover_transcript = Blake2bTranscript::new(b"dory-basic-example"); - let (proof, _) = prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( + let mut prover = dory_prover(nu, sigma, false); + prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, )?; + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = Blake2bTranscript::new(b"dory-basic-example"); - verify::<_, BN254, G1Routines, G2Routines, _>( + let mut verifier = dory_verifier(nu, sigma, false, &proof_bytes); + verify::<_, BN254, G1Routines, G2Routines, _, Transparent>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, )?; + verifier.check_eof().map_err(|e| format!("{e:?}"))?; Ok(()) } diff --git a/examples/homomorphic.rs b/examples/homomorphic.rs index 6232686..d0207df 100644 --- a/examples/homomorphic.rs +++ b/examples/homomorphic.rs @@ -3,7 +3,7 @@ //! Demonstrates: Com(r1*P1 + r2*P2 + ... + rn*Pn) = r1*Com(P1) + r2*Com(P2) + ... + rn*Com(Pn) use dory_pcs::backends::arkworks::{ - ArkFr, ArkG1, ArkworksPolynomial, Blake2bTranscript, G1Routines, G2Routines, BN254, + dory_prover, dory_verifier, ArkFr, ArkG1, ArkworksPolynomial, G1Routines, G2Routines, BN254, }; use dory_pcs::primitives::arithmetic::{Field, Group}; use dory_pcs::primitives::poly::Polynomial; @@ -86,27 +86,32 @@ fn main() -> Result<(), Box> { } assert_eq!(evaluation, expected_eval); - let mut prover_transcript = Blake2bTranscript::new(b"dory-homomorphic-example"); - let (proof, _) = prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( + let mut prover = dory_prover(nu, sigma, false); + prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( &combined_poly, &point, + &combined_tier2, + &evaluation, combined_tier1, ArkFr::zero(), nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, )?; + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = Blake2bTranscript::new(b"dory-homomorphic-example"); - verify::<_, BN254, G1Routines, G2Routines, _>( + let mut verifier = dory_verifier(nu, sigma, false, &proof_bytes); + verify::<_, BN254, G1Routines, G2Routines, _, Transparent>( combined_tier2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, )?; + verifier.check_eof().map_err(|e| format!("{e:?}"))?; Ok(()) } diff --git a/examples/homomorphic_mixed_sizes.rs b/examples/homomorphic_mixed_sizes.rs index 65412b9..a5133f5 100644 --- a/examples/homomorphic_mixed_sizes.rs +++ b/examples/homomorphic_mixed_sizes.rs @@ -4,7 +4,7 @@ //! dimensions (sizes 16 and 4, combined in a 4x4 layout). use dory_pcs::backends::arkworks::{ - ArkFr, ArkG1, ArkworksPolynomial, Blake2bTranscript, G1Routines, G2Routines, BN254, + dory_prover, dory_verifier, ArkFr, ArkG1, ArkworksPolynomial, G1Routines, G2Routines, BN254, }; use dory_pcs::primitives::arithmetic::{Field, Group}; use dory_pcs::primitives::poly::Polynomial; @@ -81,27 +81,34 @@ fn main() -> Result<(), Box> { expected = expected + coeff_scalars[1].mul(&eval2); assert_eq!(evaluation, expected); - let mut prover_transcript = Blake2bTranscript::new(b"dory-homomorphic-mixed"); - let (proof, _) = prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( + let nu = 2; + let sigma = 2; + let mut prover = dory_prover(nu, sigma, false); + prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( &combined_poly, &point, + &combined_tier2, + &evaluation, combined_tier1, ArkFr::zero(), - 2, - 2, + nu, + sigma, &prover_setup, - &mut prover_transcript, + &mut prover, )?; + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = Blake2bTranscript::new(b"dory-homomorphic-mixed"); - verify::<_, BN254, G1Routines, G2Routines, _>( + let mut verifier = dory_verifier(nu, sigma, false, &proof_bytes); + verify::<_, BN254, G1Routines, G2Routines, _, Transparent>( combined_tier2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, )?; + verifier.check_eof().map_err(|e| format!("{e:?}"))?; let padded_poly_commitment = padded_poly2 .commit::(2, 2, &prover_setup) diff --git a/examples/non_square.rs b/examples/non_square.rs index 8ae8e45..bfb5d75 100644 --- a/examples/non_square.rs +++ b/examples/non_square.rs @@ -5,7 +5,7 @@ //! Matrix dimensions: 8x16 (nu=3, sigma=4, total 128 coefficients) use dory_pcs::backends::arkworks::{ - ArkFr, ArkworksPolynomial, Blake2bTranscript, G1Routines, G2Routines, BN254, + dory_prover, dory_verifier, ArkFr, ArkworksPolynomial, G1Routines, G2Routines, BN254, }; use dory_pcs::primitives::arithmetic::Field; use dory_pcs::primitives::poly::Polynomial; @@ -30,27 +30,32 @@ fn main() -> Result<(), Box> { let point: Vec = (0..num_vars).map(|_| ArkFr::random()).collect(); let evaluation = poly.evaluate(&point); - let mut prover_transcript = Blake2bTranscript::new(b"dory-non-square-example"); - let (proof, _) = prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( + let mut prover = dory_prover(nu, sigma, false); + prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, )?; + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = Blake2bTranscript::new(b"dory-non-square-example"); - verify::<_, BN254, G1Routines, G2Routines, _>( + let mut verifier = dory_verifier(nu, sigma, false, &proof_bytes); + verify::<_, BN254, G1Routines, G2Routines, _, Transparent>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, )?; + verifier.check_eof().map_err(|e| format!("{e:?}"))?; Ok(()) } diff --git a/examples/zk_e2e.rs b/examples/zk_e2e.rs index 8e40759..26fc9fc 100644 --- a/examples/zk_e2e.rs +++ b/examples/zk_e2e.rs @@ -7,7 +7,7 @@ //! Matrix dimensions: 16x16 (nu=4, sigma=4, total 256 coefficients) use dory_pcs::backends::arkworks::{ - ArkFr, ArkworksPolynomial, Blake2bTranscript, G1Routines, G2Routines, BN254, + dory_prover, dory_verifier, ArkFr, ArkworksPolynomial, G1Routines, G2Routines, BN254, }; use dory_pcs::primitives::arithmetic::Field; use dory_pcs::primitives::poly::Polynomial; @@ -32,27 +32,32 @@ fn main() -> Result<(), Box> { let point: Vec = (0..num_vars).map(|_| ArkFr::random()).collect(); let evaluation = poly.evaluate(&point); - let mut prover_transcript = Blake2bTranscript::new(b"dory-zk-example"); - let (proof, _) = prove::<_, BN254, G1Routines, G2Routines, _, _, ZK>( + let mut prover = dory_prover(nu, sigma, true); + prove::<_, BN254, G1Routines, G2Routines, _, _, ZK>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, )?; + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = Blake2bTranscript::new(b"dory-zk-example"); - verify::<_, BN254, G1Routines, G2Routines, _>( + let mut verifier = dory_verifier(nu, sigma, true, &proof_bytes); + verify::<_, BN254, G1Routines, G2Routines, _, ZK>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, )?; + verifier.check_eof().map_err(|e| format!("{e:?}"))?; Ok(()) } diff --git a/examples/zk_statistical.rs b/examples/zk_statistical.rs index dfe4859..d46d289 100644 --- a/examples/zk_statistical.rs +++ b/examples/zk_statistical.rs @@ -1,19 +1,21 @@ //! Statistical tests for zero-knowledge property of Dory PCS //! -//! Verifies that proof elements are statistically indistinguishable from uniform +//! Verifies that proof bytes are statistically indistinguishable from uniform //! random regardless of the witness (polynomial) distribution. //! +//! With the spongefish NARG model, proofs are opaque byte arrays. We test +//! that byte distributions at various offsets are uniform and witness-independent. +//! //! ```sh //! cargo run --release --features "backends zk" --example zk_statistical //! ``` -use ark_serialize::CanonicalSerialize; use dory_pcs::backends::arkworks::{ - ArkFr, ArkworksPolynomial, Blake2bTranscript, G1Routines, G2Routines, BN254, + dory_prover, dory_verifier, ArkFr, ArkworksPolynomial, G1Routines, G2Routines, BN254, }; use dory_pcs::primitives::arithmetic::Field; use dory_pcs::primitives::poly::Polynomial; -use dory_pcs::{create_evaluation_proof, setup, verify, DoryProof, ZK}; +use dory_pcs::{prove, setup, verify, ZK}; use std::collections::HashMap; use tracing::info; @@ -31,10 +33,6 @@ fn random_point(num_vars: usize) -> Vec { (0..num_vars).map(|_| ArkFr::random()).collect() } -fn fresh_transcript() -> Blake2bTranscript { - Blake2bTranscript::new(b"dory-test") -} - struct BucketTracker { buckets: HashMap>, } @@ -71,111 +69,38 @@ impl BucketTracker { } } -fn bucket_from_serializable(elem: &T) -> usize { - let mut bytes = Vec::new(); - elem.serialize_compressed(&mut bytes).unwrap(); - (bytes[0] as usize) % NUM_BUCKETS -} - -type ArkDoryProof = DoryProof< - dory_pcs::backends::arkworks::ArkG1, - dory_pcs::backends::arkworks::ArkG2, - dory_pcs::backends::arkworks::ArkGT, ->; - -fn collect_full_zk_proof_stats(proof: &ArkDoryProof, tracker: &mut BucketTracker) { - tracker.record("zk_vmv_c", bucket_from_serializable(&proof.vmv_message.c)); - tracker.record("zk_vmv_d2", bucket_from_serializable(&proof.vmv_message.d2)); - tracker.record("zk_vmv_e1", bucket_from_serializable(&proof.vmv_message.e1)); - - if let Some(ref e2) = proof.e2 { - tracker.record("zk_vmv_e2", bucket_from_serializable(e2)); - } - if let Some(ref y_com) = proof.y_com { - tracker.record("zk_vmv_y_com", bucket_from_serializable(y_com)); - } - - for (i, msg) in proof.first_messages.iter().enumerate() { - let prefix = format!("zk_first_{i}"); - tracker.record( - &format!("{prefix}_d1_left"), - bucket_from_serializable(&msg.d1_left), - ); - tracker.record( - &format!("{prefix}_d1_right"), - bucket_from_serializable(&msg.d1_right), - ); - tracker.record( - &format!("{prefix}_d2_left"), - bucket_from_serializable(&msg.d2_left), - ); - tracker.record( - &format!("{prefix}_d2_right"), - bucket_from_serializable(&msg.d2_right), - ); - } - - for (i, msg) in proof.second_messages.iter().enumerate() { - let prefix = format!("zk_second_{i}"); - tracker.record( - &format!("{prefix}_c_plus"), - bucket_from_serializable(&msg.c_plus), - ); - tracker.record( - &format!("{prefix}_c_minus"), - bucket_from_serializable(&msg.c_minus), - ); - tracker.record( - &format!("{prefix}_e1_plus"), - bucket_from_serializable(&msg.e1_plus), - ); - tracker.record( - &format!("{prefix}_e1_minus"), - bucket_from_serializable(&msg.e1_minus), - ); - tracker.record( - &format!("{prefix}_e2_plus"), - bucket_from_serializable(&msg.e2_plus), - ); - tracker.record( - &format!("{prefix}_e2_minus"), - bucket_from_serializable(&msg.e2_minus), - ); - } - - tracker.record( - "zk_final_e1", - bucket_from_serializable(&proof.final_message.e1), - ); - tracker.record( - "zk_final_e2", - bucket_from_serializable(&proof.final_message.e2), - ); - - if let Some(ref sigma1) = proof.sigma1_proof { - tracker.record("sigma1_a1", bucket_from_serializable(&sigma1.a1)); - tracker.record("sigma1_a2", bucket_from_serializable(&sigma1.a2)); - tracker.record("sigma1_z1", bucket_from_serializable(&sigma1.z1)); - tracker.record("sigma1_z2", bucket_from_serializable(&sigma1.z2)); - tracker.record("sigma1_z3", bucket_from_serializable(&sigma1.z3)); +/// Collect byte-level statistics from proof bytes at sampled offsets. +fn collect_proof_byte_stats(proof_bytes: &[u8], tracker: &mut BucketTracker) { + let len = proof_bytes.len(); + if len == 0 { + return; } - if let Some(ref sigma2) = proof.sigma2_proof { - tracker.record("sigma2_a", bucket_from_serializable(&sigma2.a)); - tracker.record("sigma2_z1", bucket_from_serializable(&sigma2.z1)); - tracker.record("sigma2_z2", bucket_from_serializable(&sigma2.z2)); + let offsets = [ + 0, + len / 8, + len / 4, + 3 * len / 8, + len / 2, + 5 * len / 8, + 3 * len / 4, + 7 * len / 8, + len - 1, + ]; + + for &offset in &offsets { + if offset < len { + let bucket = (proof_bytes[offset] as usize) % NUM_BUCKETS; + tracker.record(&format!("byte_{offset}"), bucket); + } } - if let Some(ref sp) = proof.scalar_product_proof { - tracker.record("zk_sp_p1", bucket_from_serializable(&sp.p1)); - tracker.record("zk_sp_p2", bucket_from_serializable(&sp.p2)); - tracker.record("zk_sp_q", bucket_from_serializable(&sp.q)); - tracker.record("zk_sp_r", bucket_from_serializable(&sp.r)); - tracker.record("zk_sp_e1", bucket_from_serializable(&sp.e1)); - tracker.record("zk_sp_e2", bucket_from_serializable(&sp.e2)); - tracker.record("zk_sp_r1", bucket_from_serializable(&sp.r1)); - tracker.record("zk_sp_r2", bucket_from_serializable(&sp.r2)); - tracker.record("zk_sp_r3", bucket_from_serializable(&sp.r3)); + let pair_offsets = [0, len / 4, len / 2, 3 * len / 4]; + for &offset in &pair_offsets { + if offset + 1 < len { + let bucket = ((proof_bytes[offset] ^ proof_bytes[offset + 1]) as usize) % NUM_BUCKETS; + tracker.record(&format!("pair_{offset}"), bucket); + } } } @@ -193,31 +118,37 @@ fn prove_verify_collect( .unwrap(); let evaluation = poly.evaluate(point); - let mut transcript = fresh_transcript(); - let (proof, _) = create_evaluation_proof::<_, BN254, G1Routines, G2Routines, _, _, ZK>( + + let mut prover = dory_prover(nu, sigma, true); + prove::<_, BN254, G1Routines, G2Routines, _, _, ZK>( poly, point, - Some(tier_1), + &tier_2, + &evaluation, + tier_1, commit_blind, nu, sigma, prover_setup, - &mut transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - verify::<_, BN254, G1Routines, G2Routines, _>( + let mut verifier = dory_verifier(nu, sigma, true, &proof_bytes); + verify::<_, BN254, G1Routines, G2Routines, _, ZK>( tier_2, evaluation, point, - &proof, + nu, + sigma, verifier_setup.clone(), - &mut verifier_transcript, + &mut verifier, ) .expect("proof verification failed"); + verifier.check_eof().expect("proof has trailing bytes"); - collect_full_zk_proof_stats(&proof, tracker); + collect_proof_byte_stats(&proof_bytes, tracker); } fn two_sample_chi_squared(a: &[usize], b: &[usize]) -> f64 { diff --git a/src/backends/arkworks/ark_proof.rs b/src/backends/arkworks/ark_proof.rs deleted file mode 100644 index d0d8b0d..0000000 --- a/src/backends/arkworks/ark_proof.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! Arkworks backend proof type -//! -//! Type alias for Dory proofs using arkworks group types. -//! Serialization implementations are in ark_serde.rs. - -use super::{ArkG1, ArkG2, ArkGT}; -use crate::proof::DoryProof; - -/// Arkworks-specific Dory proof type -/// -/// This is a type alias for `DoryProof` specialized to arkworks group types. -/// Serialization support via `CanonicalSerialize` and `CanonicalDeserialize` -/// is implemented in the `ark_serde` module. -pub type ArkDoryProof = DoryProof; diff --git a/src/backends/arkworks/ark_serde.rs b/src/backends/arkworks/ark_serde.rs index 6d43998..e9700cf 100644 --- a/src/backends/arkworks/ark_serde.rs +++ b/src/backends/arkworks/ark_serde.rs @@ -9,7 +9,6 @@ use ark_serialize::{ use std::io::{Read, Write}; use super::BN254; -use crate::messages::{FirstReduceMessage, ScalarProductMessage, SecondReduceMessage, VMVMessage}; use crate::setup::{ProverSetup, VerifierSetup}; impl Valid for ArkFr { @@ -240,288 +239,6 @@ impl DoryDeserialize for ArkGT { } } -// Arkworks-specific Dory proof type -use super::ArkDoryProof; - -#[cfg(feature = "zk")] -mod zk_serde { - use ark_serialize::{ - CanonicalDeserialize as De, CanonicalSerialize as Ser, Compress, SerializationError, Valid, - Validate, - }; - use std::io::{Read, Write}; - - pub(super) fn ser_opt( - v: &Option, - w: &mut W, - c: Compress, - ) -> Result<(), SerializationError> { - match v { - Some(val) => { - Ser::serialize_with_mode(&1u8, &mut *w, c)?; - Ser::serialize_with_mode(val, w, c) - } - None => Ser::serialize_with_mode(&0u8, w, c), - } - } - - pub(super) fn de_opt( - r: &mut R, - c: Compress, - v: Validate, - ) -> Result, SerializationError> { - match ::deserialize_with_mode(&mut *r, c, v)? { - 0 => Ok(None), - 1 => Ok(Some(T::deserialize_with_mode(r, c, v)?)), - _ => Err(SerializationError::InvalidData), - } - } - - pub(super) fn size_opt(v: &Option, c: Compress) -> usize { - 1 + v.as_ref().map_or(0, |val| Ser::serialized_size(val, c)) - } - - macro_rules! impl_serde { - ($ty:ty, [$($field:ident),+]) => { - impl Valid for $ty { fn check(&self) -> Result<(), SerializationError> { Ok(()) } } - impl Ser for $ty { - fn serialize_with_mode(&self, mut w: W, c: Compress) -> Result<(), SerializationError> { - $(Ser::serialize_with_mode(&self.$field, &mut w, c)?;)+ - Ok(()) - } - fn serialized_size(&self, c: Compress) -> usize { - 0 $(+ Ser::serialized_size(&self.$field, c))+ - } - } - impl De for $ty { - fn deserialize_with_mode(mut r: R, c: Compress, v: Validate) -> Result { - Ok(Self { $($field: De::deserialize_with_mode(&mut r, c, v)?),+ }) - } - } - }; - } - - use super::{ArkFr, ArkG1, ArkG2, ArkGT}; - use crate::messages::{ScalarProductProof, Sigma1Proof, Sigma2Proof}; - - impl_serde!(Sigma1Proof, [a1, a2, z1, z2, z3]); - impl_serde!(Sigma2Proof, [a, z1, z2]); - impl_serde!(ScalarProductProof, [p1, p2, q, r, e1, e2, r1, r2, r3]); -} - -impl ArkValid for ArkDoryProof { - fn check(&self) -> Result<(), ArkSerializationError> { - Ok(()) - } -} - -impl CanonicalSerialize for ArkDoryProof { - fn serialize_with_mode( - &self, - mut writer: W, - compress: ArkCompress, - ) -> Result<(), ArkSerializationError> { - // Serialize VMV message - CanonicalSerialize::serialize_with_mode(&self.vmv_message.c, &mut writer, compress)?; - CanonicalSerialize::serialize_with_mode(&self.vmv_message.d2, &mut writer, compress)?; - CanonicalSerialize::serialize_with_mode(&self.vmv_message.e1, &mut writer, compress)?; - - // Serialize number of rounds - let num_rounds = self.first_messages.len() as u32; - CanonicalSerialize::serialize_with_mode(&num_rounds, &mut writer, compress)?; - - // Serialize first messages - for msg in &self.first_messages { - CanonicalSerialize::serialize_with_mode(&msg.d1_left, &mut writer, compress)?; - CanonicalSerialize::serialize_with_mode(&msg.d1_right, &mut writer, compress)?; - CanonicalSerialize::serialize_with_mode(&msg.d2_left, &mut writer, compress)?; - CanonicalSerialize::serialize_with_mode(&msg.d2_right, &mut writer, compress)?; - CanonicalSerialize::serialize_with_mode(&msg.e1_beta, &mut writer, compress)?; - CanonicalSerialize::serialize_with_mode(&msg.e2_beta, &mut writer, compress)?; - } - - // Serialize second messages - for msg in &self.second_messages { - CanonicalSerialize::serialize_with_mode(&msg.c_plus, &mut writer, compress)?; - CanonicalSerialize::serialize_with_mode(&msg.c_minus, &mut writer, compress)?; - CanonicalSerialize::serialize_with_mode(&msg.e1_plus, &mut writer, compress)?; - CanonicalSerialize::serialize_with_mode(&msg.e1_minus, &mut writer, compress)?; - CanonicalSerialize::serialize_with_mode(&msg.e2_plus, &mut writer, compress)?; - CanonicalSerialize::serialize_with_mode(&msg.e2_minus, &mut writer, compress)?; - } - - // Serialize final message - CanonicalSerialize::serialize_with_mode(&self.final_message.e1, &mut writer, compress)?; - CanonicalSerialize::serialize_with_mode(&self.final_message.e2, &mut writer, compress)?; - - // Serialize nu and sigma - CanonicalSerialize::serialize_with_mode(&(self.nu as u32), &mut writer, compress)?; - CanonicalSerialize::serialize_with_mode(&(self.sigma as u32), &mut writer, compress)?; - - #[cfg(feature = "zk")] - { - zk_serde::ser_opt(&self.e2, &mut writer, compress)?; - zk_serde::ser_opt(&self.y_com, &mut writer, compress)?; - zk_serde::ser_opt(&self.sigma1_proof, &mut writer, compress)?; - zk_serde::ser_opt(&self.sigma2_proof, &mut writer, compress)?; - zk_serde::ser_opt(&self.scalar_product_proof, &mut writer, compress)?; - } - - Ok(()) - } - - fn serialized_size(&self, compress: ArkCompress) -> usize { - let mut size = 0; - - // VMV message - size += CanonicalSerialize::serialized_size(&self.vmv_message.c, compress); - size += CanonicalSerialize::serialized_size(&self.vmv_message.d2, compress); - size += CanonicalSerialize::serialized_size(&self.vmv_message.e1, compress); - - // Number of rounds - size += 4; // u32 - - // First messages - for msg in &self.first_messages { - size += CanonicalSerialize::serialized_size(&msg.d1_left, compress); - size += CanonicalSerialize::serialized_size(&msg.d1_right, compress); - size += CanonicalSerialize::serialized_size(&msg.d2_left, compress); - size += CanonicalSerialize::serialized_size(&msg.d2_right, compress); - size += CanonicalSerialize::serialized_size(&msg.e1_beta, compress); - size += CanonicalSerialize::serialized_size(&msg.e2_beta, compress); - } - - // Second messages - for msg in &self.second_messages { - size += CanonicalSerialize::serialized_size(&msg.c_plus, compress); - size += CanonicalSerialize::serialized_size(&msg.c_minus, compress); - size += CanonicalSerialize::serialized_size(&msg.e1_plus, compress); - size += CanonicalSerialize::serialized_size(&msg.e1_minus, compress); - size += CanonicalSerialize::serialized_size(&msg.e2_plus, compress); - size += CanonicalSerialize::serialized_size(&msg.e2_minus, compress); - } - - // Final message - size += CanonicalSerialize::serialized_size(&self.final_message.e1, compress); - size += CanonicalSerialize::serialized_size(&self.final_message.e2, compress); - - // nu and sigma - size += 8; // 2 * u32 - - #[cfg(feature = "zk")] - { - size += zk_serde::size_opt(&self.e2, compress); - size += zk_serde::size_opt(&self.y_com, compress); - size += zk_serde::size_opt(&self.sigma1_proof, compress); - size += zk_serde::size_opt(&self.sigma2_proof, compress); - size += zk_serde::size_opt(&self.scalar_product_proof, compress); - } - - size - } -} - -impl CanonicalDeserialize for ArkDoryProof { - fn deserialize_with_mode( - mut reader: R, - compress: ArkCompress, - validate: ArkValidate, - ) -> Result { - // Deserialize VMV message - let c = CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?; - let d2 = CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?; - let e1 = CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?; - let vmv_message = VMVMessage { c, d2, e1 }; - - // Deserialize number of rounds - let num_rounds = - ::deserialize_with_mode(&mut reader, compress, validate)? - as usize; - - // Deserialize first messages - let mut first_messages = Vec::with_capacity(num_rounds); - for _ in 0..num_rounds { - let d1_left = - CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?; - let d1_right = - CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?; - let d2_left = - CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?; - let d2_right = - CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?; - let e1_beta = - CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?; - let e2_beta = - CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?; - first_messages.push(FirstReduceMessage { - d1_left, - d1_right, - d2_left, - d2_right, - e1_beta, - e2_beta, - }); - } - - // Deserialize second messages - let mut second_messages = Vec::with_capacity(num_rounds); - for _ in 0..num_rounds { - let c_plus = - CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?; - let c_minus = - CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?; - let e1_plus = - CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?; - let e1_minus = - CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?; - let e2_plus = - CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?; - let e2_minus = - CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?; - second_messages.push(SecondReduceMessage { - c_plus, - c_minus, - e1_plus, - e1_minus, - e2_plus, - e2_minus, - }); - } - - // Deserialize final message - let e1 = CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?; - let e2 = CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?; - let final_message = ScalarProductMessage { e1, e2 }; - - // Deserialize nu and sigma - let nu = - ::deserialize_with_mode(&mut reader, compress, validate)? - as usize; - let sigma = - ::deserialize_with_mode(&mut reader, compress, validate)? - as usize; - - Ok(ArkDoryProof { - vmv_message, - first_messages, - second_messages, - final_message, - nu, - sigma, - #[cfg(feature = "zk")] - e2: zk_serde::de_opt(&mut reader, compress, validate)?, - #[cfg(feature = "zk")] - y_com: zk_serde::de_opt(&mut reader, compress, validate)?, - #[cfg(feature = "zk")] - sigma1_proof: zk_serde::de_opt(&mut reader, compress, validate)?, - #[cfg(feature = "zk")] - sigma2_proof: zk_serde::de_opt(&mut reader, compress, validate)?, - #[cfg(feature = "zk")] - scalar_product_proof: zk_serde::de_opt(&mut reader, compress, validate)?, - }) - } -} - // Setup wrapper types (declared in ark_setup.rs, serialization impls here) use super::{ArkworksProverSetup, ArkworksVerifierSetup}; diff --git a/src/backends/arkworks/blake2b_transcript.rs b/src/backends/arkworks/blake2b_transcript.rs deleted file mode 100644 index 63cd149..0000000 --- a/src/backends/arkworks/blake2b_transcript.rs +++ /dev/null @@ -1,148 +0,0 @@ -//! Blake2b-based Fiat-Shamir transcript for Dory proofs - -#![allow(missing_docs)] -#![allow(clippy::missing_errors_doc)] -#![allow(clippy::missing_panics_doc)] - -use crate::primitives::arithmetic::{Group, PairingCurve}; -use crate::primitives::serialization::Compress; -use crate::primitives::transcript::Transcript; -use crate::primitives::DorySerialize; -use ark_ff::{BigInteger, PrimeField}; -use ark_serialize::CanonicalSerialize; -use blake2::Blake2b512; -use digest::{Digest, Output}; -use std::marker::PhantomData; - -#[derive(Clone)] -pub struct Blake2bTranscript { - hasher: Blake2b512, - _phantom: PhantomData, -} - -impl Blake2bTranscript { - pub fn new(domain_label: &[u8]) -> Self { - let mut hasher = Blake2b512::default(); - hasher.update(domain_label); - Self { - hasher, - _phantom: PhantomData, - } - } - - pub fn append_bytes_impl(&mut self, label: &[u8], bytes: &[u8]) { - self.hasher.update(label); - self.hasher.update((bytes.len() as u64).to_le_bytes()); - self.hasher.update(bytes); - } - - pub fn append_field_impl(&mut self, label: &[u8], x: &F) { - self.append_bytes_impl(label, &x.into_bigint().to_bytes_le()); - } - - pub fn append_group_impl(&mut self, label: &[u8], g: &G) { - let mut bytes = Vec::new(); - g.serialize_compressed(&mut bytes) - .expect("Serialization should not fail"); - self.append_bytes_impl(label, &bytes); - } - - pub fn challenge_scalar_impl(&mut self, label: &[u8]) -> F { - self.hasher.update(label); - - let h = self.hasher.clone(); - let digest: Output = h.finalize(); - - let repr = digest.to_vec(); - let fe = F::from_le_bytes_mod_order(&repr); - - if fe.is_zero() { - panic!("Challenge scalar cannot be zero") - } - - self.hasher.update(&repr); - - fe - } - - pub fn reset_impl(&mut self, domain_label: &[u8]) { - let mut hasher = Blake2b512::default(); - hasher.update(domain_label); - self.hasher = hasher; - self._phantom = PhantomData; - } -} - -impl Transcript for Blake2bTranscript { - type Curve = crate::backends::arkworks::BN254; - - fn append_bytes(&mut self, label: &[u8], bytes: &[u8]) { - self.append_bytes_impl(label, bytes); - } - - fn append_field( - &mut self, - label: &[u8], - x: &<::G1 as Group>::Scalar, - ) { - self.append_field_impl(label, &x.0); - } - - fn append_group(&mut self, label: &[u8], g: &G) { - let mut bytes: Vec = Vec::new(); - g.serialize_with_mode(&mut bytes, Compress::Yes) - .expect("DorySerialize should not fail"); - self.append_bytes_impl(label, &bytes); - } - - fn append_serde(&mut self, label: &[u8], s: &S) { - let mut bytes: Vec = Vec::new(); - s.serialize_with_mode(&mut bytes, Compress::Yes) - .expect("DorySerialize should not fail"); - self.append_bytes_impl(label, &bytes); - } - - fn challenge_scalar(&mut self, label: &[u8]) -> crate::backends::arkworks::ArkFr { - use crate::backends::arkworks::ArkFr; - ArkFr(self.challenge_scalar_impl(label)) - } - - fn reset(&mut self, domain_label: &[u8]) { - self.reset_impl(domain_label); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::backends::arkworks::BN254; - - #[test] - fn transcript_consistency() { - let mut t1: Blake2bTranscript = Blake2bTranscript::new(b"demo"); - let mut t2: Blake2bTranscript = Blake2bTranscript::new(b"demo"); - - // Same sequence of messages => same challenge - t1.append_bytes(b"m", b"hello"); - t2.append_bytes(b"m", b"hello"); - - let c1 = t1.challenge_scalar(b"x"); - let c2 = t2.challenge_scalar(b"x"); - - assert_eq!(c1, c2); - } - - #[test] - fn transcript_different_messages() { - let mut t1: Blake2bTranscript = Blake2bTranscript::new(b"demo"); - let mut t2: Blake2bTranscript = Blake2bTranscript::new(b"demo"); - - t1.append_bytes(b"m", b"hello"); - t2.append_bytes(b"m", b"world"); - - let c1 = t1.challenge_scalar(b"x"); - let c2 = t2.challenge_scalar(b"x"); - - assert_ne!(c1, c2); - } -} diff --git a/src/backends/arkworks/domain.rs b/src/backends/arkworks/domain.rs new file mode 100644 index 0000000..d709dc0 --- /dev/null +++ b/src/backends/arkworks/domain.rs @@ -0,0 +1,55 @@ +//! Domain separator and instance encoding for the Dory evaluation proof protocol. +//! +//! The domain separator identifies the protocol and binds public parameters +//! (nu, sigma, zk) into the sponge state via the instance. + +use spongefish::{ProverState, VerifierState}; + +/// Encode protocol parameters into an instance byte array for domain binding. +/// +/// The instance encodes `nu` (4 bytes LE), `sigma` (4 bytes LE), and `zk` flag +/// (1 byte) so that proofs generated with different parameters are domain-separated. +fn dory_instance(nu: usize, sigma: usize, zk: bool) -> [u8; 9] { + let mut buf = [0u8; 9]; + buf[..4].copy_from_slice(&(nu as u32).to_le_bytes()); + buf[4..8].copy_from_slice(&(sigma as u32).to_le_bytes()); + buf[8] = zk as u8; + buf +} + +/// Create a Dory prover state with the standard hash. +/// +/// Combines domain separator + instance binding. After proving, extract +/// proof bytes via `prover.narg_string().to_vec()`. +pub fn dory_prover(nu: usize, sigma: usize, zk: bool) -> ProverState { + let instance = dory_instance(nu, sigma, zk); + spongefish::domain_separator!("dory-pcs-v2") + .instance(&instance) + .std_prover() +} + +/// Create a Dory verifier state with the standard hash. +/// +/// Combines domain separator + instance binding. After verification, +/// call `verifier.check_eof()?` to assert all proof bytes were consumed. +pub fn dory_verifier(nu: usize, sigma: usize, zk: bool, proof_bytes: &[u8]) -> VerifierState<'_> { + let instance = dory_instance(nu, sigma, zk); + spongefish::domain_separator!("dory-pcs-v2") + .instance(&instance) + .std_verifier(proof_bytes) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn domain_separator_builds_without_zk() { + let _prover = dory_prover(4, 4, false); + } + + #[test] + fn domain_separator_builds_with_zk() { + let _prover = dory_prover(4, 4, true); + } +} diff --git a/src/backends/arkworks/mod.rs b/src/backends/arkworks/mod.rs index 36835b6..79689d6 100644 --- a/src/backends/arkworks/mod.rs +++ b/src/backends/arkworks/mod.rs @@ -4,10 +4,10 @@ mod ark_field; mod ark_group; mod ark_pairing; mod ark_poly; -mod ark_proof; mod ark_serde; mod ark_setup; -mod blake2b_transcript; +mod domain; +mod spongefish_codecs; #[cfg(feature = "cache")] pub mod ark_cache; @@ -16,9 +16,9 @@ pub use ark_field::ArkFr; pub use ark_group::{ArkG1, ArkG2, ArkGT, G1Routines, G2Routines}; pub use ark_pairing::BN254; pub use ark_poly::ArkworksPolynomial; -pub use ark_proof::ArkDoryProof; pub use ark_setup::{ArkworksProverSetup, ArkworksVerifierSetup}; -pub use blake2b_transcript::Blake2bTranscript; +pub use domain::{dory_prover, dory_verifier}; +pub use spongefish::{ProverState, VerifierState}; #[cfg(feature = "cache")] pub use ark_cache::{get_prepared_cache, init_cache, is_cached, PreparedCache}; diff --git a/src/backends/arkworks/spongefish_codecs.rs b/src/backends/arkworks/spongefish_codecs.rs new file mode 100644 index 0000000..ef145c3 --- /dev/null +++ b/src/backends/arkworks/spongefish_codecs.rs @@ -0,0 +1,87 @@ +//! Spongefish codec implementations for Dory's transcript traits. +//! +//! Maps [`ProverTranscript`] and [`VerifierTranscript`] to spongefish's +//! NARG protocol using the unified message API. + +use crate::backends::arkworks::{ArkFr, ArkG1, ArkG2, ArkGT, BN254}; +use crate::error::DoryError; +use crate::primitives::transcript::{ProverTranscript, VerifierTranscript}; + +use ark_bn254::{Fq12, Fr, G1Projective, G2Projective}; + +macro_rules! impl_prover_absorb { + ($($method:ident, $wrapper:ty);+ $(;)?) => { + $(fn $method(&mut self, val: &$wrapper) { + self.prover_message(&val.0); + })+ + }; +} + +macro_rules! impl_verifier_read { + ($($method:ident -> $wrapper:ident($inner:ty));+ $(;)?) => { + $(fn $method(&mut self) -> Result<$wrapper, DoryError> { + self.prover_message::<$inner>() + .map($wrapper) + .map_err(|e| DoryError::SpongeVerification(e.to_string())) + })+ + }; +} + +// --------------------------------------------------------------------------- +// ProverState implements ProverTranscript +// --------------------------------------------------------------------------- +impl ProverTranscript for spongefish::ProverState { + fn public_u32(&mut self, val: u32) { + self.public_message(&val.to_le_bytes()); + } + + fn public_gt(&mut self, val: &ArkGT) { + self.public_message(&val.0); + } + + fn public_field(&mut self, val: &ArkFr) { + self.public_message(&val.0); + } + + impl_prover_absorb! { + absorb_gt, ArkGT; + absorb_g1, ArkG1; + absorb_g2, ArkG2; + absorb_field, ArkFr; + } + + fn squeeze_scalar(&mut self) -> ArkFr { + ArkFr(self.verifier_message::()) + } +} + +// --------------------------------------------------------------------------- +// VerifierState implements VerifierTranscript +// --------------------------------------------------------------------------- +impl VerifierTranscript for spongefish::VerifierState<'_> { + fn public_u32(&mut self, val: u32) -> Result<(), DoryError> { + self.public_message(&val.to_le_bytes()); + Ok(()) + } + + fn public_gt(&mut self, val: &ArkGT) -> Result<(), DoryError> { + self.public_message(&val.0); + Ok(()) + } + + fn public_field(&mut self, val: &ArkFr) -> Result<(), DoryError> { + self.public_message(&val.0); + Ok(()) + } + + impl_verifier_read! { + read_gt -> ArkGT(Fq12); + read_g1 -> ArkG1(G1Projective); + read_g2 -> ArkG2(G2Projective); + read_field -> ArkFr(Fr); + } + + fn squeeze_scalar(&mut self) -> Result { + Ok(ArkFr(self.verifier_message::())) + } +} diff --git a/src/error.rs b/src/error.rs index 85c59bb..a169cfe 100644 --- a/src/error.rs +++ b/src/error.rs @@ -30,4 +30,8 @@ pub enum DoryError { /// Setup file not found or corrupted #[error("Invalid or missing URS file: {0}")] InvalidURS(String), + + /// Spongefish transcript verification failed + #[error("Sponge verification failed: {0}")] + SpongeVerification(String), } diff --git a/src/evaluation_proof.rs b/src/evaluation_proof.rs index f68366a..c8cba45 100644 --- a/src/evaluation_proof.rs +++ b/src/evaluation_proof.rs @@ -26,12 +26,10 @@ //! See `examples/homomorphic.rs` for a complete demonstration. use crate::error::DoryError; -use crate::messages::VMVMessage; use crate::mode::Mode; use crate::primitives::arithmetic::{DoryRoutines, Field, Group, PairingCurve}; use crate::primitives::poly::MultilinearLagrange; -use crate::primitives::transcript::Transcript; -use crate::proof::DoryProof; +use crate::primitives::transcript::ProverTranscript; use crate::reduce_and_fold::{DoryProverState, DoryVerifierState}; use crate::setup::{ProverSetup, VerifierSetup}; @@ -41,6 +39,9 @@ use crate::setup::{ProverSetup, VerifierSetup}; /// The protocol proves that polynomial(point) = evaluation via the VMV relation: /// evaluation = L^T × M × R /// +/// All proof elements are absorbed into the transcript. The transcript's NARG +/// string IS the proof — no separate proof struct is returned. +/// /// # Algorithm /// 1. Compute or use provided row commitments (Tier 1 commitment) /// 2. Split evaluation point into left and right vectors @@ -55,6 +56,8 @@ use crate::setup::{ProverSetup, VerifierSetup}; /// # Parameters /// - `polynomial`: Polynomial to prove evaluation for /// - `point`: Evaluation point (length nu + sigma) +/// - `commitment`: Tier-2 commitment (GT element) — absorbed as public message +/// - `evaluation`: Claimed evaluation result — absorbed as public message /// - `row_commitments`: Optional precomputed row commitments from polynomial.commit() /// - `commit_blind`: GT-level blinding scalar from `commit()`. Ignored when /// `row_commitments` is `None` (the blind is computed internally in that case). @@ -64,7 +67,7 @@ use crate::setup::{ProverSetup, VerifierSetup}; /// - `transcript`: Fiat-Shamir transcript for challenge generation /// /// # Returns -/// Complete Dory proof containing VMV message, reduce messages, and final message +/// In ZK mode, returns the blinding scalar `r_y`; in transparent mode, `None`. /// /// # Errors /// Returns error if dimensions are invalid (nu > sigma) or protocol fails @@ -78,13 +81,15 @@ use crate::setup::{ProverSetup, VerifierSetup}; pub fn create_evaluation_proof( polynomial: &P, point: &[F], + commitment: &E::GT, + evaluation: &F, row_commitments: Option>, commit_blind: F, nu: usize, sigma: usize, setup: &ProverSetup, transcript: &mut T, -) -> Result<(DoryProof, Option), DoryError> +) -> Result, DoryError> where F: Field, E: PairingCurve, @@ -93,7 +98,7 @@ where E::GT: Group, M1: DoryRoutines, M2: DoryRoutines, - T: Transcript, + T: ProverTranscript, P: MultilinearLagrange, Mo: Mode, { @@ -112,6 +117,18 @@ where }); } + // Public inputs: bind nu and sigma to the sponge state + transcript.public_u32(nu as u32); + transcript.public_u32(sigma as u32); + + // External inputs: bind commitment, evaluation, and point to the sponge state. + // These are public messages (not in the NARG string) — both sides absorb independently. + transcript.public_gt(commitment); + transcript.public_field(evaluation); + for coord in point { + transcript.public_field(coord); + } + let (row_commitments, commit_blind) = match row_commitments { Some(rc) => (rc, commit_blind), None => { @@ -148,26 +165,25 @@ where // E₁ = ⟨row_commitments, left_vec⟩ + r_e1·H₁ let e1 = Mo::mask(M1::msm(&row_commitments, &left_vec), &setup.h1, &r_e1); - let vmv_message = VMVMessage { c, d2, e1 }; - - transcript.append_serde(b"vmv_c", &vmv_message.c); - transcript.append_serde(b"vmv_d2", &vmv_message.d2); - transcript.append_serde(b"vmv_e1", &vmv_message.e1); + // Absorb VMV message + transcript.absorb_gt(&c); + transcript.absorb_gt(&d2); + transcript.absorb_g1(&e1); #[cfg(feature = "zk")] - let (zk_e2, zk_y_com, zk_sigma1, zk_sigma2, zk_r_y) = if Mo::BLINDING { + let zk_r_y = if Mo::BLINDING { use crate::reduce_and_fold::{generate_sigma1_proof, generate_sigma2_proof}; let y = polynomial.evaluate(point); let r_y: F = Mo::sample(); let e2 = Mo::mask(g2_fin.scale(&y), &setup.h2, &r_e2); let y_com = setup.g1_vec[0].scale(&y) + setup.h1.scale(&r_y); - transcript.append_serde(b"vmv_e2", &e2); - transcript.append_serde(b"vmv_y_com", &y_com); - let s1 = generate_sigma1_proof::(&y, &r_e2, &r_y, setup, transcript); - let s2 = generate_sigma2_proof::(&r_e1, &-r_d2, setup, transcript); - (Some(e2), Some(y_com), Some(s1), Some(s2), Some(r_y)) + transcript.absorb_g2(&e2); + transcript.absorb_g1(&y_com); + generate_sigma1_proof::(&y, &r_e2, &r_y, setup, transcript); + generate_sigma2_proof::(&r_e1, &-r_d2, setup, transcript); + Some(r_y) } else { - (None, None, None, None, None) + None }; // v₂ = v_vec · Γ₂,fin (each scalar scales g_fin) @@ -190,75 +206,51 @@ where ); prover_state.set_initial_blinds(commit_blind, r_c, r_d2, r_e1, r_e2); - let num_rounds = nu.max(sigma); - let mut first_messages = Vec::with_capacity(num_rounds); - let mut second_messages = Vec::with_capacity(num_rounds); + let num_rounds = sigma; for _round in 0..num_rounds { let first_msg = prover_state.compute_first_message::(); - transcript.append_serde(b"d1_left", &first_msg.d1_left); - transcript.append_serde(b"d1_right", &first_msg.d1_right); - transcript.append_serde(b"d2_left", &first_msg.d2_left); - transcript.append_serde(b"d2_right", &first_msg.d2_right); - transcript.append_serde(b"e1_beta", &first_msg.e1_beta); - transcript.append_serde(b"e2_beta", &first_msg.e2_beta); + transcript.absorb_gt(&first_msg.d1_left); + transcript.absorb_gt(&first_msg.d1_right); + transcript.absorb_gt(&first_msg.d2_left); + transcript.absorb_gt(&first_msg.d2_right); + transcript.absorb_g1(&first_msg.e1_beta); + transcript.absorb_g2(&first_msg.e2_beta); - let beta = transcript.challenge_scalar(b"beta"); + let beta = transcript.squeeze_scalar(); prover_state.apply_first_challenge::(&beta); - first_messages.push(first_msg); let second_msg = prover_state.compute_second_message::(); - transcript.append_serde(b"c_plus", &second_msg.c_plus); - transcript.append_serde(b"c_minus", &second_msg.c_minus); - transcript.append_serde(b"e1_plus", &second_msg.e1_plus); - transcript.append_serde(b"e1_minus", &second_msg.e1_minus); - transcript.append_serde(b"e2_plus", &second_msg.e2_plus); - transcript.append_serde(b"e2_minus", &second_msg.e2_minus); + transcript.absorb_gt(&second_msg.c_plus); + transcript.absorb_gt(&second_msg.c_minus); + transcript.absorb_g1(&second_msg.e1_plus); + transcript.absorb_g1(&second_msg.e1_minus); + transcript.absorb_g2(&second_msg.e2_plus); + transcript.absorb_g2(&second_msg.e2_minus); - let alpha = transcript.challenge_scalar(b"alpha"); + let alpha = transcript.squeeze_scalar(); prover_state.apply_second_challenge::(&alpha); - second_messages.push(second_msg); } - let gamma = transcript.challenge_scalar(b"gamma"); + let gamma = transcript.squeeze_scalar(); #[cfg(feature = "zk")] - let scalar_product_proof = if Mo::BLINDING { - Some(prover_state.scalar_product_proof(transcript)) - } else { - None - }; + if Mo::BLINDING { + prover_state.scalar_product_proof(transcript); + } let final_message = prover_state.compute_final_message::(&gamma); - transcript.append_serde(b"final_e1", &final_message.e1); - transcript.append_serde(b"final_e2", &final_message.e2); - let _d = transcript.challenge_scalar(b"d"); - - let proof = DoryProof { - vmv_message, - first_messages, - second_messages, - final_message, - nu, - sigma, - #[cfg(feature = "zk")] - e2: zk_e2, - #[cfg(feature = "zk")] - y_com: zk_y_com, - #[cfg(feature = "zk")] - sigma1_proof: zk_sigma1, - #[cfg(feature = "zk")] - sigma2_proof: zk_sigma2, - #[cfg(feature = "zk")] - scalar_product_proof, - }; + transcript.absorb_g1(&final_message.e1); + transcript.absorb_g2(&final_message.e2); + let _d = transcript.squeeze_scalar(); + #[cfg(feature = "zk")] - return Ok((proof, zk_r_y)); + return Ok(zk_r_y); #[cfg(not(feature = "zk"))] - Ok((proof, None)) + Ok(None) } /// Verify an evaluation proof @@ -266,39 +258,38 @@ where /// Verifies that a committed polynomial evaluates to the claimed value at the given point. /// Works with both square and non-square matrix layouts (nu ≤ sigma). /// +/// Proof elements are read from the NARG transcript — no separate proof struct is needed. +/// /// # Algorithm -/// 1. Extract VMV message from proof -/// 2. Compute e2 = Γ2,fin * evaluation (or use proof.e2 in ZK mode) +/// 1. Read VMV message from transcript +/// 2. Compute e2 = Γ2,fin * evaluation (or read from transcript in ZK mode) /// 3. Initialize verifier state with commitment and VMV message -/// 4. Run max(nu, sigma) rounds of reduce-and-fold verification (with automatic padding) +/// 4. Read reduce messages from transcript for max(nu, sigma) rounds /// 5. Derive gamma and d challenges /// 6. Verify final scalar product message /// /// # Parameters /// - `commitment`: Polynomial commitment (in GT) - can be a homomorphically combined commitment /// - `evaluation`: Claimed evaluation result -/// - `point`: Evaluation point (length must equal proof.nu + proof.sigma) -/// - `proof`: Evaluation proof to verify (contains nu and sigma dimensions) +/// - `point`: Evaluation point (length must equal nu + sigma) +/// - `nu`: Log₂ of number of rows +/// - `sigma`: Log₂ of number of columns /// - `setup`: Verifier setup -/// - `transcript`: Fiat-Shamir transcript for challenge generation +/// - `transcript`: Fiat-Shamir verifier transcript (reads from NARG string) /// /// # Returns /// `Ok(())` if proof is valid, `Err(DoryError)` otherwise /// -/// # Homomorphic Verification -/// This function can verify proofs for homomorphically combined polynomials. -/// The commitment parameter should be the combined commitment, and the evaluation -/// should be the evaluation of the combined polynomial. -/// /// # Errors /// Returns `DoryError::InvalidProof` if verification fails, or other variants /// if the input parameters are incorrect (e.g., point dimension mismatch). #[tracing::instrument(skip_all, name = "verify_evaluation_proof")] -pub fn verify_evaluation_proof( +pub fn verify_evaluation_proof( commitment: E::GT, evaluation: F, point: &[F], - proof: &DoryProof, + nu: usize, + sigma: usize, setup: VerifierSetup, transcript: &mut T, ) -> Result<(), DoryError> @@ -310,11 +301,9 @@ where E::GT: Group, M1: DoryRoutines, M2: DoryRoutines, - T: Transcript, + T: crate::primitives::transcript::VerifierTranscript, + Mo: Mode, { - let nu = proof.nu; - let sigma = proof.sigma; - if point.len() != nu + sigma { return Err(DoryError::InvalidPointDimension { expected: nu + sigma, @@ -322,129 +311,134 @@ where }); } - let vmv_message = &proof.vmv_message; - transcript.append_serde(b"vmv_c", &vmv_message.c); - transcript.append_serde(b"vmv_d2", &vmv_message.d2); - transcript.append_serde(b"vmv_e1", &vmv_message.e1); + // Public inputs: bind nu and sigma to the sponge state + transcript.public_u32(nu as u32)?; + transcript.public_u32(sigma as u32)?; + + // External inputs: bind commitment, evaluation, and point to the sponge state. + // These are public messages (not in the NARG string) — both sides absorb independently. + transcript.public_gt(&commitment)?; + transcript.public_field(&evaluation)?; + for coord in point { + transcript.public_field(coord)?; + } + + // Read VMV message from transcript + let vmv_c = transcript.read_gt()?; + let vmv_d2 = transcript.read_gt()?; + let vmv_e1 = transcript.read_g1()?; #[cfg(feature = "zk")] - let (e2, is_zk) = match (&proof.e2, &proof.y_com) { - (Some(pe2), Some(yc)) => { - use crate::reduce_and_fold::{verify_sigma1_proof, verify_sigma2_proof}; - transcript.append_serde(b"vmv_e2", pe2); - transcript.append_serde(b"vmv_y_com", yc); - match (&proof.sigma1_proof, &proof.sigma2_proof) { - (Some(s1), Some(s2)) => { - verify_sigma1_proof::(pe2, yc, s1, &setup, transcript)?; - verify_sigma2_proof::( - &vmv_message.e1, - &vmv_message.d2, - s2, - &setup, - transcript, - )?; - } - _ => return Err(DoryError::InvalidProof), - } - (*pe2, true) - } - (None, None) => (setup.g2_0.scale(&evaluation), false), - _ => return Err(DoryError::InvalidProof), + let e2 = if Mo::BLINDING { + use crate::reduce_and_fold::{verify_sigma1_proof, verify_sigma2_proof}; + let pe2 = transcript.read_g2()?; + let yc = transcript.read_g1()?; + verify_sigma1_proof::(&pe2, &yc, &setup, transcript)?; + verify_sigma2_proof::(&vmv_e1, &vmv_d2, &setup, transcript)?; + pe2 + } else { + setup.g2_0.scale(&evaluation) }; #[cfg(not(feature = "zk"))] - let (e2, _is_zk) = (setup.g2_0.scale(&evaluation), false); + let e2 = setup.g2_0.scale(&evaluation); - // Folded-scalar accumulation with per-round coordinates. // num_rounds = sigma (we fold column dimensions). let num_rounds = sigma; - // Bounds check: reject proofs with mismatched message counts or that exceed setup capacity. + // Bounds check: reject proofs that exceed setup capacity. let max_rounds = setup.max_log_n / 2; - if num_rounds > max_rounds - || proof.first_messages.len() != num_rounds - || proof.second_messages.len() != num_rounds - { + if num_rounds > max_rounds { return Err(DoryError::InvalidProof); } // s1 (right/prover): the σ column coordinates in natural order (LSB→MSB). - // No padding here: the verifier folds across the σ column dimensions. - // With MSB-first folding, these coordinates are only consumed after the first σ−ν rounds, - // which correspond to the padded MSB dimensions on the left tensor, matching the prover. let s1_coords: Vec = point[..sigma].to_vec(); - // s2 (left/prover): the ν row coordinates in natural order, followed by zeros for the extra - // MSB dimensions. Conceptually this is s ⊗ [1,0]^(σ−ν): under MSB-first folds, the first - // σ−ν rounds multiply s2 by α⁻¹ while contributing no right halves (since those entries are 0). + // s2 (left/prover): the ν row coordinates in natural order, followed by zeros. let mut s2_coords: Vec = vec![F::zero(); sigma]; s2_coords[..nu].copy_from_slice(&point[sigma..sigma + nu]); let mut verifier_state = DoryVerifierState::new( - vmv_message.c, // c from VMV message - commitment, // d1 = commitment - vmv_message.d2, // d2 from VMV message - vmv_message.e1, // e1 from VMV message - e2, // e2 computed from evaluation - s1_coords, // s1: columns c0..c_{σ−1} (LSB→MSB), no padding; folded across σ dims - s2_coords, // s2: rows r0..r_{ν−1} then zeros in MSB dims (emulates s ⊗ [1,0]^(σ−ν)) + vmv_c, // c from VMV message + commitment, // d1 = commitment + vmv_d2, // d2 from VMV message + vmv_e1, // e1 from VMV message + e2, // e2 computed from evaluation or read from transcript + s1_coords, + s2_coords, num_rounds, setup.clone(), ); - for round in 0..num_rounds { - let first_msg = &proof.first_messages[round]; - let second_msg = &proof.second_messages[round]; - - transcript.append_serde(b"d1_left", &first_msg.d1_left); - transcript.append_serde(b"d1_right", &first_msg.d1_right); - transcript.append_serde(b"d2_left", &first_msg.d2_left); - transcript.append_serde(b"d2_right", &first_msg.d2_right); - transcript.append_serde(b"e1_beta", &first_msg.e1_beta); - transcript.append_serde(b"e2_beta", &first_msg.e2_beta); - let beta = transcript.challenge_scalar(b"beta"); - - transcript.append_serde(b"c_plus", &second_msg.c_plus); - transcript.append_serde(b"c_minus", &second_msg.c_minus); - transcript.append_serde(b"e1_plus", &second_msg.e1_plus); - transcript.append_serde(b"e1_minus", &second_msg.e1_minus); - transcript.append_serde(b"e2_plus", &second_msg.e2_plus); - transcript.append_serde(b"e2_minus", &second_msg.e2_minus); - let alpha = transcript.challenge_scalar(b"alpha"); - - verifier_state.process_round(first_msg, second_msg, &alpha, &beta)?; + for _round in 0..num_rounds { + // Read first reduce message from transcript + let first_msg = crate::messages::FirstReduceMessage { + d1_left: transcript.read_gt()?, + d1_right: transcript.read_gt()?, + d2_left: transcript.read_gt()?, + d2_right: transcript.read_gt()?, + e1_beta: transcript.read_g1()?, + e2_beta: transcript.read_g2()?, + }; + let beta = transcript.squeeze_scalar()?; + + // Read second reduce message from transcript + let second_msg = crate::messages::SecondReduceMessage { + c_plus: transcript.read_gt()?, + c_minus: transcript.read_gt()?, + e1_plus: transcript.read_g1()?, + e1_minus: transcript.read_g1()?, + e2_plus: transcript.read_g2()?, + e2_minus: transcript.read_g2()?, + }; + let alpha = transcript.squeeze_scalar()?; + + verifier_state.process_round(&first_msg, &second_msg, &alpha, &beta)?; } - let gamma = transcript.challenge_scalar(b"gamma"); + let gamma = transcript.squeeze_scalar()?; - // In ZK mode: absorb scalar product proof into transcript before deriving d. + // In ZK mode: read scalar product proof from transcript before deriving d. #[cfg(feature = "zk")] - let zk_data = if is_zk { - if let Some(ref sp) = proof.scalar_product_proof { - for (l, v) in [ - (b"sigma_p1" as &[u8], &sp.p1), - (b"sigma_p2", &sp.p2), - (b"sigma_q", &sp.q), - (b"sigma_r", &sp.r), - ] { - transcript.append_serde(l, v); - } - let c = transcript.challenge_scalar(b"sigma_c"); - Some((sp, c)) - } else { - return Err(DoryError::InvalidProof); - } + let zk_data = if Mo::BLINDING { + let p1 = transcript.read_gt()?; + let p2 = transcript.read_gt()?; + let q = transcript.read_gt()?; + let r = transcript.read_gt()?; + let sigma_c = transcript.squeeze_scalar()?; + let sp_e1 = transcript.read_g1()?; + let sp_e2 = transcript.read_g2()?; + let r1 = transcript.read_field()?; + let r2 = transcript.read_field()?; + let r3 = transcript.read_field()?; + let sp = crate::messages::ScalarProductProof { + p1, + p2, + q, + r, + e1: sp_e1, + e2: sp_e2, + r1, + r2, + r3, + }; + Some((sp, sigma_c)) } else { None }; - // Shared: absorb final message and derive d. - transcript.append_serde(b"final_e1", &proof.final_message.e1); - transcript.append_serde(b"final_e2", &proof.final_message.e2); - let d = transcript.challenge_scalar(b"d"); + // Read final message from transcript and derive d. + let final_e1 = transcript.read_g1()?; + let final_e2 = transcript.read_g2()?; + let final_message = crate::messages::ScalarProductMessage { + e1: final_e1, + e2: final_e2, + }; + let d = transcript.squeeze_scalar()?; #[cfg(feature = "zk")] - let zk = zk_data.as_ref().map(|(sp, c)| (*sp, c)); + let zk = zk_data.as_ref().map(|(sp, c)| (sp, c)); #[cfg(not(feature = "zk"))] let zk: Option<(&crate::messages::ScalarProductProof<_, _, _, _>, _)> = None; - verifier_state.verify_final(&proof.final_message, &gamma, &d, zk) + verifier_state.verify_final(&final_message, &gamma, &d, zk) } diff --git a/src/lib.rs b/src/lib.rs index 7f2e8f5..aa52dcf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,6 @@ //! - [`evaluation_proof`] - Evaluation proof creation and verification //! - [`reduce_and_fold`] - Inner product protocol state machines (prover/verifier) //! - [`messages`] - Protocol message structures (VMV, reduce rounds, scalar product) -//! - [`proof`] - Complete proof data structure //! - [`error`] - Error types //! //! ### Backend Implementations @@ -41,7 +40,10 @@ //! //! ```ignore //! use dory_pcs::{setup, prove, verify, Transparent}; -//! use dory_pcs::backends::arkworks::{BN254, G1Routines, G2Routines, Blake2bTranscript}; +//! use dory_pcs::backends::arkworks::{ +//! BN254, G1Routines, G2Routines, +//! dory_prover, dory_verifier, +//! }; //! //! // 1. Generate setup (automatically loads from/saves to disk) //! let (prover_setup, verifier_setup) = setup::(max_log_n); @@ -50,19 +52,22 @@ //! let (tier_2_commitment, tier_1_commitments, commit_blind) = polynomial //! .commit::(nu, sigma, &prover_setup)?; //! -//! // 3. Generate evaluation proof -//! let mut prover_transcript = Blake2bTranscript::new(b"domain-separation"); -//! let proof = prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( -//! &polynomial, &point, tier_1_commitments, commit_blind, nu, sigma, -//! &prover_setup, &mut prover_transcript +//! // 3. Create prover and generate evaluation proof +//! let mut prover = dory_prover(nu, sigma, false); +//! prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( +//! &polynomial, &point, &tier_2_commitment, &evaluation, +//! tier_1_commitments, commit_blind, nu, sigma, +//! &prover_setup, &mut prover //! )?; +//! let proof_bytes = prover.narg_string().to_vec(); //! -//! // 4. Verify -//! let mut verifier_transcript = Blake2bTranscript::new(b"domain-separation"); -//! verify::<_, BN254, G1Routines, G2Routines, _>( -//! tier_2_commitment, evaluation, &point, &proof, -//! verifier_setup, &mut verifier_transcript +//! // 4. Verify (no trailing bytes allowed) +//! let mut verifier = dory_verifier(nu, sigma, false, &proof_bytes); +//! verify::<_, BN254, G1Routines, G2Routines, _, Transparent>( +//! tier_2_commitment, evaluation, &point, nu, sigma, +//! verifier_setup, &mut verifier //! )?; +//! verifier.check_eof()?; //! ``` //! //! ### Performance Optimization @@ -99,7 +104,6 @@ pub mod evaluation_proof; pub mod messages; pub mod mode; pub mod primitives; -pub mod proof; pub mod reduce_and_fold; pub mod setup; @@ -108,18 +112,14 @@ pub mod backends; pub use error::DoryError; pub use evaluation_proof::create_evaluation_proof; -pub use messages::{ - FirstReduceMessage, ScalarProductMessage, ScalarProductProof, SecondReduceMessage, VMVMessage, -}; -#[cfg(feature = "zk")] -pub use messages::{Sigma1Proof, Sigma2Proof}; +pub use messages::{FirstReduceMessage, ScalarProductMessage, SecondReduceMessage}; #[cfg(feature = "zk")] pub use mode::ZK; pub use mode::{Mode, Transparent}; use primitives::arithmetic::{DoryRoutines, Field, Group, PairingCurve}; pub use primitives::poly::{MultilinearLagrange, Polynomial}; use primitives::serialization::{DoryDeserialize, DorySerialize}; -pub use proof::DoryProof; +pub use primitives::transcript::{ProverTranscript, VerifierTranscript}; pub use reduce_and_fold::{DoryProverState, DoryVerifierState}; pub use setup::{ProverSetup, VerifierSetup}; @@ -230,54 +230,49 @@ where /// Evaluate a polynomial at a point and create proof /// /// Creates an evaluation proof for a polynomial at a given point using precomputed -/// tier-1 commitments (row commitments). +/// tier-1 commitments (row commitments). All proof elements are absorbed into +/// the transcript — the transcript's NARG string IS the proof. /// /// # Workflow /// 1. Call `polynomial.commit(nu, sigma, setup)` to get `(tier_2, row_commitments, commit_blind)` -/// 2. Call this function with the `row_commitments` and `commit_blind` to create the proof -/// 3. Use `tier_2` for verification via the `verify()` function +/// 2. Create a prover via `dory_prover(nu, sigma, zk)` +/// 3. Call this function — proof data is written to the transcript +/// 4. Extract proof bytes via `prover.narg_string().to_vec()` /// /// # Parameters /// - `polynomial`: Polynomial implementing MultilinearLagrange trait /// - `point`: Evaluation point (length must equal nu + sigma) +/// - `commitment`: Tier-2 commitment (GT element) — absorbed as public message for Fiat-Shamir binding +/// - `evaluation`: Claimed evaluation result — absorbed as public message for Fiat-Shamir binding /// - `row_commitments`: Tier-1 commitments (row commitments in G1) from `polynomial.commit()` /// - `commit_blind`: GT-level blinding scalar from `polynomial.commit()` (zero in Transparent mode) /// - `nu`: Log₂ of number of rows (constraint: nu ≤ sigma for non-square matrices) /// - `sigma`: Log₂ of number of columns /// - `setup`: Prover setup -/// - `transcript`: Fiat-Shamir transcript +/// - `transcript`: Fiat-Shamir prover transcript /// /// # Returns -/// The evaluation proof containing VMV message, reduce messages, and final message -/// -/// # Homomorphic Properties -/// Proofs can be created for homomorphically combined polynomials. If you have -/// commitments Com(P₁), Com(P₂), ..., Com(Pₙ) and want to prove evaluation of -/// r₁·P₁ + r₂·P₂ + ... + rₙ·Pₙ, you can: -/// 1. Combine tier-2 commitments: r₁·Com(P₁) + r₂·Com(P₂) + ... + rₙ·Com(Pₙ) -/// 2. Combine tier-1 commitments element-wise -/// 3. Generate proof using this function with the combined polynomial -/// -/// See `examples/homomorphic.rs` for a complete demonstration. +/// In ZK mode, returns the blinding scalar `r_y`; in transparent mode, `None`. /// /// # Errors /// Returns `DoryError` if: /// - Point dimension doesn't match nu + sigma /// - Polynomial size doesn't match 2^(nu + sigma) /// - Number of row commitments doesn't match 2^nu -#[allow(clippy::type_complexity)] #[allow(clippy::too_many_arguments)] #[tracing::instrument(skip_all, name = "prove")] pub fn prove( polynomial: &P, point: &[F], + commitment: &E::GT, + evaluation: &F, row_commitments: Vec, commit_blind: F, nu: usize, sigma: usize, setup: &ProverSetup, transcript: &mut T, -) -> Result<(DoryProof, Option), DoryError> +) -> Result, DoryError> where F: Field, E: PairingCurve, @@ -287,12 +282,14 @@ where M1: DoryRoutines, M2: DoryRoutines, P: MultilinearLagrange, - T: primitives::transcript::Transcript, + T: ProverTranscript, Mo: Mode, { evaluation_proof::create_evaluation_proof::( polynomial, point, + commitment, + evaluation, Some(row_commitments), commit_blind, nu, @@ -305,18 +302,21 @@ where /// Verify an evaluation proof /// /// Verifies that a committed polynomial evaluates to the claimed value at the given point. -/// The matrix dimensions (nu, sigma) are extracted from the proof. +/// Proof elements are read from the NARG transcript — no separate proof struct is needed. /// -/// Works with both square and non-square matrix layouts (nu ≤ sigma), and can verify -/// proofs for homomorphically combined polynomials. +/// # Workflow +/// 1. Create a verifier via `dory_verifier(nu, sigma, zk, &proof_bytes)` +/// 2. Call this function — proof data is read from the transcript +/// 3. Call `verifier.check_eof()?` to reject trailing bytes /// /// # Parameters /// - `commitment`: Polynomial commitment (in GT) - can be a combined commitment for homomorphic proofs /// - `evaluation`: Claimed evaluation result -/// - `point`: Evaluation point (length must equal proof.nu + proof.sigma) -/// - `proof`: Evaluation proof to verify (contains nu and sigma) +/// - `point`: Evaluation point (length must equal nu + sigma) +/// - `nu`: Log₂ of number of rows +/// - `sigma`: Log₂ of number of columns /// - `setup`: Verifier setup -/// - `transcript`: Fiat-Shamir transcript +/// - `transcript`: Fiat-Shamir verifier transcript (reads from NARG string) /// /// # Returns /// `Ok(())` if proof is valid, `Err(DoryError)` otherwise @@ -325,11 +325,12 @@ where /// Returns `DoryError::InvalidProof` if the proof is invalid, or other variants /// if the input parameters are incorrect (e.g., point dimension mismatch). #[tracing::instrument(skip_all, name = "verify")] -pub fn verify( +pub fn verify( commitment: E::GT, evaluation: F, point: &[F], - proof: &DoryProof, + nu: usize, + sigma: usize, setup: VerifierSetup, transcript: &mut T, ) -> Result<(), DoryError> @@ -341,9 +342,10 @@ where E::GT: Group, M1: DoryRoutines, M2: DoryRoutines, - T: primitives::transcript::Transcript, + T: VerifierTranscript, + Mo: Mode, { - evaluation_proof::verify_evaluation_proof::( - commitment, evaluation, point, proof, setup, transcript, + evaluation_proof::verify_evaluation_proof::( + commitment, evaluation, point, nu, sigma, setup, transcript, ) } diff --git a/src/messages.rs b/src/messages.rs index ddd1faa..4533208 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -41,19 +41,6 @@ pub struct SecondReduceMessage { pub e2_minus: G2, } -/// Vector-Matrix-Vector message for polynomial commitment transformation -/// -/// Contains C, D₂, E₁. Note: E₂ can be computed by verifier as y·Γ₂,fin -#[derive(Clone, Debug, PartialEq)] -pub struct VMVMessage { - /// C = e(MSM(T_vec', v_vec), Γ₂,fin) - pub c: GT, - /// D₂ = e(MSM(Γ₁\[nu\], v_vec), Γ₂,fin) - pub d2: GT, - /// E₁ = MSM(T_vec', L_vec) - pub e1: G1, -} - /// Final scalar product message (Section 3.1) /// /// Contains E₁, E₂ for the final pairing verification @@ -65,28 +52,6 @@ pub struct ScalarProductMessage { pub e2: G2, } -/// Σ-protocol 1: proves E2 and y_com commit to the same y. -#[cfg(feature = "zk")] -#[derive(Clone, Debug, PartialEq)] -#[allow(missing_docs)] -pub struct Sigma1Proof { - pub a1: G2, - pub a2: G1, - pub z1: F, - pub z2: F, - pub z3: F, -} - -/// Σ-protocol 2: proves e(E1, Γ2,fin) - D2 = e(H1, t1·Γ2,fin + t2·H2). -#[cfg(feature = "zk")] -#[derive(Clone, Debug, PartialEq)] -#[allow(missing_docs)] -pub struct Sigma2Proof { - pub a: GT, - pub z1: F, - pub z2: F, -} - /// ZK scalar product proof: proves (C, D1, D2) are consistent with blinded v1, v2. #[derive(Clone, Debug, PartialEq)] #[allow(missing_docs)] diff --git a/src/primitives/transcript.rs b/src/primitives/transcript.rs index d745d5f..f3a4bd2 100644 --- a/src/primitives/transcript.rs +++ b/src/primitives/transcript.rs @@ -1,29 +1,92 @@ -//! Transcript trait for Fiat-Shamir transformations - -#![allow(missing_docs)] +//! Fiat-Shamir transcript traits for Dory's evaluation proof protocol. +//! +//! These traits abstract the prover and verifier sides of the Fiat-Shamir +//! transform. With spongefish, proof data is serialized into a NARG string +//! (`Vec`) by the prover and deserialized by the verifier. +use crate::error::DoryError; use crate::primitives::arithmetic::{Group, PairingCurve}; -use crate::primitives::DorySerialize; -/// Transcript to standardize fiat shamir across different transcript implementations -pub trait Transcript { - type Curve: PairingCurve; - fn append_bytes(&mut self, label: &[u8], bytes: &[u8]); +/// Prover-side Fiat-Shamir transcript. +/// +/// Absorbs proof elements into the sponge and serializes them into the +/// NARG string. Challenges are squeezed deterministically. +pub trait ProverTranscript { + /// Absorb public data (not in NARG string, but bound to the sponge state). + fn public_u32(&mut self, val: u32); + + /// Absorb a public GT element (not in NARG string, but bound to the sponge state). + fn public_gt(&mut self, val: &E::GT); + + /// Absorb a public scalar field element (not in NARG string, but bound to the sponge state). + fn public_field(&mut self, val: &::Scalar); + + /// Absorb a GT element (goes into the NARG string). + fn absorb_gt(&mut self, val: &E::GT); + + /// Absorb a G1 element (goes into the NARG string). + fn absorb_g1(&mut self, val: &E::G1); + + /// Absorb a G2 element (goes into the NARG string). + fn absorb_g2(&mut self, val: &E::G2); + + /// Absorb a scalar field element (goes into the NARG string). + fn absorb_field(&mut self, val: &::Scalar); + + /// Squeeze a scalar challenge from the sponge. + fn squeeze_scalar(&mut self) -> ::Scalar; +} + +/// Verifier-side Fiat-Shamir transcript. +/// +/// Reads proof elements from the NARG string and absorbs them into +/// the sponge. Challenges are squeezed deterministically. +pub trait VerifierTranscript { + /// Absorb public data (must match the prover's `public_u32` calls). + /// + /// # Errors + /// Returns `DoryError` if the sponge state is inconsistent. + fn public_u32(&mut self, val: u32) -> Result<(), DoryError>; + + /// Absorb a public GT element (must match the prover's `public_gt` calls). + /// + /// # Errors + /// Returns `DoryError` if the sponge state is inconsistent. + fn public_gt(&mut self, val: &E::GT) -> Result<(), DoryError>; + + /// Absorb a public scalar field element (must match the prover's `public_field` calls). + /// + /// # Errors + /// Returns `DoryError` if the sponge state is inconsistent. + fn public_field(&mut self, val: &::Scalar) -> Result<(), DoryError>; - fn append_field( - &mut self, - label: &[u8], - x: &<::G1 as Group>::Scalar, - ); + /// Read a GT element from the NARG string. + /// + /// # Errors + /// Returns `DoryError` if deserialization or sponge absorption fails. + fn read_gt(&mut self) -> Result; - fn append_group(&mut self, label: &[u8], g: &G); + /// Read a G1 element from the NARG string. + /// + /// # Errors + /// Returns `DoryError` if deserialization or sponge absorption fails. + fn read_g1(&mut self) -> Result; - fn append_serde(&mut self, label: &[u8], s: &S); + /// Read a G2 element from the NARG string. + /// + /// # Errors + /// Returns `DoryError` if deserialization or sponge absorption fails. + fn read_g2(&mut self) -> Result; - fn challenge_scalar( - &mut self, - label: &[u8], - ) -> <::G1 as Group>::Scalar; + /// Read a scalar field element from the NARG string. + /// + /// # Errors + /// Returns `DoryError` if deserialization or sponge absorption fails. + fn read_field(&mut self) -> Result<::Scalar, DoryError>; - fn reset(&mut self, domain_label: &[u8]); + /// Squeeze a scalar challenge from the sponge. + /// + /// # Errors + /// Returns `DoryError` if the sponge squeeze fails. + fn squeeze_scalar(&mut self) -> Result<::Scalar, DoryError>; } diff --git a/src/proof.rs b/src/proof.rs deleted file mode 100644 index 62b339f..0000000 --- a/src/proof.rs +++ /dev/null @@ -1,55 +0,0 @@ -//! Dory proof structure -//! -//! A Dory proof consists of: -//! - VMV message (PCS transform) -//! - Multiple rounds of reduce messages (log n rounds) -//! - Final scalar product message - -use crate::messages::*; -use crate::primitives::arithmetic::Group; - -/// A complete Dory evaluation proof -/// -/// The proof demonstrates that a committed polynomial evaluates to a specific value -/// at a given point. It consists of messages from the interactive protocol made -/// non-interactive via Fiat-Shamir. -/// -/// The proof includes the matrix dimensions (nu, sigma) used during proof generation, -/// which the verifier uses to ensure consistency with the evaluation point. -#[derive(Clone, Debug, PartialEq)] -#[allow(missing_docs)] -pub struct DoryProof { - /// Vector-Matrix-Vector message for PCS transformation - pub vmv_message: VMVMessage, - - /// First reduce messages for each round (nu rounds total) - pub first_messages: Vec>, - - /// Second reduce messages for each round (nu rounds total) - pub second_messages: Vec>, - - /// Final scalar product message - pub final_message: ScalarProductMessage, - - /// Log₂ of number of rows in the coefficient matrix - pub nu: usize, - - /// Log₂ of number of columns in the coefficient matrix - pub sigma: usize, - - /// Blinded E₂ element for zero-knowledge proofs - #[cfg(feature = "zk")] - pub e2: Option, - /// Pedersen commitment to the blinding vector y - #[cfg(feature = "zk")] - pub y_com: Option, - /// Σ₁ proof: E₂ and y_com commit to the same y - #[cfg(feature = "zk")] - pub sigma1_proof: Option>, - /// Σ₂ proof: consistency of E₁ with D₂ - #[cfg(feature = "zk")] - pub sigma2_proof: Option>, - /// ZK scalar product proof: (C, D₁, D₂) consistency with blinded vectors - #[cfg(feature = "zk")] - pub scalar_product_proof: Option>, -} diff --git a/src/reduce_and_fold.rs b/src/reduce_and_fold.rs index e9b0fcf..220410e 100644 --- a/src/reduce_and_fold.rs +++ b/src/reduce_and_fold.rs @@ -16,7 +16,7 @@ use crate::setup::{ProverSetup, VerifierSetup}; use std::marker::PhantomData; #[cfg(feature = "zk")] -use crate::primitives::transcript::Transcript; +use crate::primitives::transcript::{ProverTranscript, VerifierTranscript}; type Scalar = <::G1 as Group>::Scalar; @@ -398,10 +398,7 @@ where /// Generate ZK scalar product proof. Must be called BEFORE `compute_final_message`. #[cfg(feature = "zk")] - pub fn scalar_product_proof>( - &self, - transcript: &mut T, - ) -> ScalarProductProof, E::GT> { + pub fn scalar_product_proof>(&self, transcript: &mut T) { let (v1, v2) = (self.v1[0], self.v2[0]); let (g1, g2) = (self.setup.g1_vec[0], self.setup.g2_vec[0]); let ht = &self.setup.ht; @@ -413,26 +410,24 @@ where let p2 = E::pair(&g1, &d2) + ht.scale(&rp2); let q = E::pair(&d1, &v2) + E::pair(&v1, &d2) + ht.scale(&rq); let rr_val = E::pair(&d1, &d2) + ht.scale(&rr); - for (label, val) in [ - (b"sigma_p1" as &[u8], &p1), - (b"sigma_p2", &p2), - (b"sigma_q", &q), - (b"sigma_r", &rr_val), - ] { - transcript.append_serde(label, val); - } - let c = transcript.challenge_scalar(b"sigma_c"); - ScalarProductProof { - p1, - p2, - q, - r: rr_val, - e1: d1 + c * v1, - e2: d2 + v2.scale(&c), - r1: rp1 + c * self.r_d1, - r2: rp2 + c * self.r_d2, - r3: rr + c * rq + c * c * self.r_c, - } + + transcript.absorb_gt(&p1); + transcript.absorb_gt(&p2); + transcript.absorb_gt(&q); + transcript.absorb_gt(&rr_val); + let c = transcript.squeeze_scalar(); + + let resp_e1 = d1 + c * v1; + let resp_e2 = d2 + v2.scale(&c); + let resp_r1 = rp1 + c * self.r_d1; + let resp_r2 = rp2 + c * self.r_d2; + let resp_r3 = rr + c * rq + c * c * self.r_c; + + transcript.absorb_g1(&resp_e1); + transcript.absorb_g2(&resp_e2); + transcript.absorb_field(&resp_r1); + transcript.absorb_field(&resp_r2); + transcript.absorb_field(&resp_r3); } } @@ -444,10 +439,9 @@ pub fn generate_sigma1_proof( r_y: &Scalar, setup: &ProverSetup, transcript: &mut T, -) -> Sigma1Proof> -where +) where E: PairingCurve, - T: Transcript, + T: ProverTranscript, Scalar: Field, E::G2: Group>, { @@ -459,24 +453,22 @@ where ); let a1 = g2_fin.scale(&k1) + setup.h2.scale(&k2); let a2 = k1 * g1_fin + k3 * setup.h1; - transcript.append_serde(b"sigma1_a1", &a1); - transcript.append_serde(b"sigma1_a2", &a2); - let c = transcript.challenge_scalar(b"sigma1_c"); - Sigma1Proof { - a1, - a2, - z1: k1 + c * y, - z2: k2 + c * r_e2, - z3: k3 + c * r_y, - } + transcript.absorb_g2(&a1); + transcript.absorb_g1(&a2); + let c = transcript.squeeze_scalar(); + let z1 = k1 + c * *y; + let z2 = k2 + c * *r_e2; + let z3 = k3 + c * *r_y; + transcript.absorb_field(&z1); + transcript.absorb_field(&z2); + transcript.absorb_field(&z3); } -/// Verify Sigma1 proof. +/// Verify Sigma1 proof by reading elements from the NARG transcript. #[cfg(feature = "zk")] -pub fn verify_sigma1_proof>( +pub fn verify_sigma1_proof>( e2: &E::G2, y_commit: &E::G1, - proof: &Sigma1Proof>, setup: &VerifierSetup, transcript: &mut T, ) -> Result<(), DoryError> @@ -484,13 +476,16 @@ where Scalar: Field, E::G2: Group>, { - transcript.append_serde(b"sigma1_a1", &proof.a1); - transcript.append_serde(b"sigma1_a2", &proof.a2); - let c = transcript.challenge_scalar(b"sigma1_c"); - if setup.g2_0.scale(&proof.z1) + setup.h2.scale(&proof.z2) != proof.a1 + e2.scale(&c) { + let a1 = transcript.read_g2()?; + let a2 = transcript.read_g1()?; + let c = transcript.squeeze_scalar()?; + let z1 = transcript.read_field()?; + let z2 = transcript.read_field()?; + let z3 = transcript.read_field()?; + if setup.g2_0.scale(&z1) + setup.h2.scale(&z2) != a1 + e2.scale(&c) { return Err(DoryError::InvalidProof); } - if proof.z1 * setup.g1_0 + proof.z3 * setup.h1 != proof.a2 + c * y_commit { + if z1 * setup.g1_0 + z3 * setup.h1 != a2 + c * *y_commit { return Err(DoryError::InvalidProof); } Ok(()) @@ -503,10 +498,9 @@ pub fn generate_sigma2_proof( t2: &Scalar, setup: &ProverSetup, transcript: &mut T, -) -> Sigma2Proof, E::GT> -where +) where E: PairingCurve, - T: Transcript, + T: ProverTranscript, Scalar: Field, E::G2: Group>, E::GT: Group>, @@ -516,21 +510,19 @@ where &setup.h1, &(setup.g2_vec[0].scale(&k1) + setup.h2.scale(&k2)), ); - transcript.append_serde(b"sigma2_a", &a); - let c = transcript.challenge_scalar(b"sigma2_c"); - Sigma2Proof { - a, - z1: k1 + c * t1, - z2: k2 + c * t2, - } + transcript.absorb_gt(&a); + let c = transcript.squeeze_scalar(); + let z1 = k1 + c * *t1; + let z2 = k2 + c * *t2; + transcript.absorb_field(&z1); + transcript.absorb_field(&z2); } -/// Verify Sigma2 proof. +/// Verify Sigma2 proof by reading elements from the NARG transcript. #[cfg(feature = "zk")] -pub fn verify_sigma2_proof>( +pub fn verify_sigma2_proof>( e1: &E::G1, d2: &E::GT, - proof: &Sigma2Proof, E::GT>, setup: &VerifierSetup, transcript: &mut T, ) -> Result<(), DoryError> @@ -539,14 +531,13 @@ where E::G2: Group>, E::GT: Group>, { - transcript.append_serde(b"sigma2_a", &proof.a); - let c = transcript.challenge_scalar(b"sigma2_c"); + let a = transcript.read_gt()?; + let c = transcript.squeeze_scalar()?; + let z1 = transcript.read_field()?; + let z2 = transcript.read_field()?; let expected = E::pair(e1, &setup.g2_0) - *d2; - let lhs = E::pair( - &setup.h1, - &(setup.g2_0.scale(&proof.z1) + setup.h2.scale(&proof.z2)), - ); - if lhs == proof.a + expected.scale(&c) { + let lhs = E::pair(&setup.h1, &(setup.g2_0.scale(&z1) + setup.h2.scale(&z2))); + if lhs == a + expected.scale(&c) { Ok(()) } else { Err(DoryError::InvalidProof) diff --git a/tests/arkworks/evaluation.rs b/tests/arkworks/evaluation.rs index 0235541..eaa5a61 100644 --- a/tests/arkworks/evaluation.rs +++ b/tests/arkworks/evaluation.rs @@ -19,33 +19,37 @@ fn test_evaluation_proof_small() { .commit::(nu, sigma, &setup) .unwrap(); - let mut prover_transcript = fresh_transcript(); + let evaluation = poly.evaluate(&point); + let mut prover = test_prover(nu, sigma); let result = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &setup, - &mut prover_transcript, + &mut prover, ); assert!(result.is_ok()); - let (proof, _) = result.unwrap(); - let evaluation = poly.evaluate(&point); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let verify_result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(nu, sigma, &proof_bytes); + let verify_result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); assert!(verify_result.is_ok()); + verifier.check_eof().unwrap(); } #[test] @@ -63,33 +67,37 @@ fn test_evaluation_proof_with_precomputed_commitment() { .commit::(nu, sigma, &setup) .unwrap(); - let mut prover_transcript = fresh_transcript(); + let evaluation = poly.evaluate(&point); + let mut prover = test_prover(nu, sigma); let result = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &setup, - &mut prover_transcript, + &mut prover, ); assert!(result.is_ok()); - let (proof, _) = result.unwrap(); - let evaluation = poly.evaluate(&point); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let verify_result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(nu, sigma, &proof_bytes); + let verify_result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); assert!(verify_result.is_ok()); + verifier.check_eof().unwrap(); } #[test] @@ -110,33 +118,38 @@ fn test_evaluation_proof_constant_polynomial() { .commit::(nu, sigma, &setup) .unwrap(); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + let evaluation = poly.evaluate(&point); + assert_eq!(evaluation, ArkFr::from_u64(7)); + let mut prover = test_prover(nu, sigma); + prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); - let evaluation = poly.evaluate(&point); - assert_eq!(evaluation, ArkFr::from_u64(7)); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let verify_result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(nu, sigma, &proof_bytes); + let verify_result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); assert!(verify_result.is_ok()); + verifier.check_eof().unwrap(); } #[test] @@ -154,30 +167,34 @@ fn test_evaluation_proof_wrong_evaluation_fails() { .commit::(nu, sigma, &setup) .unwrap(); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + let evaluation = poly.evaluate(&point); + let wrong_evaluation = evaluation + ArkFr::one(); + let mut prover = test_prover(nu, sigma); + prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); - let evaluation = poly.evaluate(&point); - let wrong_evaluation = evaluation + ArkFr::one(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let verify_result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(nu, sigma, &proof_bytes); + let verify_result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, wrong_evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); assert!(verify_result.is_err()); @@ -191,35 +208,42 @@ fn test_evaluation_proof_different_sizes() { let poly = random_polynomial(4); let point = random_point(2); + let nu = 1; + let sigma = 1; let (tier_2, tier_1, commit_blind) = poly - .commit::(1, 1, &setup) + .commit::(nu, sigma, &setup) .unwrap(); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + let evaluation = poly.evaluate(&point); + let mut prover = test_prover(nu, sigma); + prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, - 1, - 1, + nu, + sigma, &setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); - let evaluation = poly.evaluate(&point); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(nu, sigma, &proof_bytes); + let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); assert!(result.is_ok()); + verifier.check_eof().unwrap(); } { @@ -228,35 +252,42 @@ fn test_evaluation_proof_different_sizes() { let poly = random_polynomial(64); let point = random_point(6); + let nu = 3; + let sigma = 3; let (tier_2, tier_1, commit_blind) = poly - .commit::(3, 3, &setup) + .commit::(nu, sigma, &setup) .unwrap(); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + let evaluation = poly.evaluate(&point); + let mut prover = test_prover(nu, sigma); + prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, - 3, - 3, + nu, + sigma, &setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); - let evaluation = poly.evaluate(&point); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(nu, sigma, &proof_bytes); + let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); assert!(result.is_ok()); + verifier.check_eof().unwrap(); } } @@ -276,30 +307,35 @@ fn test_multiple_evaluations_same_commitment() { for _ in 0..3 { let point = random_point(4); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + let evaluation = poly.evaluate(&point); + let mut prover = test_prover(nu, sigma); + prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1.clone(), commit_blind, nu, sigma, &setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); - let evaluation = poly.evaluate(&point); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(nu, sigma, &proof_bytes); + let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup.clone(), - &mut verifier_transcript, + &mut verifier, ); assert!(result.is_ok()); + verifier.check_eof().unwrap(); } } diff --git a/tests/arkworks/homomorphic.rs b/tests/arkworks/homomorphic.rs index 6a4fb2d..472f3f2 100644 --- a/tests/arkworks/homomorphic.rs +++ b/tests/arkworks/homomorphic.rs @@ -85,33 +85,38 @@ fn test_homomorphic_combination_e2e() { ); // Create evaluation proof using combined commitment - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + let mut prover = test_prover(nu, sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &combined_poly, &point, + &combined_tier2, + &evaluation, combined_tier1, ArkFr::zero(), nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(nu, sigma, &proof_bytes); + let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( combined_tier2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); assert!( result.is_ok(), "Verification should succeed for homomorphically combined commitment" ); + verifier.check_eof().unwrap(); } #[test] @@ -175,28 +180,33 @@ fn test_homomorphic_combination_small() { let point = random_point(num_vars); let evaluation = combined_poly.evaluate(&point); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + let mut prover = test_prover(nu, sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &combined_poly, &point, + &combined_tier2, + &evaluation, combined_tier1, ArkFr::zero(), nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(nu, sigma, &proof_bytes); + let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( combined_tier2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); assert!(result.is_ok()); + verifier.check_eof().unwrap(); } diff --git a/tests/arkworks/integration.rs b/tests/arkworks/integration.rs index a8e61ba..2d5e451 100644 --- a/tests/arkworks/integration.rs +++ b/tests/arkworks/integration.rs @@ -22,32 +22,37 @@ fn test_full_workflow() { let point = random_point(8); let expected_evaluation = poly.evaluate(&point); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + let evaluation = poly.evaluate(&point); + assert_eq!(evaluation, expected_evaluation); + let mut prover = test_prover(nu, sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); - let evaluation = poly.evaluate(&point); - assert_eq!(evaluation, expected_evaluation); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(nu, sigma, &proof_bytes); + let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); assert!(result.is_ok()); + verifier.check_eof().unwrap(); } #[test] @@ -65,31 +70,36 @@ fn test_workflow_without_precommitment() { .commit::(nu, sigma, &prover_setup) .unwrap(); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + let evaluation = poly.evaluate(&point); + let mut prover = test_prover(nu, sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); - let evaluation = poly.evaluate(&point); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(nu, sigma, &proof_bytes); + let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); assert!(result.is_ok()); + verifier.check_eof().unwrap(); } #[test] @@ -104,34 +114,39 @@ fn test_batched_proofs() { .commit::(nu, sigma, &prover_setup) .unwrap(); - for i in 0..5 { + for _i in 0..5 { let point = random_point(8); - let mut prover_transcript = Blake2bTranscript::new(format!("test-{i}").as_bytes()); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + let evaluation = poly.evaluate(&point); + let mut prover = test_prover(nu, sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1.clone(), commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); - let evaluation = poly.evaluate(&point); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = Blake2bTranscript::new(format!("test-{i}").as_bytes()); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(nu, sigma, &proof_bytes); + let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup.clone(), - &mut verifier_transcript, + &mut verifier, ); - assert!(result.is_ok(), "Proof {i} should verify"); + assert!(result.is_ok(), "Proof should verify"); + verifier.check_eof().unwrap(); } } @@ -160,34 +175,38 @@ fn test_linear_polynomial() { .commit::(nu, sigma, &prover_setup) .unwrap(); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + let evaluation = poly.evaluate(&point); + let expected_eval = poly.evaluate(&point); + assert_eq!(evaluation, expected_eval); + let mut prover = test_prover(nu, sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.narg_string().to_vec(); - let evaluation = poly.evaluate(&point); - let expected_eval = poly.evaluate(&point); - assert_eq!(evaluation, expected_eval); - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(nu, sigma, &proof_bytes); + let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); assert!(result.is_ok()); + verifier.check_eof().unwrap(); } #[test] @@ -204,77 +223,35 @@ fn test_zero_polynomial() { .commit::(nu, sigma, &prover_setup) .unwrap(); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + let evaluation = poly.evaluate(&point); + assert_eq!(evaluation, ArkFr::zero()); + let mut prover = test_prover(nu, sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.narg_string().to_vec(); - let evaluation = poly.evaluate(&point); - assert_eq!(evaluation, ArkFr::zero()); - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(nu, sigma, &proof_bytes); + let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, &point, - &proof, - verifier_setup, - &mut verifier_transcript, - ); - - assert!(result.is_ok()); -} - -#[test] -fn test_soundness_wrong_commitment() { - let (prover_setup, verifier_setup) = setup::(10); - - let poly1 = random_polynomial(256); - let poly2 = random_polynomial(256); - let point = random_point(8); - - let nu = 4; - let sigma = 4; - - let (commitment1, _, _commit_blind) = poly1 - .commit::(nu, sigma, &prover_setup) - .unwrap(); - - let (_, tier_1_poly2, commit_blind) = poly2 - .commit::(nu, sigma, &prover_setup) - .unwrap(); - - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( - &poly2, - &point, - tier_1_poly2, - commit_blind, nu, sigma, - &prover_setup, - &mut prover_transcript, - ) - .unwrap(); - let evaluation = poly2.evaluate(&point); - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment1, - evaluation, - &point, - &proof, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); - assert!(result.is_err()); + assert!(result.is_ok()); + verifier.check_eof().unwrap(); } diff --git a/tests/arkworks/mod.rs b/tests/arkworks/mod.rs index bb7042c..2aaa28a 100644 --- a/tests/arkworks/mod.rs +++ b/tests/arkworks/mod.rs @@ -3,10 +3,10 @@ #![allow(unreachable_pub)] use dory_pcs::backends::arkworks::{ - ArkFr, ArkworksPolynomial, Blake2bTranscript, G1Routines, G2Routines, BN254, + dory_prover, dory_verifier, ArkFr, ArkworksPolynomial, G1Routines, G2Routines, ProverState, + VerifierState, BN254, }; use dory_pcs::primitives::arithmetic::Field; -use dory_pcs::proof::DoryProof; use dory_pcs::setup::{ProverSetup, VerifierSetup}; pub mod cache; @@ -47,8 +47,26 @@ pub fn test_setup_pair(max_log_n: usize) -> (ProverSetup, VerifierSetup Blake2bTranscript { - Blake2bTranscript::new(b"dory-test") +/// Create a prover state for transparent mode testing. +pub fn test_prover(nu: usize, sigma: usize) -> ProverState { + dory_prover(nu, sigma, false) +} + +/// Create a verifier state for transparent mode testing. +pub fn test_verifier(nu: usize, sigma: usize, proof_bytes: &[u8]) -> VerifierState<'_> { + dory_verifier(nu, sigma, false, proof_bytes) +} + +/// Create a prover state for ZK mode testing. +#[cfg(feature = "zk")] +pub fn test_prover_zk(nu: usize, sigma: usize) -> ProverState { + dory_prover(nu, sigma, true) +} + +/// Create a verifier state for ZK mode testing. +#[cfg(feature = "zk")] +pub fn test_verifier_zk(nu: usize, sigma: usize, proof_bytes: &[u8]) -> VerifierState<'_> { + dory_verifier(nu, sigma, true, proof_bytes) } pub type TestG1Routines = G1Routines; diff --git a/tests/arkworks/non_square.rs b/tests/arkworks/non_square.rs index 2254f6c..bd90dc7 100644 --- a/tests/arkworks/non_square.rs +++ b/tests/arkworks/non_square.rs @@ -21,32 +21,37 @@ fn test_non_square_matrix_nu_eq_sigma_minus_1() { .commit::(nu, sigma, &prover_setup) .expect("Commitment should succeed"); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + let evaluation = poly.evaluate(&point); + let mut prover = test_prover(nu, sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .expect("Proof generation should succeed"); - let evaluation = poly.evaluate(&point); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(nu, sigma, &proof_bytes); + let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); assert!(result.is_ok(), "Verification should succeed for nu < sigma"); + verifier.check_eof().unwrap(); } #[test] @@ -62,20 +67,23 @@ fn test_non_square_matrix_nu_greater_than_sigma_rejected() { let poly = random_polynomial(poly_size); let point = random_point(num_vars); - let (_, tier_1, commit_blind) = poly + let (tier_2, tier_1, commit_blind) = poly .commit::(nu, sigma, &prover_setup) .expect("Commitment should succeed"); - let mut prover_transcript = fresh_transcript(); + let evaluation = poly.evaluate(&point); + let mut prover = test_prover(nu, sigma); let proof_result = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ); assert!( @@ -101,35 +109,40 @@ fn test_non_square_matrix_small() { .commit::(nu, sigma, &prover_setup) .expect("Commitment should succeed"); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + let evaluation = poly.evaluate(&point); + let mut prover = test_prover(nu, sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .expect("Proof generation should succeed"); - let evaluation = poly.evaluate(&point); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(nu, sigma, &proof_bytes); + let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); assert!( result.is_ok(), "Verification should succeed for nu < sigma (small)" ); + verifier.check_eof().unwrap(); } #[test] @@ -150,33 +163,38 @@ fn test_non_square_matrix_very_rectangular() { .commit::(nu, sigma, &prover_setup) .expect("Commitment should succeed"); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + let evaluation = poly.evaluate(&point); + let mut prover = test_prover(nu, sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .expect("Proof generation should succeed"); - let evaluation = poly.evaluate(&point); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(nu, sigma, &proof_bytes); + let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); assert!( result.is_ok(), "Verification should succeed for nu << sigma (very rectangular)" ); + verifier.check_eof().unwrap(); } diff --git a/tests/arkworks/serialization.rs b/tests/arkworks/serialization.rs index adcae96..79fd4d9 100644 --- a/tests/arkworks/serialization.rs +++ b/tests/arkworks/serialization.rs @@ -1,234 +1,232 @@ -//! Proof serialization round-trip tests +//! Proof serialization tests +//! +//! With spongefish, proofs are just `Vec` NARG strings — no struct +//! serialization needed. These tests verify proof byte properties and +//! round-tripping through prove/verify. use super::*; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use dory_pcs::backends::arkworks::ArkDoryProof; use dory_pcs::primitives::poly::Polynomial; use dory_pcs::{prove, verify, Transparent}; -fn make_transparent_proof() -> ( - ArkDoryProof, - dory_pcs::backends::arkworks::ArkGT, - Vec, -) { - let (setup, verifier_setup) = test_setup_pair(4); - - let poly = random_polynomial(16); - let point = random_point(4); +/// Prove with transparent mode and return the raw proof bytes. +fn make_transparent_proof_bytes(nu: usize, sigma: usize) -> Vec { + let setup = test_setup(nu + sigma + 2); + let poly_size = 1 << (nu + sigma); + let poly = random_polynomial(poly_size); + let point = random_point(nu + sigma); let (tier_2, tier_1, commit_blind) = poly - .commit::(2, 2, &setup) + .commit::(nu, sigma, &setup) .unwrap(); - let mut transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + + let evaluation = poly.evaluate(&point); + let mut prover = test_prover(nu, sigma); + prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, - 2, - 2, + nu, + sigma, &setup, - &mut transcript, - ) - .unwrap(); - - // Sanity: verify before serialization - let eval = poly.evaluate(&point); - let mut vt = fresh_transcript(); - verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - tier_2, - eval, - &point, - &proof, - verifier_setup, - &mut vt, + &mut prover, ) .unwrap(); - (proof, tier_2, point) + prover.narg_string().to_vec() } #[test] -fn test_transparent_proof_roundtrip_compressed() { - let (proof, _, _) = make_transparent_proof(); - - let mut buf = Vec::new(); - proof.serialize_compressed(&mut buf).unwrap(); - assert_eq!(buf.len(), proof.compressed_size()); - - let decoded = ArkDoryProof::deserialize_compressed(&buf[..]).unwrap(); - assert_eq!(proof, decoded); +fn test_proof_bytes_consistent_length() { + let nu = 2; + let sigma = 2; + + let lengths: Vec = (0..3) + .map(|_| make_transparent_proof_bytes(nu, sigma).len()) + .collect(); + + assert!( + lengths.windows(2).all(|w| w[0] == w[1]), + "proof byte lengths should be consistent for same parameters, got {lengths:?}" + ); } #[test] -fn test_transparent_proof_roundtrip_uncompressed() { - let (proof, _, _) = make_transparent_proof(); - - let mut buf = Vec::new(); - proof.serialize_uncompressed(&mut buf).unwrap(); - assert_eq!(buf.len(), proof.uncompressed_size()); +fn test_proof_size_matches_expected() { + let sigma = 2; + let proof_bytes_a = make_transparent_proof_bytes(2, sigma); + let proof_bytes_b = make_transparent_proof_bytes(2, sigma); + assert_eq!( + proof_bytes_a.len(), + proof_bytes_b.len(), + "proof sizes should match for same parameters", + ); +} - let decoded = ArkDoryProof::deserialize_uncompressed(&buf[..]).unwrap(); - assert_eq!(proof, decoded); +#[test] +fn test_proof_size_increases_with_sigma() { + let small = make_transparent_proof_bytes(2, 2); + let large = make_transparent_proof_bytes(4, 4); + assert!( + large.len() > small.len(), + "proof with sigma=4 ({}) should be larger than sigma=2 ({})", + large.len(), + small.len(), + ); } #[test] -fn test_transparent_proof_roundtrip_verifies() { - let (setup, verifier_setup) = test_setup_pair(4); +fn test_proof_bytes_roundtrip_through_verify() { + let nu = 2; + let sigma = 2; + let (setup, verifier_setup) = test_setup_pair(nu + sigma + 2); let poly = random_polynomial(16); let point = random_point(4); let (tier_2, tier_1, commit_blind) = poly - .commit::(2, 2, &setup) + .commit::(nu, sigma, &setup) .unwrap(); - let mut transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + let evaluation = poly.evaluate(&point); + let mut prover = test_prover(nu, sigma); + prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, - 2, - 2, + nu, + sigma, &setup, - &mut transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.narg_string().to_vec(); - // Round-trip through serialization - let mut buf = Vec::new(); - proof.serialize_compressed(&mut buf).unwrap(); - let decoded = ArkDoryProof::deserialize_compressed(&buf[..]).unwrap(); - - // Verify the deserialized proof - let eval = poly.evaluate(&point); - let mut vt = fresh_transcript(); - verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(nu, sigma, &proof_bytes); + verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, - eval, + evaluation, &point, - &decoded, + nu, + sigma, verifier_setup, - &mut vt, + &mut verifier, ) .unwrap(); + verifier.check_eof().unwrap(); +} + +#[test] +fn test_proof_bytes_nonzero() { + let proof_bytes = make_transparent_proof_bytes(2, 2); + + assert!(!proof_bytes.is_empty(), "proof bytes should not be empty"); + assert!( + proof_bytes.iter().any(|&b| b != 0), + "proof bytes should not be all zeros" + ); } #[cfg(feature = "zk")] -mod zk_roundtrip { +mod zk_narg { use super::*; use dory_pcs::{prove, verify, ZK}; - fn make_zk_proof() -> ( - ArkDoryProof, - dory_pcs::backends::arkworks::ArkGT, - Vec, - ) { - let (setup, verifier_setup) = test_setup_pair(4); - - let poly = random_polynomial(16); - let point = random_point(4); + fn make_zk_proof_bytes(nu: usize, sigma: usize) -> Vec { + let setup = test_setup(nu + sigma + 2); + let poly_size = 1 << (nu + sigma); + let poly = random_polynomial(poly_size); + let point = random_point(nu + sigma); let (tier_2, tier_1, commit_blind) = poly - .commit::(2, 2, &setup) + .commit::(nu, sigma, &setup) .unwrap(); - let mut transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( + let evaluation = poly.evaluate(&point); + let mut prover = test_prover_zk(nu, sigma); + prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, - 2, - 2, + nu, + sigma, &setup, - &mut transcript, + &mut prover, ) .unwrap(); - // Sanity: ZK fields must be populated - assert!(proof.e2.is_some()); - assert!(proof.y_com.is_some()); - assert!(proof.sigma1_proof.is_some()); - assert!(proof.sigma2_proof.is_some()); - assert!(proof.scalar_product_proof.is_some()); - - let eval = poly.evaluate(&point); - let mut vt = fresh_transcript(); - verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - tier_2, - eval, - &point, - &proof, - verifier_setup, - &mut vt, - ) - .unwrap(); - - (proof, tier_2, point) + prover.narg_string().to_vec() } #[test] - fn test_zk_proof_roundtrip_compressed() { - let (proof, _, _) = make_zk_proof(); - - let mut buf = Vec::new(); - proof.serialize_compressed(&mut buf).unwrap(); - assert_eq!(buf.len(), proof.compressed_size()); - - let decoded = ArkDoryProof::deserialize_compressed(&buf[..]).unwrap(); - assert_eq!(proof, decoded); + fn test_zk_proof_size_matches_expected() { + let sigma = 2; + let proof_bytes_a = make_zk_proof_bytes(2, sigma); + let proof_bytes_b = make_zk_proof_bytes(2, sigma); + assert_eq!( + proof_bytes_a.len(), + proof_bytes_b.len(), + "ZK proof sizes should match for same parameters", + ); } #[test] - fn test_zk_proof_roundtrip_verifies() { - let (setup, verifier_setup) = test_setup_pair(4); + fn test_zk_proof_bytes_roundtrip_through_verify() { + let nu = 2; + let sigma = 2; + let (setup, verifier_setup) = test_setup_pair(nu + sigma + 2); let poly = random_polynomial(16); let point = random_point(4); let (tier_2, tier_1, commit_blind) = poly - .commit::(2, 2, &setup) + .commit::(nu, sigma, &setup) .unwrap(); - let mut transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( + let evaluation = poly.evaluate(&point); + let mut prover = test_prover_zk(nu, sigma); + prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, - 2, - 2, + nu, + sigma, &setup, - &mut transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.narg_string().to_vec(); - let mut buf = Vec::new(); - proof.serialize_compressed(&mut buf).unwrap(); - let decoded = ArkDoryProof::deserialize_compressed(&buf[..]).unwrap(); - - let eval = poly.evaluate(&point); - let mut vt = fresh_transcript(); - verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier_zk(nu, sigma, &proof_bytes); + verify::<_, BN254, TestG1Routines, TestG2Routines, _, ZK>( tier_2, - eval, + evaluation, &point, - &decoded, + nu, + sigma, verifier_setup, - &mut vt, + &mut verifier, ) .unwrap(); + verifier.check_eof().unwrap(); } #[test] - fn test_zk_proof_larger_size_than_transparent() { - let (zk_proof, _, _) = make_zk_proof(); - let (transparent_proof, _, _) = super::make_transparent_proof(); - - let zk_size = zk_proof.compressed_size(); - let transparent_size = transparent_proof.compressed_size(); - + fn test_zk_proof_larger_than_transparent() { + let zk = make_zk_proof_bytes(4, 4); + let transparent = make_transparent_proof_bytes(4, 4); assert!( - zk_size > transparent_size, - "ZK proof ({zk_size}) should be larger than transparent ({transparent_size})" + zk.len() > transparent.len(), + "ZK proof ({}) should be larger than transparent ({})", + zk.len(), + transparent.len(), ); } } diff --git a/tests/arkworks/soundness.rs b/tests/arkworks/soundness.rs index 27a1097..990cb62 100644 --- a/tests/arkworks/soundness.rs +++ b/tests/arkworks/soundness.rs @@ -1,15 +1,18 @@ //! Comprehensive soundness tests for Dory PCS +//! +//! With the spongefish NARG API, proofs are opaque byte arrays. +//! Soundness is tested by corrupting the proof bytes in various ways +//! and verifying that the verifier rejects them. use super::*; -use ark_bn254::{Fq12, Fr, G1Projective, G2Projective}; +use ark_bn254::Fr; use ark_ff::UniformRand; -use dory_pcs::backends::arkworks::{ArkFr, ArkG1, ArkG2, ArkGT}; +use dory_pcs::backends::arkworks::{ArkFr, ArkGT}; use dory_pcs::primitives::poly::Polynomial; use dory_pcs::{prove, verify, Transparent}; -use std::mem::swap; #[allow(clippy::type_complexity)] -fn create_valid_proof_components( +fn create_valid_proof( size: usize, nu: usize, sigma: usize, @@ -20,29 +23,31 @@ fn create_valid_proof_components( Vec, ArkGT, ArkFr, - DoryProof, + Vec, ) { let (prover_setup, verifier_setup) = test_setup_pair(nu + sigma + 2); - let poly = random_polynomial(size); let point = random_point(nu + sigma); - let (tier_2, tier_1, commit_blind) = poly .commit::(nu, sigma, &prover_setup) .unwrap(); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + + let evaluation = poly.evaluate(&point); + let mut prover = test_prover(nu, sigma); + prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); - let evaluation = poly.evaluate(&point); + let proof_bytes = prover.narg_string().to_vec(); ( prover_setup, @@ -51,595 +56,454 @@ fn create_valid_proof_components( point, tier_2, evaluation, - proof, + proof_bytes, ) } -#[test] -fn test_soundness_tamper_vmv_c() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); - - proof.vmv_message.c = ArkGT(Fq12::rand(&mut rand::thread_rng())); - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( +/// Helper: attempt verification with given proof bytes, including `check_eof()`. +fn try_verify( + commitment: ArkGT, + evaluation: ArkFr, + point: &[ArkFr], + nu: usize, + sigma: usize, + verifier_setup: VerifierSetup, + proof_bytes: &[u8], +) -> Result<(), dory_pcs::DoryError> { + let mut verifier = test_verifier(nu, sigma, proof_bytes); + verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( commitment, evaluation, - &point, - &proof, + point, + nu, + sigma, verifier_setup, - &mut verifier_transcript, - ); - - assert!(result.is_err(), "Should fail with tampered VMV C"); + &mut verifier, + )?; + verifier + .check_eof() + .map_err(|e| dory_pcs::DoryError::SpongeVerification(format!("{e:?}"))) } -#[test] -fn test_soundness_tamper_vmv_d2() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); - - proof.vmv_message.d2 = ArkGT(Fq12::rand(&mut rand::thread_rng())); - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - evaluation, - &point, - &proof, - verifier_setup, - &mut verifier_transcript, - ); - - assert!(result.is_err(), "Should fail with tampered VMV D2"); -} +// --------------------------------------------------------------------------- +// 1. Wrong commitment +// --------------------------------------------------------------------------- #[test] -fn test_soundness_tamper_vmv_e1() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); - - proof.vmv_message.e1 = ArkG1(G1Projective::rand(&mut rand::thread_rng())); - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - evaluation, - &point, - &proof, - verifier_setup, - &mut verifier_transcript, - ); - - assert!(result.is_err(), "Should fail with tampered VMV E1"); -} +fn test_soundness_wrong_commitment() { + let nu = 4; + let sigma = 4; + let (prover_setup, verifier_setup, _, _, _, _, _) = create_valid_proof(256, nu, sigma); -#[test] -fn test_soundness_tamper_d1_left() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); + let poly1 = random_polynomial(256); + let poly2 = random_polynomial(256); + let point = random_point(nu + sigma); - if !proof.first_messages.is_empty() { - proof.first_messages[0].d1_left = ArkGT(Fq12::rand(&mut rand::thread_rng())); - } + let (commitment1, _, _) = poly1 + .commit::(nu, sigma, &prover_setup) + .unwrap(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - evaluation, + let (commitment2, tier_1_poly2, commit_blind2) = poly2 + .commit::(nu, sigma, &prover_setup) + .unwrap(); + let evaluation = poly2.evaluate(&point); + let mut prover = test_prover(nu, sigma); + prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + &poly2, &point, - &proof, - verifier_setup, - &mut verifier_transcript, - ); - - assert!(result.is_err(), "Should fail with tampered d1_left"); -} - -#[test] -fn test_soundness_tamper_d1_right() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); - - if !proof.first_messages.is_empty() { - proof.first_messages[0].d1_right = ArkGT(Fq12::rand(&mut rand::thread_rng())); - } + &commitment2, + &evaluation, + tier_1_poly2, + commit_blind2, + nu, + sigma, + &prover_setup, + &mut prover, + ) + .unwrap(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, + let result = try_verify( + commitment1, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &proof_bytes, ); - assert!(result.is_err(), "Should fail with tampered d1_right"); -} - -#[test] -fn test_soundness_tamper_d2_left() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); - - if !proof.first_messages.is_empty() { - proof.first_messages[0].d2_left = ArkGT(Fq12::rand(&mut rand::thread_rng())); - } - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - evaluation, - &point, - &proof, - verifier_setup, - &mut verifier_transcript, + assert!( + result.is_err(), + "Should fail when commitment doesn't match proof" ); - - assert!(result.is_err(), "Should fail with tampered d2_left"); } -#[test] -fn test_soundness_tamper_d2_right() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); - - if !proof.first_messages.is_empty() { - proof.first_messages[0].d2_right = ArkGT(Fq12::rand(&mut rand::thread_rng())); - } - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - evaluation, - &point, - &proof, - verifier_setup, - &mut verifier_transcript, - ); - - assert!(result.is_err(), "Should fail with tampered d2_right"); -} +// --------------------------------------------------------------------------- +// 2. Byte-level tampering: flip random bytes in proof +// --------------------------------------------------------------------------- #[test] -fn test_soundness_tamper_e1_beta() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); - - if !proof.first_messages.is_empty() { - proof.first_messages[0].e1_beta = ArkG1(G1Projective::rand(&mut rand::thread_rng())); +fn test_soundness_byte_tampering() { + let nu = 4; + let sigma = 4; + let (_, verifier_setup, _, point, commitment, evaluation, proof_bytes) = + create_valid_proof(256, nu, sigma); + + let positions_to_flip = [ + 0, + 1, + proof_bytes.len() / 4, + proof_bytes.len() / 2, + proof_bytes.len() - 1, + ]; + + for &pos in &positions_to_flip { + if pos >= proof_bytes.len() { + continue; + } + let mut tampered = proof_bytes.clone(); + tampered[pos] ^= 0xFF; + + let result = try_verify( + commitment, + evaluation, + &point, + nu, + sigma, + verifier_setup.clone(), + &tampered, + ); + + assert!( + result.is_err(), + "Should fail with byte flipped at position {pos}" + ); } - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - evaluation, - &point, - &proof, - verifier_setup, - &mut verifier_transcript, - ); - - assert!(result.is_err(), "Should fail with tampered e1_beta"); } #[test] -fn test_soundness_tamper_e2_beta() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); - - if !proof.first_messages.is_empty() { - proof.first_messages[0].e2_beta = ArkG2(G2Projective::rand(&mut rand::thread_rng())); +fn test_soundness_flip_sampled_bytes() { + let nu = 4; + let sigma = 4; + let (_, verifier_setup, _, point, commitment, evaluation, proof_bytes) = + create_valid_proof(256, nu, sigma); + + // Sample positions throughout the proof rather than flipping every byte + let len = proof_bytes.len(); + let positions = [ + 0, + 1, + len / 8, + len / 4, + len / 3, + len / 2, + 2 * len / 3, + 3 * len / 4, + 7 * len / 8, + len - 2, + len - 1, + ]; + + for &pos in &positions { + let mut tampered = proof_bytes.clone(); + tampered[pos] ^= 0xFF; + + let result = try_verify( + commitment, + evaluation, + &point, + nu, + sigma, + verifier_setup.clone(), + &tampered, + ); + + assert!( + result.is_err(), + "Should fail with byte flipped at position {pos}" + ); } - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - evaluation, - &point, - &proof, - verifier_setup, - &mut verifier_transcript, - ); - - assert!(result.is_err(), "Should fail with tampered e2_beta"); } -#[test] -fn test_soundness_tamper_c_plus() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); - - if !proof.second_messages.is_empty() { - proof.second_messages[0].c_plus = ArkGT(Fq12::rand(&mut rand::thread_rng())); - } - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - evaluation, - &point, - &proof, - verifier_setup, - &mut verifier_transcript, - ); - - assert!(result.is_err(), "Should fail with tampered c_plus"); -} +// --------------------------------------------------------------------------- +// 3. Truncated proof +// --------------------------------------------------------------------------- #[test] -fn test_soundness_tamper_c_minus() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); - - if !proof.second_messages.is_empty() { - proof.second_messages[0].c_minus = ArkGT(Fq12::rand(&mut rand::thread_rng())); +fn test_soundness_truncated_proof() { + let nu = 4; + let sigma = 4; + let (_, verifier_setup, _, point, commitment, evaluation, proof_bytes) = + create_valid_proof(256, nu, sigma); + + let truncation_points = [0, 1, proof_bytes.len() / 2, proof_bytes.len() - 1]; + + for &len in &truncation_points { + if len >= proof_bytes.len() { + continue; + } + let truncated = &proof_bytes[..len]; + + let result = try_verify( + commitment, + evaluation, + &point, + nu, + sigma, + verifier_setup.clone(), + truncated, + ); + + assert!( + result.is_err(), + "Should fail with proof truncated to {len} bytes" + ); } - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - evaluation, - &point, - &proof, - verifier_setup, - &mut verifier_transcript, - ); - - assert!(result.is_err(), "Should fail with tampered c_minus"); } -#[test] -fn test_soundness_tamper_e1_plus() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); - - if !proof.second_messages.is_empty() { - proof.second_messages[0].e1_plus = ArkG1(G1Projective::rand(&mut rand::thread_rng())); - } - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - evaluation, - &point, - &proof, - verifier_setup, - &mut verifier_transcript, - ); - - assert!(result.is_err(), "Should fail with tampered e1_plus"); -} +// --------------------------------------------------------------------------- +// 4. Extra bytes appended — caught by check_eof on checked verifier +// --------------------------------------------------------------------------- #[test] -fn test_soundness_tamper_e1_minus() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); - - if !proof.second_messages.is_empty() { - proof.second_messages[0].e1_minus = ArkG1(G1Projective::rand(&mut rand::thread_rng())); - } - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - evaluation, +fn test_soundness_extra_bytes_rejected_by_length_check() { + let nu = 4; + let sigma = 4; + let (_, verifier_setup, _, point, tier_2, eval, proof_bytes) = + create_valid_proof(256, nu, sigma); + + // Valid proof passes check_eof + let mut verifier = test_verifier(nu, sigma, &proof_bytes); + verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( + tier_2, + eval, &point, - &proof, - verifier_setup, - &mut verifier_transcript, - ); - - assert!(result.is_err(), "Should fail with tampered e1_minus"); -} - -#[test] -fn test_soundness_tamper_e2_plus() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); - - if !proof.second_messages.is_empty() { - proof.second_messages[0].e2_plus = ArkG2(G2Projective::rand(&mut rand::thread_rng())); - } + nu, + sigma, + verifier_setup.clone(), + &mut verifier, + ) + .unwrap(); + assert!(verifier.check_eof().is_ok()); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - evaluation, + // Appending even one byte fails check_eof + let mut extended = proof_bytes.clone(); + extended.push(0x00); + let mut verifier = test_verifier(nu, sigma, &extended); + verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( + tier_2, + eval, &point, - &proof, - verifier_setup, - &mut verifier_transcript, - ); - - assert!(result.is_err(), "Should fail with tampered e2_plus"); -} - -#[test] -fn test_soundness_tamper_e2_minus() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); - - if !proof.second_messages.is_empty() { - proof.second_messages[0].e2_minus = ArkG2(G2Projective::rand(&mut rand::thread_rng())); - } + nu, + sigma, + verifier_setup.clone(), + &mut verifier, + ) + .unwrap(); + assert!(verifier.check_eof().is_err()); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - evaluation, + // Truncated also fails + let truncated = &proof_bytes[..proof_bytes.len() - 1]; + let mut verifier = test_verifier(nu, sigma, truncated); + let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( + tier_2, + eval, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); - - assert!(result.is_err(), "Should fail with tampered e2_minus"); + assert!(result.is_err()); } -#[test] -fn test_soundness_tamper_final_e1() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); - - proof.final_message.e1 = ArkG1(G1Projective::rand(&mut rand::thread_rng())); - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - evaluation, - &point, - &proof, - verifier_setup, - &mut verifier_transcript, - ); - - assert!(result.is_err(), "Should fail with tampered final e1"); -} +// --------------------------------------------------------------------------- +// 5. Wrong evaluation value +// --------------------------------------------------------------------------- #[test] -fn test_soundness_tamper_final_e2() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); +fn test_soundness_wrong_evaluation() { + let nu = 4; + let sigma = 4; + let (_, verifier_setup, _, point, commitment, _, proof_bytes) = + create_valid_proof(256, nu, sigma); - proof.final_message.e2 = ArkG2(G2Projective::rand(&mut rand::thread_rng())); + let wrong_evaluation = ArkFr(Fr::rand(&mut rand::thread_rng())); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let result = try_verify( commitment, - evaluation, + wrong_evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &proof_bytes, ); - assert!(result.is_err(), "Should fail with tampered final e2"); + assert!(result.is_err(), "Should fail with wrong evaluation"); } #[test] -fn test_soundness_tamper_both_final_elements() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); +fn test_soundness_zero_evaluation() { + let nu = 4; + let sigma = 4; + let (_, verifier_setup, _, point, commitment, evaluation, proof_bytes) = + create_valid_proof(256, nu, sigma); + + use dory_pcs::primitives::arithmetic::Field; + let zero_eval = ArkFr::zero(); + + // Only test if the real evaluation is non-zero (almost certainly true for random poly) + if evaluation != zero_eval { + let result = try_verify( + commitment, + zero_eval, + &point, + nu, + sigma, + verifier_setup, + &proof_bytes, + ); + + assert!( + result.is_err(), + "Should fail with zero evaluation for non-zero poly" + ); + } +} - proof.final_message.e1 = ArkG1(G1Projective::rand(&mut rand::thread_rng())); - proof.final_message.e2 = ArkG2(G2Projective::rand(&mut rand::thread_rng())); +// --------------------------------------------------------------------------- +// 6. Proof from one instance used with another's commitment +// --------------------------------------------------------------------------- - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - evaluation, - &point, - &proof, - verifier_setup, - &mut verifier_transcript, +#[test] +fn test_soundness_cross_instance_proof() { + let nu = 4; + let sigma = 4; + + let (_, verifier_setup1, _, point1, commitment1, evaluation1, _proof_bytes1) = + create_valid_proof(256, nu, sigma); + let (_, _verifier_setup2, _, _point2, _commitment2, _evaluation2, proof_bytes2) = + create_valid_proof(256, nu, sigma); + + // Use proof from instance 2 with commitment/evaluation/point from instance 1 + let result = try_verify( + commitment1, + evaluation1, + &point1, + nu, + sigma, + verifier_setup1, + &proof_bytes2, ); assert!( result.is_err(), - "Should fail with both final elements tampered" + "Should fail when using proof from a different instance" ); } #[test] -fn test_soundness_swap_d1_values() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); - - if !proof.first_messages.is_empty() { - let msg = &mut proof.first_messages[0]; - swap(&mut msg.d1_left, &mut msg.d1_right); - } - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - evaluation, - &point, - &proof, - verifier_setup, - &mut verifier_transcript, +fn test_soundness_cross_instance_swapped_commitment() { + let nu = 4; + let sigma = 4; + + let (_, _verifier_setup1, _, _point1, commitment1, _evaluation1, _proof_bytes1) = + create_valid_proof(256, nu, sigma); + let (_, verifier_setup2, _, point2, _commitment2, evaluation2, proof_bytes2) = + create_valid_proof(256, nu, sigma); + + // Use commitment from instance 1 with proof/evaluation/point from instance 2 + let result = try_verify( + commitment1, + evaluation2, + &point2, + nu, + sigma, + verifier_setup2, + &proof_bytes2, ); - assert!(result.is_err(), "Should fail with swapped d1 values"); -} - -#[test] -fn test_soundness_swap_c_values() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); - - if !proof.second_messages.is_empty() { - let msg = &mut proof.second_messages[0]; - swap(&mut msg.c_plus, &mut msg.c_minus); - } - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - evaluation, - &point, - &proof, - verifier_setup, - &mut verifier_transcript, + assert!( + result.is_err(), + "Should fail with swapped commitment from another instance" ); - - assert!(result.is_err(), "Should fail with swapped c values"); } -#[test] -fn test_soundness_scale_d1_values() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); - - if !proof.first_messages.is_empty() { - use dory_pcs::primitives::arithmetic::Group; - - let scale = ArkFr(Fr::rand(&mut rand::thread_rng())); - proof.first_messages[0].d1_left = proof.first_messages[0].d1_left.scale(&scale); - proof.first_messages[0].d1_right = proof.first_messages[0].d1_right.scale(&scale); - } - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - evaluation, - &point, - &proof, - verifier_setup, - &mut verifier_transcript, - ); - - assert!(result.is_err(), "Should fail with scaled d1 values"); -} +// --------------------------------------------------------------------------- +// 7. Empty proof bytes +// --------------------------------------------------------------------------- #[test] -fn test_soundness_multi_round_tampering() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); - - if proof.first_messages.len() >= 2 { - proof.first_messages[0].d1_left = ArkGT(Fq12::rand(&mut rand::thread_rng())); - proof.first_messages[1].e2_beta = ArkG2(G2Projective::rand(&mut rand::thread_rng())); - } +fn test_soundness_empty_proof() { + let nu = 4; + let sigma = 4; + let (_, verifier_setup, _, point, commitment, evaluation, _proof_bytes) = + create_valid_proof(256, nu, sigma); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let result = try_verify( commitment, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &[], ); - assert!(result.is_err(), "Should fail with multi-round tampering"); + assert!(result.is_err(), "Should fail with empty proof bytes"); } -#[test] -fn test_soundness_last_round_tampering() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); - - let last_round = proof.first_messages.len().saturating_sub(1); - if !proof.first_messages.is_empty() { - proof.first_messages[last_round].d2_right = ArkGT(Fq12::rand(&mut rand::thread_rng())); - } - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - evaluation, - &point, - &proof, - verifier_setup, - &mut verifier_transcript, - ); - - assert!(result.is_err(), "Should fail with last round tampering"); -} +// --------------------------------------------------------------------------- +// Additional: random garbage proof and zeroed proof +// --------------------------------------------------------------------------- #[test] -fn test_soundness_identity_elements() { - let (_, verifier_setup, _, point, commitment, evaluation, mut proof) = - create_valid_proof_components(256, 4, 4); +fn test_soundness_random_garbage_proof() { + let nu = 4; + let sigma = 4; + let (_, verifier_setup, _, point, commitment, evaluation, proof_bytes) = + create_valid_proof(256, nu, sigma); - use dory_pcs::primitives::arithmetic::Group; - proof.final_message.e1 = ArkG1::identity(); - proof.final_message.e2 = ArkG2::identity(); + let garbage: Vec = (0..proof_bytes.len()) + .map(|_| rand::random::()) + .collect(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let result = try_verify( commitment, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &garbage, ); - assert!(result.is_err(), "Should fail with identity elements"); -} - -#[test] -fn test_soundness_wrong_evaluation() { - let (_, verifier_setup, _, point, commitment, _, proof) = - create_valid_proof_components(256, 4, 4); - - let wrong_evaluation = ArkFr(Fr::rand(&mut rand::thread_rng())); - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - wrong_evaluation, - &point, - &proof, - verifier_setup, - &mut verifier_transcript, + assert!( + result.is_err(), + "Should fail with random garbage proof bytes" ); - - assert!(result.is_err(), "Should fail with wrong evaluation"); } #[test] -fn test_soundness_mixed_proofs() { - let (_, _, _, _point1, _, _, proof1) = create_valid_proof_components(256, 4, 4); - let (_, verifier_setup, _, point2, commitment2, evaluation2, mut proof2) = - create_valid_proof_components(256, 4, 4); +fn test_soundness_zeroed_proof() { + let nu = 4; + let sigma = 4; + let (_, verifier_setup, _, point, commitment, evaluation, proof_bytes) = + create_valid_proof(256, nu, sigma); - if !proof1.first_messages.is_empty() && !proof2.first_messages.is_empty() { - proof2.first_messages[0].d1_left = proof1.first_messages[0].d1_left; - } + let zeroed = vec![0u8; proof_bytes.len()]; - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment2, - evaluation2, - &point2, - &proof2, - verifier_setup, - &mut verifier_transcript, - ); - - assert!(result.is_err(), "Should fail with mixed proof elements"); -} - -#[test] -fn test_soundness_different_transcript() { - let (_, verifier_setup, _, point, commitment, evaluation, proof) = - create_valid_proof_components(256, 4, 4); - - let mut verifier_transcript = Blake2bTranscript::new(b"different-domain"); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let result = try_verify( commitment, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &zeroed, ); - assert!(result.is_err(), "Should fail with different transcript"); + assert!(result.is_err(), "Should fail with zeroed proof"); } diff --git a/tests/arkworks/zk.rs b/tests/arkworks/zk.rs index 9915a6d..2df1a8a 100644 --- a/tests/arkworks/zk.rs +++ b/tests/arkworks/zk.rs @@ -1,11 +1,9 @@ //! Zero-knowledge mode tests for Dory PCS use super::*; -use ark_bn254::{Fq12, Fr, G1Projective, G2Projective}; -use ark_ff::UniformRand; -use dory_pcs::backends::arkworks::{ArkFr, ArkG1, ArkG2, ArkGT}; +use dory_pcs::backends::arkworks::ArkGT; use dory_pcs::primitives::poly::Polynomial; -use dory_pcs::{create_evaluation_proof, prove, setup, verify, ZK}; +use dory_pcs::{create_evaluation_proof, prove, setup, verify, Transparent, ZK}; #[test] fn test_zk_full_workflow() { @@ -23,33 +21,38 @@ fn test_zk_full_workflow() { let point = random_point(8); let expected_evaluation = poly.evaluate(&point); + let evaluation = poly.evaluate(&point); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( + let mut prover = test_prover_zk(nu, sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); - let evaluation = poly.evaluate(&point); assert_eq!(evaluation, expected_evaluation); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier_zk(nu, sigma, &proof_bytes); + let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, ZK>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); assert!(result.is_ok(), "ZK proof verification failed: {:?}", result); + verifier.check_eof().unwrap(); } #[test] @@ -67,27 +70,31 @@ fn test_zk_small_polynomial() { let point = random_point(2); let evaluation = poly.evaluate(&point); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( + let mut prover = test_prover_zk(nu, sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier_zk(nu, sigma, &proof_bytes); + let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, ZK>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); assert!( @@ -95,6 +102,7 @@ fn test_zk_small_polynomial() { "ZK small polynomial test failed: {:?}", result ); + verifier.check_eof().unwrap(); } #[test] @@ -112,27 +120,31 @@ fn test_zk_larger_polynomial() { let point = random_point(10); let evaluation = poly.evaluate(&point); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( + let mut prover = test_prover_zk(nu, sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier_zk(nu, sigma, &proof_bytes); + let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, ZK>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); assert!( @@ -140,6 +152,7 @@ fn test_zk_larger_polynomial() { "ZK larger polynomial test failed: {:?}", result ); + verifier.check_eof().unwrap(); } #[test] @@ -158,27 +171,31 @@ fn test_zk_non_square_matrix() { let point = random_point(7); // nu + sigma = 7 let evaluation = poly.evaluate(&point); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( + let mut prover = test_prover_zk(nu, sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier_zk(nu, sigma, &proof_bytes); + let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, ZK>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); assert!( @@ -186,6 +203,7 @@ fn test_zk_non_square_matrix() { "ZK non-square matrix test failed: {:?}", result ); + verifier.check_eof().unwrap(); } #[test] @@ -204,30 +222,37 @@ fn test_zk_hidden_evaluation() { let evaluation = poly.evaluate(&point); // Create ZK proof using unified API with ZK mode - let mut prover_transcript = fresh_transcript(); - let (proof, _) = create_evaluation_proof::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( + let mut prover = test_prover_zk(nu, sigma); + let _y = create_evaluation_proof::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( &poly, &point, + &tier_2, + &evaluation, Some(tier_1), commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.narg_string().to_vec(); - assert!(proof.y_com.is_some(), "ZK proof should contain y_com"); - assert!(proof.e2.is_some(), "ZK proof should contain e2"); + // Verify that ZK proof bytes are non-empty + assert!( + !proof_bytes.is_empty(), + "ZK proof should produce non-empty bytes" + ); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier_zk(nu, sigma, &proof_bytes); + let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, ZK>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); assert!( @@ -235,55 +260,7 @@ fn test_zk_hidden_evaluation() { "ZK hidden evaluation proof verification failed: {:?}", result ); -} - -/// Test that tampered e2 in proof is rejected -#[test] -fn test_zk_tampered_e2_rejected() { - use dory_pcs::primitives::arithmetic::Group; - - let (prover_setup, verifier_setup) = test_setup_pair(6); - - let poly = random_polynomial(16); - let nu = 2; - let sigma = 2; - - let (tier_2, tier_1, commit_blind) = poly - .commit::(nu, sigma, &prover_setup) - .unwrap(); - - let point = random_point(4); - let evaluation = poly.evaluate(&point); - - let mut prover_transcript = fresh_transcript(); - let (mut proof, _) = - create_evaluation_proof::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( - &poly, - &point, - Some(tier_1), - commit_blind, - nu, - sigma, - &prover_setup, - &mut prover_transcript, - ) - .unwrap(); - - if let Some(ref mut e2) = proof.e2 { - *e2 = *e2 + prover_setup.h2.scale(&ArkFr::from_u64(42)); - } - - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - tier_2, - evaluation, - &point, - &proof, - verifier_setup, - &mut verifier_transcript, - ); - - assert!(result.is_err(), "Verification should fail with tampered e2"); + verifier.check_eof().unwrap(); } /// Test full ZK with larger polynomial @@ -302,27 +279,31 @@ fn test_zk_hidden_evaluation_larger() { let point = random_point(8); let evaluation = poly.evaluate(&point); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = create_evaluation_proof::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( + let mut prover = test_prover_zk(nu, sigma); + let _y = create_evaluation_proof::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( &poly, &point, + &tier_2, + &evaluation, Some(tier_1), commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier_zk(nu, sigma, &proof_bytes); + let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, ZK>( tier_2, evaluation, &point, - &proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, + &mut verifier, ); assert!( @@ -330,23 +311,26 @@ fn test_zk_hidden_evaluation_larger() { "ZK hidden evaluation (larger) failed: {:?}", result ); + verifier.check_eof().unwrap(); } // --------------------------------------------------------------------------- // ZK Soundness Tests // --------------------------------------------------------------------------- +/// Create a valid ZK proof and return (proof_bytes, sigma, verifier_setup, point, commitment, evaluation). #[allow(clippy::type_complexity)] -fn create_valid_zk_proof_components( +fn create_valid_zk_proof_bytes( size: usize, nu: usize, sigma: usize, ) -> ( + Vec, + usize, VerifierSetup, Vec, ArkGT, ArkFr, - DoryProof, ) { let (prover_setup, verifier_setup) = test_setup_pair(nu + sigma + 2); @@ -356,256 +340,340 @@ fn create_valid_zk_proof_components( let (tier_2, tier_1, commit_blind) = poly .commit::(nu, sigma, &prover_setup) .unwrap(); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( + + let evaluation = poly.evaluate(&point); + + let mut prover = test_prover_zk(nu, sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); - let evaluation = poly.evaluate(&point); + let proof_bytes = prover.narg_string().to_vec(); - (verifier_setup, point, tier_2, evaluation, proof) + ( + proof_bytes, + sigma, + verifier_setup, + point, + tier_2, + evaluation, + ) } +#[allow(clippy::too_many_arguments)] fn verify_tampered_zk_proof( commitment: ArkGT, evaluation: ArkFr, point: &[ArkFr], - proof: &DoryProof, + nu: usize, + sigma: usize, + proof_bytes: &[u8], verifier_setup: VerifierSetup, ) -> Result<(), dory_pcs::DoryError> { - let mut verifier_transcript = fresh_transcript(); - verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier_zk(nu, sigma, proof_bytes); + verify::<_, BN254, TestG1Routines, TestG2Routines, _, ZK>( commitment, evaluation, point, - proof, + nu, + sigma, verifier_setup, - &mut verifier_transcript, - ) -} - -#[test] -fn test_zk_soundness_missing_sigma1_proof() { - let (verifier_setup, point, commitment, evaluation, mut proof) = - create_valid_zk_proof_components(256, 4, 4); - - proof.sigma1_proof = None; - - let result = verify_tampered_zk_proof(commitment, evaluation, &point, &proof, verifier_setup); - assert!(result.is_err(), "Should fail with missing sigma1_proof"); + &mut verifier, + )?; + verifier + .check_eof() + .map_err(|e| dory_pcs::DoryError::SpongeVerification(format!("{e:?}"))) } +/// Verify that valid ZK proof passes (sanity check). #[test] -fn test_zk_soundness_missing_sigma2_proof() { - let (verifier_setup, point, commitment, evaluation, mut proof) = - create_valid_zk_proof_components(256, 4, 4); - - proof.sigma2_proof = None; +fn test_zk_soundness_valid_proof() { + let nu = 4; + let sigma = 4; + let (proof_bytes, _sigma, verifier_setup, point, commitment, evaluation) = + create_valid_zk_proof_bytes(256, nu, sigma); - let result = verify_tampered_zk_proof(commitment, evaluation, &point, &proof, verifier_setup); - assert!(result.is_err(), "Should fail with missing sigma2_proof"); + let result = verify_tampered_zk_proof( + commitment, + evaluation, + &point, + nu, + sigma, + &proof_bytes, + verifier_setup, + ); + assert!(result.is_ok(), "Valid ZK proof should verify"); } +/// Flipping a byte at the start should fail. #[test] -fn test_zk_soundness_missing_scalar_product_proof() { - let (verifier_setup, point, commitment, evaluation, mut proof) = - create_valid_zk_proof_components(256, 4, 4); +fn test_zk_soundness_flip_first_byte() { + let nu = 4; + let sigma = 4; + let (proof_bytes, _sigma, verifier_setup, point, commitment, evaluation) = + create_valid_zk_proof_bytes(256, nu, sigma); - proof.scalar_product_proof = None; + let mut corrupted = proof_bytes.clone(); + if !corrupted.is_empty() { + corrupted[0] ^= 0xFF; + } - let result = verify_tampered_zk_proof(commitment, evaluation, &point, &proof, verifier_setup); - assert!( - result.is_err(), - "Should fail with missing scalar_product_proof" + let result = verify_tampered_zk_proof( + commitment, + evaluation, + &point, + nu, + sigma, + &corrupted, + verifier_setup, ); + assert!(result.is_err(), "Should fail with flipped first byte"); } +/// Flipping a byte in the middle should fail. #[test] -fn test_zk_soundness_partial_zk_e2_only() { - let (verifier_setup, point, commitment, evaluation, mut proof) = - create_valid_zk_proof_components(256, 4, 4); +fn test_zk_soundness_flip_middle_byte() { + let nu = 4; + let sigma = 4; + let (proof_bytes, _sigma, verifier_setup, point, commitment, evaluation) = + create_valid_zk_proof_bytes(256, nu, sigma); - proof.y_com = None; + let mut corrupted = proof_bytes.clone(); + let mid = corrupted.len() / 2; + if mid < corrupted.len() { + corrupted[mid] ^= 0xFF; + } - let result = verify_tampered_zk_proof(commitment, evaluation, &point, &proof, verifier_setup); - assert!( - result.is_err(), - "Should fail with partial ZK fields (e2 only)" + let result = verify_tampered_zk_proof( + commitment, + evaluation, + &point, + nu, + sigma, + &corrupted, + verifier_setup, ); + assert!(result.is_err(), "Should fail with flipped middle byte"); } +/// Flipping the last byte should fail. #[test] -fn test_zk_soundness_partial_zk_ycom_only() { - let (verifier_setup, point, commitment, evaluation, mut proof) = - create_valid_zk_proof_components(256, 4, 4); +fn test_zk_soundness_flip_last_byte() { + let nu = 4; + let sigma = 4; + let (proof_bytes, _sigma, verifier_setup, point, commitment, evaluation) = + create_valid_zk_proof_bytes(256, nu, sigma); - proof.e2 = None; + // Flip a byte near the end (but not the very last, which may be + // trailing padding that spongefish ignores). + let pos = proof_bytes.len().saturating_sub(10).max(1); + let mut corrupted = proof_bytes.clone(); + corrupted[pos] ^= 0xFF; - let result = verify_tampered_zk_proof(commitment, evaluation, &point, &proof, verifier_setup); + let result = verify_tampered_zk_proof( + commitment, + evaluation, + &point, + nu, + sigma, + &corrupted, + verifier_setup, + ); assert!( result.is_err(), - "Should fail with partial ZK fields (y_com only)" + "Should fail with flipped byte near end of proof" ); } +/// Truncating ZK proof should fail. #[test] -fn test_zk_soundness_tampered_sigma1_z1() { - let (verifier_setup, point, commitment, evaluation, mut proof) = - create_valid_zk_proof_components(256, 4, 4); - - if let Some(ref mut s) = proof.sigma1_proof { - s.z1 = ArkFr(Fr::rand(&mut rand::thread_rng())); - } - - let result = verify_tampered_zk_proof(commitment, evaluation, &point, &proof, verifier_setup); - assert!(result.is_err(), "Should fail with tampered sigma1 z1"); -} - -#[test] -fn test_zk_soundness_tampered_sigma1_a1() { - let (verifier_setup, point, commitment, evaluation, mut proof) = - create_valid_zk_proof_components(256, 4, 4); - - if let Some(ref mut s) = proof.sigma1_proof { - s.a1 = ArkG2(G2Projective::rand(&mut rand::thread_rng())); - } - - let result = verify_tampered_zk_proof(commitment, evaluation, &point, &proof, verifier_setup); - assert!(result.is_err(), "Should fail with tampered sigma1 a1"); -} - -#[test] -fn test_zk_soundness_tampered_sigma2_z1() { - let (verifier_setup, point, commitment, evaluation, mut proof) = - create_valid_zk_proof_components(256, 4, 4); - - if let Some(ref mut s) = proof.sigma2_proof { - s.z1 = ArkFr(Fr::rand(&mut rand::thread_rng())); - } - - let result = verify_tampered_zk_proof(commitment, evaluation, &point, &proof, verifier_setup); - assert!(result.is_err(), "Should fail with tampered sigma2 z1"); -} - -#[test] -fn test_zk_soundness_tampered_sigma2_a() { - let (verifier_setup, point, commitment, evaluation, mut proof) = - create_valid_zk_proof_components(256, 4, 4); - - if let Some(ref mut s) = proof.sigma2_proof { - s.a = ArkGT(Fq12::rand(&mut rand::thread_rng())); - } - - let result = verify_tampered_zk_proof(commitment, evaluation, &point, &proof, verifier_setup); - assert!(result.is_err(), "Should fail with tampered sigma2 a"); -} - -#[test] -fn test_zk_soundness_tampered_sp_e1() { - let (verifier_setup, point, commitment, evaluation, mut proof) = - create_valid_zk_proof_components(256, 4, 4); +fn test_zk_soundness_truncated_proof() { + let nu = 4; + let sigma = 4; + let (proof_bytes, _sigma, verifier_setup, point, commitment, evaluation) = + create_valid_zk_proof_bytes(256, nu, sigma); - if let Some(ref mut sp) = proof.scalar_product_proof { - sp.e1 = ArkG1(G1Projective::rand(&mut rand::thread_rng())); - } + let truncated = proof_bytes[..proof_bytes.len() / 2].to_vec(); - let result = verify_tampered_zk_proof(commitment, evaluation, &point, &proof, verifier_setup); - assert!( - result.is_err(), - "Should fail with tampered scalar product e1" + let result = verify_tampered_zk_proof( + commitment, + evaluation, + &point, + nu, + sigma, + &truncated, + verifier_setup, ); + assert!(result.is_err(), "Should fail with truncated ZK proof"); } +/// Empty proof should fail. #[test] -fn test_zk_soundness_tampered_sp_p1() { - let (verifier_setup, point, commitment, evaluation, mut proof) = - create_valid_zk_proof_components(256, 4, 4); +fn test_zk_soundness_empty_proof() { + let nu = 4; + let sigma = 4; + let (_proof_bytes, _sigma, verifier_setup, point, commitment, evaluation) = + create_valid_zk_proof_bytes(256, nu, sigma); - if let Some(ref mut sp) = proof.scalar_product_proof { - sp.p1 = ArkGT(Fq12::rand(&mut rand::thread_rng())); - } + let empty: Vec = Vec::new(); - let result = verify_tampered_zk_proof(commitment, evaluation, &point, &proof, verifier_setup); - assert!( - result.is_err(), - "Should fail with tampered scalar product p1" + let result = verify_tampered_zk_proof( + commitment, + evaluation, + &point, + nu, + sigma, + &empty, + verifier_setup, ); + assert!(result.is_err(), "Should fail with empty ZK proof"); } +/// In ZK mode, the evaluation is hidden inside the proof and verified +/// via sigma proofs. However, evaluation is now absorbed as a public +/// message into the sponge state, so passing a wrong evaluation causes +/// the sponge states to diverge and verification to fail. #[test] -fn test_zk_soundness_tampered_sp_r3() { - let (verifier_setup, point, commitment, evaluation, mut proof) = - create_valid_zk_proof_components(256, 4, 4); +fn test_zk_wrong_evaluation_rejected() { + let nu = 4; + let sigma = 4; + let (proof_bytes, _sigma, verifier_setup, point, commitment, _evaluation) = + create_valid_zk_proof_bytes(256, nu, sigma); - if let Some(ref mut sp) = proof.scalar_product_proof { - sp.r3 = ArkFr(Fr::rand(&mut rand::thread_rng())); - } + // Even in ZK mode, the evaluation is now bound to the transcript as a + // public message. Passing an arbitrary evaluation diverges the sponge. + let arbitrary_evaluation = ArkFr::random(); - let result = verify_tampered_zk_proof(commitment, evaluation, &point, &proof, verifier_setup); + let result = verify_tampered_zk_proof( + commitment, + arbitrary_evaluation, + &point, + nu, + sigma, + &proof_bytes, + verifier_setup, + ); assert!( result.is_err(), - "Should fail with tampered scalar product r3" + "Wrong evaluation should fail even in ZK mode — evaluation is transcript-bound" ); } +/// Zeroed proof bytes should fail. #[test] -fn test_zk_soundness_tampered_vmv_c() { - let (verifier_setup, point, commitment, evaluation, mut proof) = - create_valid_zk_proof_components(256, 4, 4); +fn test_zk_soundness_zeroed_proof() { + let nu = 4; + let sigma = 4; + let (proof_bytes, _sigma, verifier_setup, point, commitment, evaluation) = + create_valid_zk_proof_bytes(256, nu, sigma); - proof.vmv_message.c = ArkGT(Fq12::rand(&mut rand::thread_rng())); + let zeroed = vec![0u8; proof_bytes.len()]; - let result = verify_tampered_zk_proof(commitment, evaluation, &point, &proof, verifier_setup); - assert!(result.is_err(), "Should fail with tampered VMV c in ZK"); + let result = verify_tampered_zk_proof( + commitment, + evaluation, + &point, + nu, + sigma, + &zeroed, + verifier_setup, + ); + assert!(result.is_err(), "Should fail with zeroed ZK proof"); } +/// Using transparent proof bytes with ZK verifier should fail. #[test] -fn test_zk_soundness_tampered_vmv_d2() { - let (verifier_setup, point, commitment, evaluation, mut proof) = - create_valid_zk_proof_components(256, 4, 4); - - proof.vmv_message.d2 = ArkGT(Fq12::rand(&mut rand::thread_rng())); +fn test_zk_soundness_transparent_proof_as_zk() { + let nu = 4; + let sigma = 4; - let result = verify_tampered_zk_proof(commitment, evaluation, &point, &proof, verifier_setup); - assert!(result.is_err(), "Should fail with tampered VMV d2 in ZK"); -} + // Create a transparent proof + let (prover_setup, verifier_setup) = test_setup_pair(nu + sigma + 2); + let poly = random_polynomial(256); + let point = random_point(nu + sigma); + let (tier_2, tier_1, commit_blind) = poly + .commit::(nu, sigma, &prover_setup) + .unwrap(); -#[test] -fn test_zk_soundness_tampered_vmv_e1() { - let (verifier_setup, point, commitment, evaluation, mut proof) = - create_valid_zk_proof_components(256, 4, 4); + let evaluation = poly.evaluate(&point); - proof.vmv_message.e1 = ArkG1(G1Projective::rand(&mut rand::thread_rng())); + let mut prover = test_prover(nu, sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + &poly, + &point, + &tier_2, + &evaluation, + tier_1, + commit_blind, + nu, + sigma, + &prover_setup, + &mut prover, + ) + .unwrap(); + let transparent_bytes = prover.narg_string().to_vec(); - let result = verify_tampered_zk_proof(commitment, evaluation, &point, &proof, verifier_setup); - assert!(result.is_err(), "Should fail with tampered VMV e1 in ZK"); + // Try to verify transparent proof bytes using ZK domain + let result = verify_tampered_zk_proof( + tier_2, + evaluation, + &point, + nu, + sigma, + &transparent_bytes, + verifier_setup, + ); + assert!( + result.is_err(), + "Should fail when using transparent proof bytes with ZK verifier" + ); } +/// ZK proof bytes should be larger than transparent proof bytes for same parameters. #[test] -fn test_zk_soundness_tampered_e2() { - let (verifier_setup, point, commitment, evaluation, mut proof) = - create_valid_zk_proof_components(256, 4, 4); - - proof.e2 = Some(ArkG2(G2Projective::rand(&mut rand::thread_rng()))); +fn test_zk_proof_bytes_larger_than_transparent() { + let nu = 4; + let sigma = 4; + let (zk_bytes, _, _, _, _, _) = create_valid_zk_proof_bytes(256, nu, sigma); - let result = verify_tampered_zk_proof(commitment, evaluation, &point, &proof, verifier_setup); - assert!(result.is_err(), "Should fail with tampered e2 in ZK"); -} + let (prover_setup, _) = test_setup_pair(nu + sigma + 2); + let poly = random_polynomial(256); + let point = random_point(nu + sigma); + let (tier_2, tier_1, commit_blind) = poly + .commit::(nu, sigma, &prover_setup) + .unwrap(); -#[test] -fn test_zk_soundness_tampered_y_com() { - let (verifier_setup, point, commitment, evaluation, mut proof) = - create_valid_zk_proof_components(256, 4, 4); + let evaluation = poly.evaluate(&point); - proof.y_com = Some(ArkG1(G1Projective::rand(&mut rand::thread_rng()))); + let mut prover = test_prover(nu, sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + &poly, + &point, + &tier_2, + &evaluation, + tier_1, + commit_blind, + nu, + sigma, + &prover_setup, + &mut prover, + ) + .unwrap(); + let transparent_bytes = prover.narg_string().to_vec(); - let result = verify_tampered_zk_proof(commitment, evaluation, &point, &proof, verifier_setup); - assert!(result.is_err(), "Should fail with tampered y_com in ZK"); + assert!( + zk_bytes.len() > transparent_bytes.len(), + "ZK proof ({}) should be larger than transparent ({})", + zk_bytes.len(), + transparent_bytes.len() + ); }