From 2a45e4cfc99cb1bff3125c8236c5a24ae5b7d476 Mon Sep 17 00:00:00 2001 From: shreyas-londhe Date: Fri, 6 Mar 2026 15:27:31 +0530 Subject: [PATCH 1/3] refactor: migrate Fiat-Shamir transcript to spongefish NARG protocol Replace the custom Blake2b rolling-hash transcript with spongefish's duplex-sponge construction. Proofs are now opaque NARG byte strings instead of serialized Proof structs. Key changes: - Add CheckedProverState/CheckedVerifierState with InteractionPattern enforcement for compile-time protocol structure validation - Declare reusable sub-patterns (sigma1, sigma2, reduce round, scalar product) composed via scoped nesting for diagnostic paths - Domain separator binds (sigma, zk) into sponge instance - Remove Proof struct, blake2b transcript, ark_proof, VMVMessage - Add check_eof() to all tests, benches, and examples - Use macros to reduce spongefish codec boilerplate - Pin spongefish to commit 45df37a7 on fork Co-Authored-By: Claude Opus 4.6 --- Cargo.lock | 284 ++++++- Cargo.toml | 6 +- benches/arkworks_proof.rs | 54 +- examples/basic_e2e.rs | 19 +- examples/homomorphic.rs | 19 +- examples/homomorphic_mixed_sizes.rs | 25 +- examples/non_square.rs | 19 +- examples/zk_e2e.rs | 19 +- examples/zk_statistical.rs | 165 ++-- src/backends/arkworks/ark_proof.rs | 14 - src/backends/arkworks/ark_serde.rs | 283 ------- src/backends/arkworks/blake2b_transcript.rs | 148 ---- src/backends/arkworks/domain.rs | 216 +++++ src/backends/arkworks/mod.rs | 8 +- src/backends/arkworks/spongefish_codecs.rs | 69 ++ src/error.rs | 4 + src/evaluation_proof.rs | 322 ++++---- src/lib.rs | 90 +-- src/messages.rs | 35 - src/primitives/transcript.rs | 85 +- src/proof.rs | 55 -- src/reduce_and_fold.rs | 123 ++- tests/arkworks/evaluation.rs | 134 ++-- tests/arkworks/homomorphic.rs | 34 +- tests/arkworks/integration.rs | 133 ++- tests/arkworks/mod.rs | 26 +- tests/arkworks/non_square.rs | 55 +- tests/arkworks/serialization.rs | 266 +++--- tests/arkworks/soundness.rs | 845 ++++++++------------ tests/arkworks/zk.rs | 580 +++++++------- 30 files changed, 2014 insertions(+), 2121 deletions(-) delete mode 100644 src/backends/arkworks/ark_proof.rs delete mode 100644 src/backends/arkworks/blake2b_transcript.rs create mode 100644 src/backends/arkworks/domain.rs create mode 100644 src/backends/arkworks/spongefish_codecs.rs delete mode 100644 src/proof.rs diff --git a/Cargo.lock b/Cargo.lock index 1ad9d5d..800bca1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", ] @@ -278,6 +278,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "criterion" version = "0.5.1" @@ -385,14 +394,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", @@ -518,6 +526,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 +551,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -546,6 +572,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 +642,157 @@ version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" +[[package]] +name = "p3-challenger" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20e42ba74a49c08c6e99f74cd9b343bfa31aa5721fea55079b18e3fd65f1dcbc" +dependencies = [ + "p3-field", + "p3-maybe-rayon", + "p3-monty-31", + "p3-symmetric", + "p3-util", + "tracing", +] + +[[package]] +name = "p3-dft" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e63fa5eb1bd12a240089e72ae3fe10350944d9c166d00a3bfd2a1794db65cf5c" +dependencies = [ + "itertools 0.14.0", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-util", + "spin", + "tracing", +] + +[[package]] +name = "p3-field" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ebfdb6ef992ae64e9e8f449ac46516ffa584f11afbdf9ee244288c2a633cdf4" +dependencies = [ + "itertools 0.14.0", + "num-bigint", + "p3-maybe-rayon", + "p3-util", + "paste", + "rand 0.9.2", + "serde", + "tracing", +] + +[[package]] +name = "p3-koala-bear" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5113f50002c56006685b7d7ae12db568150aa1d4bfb092b883d64ece20138042" +dependencies = [ + "p3-challenger", + "p3-field", + "p3-monty-31", + "p3-poseidon2", + "p3-symmetric", + "rand 0.9.2", +] + +[[package]] +name = "p3-matrix" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5542f96504dae8100c91398fb1e3f5ec669eb9c73d9e0b018a93b5fe32bad230" +dependencies = [ + "itertools 0.14.0", + "p3-field", + "p3-maybe-rayon", + "p3-util", + "rand 0.9.2", + "serde", + "tracing", + "transpose", +] + +[[package]] +name = "p3-maybe-rayon" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e5669ca75645f99cd001e9d0289a4eeff2bc2cd9dc3c6c3aaf22643966e83df" + +[[package]] +name = "p3-mds" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038763af23df9da653065867fd85b38626079031576c86fd537097e5be6a0da0" +dependencies = [ + "p3-dft", + "p3-field", + "p3-symmetric", + "p3-util", + "rand 0.9.2", +] + +[[package]] +name = "p3-monty-31" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a981d60da3d8cbf8561014e2c186068578405fd69098fa75b43d4afb364a47" +dependencies = [ + "itertools 0.14.0", + "num-bigint", + "p3-dft", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-mds", + "p3-poseidon2", + "p3-symmetric", + "p3-util", + "paste", + "rand 0.9.2", + "serde", + "spin", + "tracing", + "transpose", +] + +[[package]] +name = "p3-poseidon2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "903b73e4f9a7781a18561c74dc169cf03333497b57a8dd02aaeb130c0f386599" +dependencies = [ + "p3-field", + "p3-mds", + "p3-symmetric", + "p3-util", + "rand 0.9.2", +] + +[[package]] +name = "p3-symmetric" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cd788f04e86dd5c35dd87cad29eefdb6371d2fd5f7664451382eeacae3c3ed0" +dependencies = [ + "itertools 0.14.0", + "p3-field", + "serde", +] + +[[package]] +name = "p3-util" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "663b16021930bc600ecada915c6c3965730a3b9d6a6c23434ccf70bfc29d6881" +dependencies = [ + "serde", +] + [[package]] name = "paste" version = "1.0.15" @@ -682,7 +868,16 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_core 0.9.5", ] [[package]] @@ -692,7 +887,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 +899,12 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" + [[package]] name = "rayon" version = "1.11.0" @@ -768,6 +969,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 +1018,27 @@ dependencies = [ "zmij", ] +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -826,6 +1054,38 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spongefish" +version = "1.0.0-rc1" +source = "git+https://github.com/shreyas-londhe/spongefish?rev=45df37a7d4d707f1e416864f18c0b1458ec5942c#45df37a7d4d707f1e416864f18c0b1458ec5942c" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "blake2", + "digest", + "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 = "subtle" version = "2.6.1" @@ -939,6 +1199,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..5047352 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/shreyas-londhe/spongefish", rev = "45df37a7d4d707f1e416864f18c0b1458ec5942c", optional = true, features = ["ark-ec", "blake2", "pattern"] } rayon = { version = "1.10", optional = true } [dev-dependencies] diff --git a/benches/arkworks_proof.rs b/benches/arkworks_proof.rs index c5dc4fd..f1cac2c 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); @@ -80,7 +78,7 @@ fn bench_prove(c: &mut Criterion) { c.bench_function("prove_2^26_coefficients", |b| { b.iter(|| { - let mut transcript = Blake2bTranscript::new(b"dory-bench"); + let mut prover = dory_prover(sigma, false); prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( black_box(&poly), black_box(&point), @@ -89,7 +87,7 @@ fn bench_prove(c: &mut Criterion) { black_box(nu), black_box(sigma), black_box(&prover_setup), - black_box(&mut transcript), + black_box(&mut prover), ) .unwrap() }) @@ -105,8 +103,8 @@ 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 mut prover = dory_prover(sigma, false); + prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( &poly, &point, tier_1, @@ -114,24 +112,27 @@ fn bench_verify(c: &mut Criterion) { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.check_complete().narg_string().to_vec(); let evaluation = poly.evaluate(&point); 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(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 +141,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,25 +152,21 @@ 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(sigma, false); + prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( &poly, &point, tier_1, @@ -178,21 +174,23 @@ fn bench_end_to_end(c: &mut Criterion) { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - // Verify - let mut verifier_transcript = Blake2bTranscript::new(b"dory-bench"); - verify::<_, BN254, G1Routines, G2Routines, _>( + let mut verifier = dory_verifier(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..1277774 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,8 +31,8 @@ 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(sigma, false); + prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( &poly, &point, tier_1, @@ -40,18 +40,21 @@ fn main() -> Result<(), Box> { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, )?; + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = Blake2bTranscript::new(b"dory-basic-example"); - verify::<_, BN254, G1Routines, G2Routines, _>( + let mut verifier = dory_verifier(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..02e8b54 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,8 +86,8 @@ 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(sigma, false); + prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( &combined_poly, &point, combined_tier1, @@ -95,18 +95,21 @@ fn main() -> Result<(), Box> { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, )?; + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = Blake2bTranscript::new(b"dory-homomorphic-example"); - verify::<_, BN254, G1Routines, G2Routines, _>( + let mut verifier = dory_verifier(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..fae87fe 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,32 @@ 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(sigma, false); + prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( &combined_poly, &point, combined_tier1, ArkFr::zero(), - 2, - 2, + nu, + sigma, &prover_setup, - &mut prover_transcript, + &mut prover, )?; + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = Blake2bTranscript::new(b"dory-homomorphic-mixed"); - verify::<_, BN254, G1Routines, G2Routines, _>( + let mut verifier = dory_verifier(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..0e2a918 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,8 +30,8 @@ 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(sigma, false); + prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( &poly, &point, tier_1, @@ -39,18 +39,21 @@ fn main() -> Result<(), Box> { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, )?; + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = Blake2bTranscript::new(b"dory-non-square-example"); - verify::<_, BN254, G1Routines, G2Routines, _>( + let mut verifier = dory_verifier(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..11241cb 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,8 +32,8 @@ 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(sigma, true); + prove::<_, BN254, G1Routines, G2Routines, _, _, ZK>( &poly, &point, tier_1, @@ -41,18 +41,21 @@ fn main() -> Result<(), Box> { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, )?; + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = Blake2bTranscript::new(b"dory-zk-example"); - verify::<_, BN254, G1Routines, G2Routines, _>( + let mut verifier = dory_verifier(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..47d8144 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,35 @@ 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(sigma, true); + prove::<_, BN254, G1Routines, G2Routines, _, _, ZK>( poly, point, - Some(tier_1), + tier_1, commit_blind, nu, sigma, prover_setup, - &mut transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - verify::<_, BN254, G1Routines, G2Routines, _>( + let mut verifier = dory_verifier(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..b431450 --- /dev/null +++ b/src/backends/arkworks/domain.rs @@ -0,0 +1,216 @@ +//! Domain separator and interaction pattern for the Dory evaluation proof protocol. +//! +//! The domain separator identifies the protocol and binds public parameters +//! (sigma, zk) into the sponge state via the instance. +//! +//! The interaction pattern declares the exact sequence of prover/verifier/public +//! messages in the protocol. `CheckedProverState` and `CheckedVerifierState` enforce +//! this pattern at runtime — any deviation (wrong message kind, wrong type, wrong +//! count, extra steps, or incomplete patterns) panics with diagnostics. +//! +//! Sub-protocols (sigma1, sigma2, reduce rounds, scalar product) are declared as +//! reusable pattern builders and composed into the top-level pattern via scoped +//! nesting, giving clear diagnostic paths like `"zk::sigma1::challenge"`. + +use ark_bn254::{Fq12, Fr, G1Projective, G2Projective}; +use spongefish::{CheckedProverState, CheckedVerifierState, InteractionPattern, PatternBuilder}; + +/// Encode protocol parameters into an instance byte array for domain binding. +/// +/// The instance encodes `sigma` (4 bytes LE) and `zk` flag (1 byte) so that +/// proofs generated with different parameters are domain-separated. +fn dory_instance(sigma: usize, zk: bool) -> [u8; 5] { + let mut buf = [0u8; 5]; + buf[..4].copy_from_slice(&(sigma as u32).to_le_bytes()); + buf[4] = zk as u8; + buf +} + +// --------------------------------------------------------------------------- +// Reusable sub-protocol patterns +// --------------------------------------------------------------------------- + +/// Sigma1 proof pattern: proves knowledge of (y, rE2, ry). +/// +/// Messages: a1 (G2), a2 (G1), challenge (Fr), z1 z2 z3 (Fr) +fn sigma1_pattern(b: &mut PatternBuilder) { + b.prover_message::("a1"); + b.prover_message::("a2"); + b.verifier_message::("challenge"); + b.prover_message::("z1"); + b.prover_message::("z2"); + b.prover_message::("z3"); +} + +/// Sigma2 proof pattern: proves e(E1, Γ2,fin) - D2 consistency. +/// +/// Messages: a (GT), challenge (Fr), z1 z2 (Fr) +fn sigma2_pattern(b: &mut PatternBuilder) { + b.prover_message::("a"); + b.verifier_message::("challenge"); + b.prover_message::("z1"); + b.prover_message::("z2"); +} + +/// Single Dory-Reduce round pattern (first + second reduce messages). +/// +/// First reduce: D1L, D1R, D2L, D2R (GT), E1β (G1), E2β (G2), β challenge +/// Second reduce: C+, C- (GT), E1+, E1- (G1), E2+, E2- (G2), α challenge +fn reduce_round_pattern(b: &mut PatternBuilder) { + // First reduce message + b.prover_message::("d1-left"); + b.prover_message::("d1-right"); + b.prover_message::("d2-left"); + b.prover_message::("d2-right"); + b.prover_message::("e1-beta"); + b.prover_message::("e2-beta"); + b.verifier_message::("beta"); + + // Second reduce message + b.prover_message::("c-plus"); + b.prover_message::("c-minus"); + b.prover_message::("e1-plus"); + b.prover_message::("e1-minus"); + b.prover_message::("e2-plus"); + b.prover_message::("e2-minus"); + b.verifier_message::("alpha"); +} + +/// ZK scalar product proof pattern. +/// +/// Messages: p1 p2 q r (GT), challenge (Fr), e1 (G1), e2 (G2), r1 r2 r3 (Fr) +fn scalar_product_proof_pattern(b: &mut PatternBuilder) { + b.prover_message::("p1"); + b.prover_message::("p2"); + b.prover_message::("q"); + b.prover_message::("r"); + b.verifier_message::("challenge"); + b.prover_message::("e1"); + b.prover_message::("e2"); + b.prover_message::("r1"); + b.prover_message::("r2"); + b.prover_message::("r3"); +} + +// --------------------------------------------------------------------------- +// Top-level pattern composition +// --------------------------------------------------------------------------- + +/// Declare the interaction pattern for a Dory evaluation proof. +/// +/// Composes sub-protocol patterns (sigma proofs, reduce rounds, scalar product) +/// into the full protocol using scoped nesting. Diagnostic paths look like +/// `"zk::sigma1::challenge"` or `"round-0::beta"`. +/// +/// # Parameters +/// - `sigma`: Log₂ of number of columns (determines number of reduce rounds) +/// - `zk`: Whether zero-knowledge mode is enabled +fn dory_pattern(sigma: usize, zk: bool) -> InteractionPattern { + let mut b = PatternBuilder::new(); + + // Public inputs + b.public_message::<[u8; 4]>("nu"); + b.public_message::<[u8; 4]>("sigma"); + + // VMV message + b.scope("vmv", |b| { + b.prover_message::("c"); + b.prover_message::("d2"); + b.prover_message::("e1"); + }); + + // ZK: commitment blinds + sigma proofs + if zk { + b.scope("zk", |b| { + b.prover_message::("e2"); + b.prover_message::("y-com"); + b.scope("sigma1", sigma1_pattern); + b.scope("sigma2", sigma2_pattern); + }); + } + + // Reduce rounds + for round in 0..sigma { + b.scope(&format!("round-{round}"), reduce_round_pattern); + } + + // Gamma challenge + b.verifier_message::("gamma"); + + // ZK: scalar product proof + if zk { + b.scope("scalar-product", scalar_product_proof_pattern); + } + + // Final scalar product message + b.scope("final", |b| { + b.prover_message::("e1"); + b.prover_message::("e2"); + }); + b.verifier_message::("d"); + + b.finalize() +} + +/// Create a checked Dory prover state with the standard hash. +/// +/// Combines domain separator + instance binding + interaction pattern enforcement. +/// Every `prover_message`/`verifier_message`/`public_message` call on the returned +/// state is validated against the pre-declared pattern — any mismatch panics. +/// +/// After proving, call `prover.check_complete()` to assert the full pattern was +/// followed, then extract proof bytes via `.narg_string().to_vec()`. +pub fn dory_prover(sigma: usize, zk: bool) -> CheckedProverState { + let instance = dory_instance(sigma, zk); + let pattern = dory_pattern(sigma, zk); + let inner = spongefish::domain_separator!("dory-pcs-v2") + .instance(&instance) + .std_prover(); + CheckedProverState::new(inner, pattern) +} + +/// Create a checked Dory verifier state with the standard hash. +/// +/// Combines domain separator + instance binding + interaction pattern enforcement. +/// Every message read from the proof is validated against the pre-declared pattern. +/// +/// After verification, call `verifier.check_eof()` to assert: +/// 1. The full pattern was followed (panics if incomplete) +/// 2. All proof bytes were consumed (returns `Err` if trailing bytes exist) +pub fn dory_verifier(sigma: usize, zk: bool, proof_bytes: &[u8]) -> CheckedVerifierState<'_> { + let instance = dory_instance(sigma, zk); + let pattern = dory_pattern(sigma, zk); + let inner = spongefish::domain_separator!("dory-pcs-v2") + .instance(&instance) + .std_verifier(proof_bytes); + CheckedVerifierState::new(inner, pattern) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn domain_separator_builds_without_zk() { + let _prover = dory_prover(4, false); + } + + #[test] + fn domain_separator_builds_with_zk() { + let _prover = dory_prover(4, true); + } + + #[test] + fn pattern_round_count_increases_with_sigma() { + let p1 = dory_pattern(2, false); + let p2 = dory_pattern(3, false); + assert!(p2.len() > p1.len()); + } + + #[test] + fn zk_pattern_larger_than_transparent() { + let transparent = dory_pattern(4, false); + let zk = dory_pattern(4, true); + assert!(zk.len() > transparent.len()); + } +} diff --git a/src/backends/arkworks/mod.rs b/src/backends/arkworks/mod.rs index 36835b6..2fca411 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::{CheckedProverState, CheckedVerifierState}; #[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..c9998ef --- /dev/null +++ b/src/backends/arkworks/spongefish_codecs.rs @@ -0,0 +1,69 @@ +//! 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())) + })+ + }; +} + +// --------------------------------------------------------------------------- +// CheckedProverState implements ProverTranscript +// --------------------------------------------------------------------------- +impl ProverTranscript for spongefish::CheckedProverState { + fn public_u32(&mut self, val: u32) { + self.public_message(&val.to_le_bytes()); + } + + 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::()) + } +} + +// --------------------------------------------------------------------------- +// CheckedVerifierState implements VerifierTranscript +// --------------------------------------------------------------------------- +impl VerifierTranscript for spongefish::CheckedVerifierState<'_> { + fn public_u32(&mut self, val: u32) -> Result<(), DoryError> { + self.public_message(&val.to_le_bytes()); + 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..7b5009c 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 @@ -64,7 +65,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 @@ -84,7 +85,7 @@ pub fn create_evaluation_proof( sigma: usize, setup: &ProverSetup, transcript: &mut T, -) -> Result<(DoryProof, Option), DoryError> +) -> Result, DoryError> where F: Field, E: PairingCurve, @@ -93,7 +94,7 @@ where E::GT: Group, M1: DoryRoutines, M2: DoryRoutines, - T: Transcript, + T: ProverTranscript, P: MultilinearLagrange, Mo: Mode, { @@ -112,6 +113,10 @@ where }); } + // Public inputs: bind nu and sigma to the sponge state + transcript.public_u32(nu as u32); + transcript.public_u32(sigma as u32); + let (row_commitments, commit_blind) = match row_commitments { Some(rc) => (rc, commit_blind), None => { @@ -148,26 +153,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 +194,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 +246,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 +289,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 +299,126 @@ 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)?; + + // 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..bb74dea 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,21 @@ //! 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>( +//! // 3. Create checked prover and generate evaluation proof +//! let mut prover = dory_prover(sigma, false); +//! prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( //! &polynomial, &point, tier_1_commitments, commit_blind, nu, sigma, -//! &prover_setup, &mut prover_transcript +//! &prover_setup, &mut prover //! )?; +//! let proof_bytes = prover.check_complete().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 with checked verifier (enforces pattern + no trailing bytes) +//! let mut verifier = dory_verifier(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 +103,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 +111,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,12 +229,15 @@ 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 checked prover via `dory_prover(sigma, zk)` +/// 3. Call this function — proof data is written to the transcript +/// 4. Call `prover.check_complete()` to assert the full pattern was followed +/// 5. Extract proof bytes via `.narg_string().to_vec()` /// /// # Parameters /// - `polynomial`: Polynomial implementing MultilinearLagrange trait @@ -245,27 +247,16 @@ where /// - `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( @@ -277,7 +268,7 @@ pub fn prove( sigma: usize, setup: &ProverSetup, transcript: &mut T, -) -> Result<(DoryProof, Option), DoryError> +) -> Result, DoryError> where F: Field, E: PairingCurve, @@ -287,7 +278,7 @@ where M1: DoryRoutines, M2: DoryRoutines, P: MultilinearLagrange, - T: primitives::transcript::Transcript, + T: ProverTranscript, Mo: Mode, { evaluation_proof::create_evaluation_proof::( @@ -305,18 +296,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 checked verifier via `dory_verifier(sigma, zk, &proof_bytes)` +/// 2. Call this function — proof data is read from the transcript +/// 3. Call `verifier.check_eof()?` to assert pattern completeness and 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 +319,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 +336,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..2d8b6e5 100644 --- a/src/primitives/transcript.rs +++ b/src/primitives/transcript.rs @@ -1,29 +1,74 @@ -//! 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 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>; - 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..225ac5d 100644 --- a/tests/arkworks/evaluation.rs +++ b/tests/arkworks/evaluation.rs @@ -19,7 +19,7 @@ fn test_evaluation_proof_small() { .commit::(nu, sigma, &setup) .unwrap(); - let mut prover_transcript = fresh_transcript(); + let mut prover = test_prover(sigma); let result = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, @@ -28,24 +28,27 @@ fn test_evaluation_proof_small() { nu, sigma, &setup, - &mut prover_transcript, + &mut prover, ); assert!(result.is_ok()); - let (proof, _) = result.unwrap(); + let _y = result.unwrap(); let evaluation = poly.evaluate(&point); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let verify_result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(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,7 +66,7 @@ fn test_evaluation_proof_with_precomputed_commitment() { .commit::(nu, sigma, &setup) .unwrap(); - let mut prover_transcript = fresh_transcript(); + let mut prover = test_prover(sigma); let result = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, @@ -72,24 +75,27 @@ fn test_evaluation_proof_with_precomputed_commitment() { nu, sigma, &setup, - &mut prover_transcript, + &mut prover, ); assert!(result.is_ok()); - let (proof, _) = result.unwrap(); + let _y = result.unwrap(); let evaluation = poly.evaluate(&point); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let verify_result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(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,8 +116,8 @@ 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 mut prover = test_prover(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, tier_1, @@ -119,24 +125,27 @@ fn test_evaluation_proof_constant_polynomial() { 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.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let verify_result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(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,8 +163,8 @@ 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 mut prover = test_prover(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, tier_1, @@ -163,21 +172,23 @@ fn test_evaluation_proof_wrong_evaluation_fails() { nu, sigma, &setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); let evaluation = poly.evaluate(&point); let wrong_evaluation = evaluation + ArkFr::one(); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let verify_result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(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 +202,40 @@ 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 mut prover = test_prover(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, tier_1, commit_blind, - 1, - 1, + nu, + sigma, &setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); let evaluation = poly.evaluate(&point); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(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 +244,40 @@ 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 mut prover = test_prover(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, tier_1, commit_blind, - 3, - 3, + nu, + sigma, &setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); let evaluation = poly.evaluate(&point); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(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,8 +297,8 @@ 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 mut prover = test_prover(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, tier_1.clone(), @@ -285,21 +306,24 @@ fn test_multiple_evaluations_same_commitment() { nu, sigma, &setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); let evaluation = poly.evaluate(&point); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(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..c4dd8bd 100644 --- a/tests/arkworks/homomorphic.rs +++ b/tests/arkworks/homomorphic.rs @@ -85,8 +85,8 @@ 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(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &combined_poly, &point, combined_tier1, @@ -94,24 +94,27 @@ fn test_homomorphic_combination_e2e() { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(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,8 +178,8 @@ 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(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &combined_poly, &point, combined_tier1, @@ -184,19 +187,22 @@ fn test_homomorphic_combination_small() { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(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..36912b7 100644 --- a/tests/arkworks/integration.rs +++ b/tests/arkworks/integration.rs @@ -22,8 +22,8 @@ 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 mut prover = test_prover(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, tier_1, @@ -31,23 +31,26 @@ fn test_full_workflow() { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); let evaluation = poly.evaluate(&point); assert_eq!(evaluation, expected_evaluation); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(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,8 +68,8 @@ 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 mut prover = test_prover(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, tier_1, @@ -74,22 +77,25 @@ fn test_workflow_without_precommitment() { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); let evaluation = poly.evaluate(&point); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(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,11 +110,11 @@ 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 mut prover = test_prover(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, tier_1.clone(), @@ -116,22 +122,25 @@ fn test_batched_proofs() { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); let evaluation = poly.evaluate(&point); + let proof_bytes = prover.check_complete().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(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,8 +169,8 @@ fn test_linear_polynomial() { .commit::(nu, sigma, &prover_setup) .unwrap(); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + let mut prover = test_prover(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, tier_1, @@ -169,25 +178,28 @@ fn test_linear_polynomial() { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); let evaluation = poly.evaluate(&point); let expected_eval = poly.evaluate(&point); assert_eq!(evaluation, expected_eval); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(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,8 +216,8 @@ fn test_zero_polynomial() { .commit::(nu, sigma, &prover_setup) .unwrap(); - let mut prover_transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + let mut prover = test_prover(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, tier_1, @@ -213,68 +225,25 @@ fn test_zero_polynomial() { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); let evaluation = poly.evaluate(&point); assert_eq!(evaluation, ArkFr::zero()); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(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..8d9674b 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, CheckedProverState, + CheckedVerifierState, G1Routines, G2Routines, 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 checked prover state for transparent mode testing. +pub fn test_prover(sigma: usize) -> CheckedProverState { + dory_prover(sigma, false) +} + +/// Create a checked verifier state for transparent mode testing. +pub fn test_verifier(sigma: usize, proof_bytes: &[u8]) -> CheckedVerifierState<'_> { + dory_verifier(sigma, false, proof_bytes) +} + +/// Create a checked prover state for ZK mode testing. +#[cfg(feature = "zk")] +pub fn test_prover_zk(sigma: usize) -> CheckedProverState { + dory_prover(sigma, true) +} + +/// Create a checked verifier state for ZK mode testing. +#[cfg(feature = "zk")] +pub fn test_verifier_zk(sigma: usize, proof_bytes: &[u8]) -> CheckedVerifierState<'_> { + dory_verifier(sigma, true, proof_bytes) } pub type TestG1Routines = G1Routines; diff --git a/tests/arkworks/non_square.rs b/tests/arkworks/non_square.rs index 2254f6c..9c69cf4 100644 --- a/tests/arkworks/non_square.rs +++ b/tests/arkworks/non_square.rs @@ -21,8 +21,8 @@ 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 mut prover = test_prover(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, tier_1, @@ -30,23 +30,26 @@ fn test_non_square_matrix_nu_eq_sigma_minus_1() { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .expect("Proof generation should succeed"); let evaluation = poly.evaluate(&point); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(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] @@ -66,7 +69,7 @@ fn test_non_square_matrix_nu_greater_than_sigma_rejected() { .commit::(nu, sigma, &prover_setup) .expect("Commitment should succeed"); - let mut prover_transcript = fresh_transcript(); + let mut prover = test_prover(sigma); let proof_result = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, @@ -75,7 +78,7 @@ fn test_non_square_matrix_nu_greater_than_sigma_rejected() { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ); assert!( @@ -101,8 +104,8 @@ 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 mut prover = test_prover(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, tier_1, @@ -110,26 +113,29 @@ fn test_non_square_matrix_small() { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .expect("Proof generation should succeed"); let evaluation = poly.evaluate(&point); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(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,8 +156,8 @@ 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 mut prover = test_prover(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, tier_1, @@ -159,24 +165,27 @@ fn test_non_square_matrix_very_rectangular() { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .expect("Proof generation should succeed"); let evaluation = poly.evaluate(&point); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier(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..5b60f01 100644 --- a/tests/arkworks/serialization.rs +++ b/tests/arkworks/serialization.rs @@ -1,234 +1,222 @@ -//! 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); - let (tier_2, tier_1, commit_blind) = poly - .commit::(2, 2, &setup) +/// 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_1, commit_blind) = poly + .commit::(nu, sigma, &setup) .unwrap(); - let mut transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + + let mut prover = test_prover(sigma); + prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, tier_1, commit_blind, - 2, - 2, + nu, + sigma, &setup, - &mut transcript, + &mut prover, ) .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, - ) - .unwrap(); - - (proof, tier_2, point) + prover.check_complete().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 mut prover = test_prover(sigma); + prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, tier_1, commit_blind, - 2, - 2, + nu, + sigma, &setup, - &mut transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.check_complete().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(sigma, &proof_bytes); + verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, eval, &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); - let (tier_2, tier_1, commit_blind) = poly - .commit::(2, 2, &setup) + 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_1, commit_blind) = poly + .commit::(nu, sigma, &setup) .unwrap(); - let mut transcript = fresh_transcript(); - let (proof, _) = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( + let mut prover = test_prover_zk(sigma); + prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( &poly, &point, tier_1, commit_blind, - 2, - 2, + nu, + sigma, &setup, - &mut transcript, - ) - .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, + &mut prover, ) .unwrap(); - (proof, tier_2, point) + prover.check_complete().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 mut prover = test_prover_zk(sigma); + prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( &poly, &point, tier_1, commit_blind, - 2, - 2, + nu, + sigma, &setup, - &mut transcript, + &mut prover, ) .unwrap(); - - let mut buf = Vec::new(); - proof.serialize_compressed(&mut buf).unwrap(); - let decoded = ArkDoryProof::deserialize_compressed(&buf[..]).unwrap(); + let proof_bytes = prover.check_complete().narg_string().to_vec(); let eval = poly.evaluate(&point); - let mut vt = fresh_transcript(); - verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier_zk(sigma, &proof_bytes); + verify::<_, BN254, TestG1Routines, TestG2Routines, _, ZK>( tier_2, eval, &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..7417eb1 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,18 +23,17 @@ 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 mut prover = test_prover(sigma); + prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly, &point, tier_1, @@ -39,10 +41,11 @@ fn create_valid_proof_components( nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); let evaluation = poly.evaluate(&point); + let proof_bytes = prover.check_complete().narg_string().to_vec(); ( prover_setup, @@ -51,595 +54,453 @@ 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(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); +fn test_soundness_wrong_commitment() { + let nu = 4; + let sigma = 4; + let (prover_setup, verifier_setup, _, _, _, _, _) = create_valid_proof(256, nu, sigma); - 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"); -} + let poly1 = random_polynomial(256); + let poly2 = random_polynomial(256); + let point = random_point(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 (commitment1, _, _) = poly1 + .commit::(nu, sigma, &prover_setup) + .unwrap(); - if !proof.first_messages.is_empty() { - proof.first_messages[0].d1_left = ArkGT(Fq12::rand(&mut rand::thread_rng())); - } + let (_, tier_1_poly2, commit_blind2) = poly2 + .commit::(nu, sigma, &prover_setup) + .unwrap(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( - commitment, - evaluation, + let mut prover = test_prover(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())); - } + tier_1_poly2, + commit_blind2, + nu, + sigma, + &prover_setup, + &mut prover, + ) + .unwrap(); + let evaluation = poly2.evaluate(&point); + let proof_bytes = prover.check_complete().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(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(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(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..9e23485 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() { @@ -24,8 +22,8 @@ fn test_zk_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, _, _, ZK>( + let mut prover = test_prover_zk(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( &poly, &point, tier_1, @@ -33,23 +31,26 @@ fn test_zk_full_workflow() { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); let evaluation = poly.evaluate(&point); assert_eq!(evaluation, expected_evaluation); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier_zk(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,8 +68,8 @@ 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(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( &poly, &point, tier_1, @@ -76,18 +77,20 @@ fn test_zk_small_polynomial() { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier_zk(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 +98,7 @@ fn test_zk_small_polynomial() { "ZK small polynomial test failed: {:?}", result ); + verifier.check_eof().unwrap(); } #[test] @@ -112,8 +116,8 @@ 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(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( &poly, &point, tier_1, @@ -121,18 +125,20 @@ fn test_zk_larger_polynomial() { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier_zk(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 +146,7 @@ fn test_zk_larger_polynomial() { "ZK larger polynomial test failed: {:?}", result ); + verifier.check_eof().unwrap(); } #[test] @@ -158,8 +165,8 @@ 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(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( &poly, &point, tier_1, @@ -167,18 +174,20 @@ fn test_zk_non_square_matrix() { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier_zk(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 +195,7 @@ fn test_zk_non_square_matrix() { "ZK non-square matrix test failed: {:?}", result ); + verifier.check_eof().unwrap(); } #[test] @@ -204,8 +214,8 @@ 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(sigma); + let _y = create_evaluation_proof::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( &poly, &point, Some(tier_1), @@ -213,21 +223,26 @@ fn test_zk_hidden_evaluation() { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.check_complete().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(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 +250,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,8 +269,8 @@ 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(sigma); + let _y = create_evaluation_proof::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( &poly, &point, Some(tier_1), @@ -311,18 +278,20 @@ fn test_zk_hidden_evaluation_larger() { nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); + let proof_bytes = prover.check_complete().narg_string().to_vec(); - let mut verifier_transcript = fresh_transcript(); - let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _>( + let mut verifier = test_verifier_zk(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 +299,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,8 +328,9 @@ 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 mut prover = test_prover_zk(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, ZK>( &poly, &point, tier_1, @@ -365,247 +338,320 @@ fn create_valid_zk_proof_components( nu, sigma, &prover_setup, - &mut prover_transcript, + &mut prover, ) .unwrap(); let evaluation = poly.evaluate(&point); + let proof_bytes = prover.check_complete().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(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); +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 s) = proof.sigma2_proof { - s.z1 = ArkFr(Fr::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 sigma2 z1"); + 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_sigma2_a() { - 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 s) = proof.sigma2_proof { - s.a = 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 sigma2 a"); + 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 commitment and verified +/// via sigma proofs. The `evaluation` parameter to `verify()` is not used +/// in ZK mode — the proof itself binds the correct evaluation. This is +/// correct ZK behavior: the evaluation is private to the prover. #[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_evaluation_is_hidden() { + 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())); - } + // In ZK mode, any evaluation value can be passed — the proof is + // self-contained and the sigma proofs bind the correct evaluation. + 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 e1" + result.is_ok(), + "ZK verify ignores evaluation param — proof is self-contained" ); } +/// Zeroed proof bytes 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_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); - if let Some(ref mut sp) = proof.scalar_product_proof { - sp.p1 = 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 scalar product p1" + 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_sp_r3() { - let (verifier_setup, point, commitment, evaluation, mut proof) = - create_valid_zk_proof_components(256, 4, 4); +fn test_zk_soundness_transparent_proof_as_zk() { + let nu = 4; + let sigma = 4; - if let Some(ref mut sp) = proof.scalar_product_proof { - sp.r3 = ArkFr(Fr::rand(&mut rand::thread_rng())); - } + // 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(); - let result = verify_tampered_zk_proof(commitment, evaluation, &point, &proof, verifier_setup); + let mut prover = test_prover(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + &poly, + &point, + tier_1, + commit_blind, + nu, + sigma, + &prover_setup, + &mut prover, + ) + .unwrap(); + let transparent_bytes = prover.check_complete().narg_string().to_vec(); + let evaluation = poly.evaluate(&point); + + // 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 with tampered scalar product r3" + "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_vmv_c() { - let (verifier_setup, point, commitment, evaluation, mut proof) = - create_valid_zk_proof_components(256, 4, 4); - - proof.vmv_message.c = 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 VMV c in ZK"); -} - -#[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())); - - let result = verify_tampered_zk_proof(commitment, evaluation, &point, &proof, verifier_setup); - assert!(result.is_err(), "Should fail with tampered VMV d2 in ZK"); -} - -#[test] -fn test_zk_soundness_tampered_vmv_e1() { - let (verifier_setup, point, commitment, evaluation, mut proof) = - create_valid_zk_proof_components(256, 4, 4); - - proof.vmv_message.e1 = ArkG1(G1Projective::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 VMV e1 in ZK"); -} - -#[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()))); - - let result = verify_tampered_zk_proof(commitment, evaluation, &point, &proof, verifier_setup); - assert!(result.is_err(), "Should fail with tampered e2 in ZK"); -} +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); -#[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 (prover_setup, _) = test_setup_pair(nu + sigma + 2); + let poly = random_polynomial(256); + let point = random_point(nu + sigma); + let (_, tier_1, commit_blind) = poly + .commit::(nu, sigma, &prover_setup) + .unwrap(); - proof.y_com = Some(ArkG1(G1Projective::rand(&mut rand::thread_rng()))); + let mut prover = test_prover(sigma); + let _y = prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( + &poly, + &point, + tier_1, + commit_blind, + nu, + sigma, + &prover_setup, + &mut prover, + ) + .unwrap(); + let transparent_bytes = prover.check_complete().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() + ); } From fafce858445ae06faccb7830055322fab065d6cc Mon Sep 17 00:00:00 2001 From: shreyas-londhe Date: Mon, 6 Apr 2026 11:59:48 +0530 Subject: [PATCH 2/3] refactor: replace rolling-hash transcript with spongefish NARG protocol Replace Blake2b rolling-hash Fiat-Shamir with spongefish duplex-sponge (upstream arkworks-rs/spongefish v0.5.1). Proofs become opaque NARG byte strings. Bind commitment, evaluation, and point as public messages into the sponge state for defense-in-depth Fiat-Shamir domain separation. --- Cargo.lock | 174 ++++++++++------- Cargo.toml | 2 +- src/backends/arkworks/domain.rs | 213 +++------------------ src/backends/arkworks/mod.rs | 2 +- src/backends/arkworks/spongefish_codecs.rs | 26 ++- src/evaluation_proof.rs | 20 ++ src/lib.rs | 28 +-- src/primitives/transcript.rs | 18 ++ 8 files changed, 208 insertions(+), 275 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 800bca1..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", ] @@ -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,11 +269,17 @@ 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.2.17" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" dependencies = [ "libc", ] @@ -364,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]] @@ -497,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" @@ -553,10 +577,11 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.6" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa" dependencies = [ + "cfg-if", "cpufeatures", ] @@ -644,9 +669,9 @@ checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "p3-challenger" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20e42ba74a49c08c6e99f74cd9b343bfa31aa5721fea55079b18e3fd65f1dcbc" +checksum = "4a0b490c745a7d2adeeafff06411814c8078c432740162332b3cd71be0158a76" dependencies = [ "p3-field", "p3-maybe-rayon", @@ -658,9 +683,9 @@ dependencies = [ [[package]] name = "p3-dft" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e63fa5eb1bd12a240089e72ae3fe10350944d9c166d00a3bfd2a1794db65cf5c" +checksum = "55301e91544440254977108b85c32c09d7ea05f2f0dd61092a2825339906a4a7" dependencies = [ "itertools 0.14.0", "p3-field", @@ -673,74 +698,75 @@ dependencies = [ [[package]] name = "p3-field" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ebfdb6ef992ae64e9e8f449ac46516ffa584f11afbdf9ee244288c2a633cdf4" +checksum = "85affca7fc983889f260655c4cf74163eebb94605f702e4b6809ead707cba54f" dependencies = [ "itertools 0.14.0", "num-bigint", "p3-maybe-rayon", "p3-util", "paste", - "rand 0.9.2", + "rand 0.10.0", "serde", "tracing", ] [[package]] name = "p3-koala-bear" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5113f50002c56006685b7d7ae12db568150aa1d4bfb092b883d64ece20138042" +checksum = "7369a8eb2a27b314338f6b4b77e6a701ad31f1deff25b75bd302fc08c924c9b3" dependencies = [ "p3-challenger", "p3-field", + "p3-mds", "p3-monty-31", + "p3-poseidon1", "p3-poseidon2", "p3-symmetric", - "rand 0.9.2", + "rand 0.10.0", ] [[package]] name = "p3-matrix" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5542f96504dae8100c91398fb1e3f5ec669eb9c73d9e0b018a93b5fe32bad230" +checksum = "53428126b009071563d1d07305a9de8be0d21de00b57d2475289ee32ffca6577" dependencies = [ "itertools 0.14.0", "p3-field", "p3-maybe-rayon", "p3-util", - "rand 0.9.2", + "rand 0.10.0", "serde", "tracing", - "transpose", ] [[package]] name = "p3-maybe-rayon" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e5669ca75645f99cd001e9d0289a4eeff2bc2cd9dc3c6c3aaf22643966e83df" +checksum = "082bf467011c06c768c579ec6eb9accb5e1e62108891634cc770396e917f978a" [[package]] name = "p3-mds" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038763af23df9da653065867fd85b38626079031576c86fd537097e5be6a0da0" +checksum = "35209e6214102ea6ec6b8cb1b9c15a9b8e597a39f9173597c957f123bced81b3" dependencies = [ "p3-dft", "p3-field", "p3-symmetric", "p3-util", - "rand 0.9.2", + "rand 0.10.0", ] [[package]] name = "p3-monty-31" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a981d60da3d8cbf8561014e2c186068578405fd69098fa75b43d4afb364a47" +checksum = "ffa8c99ec50c035020bbf5457c6a729ba6a975719c1a8dd3f16421081e4f650c" dependencies = [ "itertools 0.14.0", "num-bigint", @@ -749,48 +775,61 @@ dependencies = [ "p3-matrix", "p3-maybe-rayon", "p3-mds", + "p3-poseidon1", "p3-poseidon2", "p3-symmetric", "p3-util", "paste", - "rand 0.9.2", + "rand 0.10.0", "serde", "spin", "tracing", - "transpose", +] + +[[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.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "903b73e4f9a7781a18561c74dc169cf03333497b57a8dd02aaeb130c0f386599" +checksum = "256a668a9ba916f8767552f13d0ba50d18968bc74a623bfdafa41e2970c944d0" dependencies = [ "p3-field", "p3-mds", "p3-symmetric", "p3-util", - "rand 0.9.2", + "rand 0.10.0", ] [[package]] name = "p3-symmetric" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd788f04e86dd5c35dd87cad29eefdb6371d2fd5f7664451382eeacae3c3ed0" +checksum = "6c60a71a1507c13611b0f2b0b6e83669fd5b76f8e3115bcbced5ccfdf3ca7807" dependencies = [ "itertools 0.14.0", "p3-field", + "p3-util", "serde", ] [[package]] name = "p3-util" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "663b16021930bc600ecada915c6c3965730a3b9d6a6c23434ccf70bfc29d6881" +checksum = "f8b766b9e9254bf3fa98d76e42cf8a5b30628c182dfd5272d270076ee12f0fc0" dependencies = [ "serde", + "transpose", ] [[package]] @@ -873,11 +912,11 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.2" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" dependencies = [ - "rand_core 0.9.5", + "rand_core 0.10.0", ] [[package]] @@ -901,9 +940,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.9.5" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" [[package]] name = "rayon" @@ -1020,22 +1059,22 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.9" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.11.2", ] [[package]] name = "sha3" -version = "0.10.8" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1" dependencies = [ - "digest", + "digest 0.11.2", "keccak", ] @@ -1065,14 +1104,13 @@ dependencies = [ [[package]] name = "spongefish" -version = "1.0.0-rc1" -source = "git+https://github.com/shreyas-londhe/spongefish?rev=45df37a7d4d707f1e416864f18c0b1458ec5942c#45df37a7d4d707f1e416864f18c0b1458ec5942c" +version = "0.5.1" +source = "git+https://github.com/arkworks-rs/spongefish?tag=v0.5.1#715a277ef522472bb71b06037507e541d1070555" dependencies = [ "ark-ec", "ark-ff", "ark-serialize", - "blake2", - "digest", + "digest 0.11.2", "p3-koala-bear", "rand 0.8.5", "sha2", @@ -1086,12 +1124,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - [[package]] name = "syn" version = "2.0.117" diff --git a/Cargo.toml b/Cargo.toml index 5047352..0e7693d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,7 +64,7 @@ ark-serialize = { version = "0.5", optional = true } ark-std = { version = "0.5", optional = true } bincode = { version = "1.3", optional = true } serde = { version = "1.0", optional = true, features = ["derive"] } -spongefish = { git = "https://github.com/shreyas-londhe/spongefish", rev = "45df37a7d4d707f1e416864f18c0b1458ec5942c", optional = true, features = ["ark-ec", "blake2", "pattern"] } +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/src/backends/arkworks/domain.rs b/src/backends/arkworks/domain.rs index b431450..d709dc0 100644 --- a/src/backends/arkworks/domain.rs +++ b/src/backends/arkworks/domain.rs @@ -1,189 +1,42 @@ -//! Domain separator and interaction pattern for the Dory evaluation proof protocol. +//! Domain separator and instance encoding for the Dory evaluation proof protocol. //! //! The domain separator identifies the protocol and binds public parameters -//! (sigma, zk) into the sponge state via the instance. -//! -//! The interaction pattern declares the exact sequence of prover/verifier/public -//! messages in the protocol. `CheckedProverState` and `CheckedVerifierState` enforce -//! this pattern at runtime — any deviation (wrong message kind, wrong type, wrong -//! count, extra steps, or incomplete patterns) panics with diagnostics. -//! -//! Sub-protocols (sigma1, sigma2, reduce rounds, scalar product) are declared as -//! reusable pattern builders and composed into the top-level pattern via scoped -//! nesting, giving clear diagnostic paths like `"zk::sigma1::challenge"`. +//! (nu, sigma, zk) into the sponge state via the instance. -use ark_bn254::{Fq12, Fr, G1Projective, G2Projective}; -use spongefish::{CheckedProverState, CheckedVerifierState, InteractionPattern, PatternBuilder}; +use spongefish::{ProverState, VerifierState}; /// Encode protocol parameters into an instance byte array for domain binding. /// -/// The instance encodes `sigma` (4 bytes LE) and `zk` flag (1 byte) so that -/// proofs generated with different parameters are domain-separated. -fn dory_instance(sigma: usize, zk: bool) -> [u8; 5] { - let mut buf = [0u8; 5]; - buf[..4].copy_from_slice(&(sigma as u32).to_le_bytes()); - buf[4] = zk as u8; +/// 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 } -// --------------------------------------------------------------------------- -// Reusable sub-protocol patterns -// --------------------------------------------------------------------------- - -/// Sigma1 proof pattern: proves knowledge of (y, rE2, ry). -/// -/// Messages: a1 (G2), a2 (G1), challenge (Fr), z1 z2 z3 (Fr) -fn sigma1_pattern(b: &mut PatternBuilder) { - b.prover_message::("a1"); - b.prover_message::("a2"); - b.verifier_message::("challenge"); - b.prover_message::("z1"); - b.prover_message::("z2"); - b.prover_message::("z3"); -} - -/// Sigma2 proof pattern: proves e(E1, Γ2,fin) - D2 consistency. -/// -/// Messages: a (GT), challenge (Fr), z1 z2 (Fr) -fn sigma2_pattern(b: &mut PatternBuilder) { - b.prover_message::("a"); - b.verifier_message::("challenge"); - b.prover_message::("z1"); - b.prover_message::("z2"); -} - -/// Single Dory-Reduce round pattern (first + second reduce messages). -/// -/// First reduce: D1L, D1R, D2L, D2R (GT), E1β (G1), E2β (G2), β challenge -/// Second reduce: C+, C- (GT), E1+, E1- (G1), E2+, E2- (G2), α challenge -fn reduce_round_pattern(b: &mut PatternBuilder) { - // First reduce message - b.prover_message::("d1-left"); - b.prover_message::("d1-right"); - b.prover_message::("d2-left"); - b.prover_message::("d2-right"); - b.prover_message::("e1-beta"); - b.prover_message::("e2-beta"); - b.verifier_message::("beta"); - - // Second reduce message - b.prover_message::("c-plus"); - b.prover_message::("c-minus"); - b.prover_message::("e1-plus"); - b.prover_message::("e1-minus"); - b.prover_message::("e2-plus"); - b.prover_message::("e2-minus"); - b.verifier_message::("alpha"); -} - -/// ZK scalar product proof pattern. -/// -/// Messages: p1 p2 q r (GT), challenge (Fr), e1 (G1), e2 (G2), r1 r2 r3 (Fr) -fn scalar_product_proof_pattern(b: &mut PatternBuilder) { - b.prover_message::("p1"); - b.prover_message::("p2"); - b.prover_message::("q"); - b.prover_message::("r"); - b.verifier_message::("challenge"); - b.prover_message::("e1"); - b.prover_message::("e2"); - b.prover_message::("r1"); - b.prover_message::("r2"); - b.prover_message::("r3"); -} - -// --------------------------------------------------------------------------- -// Top-level pattern composition -// --------------------------------------------------------------------------- - -/// Declare the interaction pattern for a Dory evaluation proof. -/// -/// Composes sub-protocol patterns (sigma proofs, reduce rounds, scalar product) -/// into the full protocol using scoped nesting. Diagnostic paths look like -/// `"zk::sigma1::challenge"` or `"round-0::beta"`. -/// -/// # Parameters -/// - `sigma`: Log₂ of number of columns (determines number of reduce rounds) -/// - `zk`: Whether zero-knowledge mode is enabled -fn dory_pattern(sigma: usize, zk: bool) -> InteractionPattern { - let mut b = PatternBuilder::new(); - - // Public inputs - b.public_message::<[u8; 4]>("nu"); - b.public_message::<[u8; 4]>("sigma"); - - // VMV message - b.scope("vmv", |b| { - b.prover_message::("c"); - b.prover_message::("d2"); - b.prover_message::("e1"); - }); - - // ZK: commitment blinds + sigma proofs - if zk { - b.scope("zk", |b| { - b.prover_message::("e2"); - b.prover_message::("y-com"); - b.scope("sigma1", sigma1_pattern); - b.scope("sigma2", sigma2_pattern); - }); - } - - // Reduce rounds - for round in 0..sigma { - b.scope(&format!("round-{round}"), reduce_round_pattern); - } - - // Gamma challenge - b.verifier_message::("gamma"); - - // ZK: scalar product proof - if zk { - b.scope("scalar-product", scalar_product_proof_pattern); - } - - // Final scalar product message - b.scope("final", |b| { - b.prover_message::("e1"); - b.prover_message::("e2"); - }); - b.verifier_message::("d"); - - b.finalize() -} - -/// Create a checked Dory prover state with the standard hash. -/// -/// Combines domain separator + instance binding + interaction pattern enforcement. -/// Every `prover_message`/`verifier_message`/`public_message` call on the returned -/// state is validated against the pre-declared pattern — any mismatch panics. +/// Create a Dory prover state with the standard hash. /// -/// After proving, call `prover.check_complete()` to assert the full pattern was -/// followed, then extract proof bytes via `.narg_string().to_vec()`. -pub fn dory_prover(sigma: usize, zk: bool) -> CheckedProverState { - let instance = dory_instance(sigma, zk); - let pattern = dory_pattern(sigma, zk); - let inner = spongefish::domain_separator!("dory-pcs-v2") +/// 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(); - CheckedProverState::new(inner, pattern) + .std_prover() } -/// Create a checked Dory verifier state with the standard hash. +/// Create a Dory verifier state with the standard hash. /// -/// Combines domain separator + instance binding + interaction pattern enforcement. -/// Every message read from the proof is validated against the pre-declared pattern. -/// -/// After verification, call `verifier.check_eof()` to assert: -/// 1. The full pattern was followed (panics if incomplete) -/// 2. All proof bytes were consumed (returns `Err` if trailing bytes exist) -pub fn dory_verifier(sigma: usize, zk: bool, proof_bytes: &[u8]) -> CheckedVerifierState<'_> { - let instance = dory_instance(sigma, zk); - let pattern = dory_pattern(sigma, zk); - let inner = spongefish::domain_separator!("dory-pcs-v2") +/// 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); - CheckedVerifierState::new(inner, pattern) + .std_verifier(proof_bytes) } #[cfg(test)] @@ -192,25 +45,11 @@ mod tests { #[test] fn domain_separator_builds_without_zk() { - let _prover = dory_prover(4, false); + let _prover = dory_prover(4, 4, false); } #[test] fn domain_separator_builds_with_zk() { - let _prover = dory_prover(4, true); - } - - #[test] - fn pattern_round_count_increases_with_sigma() { - let p1 = dory_pattern(2, false); - let p2 = dory_pattern(3, false); - assert!(p2.len() > p1.len()); - } - - #[test] - fn zk_pattern_larger_than_transparent() { - let transparent = dory_pattern(4, false); - let zk = dory_pattern(4, true); - assert!(zk.len() > transparent.len()); + let _prover = dory_prover(4, 4, true); } } diff --git a/src/backends/arkworks/mod.rs b/src/backends/arkworks/mod.rs index 2fca411..79689d6 100644 --- a/src/backends/arkworks/mod.rs +++ b/src/backends/arkworks/mod.rs @@ -18,7 +18,7 @@ pub use ark_pairing::BN254; pub use ark_poly::ArkworksPolynomial; pub use ark_setup::{ArkworksProverSetup, ArkworksVerifierSetup}; pub use domain::{dory_prover, dory_verifier}; -pub use spongefish::{CheckedProverState, CheckedVerifierState}; +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 index c9998ef..ef145c3 100644 --- a/src/backends/arkworks/spongefish_codecs.rs +++ b/src/backends/arkworks/spongefish_codecs.rs @@ -28,13 +28,21 @@ macro_rules! impl_verifier_read { } // --------------------------------------------------------------------------- -// CheckedProverState implements ProverTranscript +// ProverState implements ProverTranscript // --------------------------------------------------------------------------- -impl ProverTranscript for spongefish::CheckedProverState { +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; @@ -48,14 +56,24 @@ impl ProverTranscript for spongefish::CheckedProverState { } // --------------------------------------------------------------------------- -// CheckedVerifierState implements VerifierTranscript +// VerifierState implements VerifierTranscript // --------------------------------------------------------------------------- -impl VerifierTranscript for spongefish::CheckedVerifierState<'_> { +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); diff --git a/src/evaluation_proof.rs b/src/evaluation_proof.rs index 7b5009c..c8cba45 100644 --- a/src/evaluation_proof.rs +++ b/src/evaluation_proof.rs @@ -56,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). @@ -79,6 +81,8 @@ 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, @@ -117,6 +121,14 @@ where 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 => { @@ -303,6 +315,14 @@ where 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()?; diff --git a/src/lib.rs b/src/lib.rs index bb74dea..aa52dcf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,16 +52,17 @@ //! let (tier_2_commitment, tier_1_commitments, commit_blind) = polynomial //! .commit::(nu, sigma, &prover_setup)?; //! -//! // 3. Create checked prover and generate evaluation proof -//! let mut prover = dory_prover(sigma, false); +//! // 3. Create prover and generate evaluation proof +//! let mut prover = dory_prover(nu, sigma, false); //! prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( -//! &polynomial, &point, tier_1_commitments, commit_blind, nu, sigma, +//! &polynomial, &point, &tier_2_commitment, &evaluation, +//! tier_1_commitments, commit_blind, nu, sigma, //! &prover_setup, &mut prover //! )?; -//! let proof_bytes = prover.check_complete().narg_string().to_vec(); +//! let proof_bytes = prover.narg_string().to_vec(); //! -//! // 4. Verify with checked verifier (enforces pattern + no trailing bytes) -//! let mut verifier = dory_verifier(sigma, false, &proof_bytes); +//! // 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 @@ -234,14 +235,15 @@ where /// /// # Workflow /// 1. Call `polynomial.commit(nu, sigma, setup)` to get `(tier_2, row_commitments, commit_blind)` -/// 2. Create a checked prover via `dory_prover(sigma, zk)` +/// 2. Create a prover via `dory_prover(nu, sigma, zk)` /// 3. Call this function — proof data is written to the transcript -/// 4. Call `prover.check_complete()` to assert the full pattern was followed -/// 5. Extract proof bytes via `.narg_string().to_vec()` +/// 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) @@ -262,6 +264,8 @@ where pub fn prove( polynomial: &P, point: &[F], + commitment: &E::GT, + evaluation: &F, row_commitments: Vec, commit_blind: F, nu: usize, @@ -284,6 +288,8 @@ where evaluation_proof::create_evaluation_proof::( polynomial, point, + commitment, + evaluation, Some(row_commitments), commit_blind, nu, @@ -299,9 +305,9 @@ where /// Proof elements are read from the NARG transcript — no separate proof struct is needed. /// /// # Workflow -/// 1. Create a checked verifier via `dory_verifier(sigma, zk, &proof_bytes)` +/// 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 assert pattern completeness and reject trailing bytes +/// 3. Call `verifier.check_eof()?` to reject trailing bytes /// /// # Parameters /// - `commitment`: Polynomial commitment (in GT) - can be a combined commitment for homomorphic proofs diff --git a/src/primitives/transcript.rs b/src/primitives/transcript.rs index 2d8b6e5..f3a4bd2 100644 --- a/src/primitives/transcript.rs +++ b/src/primitives/transcript.rs @@ -15,6 +15,12 @@ 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); @@ -42,6 +48,18 @@ pub trait VerifierTranscript { /// 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>; + /// Read a GT element from the NARG string. /// /// # Errors From 16364e8b1874ac21c105538af8bb0153b629c9df Mon Sep 17 00:00:00 2001 From: shreyas-londhe Date: Mon, 6 Apr 2026 11:59:57 +0530 Subject: [PATCH 3/3] test: update tests, benches, and examples for spongefish NARG API Migrate all call sites to upstream ProverState/VerifierState. Remove check_complete() calls (pattern enforcement dropped). Proof extraction is now prover.narg_string().to_vec() directly. --- benches/arkworks_proof.rs | 28 ++++++--- examples/basic_e2e.rs | 8 ++- examples/homomorphic.rs | 8 ++- examples/homomorphic_mixed_sizes.rs | 8 ++- examples/non_square.rs | 8 ++- examples/zk_e2e.rs | 8 ++- examples/zk_statistical.rs | 8 ++- tests/arkworks/evaluation.rs | 86 ++++++++++++++----------- tests/arkworks/homomorphic.rs | 16 +++-- tests/arkworks/integration.rs | 60 ++++++++++-------- tests/arkworks/mod.rs | 28 ++++----- tests/arkworks/non_square.rs | 37 ++++++----- tests/arkworks/serialization.rs | 42 ++++++++----- tests/arkworks/soundness.rs | 27 ++++---- tests/arkworks/zk.rs | 98 ++++++++++++++++++----------- 15 files changed, 279 insertions(+), 191 deletions(-) diff --git a/benches/arkworks_proof.rs b/benches/arkworks_proof.rs index f1cac2c..d83415c 100644 --- a/benches/arkworks_proof.rs +++ b/benches/arkworks_proof.rs @@ -72,16 +72,20 @@ 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 prover = dory_prover(sigma, false); + 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), @@ -103,10 +107,14 @@ fn bench_verify(c: &mut Criterion) { .commit::(nu, sigma, &prover_setup) .unwrap(); - let mut prover = dory_prover(sigma, false); + 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, @@ -115,13 +123,11 @@ fn bench_verify(c: &mut Criterion) { &mut prover, ) .unwrap(); - let proof_bytes = prover.check_complete().narg_string().to_vec(); - - 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 verifier = dory_verifier(sigma, false, &proof_bytes); + let mut verifier = dory_verifier(nu, sigma, false, &proof_bytes); verify::<_, BN254, G1Routines, G2Routines, _, Transparent>( black_box(tier_2), black_box(evaluation), @@ -165,10 +171,12 @@ fn bench_end_to_end(c: &mut Criterion) { let point: Vec = (0..num_vars).map(|_| ArkFr::random()).collect(); let evaluation = poly.evaluate(&point); - let mut prover = dory_prover(sigma, false); + let mut prover = dory_prover(nu, sigma, false); prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, @@ -177,9 +185,9 @@ fn bench_end_to_end(c: &mut Criterion) { &mut prover, ) .unwrap(); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = dory_verifier(sigma, false, &proof_bytes); + let mut verifier = dory_verifier(nu, sigma, false, &proof_bytes); verify::<_, BN254, G1Routines, G2Routines, _, Transparent>( tier_2, evaluation, diff --git a/examples/basic_e2e.rs b/examples/basic_e2e.rs index 1277774..0900607 100644 --- a/examples/basic_e2e.rs +++ b/examples/basic_e2e.rs @@ -31,10 +31,12 @@ fn main() -> Result<(), Box> { let point: Vec = (0..num_vars).map(|_| ArkFr::random()).collect(); let evaluation = poly.evaluate(&point); - let mut prover = dory_prover(sigma, false); + let mut prover = dory_prover(nu, sigma, false); prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, @@ -42,9 +44,9 @@ fn main() -> Result<(), Box> { &prover_setup, &mut prover, )?; - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = dory_verifier(sigma, false, &proof_bytes); + let mut verifier = dory_verifier(nu, sigma, false, &proof_bytes); verify::<_, BN254, G1Routines, G2Routines, _, Transparent>( tier_2, evaluation, diff --git a/examples/homomorphic.rs b/examples/homomorphic.rs index 02e8b54..d0207df 100644 --- a/examples/homomorphic.rs +++ b/examples/homomorphic.rs @@ -86,10 +86,12 @@ fn main() -> Result<(), Box> { } assert_eq!(evaluation, expected_eval); - let mut prover = dory_prover(sigma, false); + let mut prover = dory_prover(nu, sigma, false); prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( &combined_poly, &point, + &combined_tier2, + &evaluation, combined_tier1, ArkFr::zero(), nu, @@ -97,9 +99,9 @@ fn main() -> Result<(), Box> { &prover_setup, &mut prover, )?; - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = dory_verifier(sigma, false, &proof_bytes); + let mut verifier = dory_verifier(nu, sigma, false, &proof_bytes); verify::<_, BN254, G1Routines, G2Routines, _, Transparent>( combined_tier2, evaluation, diff --git a/examples/homomorphic_mixed_sizes.rs b/examples/homomorphic_mixed_sizes.rs index fae87fe..a5133f5 100644 --- a/examples/homomorphic_mixed_sizes.rs +++ b/examples/homomorphic_mixed_sizes.rs @@ -83,10 +83,12 @@ fn main() -> Result<(), Box> { let nu = 2; let sigma = 2; - let mut prover = dory_prover(sigma, false); + let mut prover = dory_prover(nu, sigma, false); prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( &combined_poly, &point, + &combined_tier2, + &evaluation, combined_tier1, ArkFr::zero(), nu, @@ -94,9 +96,9 @@ fn main() -> Result<(), Box> { &prover_setup, &mut prover, )?; - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = dory_verifier(sigma, false, &proof_bytes); + let mut verifier = dory_verifier(nu, sigma, false, &proof_bytes); verify::<_, BN254, G1Routines, G2Routines, _, Transparent>( combined_tier2, evaluation, diff --git a/examples/non_square.rs b/examples/non_square.rs index 0e2a918..bfb5d75 100644 --- a/examples/non_square.rs +++ b/examples/non_square.rs @@ -30,10 +30,12 @@ fn main() -> Result<(), Box> { let point: Vec = (0..num_vars).map(|_| ArkFr::random()).collect(); let evaluation = poly.evaluate(&point); - let mut prover = dory_prover(sigma, false); + let mut prover = dory_prover(nu, sigma, false); prove::<_, BN254, G1Routines, G2Routines, _, _, Transparent>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, @@ -41,9 +43,9 @@ fn main() -> Result<(), Box> { &prover_setup, &mut prover, )?; - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = dory_verifier(sigma, false, &proof_bytes); + let mut verifier = dory_verifier(nu, sigma, false, &proof_bytes); verify::<_, BN254, G1Routines, G2Routines, _, Transparent>( tier_2, evaluation, diff --git a/examples/zk_e2e.rs b/examples/zk_e2e.rs index 11241cb..26fc9fc 100644 --- a/examples/zk_e2e.rs +++ b/examples/zk_e2e.rs @@ -32,10 +32,12 @@ fn main() -> Result<(), Box> { let point: Vec = (0..num_vars).map(|_| ArkFr::random()).collect(); let evaluation = poly.evaluate(&point); - let mut prover = dory_prover(sigma, true); + let mut prover = dory_prover(nu, sigma, true); prove::<_, BN254, G1Routines, G2Routines, _, _, ZK>( &poly, &point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, @@ -43,9 +45,9 @@ fn main() -> Result<(), Box> { &prover_setup, &mut prover, )?; - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = dory_verifier(sigma, true, &proof_bytes); + let mut verifier = dory_verifier(nu, sigma, true, &proof_bytes); verify::<_, BN254, G1Routines, G2Routines, _, ZK>( tier_2, evaluation, diff --git a/examples/zk_statistical.rs b/examples/zk_statistical.rs index 47d8144..d46d289 100644 --- a/examples/zk_statistical.rs +++ b/examples/zk_statistical.rs @@ -119,10 +119,12 @@ fn prove_verify_collect( let evaluation = poly.evaluate(point); - let mut prover = dory_prover(sigma, true); + let mut prover = dory_prover(nu, sigma, true); prove::<_, BN254, G1Routines, G2Routines, _, _, ZK>( poly, point, + &tier_2, + &evaluation, tier_1, commit_blind, nu, @@ -131,9 +133,9 @@ fn prove_verify_collect( &mut prover, ) .unwrap(); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = dory_verifier(sigma, true, &proof_bytes); + let mut verifier = dory_verifier(nu, sigma, true, &proof_bytes); verify::<_, BN254, G1Routines, G2Routines, _, ZK>( tier_2, evaluation, diff --git a/tests/arkworks/evaluation.rs b/tests/arkworks/evaluation.rs index 225ac5d..eaa5a61 100644 --- a/tests/arkworks/evaluation.rs +++ b/tests/arkworks/evaluation.rs @@ -19,10 +19,13 @@ fn test_evaluation_proof_small() { .commit::(nu, sigma, &setup) .unwrap(); - let mut prover = test_prover(sigma); + 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, @@ -32,11 +35,9 @@ fn test_evaluation_proof_small() { ); assert!(result.is_ok()); - let _y = result.unwrap(); - let evaluation = poly.evaluate(&point); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = test_verifier(sigma, &proof_bytes); + let mut verifier = test_verifier(nu, sigma, &proof_bytes); let verify_result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, @@ -66,10 +67,13 @@ fn test_evaluation_proof_with_precomputed_commitment() { .commit::(nu, sigma, &setup) .unwrap(); - let mut prover = test_prover(sigma); + 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, @@ -79,11 +83,9 @@ fn test_evaluation_proof_with_precomputed_commitment() { ); assert!(result.is_ok()); - let _y = result.unwrap(); - let evaluation = poly.evaluate(&point); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = test_verifier(sigma, &proof_bytes); + let mut verifier = test_verifier(nu, sigma, &proof_bytes); let verify_result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, @@ -116,10 +118,14 @@ fn test_evaluation_proof_constant_polynomial() { .commit::(nu, sigma, &setup) .unwrap(); - let mut prover = test_prover(sigma); - let _y = 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, @@ -129,11 +135,9 @@ fn test_evaluation_proof_constant_polynomial() { ) .unwrap(); - let evaluation = poly.evaluate(&point); - assert_eq!(evaluation, ArkFr::from_u64(7)); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = test_verifier(sigma, &proof_bytes); + let mut verifier = test_verifier(nu, sigma, &proof_bytes); let verify_result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, @@ -163,10 +167,14 @@ fn test_evaluation_proof_wrong_evaluation_fails() { .commit::(nu, sigma, &setup) .unwrap(); - let mut prover = test_prover(sigma); - let _y = 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, @@ -176,11 +184,9 @@ fn test_evaluation_proof_wrong_evaluation_fails() { ) .unwrap(); - let evaluation = poly.evaluate(&point); - let wrong_evaluation = evaluation + ArkFr::one(); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = test_verifier(sigma, &proof_bytes); + let mut verifier = test_verifier(nu, sigma, &proof_bytes); let verify_result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, wrong_evaluation, @@ -209,10 +215,13 @@ fn test_evaluation_proof_different_sizes() { .commit::(nu, sigma, &setup) .unwrap(); - let mut prover = test_prover(sigma); - let _y = 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, @@ -221,10 +230,9 @@ fn test_evaluation_proof_different_sizes() { &mut prover, ) .unwrap(); - let evaluation = poly.evaluate(&point); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = test_verifier(sigma, &proof_bytes); + let mut verifier = test_verifier(nu, sigma, &proof_bytes); let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, @@ -251,10 +259,13 @@ fn test_evaluation_proof_different_sizes() { .commit::(nu, sigma, &setup) .unwrap(); - let mut prover = test_prover(sigma); - let _y = 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, @@ -263,10 +274,9 @@ fn test_evaluation_proof_different_sizes() { &mut prover, ) .unwrap(); - let evaluation = poly.evaluate(&point); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = test_verifier(sigma, &proof_bytes); + let mut verifier = test_verifier(nu, sigma, &proof_bytes); let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, @@ -297,10 +307,13 @@ fn test_multiple_evaluations_same_commitment() { for _ in 0..3 { let point = random_point(4); - let mut prover = test_prover(sigma); - let _y = 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, @@ -310,10 +323,9 @@ fn test_multiple_evaluations_same_commitment() { ) .unwrap(); - let evaluation = poly.evaluate(&point); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = test_verifier(sigma, &proof_bytes); + let mut verifier = test_verifier(nu, sigma, &proof_bytes); let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, diff --git a/tests/arkworks/homomorphic.rs b/tests/arkworks/homomorphic.rs index c4dd8bd..472f3f2 100644 --- a/tests/arkworks/homomorphic.rs +++ b/tests/arkworks/homomorphic.rs @@ -85,10 +85,12 @@ fn test_homomorphic_combination_e2e() { ); // Create evaluation proof using combined commitment - let mut prover = test_prover(sigma); + 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, @@ -97,9 +99,9 @@ fn test_homomorphic_combination_e2e() { &mut prover, ) .unwrap(); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = test_verifier(sigma, &proof_bytes); + let mut verifier = test_verifier(nu, sigma, &proof_bytes); let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( combined_tier2, evaluation, @@ -178,10 +180,12 @@ fn test_homomorphic_combination_small() { let point = random_point(num_vars); let evaluation = combined_poly.evaluate(&point); - let mut prover = test_prover(sigma); + 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, @@ -190,9 +194,9 @@ fn test_homomorphic_combination_small() { &mut prover, ) .unwrap(); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = test_verifier(sigma, &proof_bytes); + let mut verifier = test_verifier(nu, sigma, &proof_bytes); let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( combined_tier2, evaluation, diff --git a/tests/arkworks/integration.rs b/tests/arkworks/integration.rs index 36912b7..2d5e451 100644 --- a/tests/arkworks/integration.rs +++ b/tests/arkworks/integration.rs @@ -22,10 +22,14 @@ fn test_full_workflow() { let point = random_point(8); let expected_evaluation = poly.evaluate(&point); - let mut prover = test_prover(sigma); + 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, @@ -34,11 +38,9 @@ fn test_full_workflow() { &mut prover, ) .unwrap(); - let evaluation = poly.evaluate(&point); - assert_eq!(evaluation, expected_evaluation); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = test_verifier(sigma, &proof_bytes); + let mut verifier = test_verifier(nu, sigma, &proof_bytes); let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, @@ -68,10 +70,13 @@ fn test_workflow_without_precommitment() { .commit::(nu, sigma, &prover_setup) .unwrap(); - let mut prover = test_prover(sigma); + 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, @@ -80,10 +85,9 @@ fn test_workflow_without_precommitment() { &mut prover, ) .unwrap(); - let evaluation = poly.evaluate(&point); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = test_verifier(sigma, &proof_bytes); + let mut verifier = test_verifier(nu, sigma, &proof_bytes); let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, @@ -113,10 +117,13 @@ fn test_batched_proofs() { for _i in 0..5 { let point = random_point(8); - let mut prover = test_prover(sigma); + 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, @@ -125,10 +132,9 @@ fn test_batched_proofs() { &mut prover, ) .unwrap(); - let evaluation = poly.evaluate(&point); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = test_verifier(sigma, &proof_bytes); + let mut verifier = test_verifier(nu, sigma, &proof_bytes); let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, @@ -169,10 +175,15 @@ fn test_linear_polynomial() { .commit::(nu, sigma, &prover_setup) .unwrap(); - let mut prover = test_prover(sigma); + 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, @@ -181,13 +192,9 @@ fn test_linear_polynomial() { &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 proof_bytes = prover.check_complete().narg_string().to_vec(); - - let mut verifier = test_verifier(sigma, &proof_bytes); + let mut verifier = test_verifier(nu, sigma, &proof_bytes); let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, @@ -216,10 +223,14 @@ fn test_zero_polynomial() { .commit::(nu, sigma, &prover_setup) .unwrap(); - let mut prover = test_prover(sigma); + 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, @@ -228,12 +239,9 @@ fn test_zero_polynomial() { &mut prover, ) .unwrap(); + let proof_bytes = prover.narg_string().to_vec(); - let evaluation = poly.evaluate(&point); - assert_eq!(evaluation, ArkFr::zero()); - let proof_bytes = prover.check_complete().narg_string().to_vec(); - - let mut verifier = test_verifier(sigma, &proof_bytes); + let mut verifier = test_verifier(nu, sigma, &proof_bytes); let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, diff --git a/tests/arkworks/mod.rs b/tests/arkworks/mod.rs index 8d9674b..2aaa28a 100644 --- a/tests/arkworks/mod.rs +++ b/tests/arkworks/mod.rs @@ -3,8 +3,8 @@ #![allow(unreachable_pub)] use dory_pcs::backends::arkworks::{ - dory_prover, dory_verifier, ArkFr, ArkworksPolynomial, CheckedProverState, - CheckedVerifierState, G1Routines, G2Routines, BN254, + dory_prover, dory_verifier, ArkFr, ArkworksPolynomial, G1Routines, G2Routines, ProverState, + VerifierState, BN254, }; use dory_pcs::primitives::arithmetic::Field; use dory_pcs::setup::{ProverSetup, VerifierSetup}; @@ -47,26 +47,26 @@ pub fn test_setup_pair(max_log_n: usize) -> (ProverSetup, VerifierSetup CheckedProverState { - dory_prover(sigma, false) +/// Create a prover state for transparent mode testing. +pub fn test_prover(nu: usize, sigma: usize) -> ProverState { + dory_prover(nu, sigma, false) } -/// Create a checked verifier state for transparent mode testing. -pub fn test_verifier(sigma: usize, proof_bytes: &[u8]) -> CheckedVerifierState<'_> { - dory_verifier(sigma, false, proof_bytes) +/// 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 checked prover state for ZK mode testing. +/// Create a prover state for ZK mode testing. #[cfg(feature = "zk")] -pub fn test_prover_zk(sigma: usize) -> CheckedProverState { - dory_prover(sigma, true) +pub fn test_prover_zk(nu: usize, sigma: usize) -> ProverState { + dory_prover(nu, sigma, true) } -/// Create a checked verifier state for ZK mode testing. +/// Create a verifier state for ZK mode testing. #[cfg(feature = "zk")] -pub fn test_verifier_zk(sigma: usize, proof_bytes: &[u8]) -> CheckedVerifierState<'_> { - dory_verifier(sigma, true, proof_bytes) +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 9c69cf4..bd90dc7 100644 --- a/tests/arkworks/non_square.rs +++ b/tests/arkworks/non_square.rs @@ -21,10 +21,13 @@ fn test_non_square_matrix_nu_eq_sigma_minus_1() { .commit::(nu, sigma, &prover_setup) .expect("Commitment should succeed"); - let mut prover = test_prover(sigma); + 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, @@ -34,10 +37,9 @@ fn test_non_square_matrix_nu_eq_sigma_minus_1() { ) .expect("Proof generation should succeed"); - let evaluation = poly.evaluate(&point); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = test_verifier(sigma, &proof_bytes); + let mut verifier = test_verifier(nu, sigma, &proof_bytes); let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, @@ -65,14 +67,17 @@ 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 = test_prover(sigma); + 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, @@ -104,10 +109,13 @@ fn test_non_square_matrix_small() { .commit::(nu, sigma, &prover_setup) .expect("Commitment should succeed"); - let mut prover = test_prover(sigma); + 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, @@ -117,10 +125,9 @@ fn test_non_square_matrix_small() { ) .expect("Proof generation should succeed"); - let evaluation = poly.evaluate(&point); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = test_verifier(sigma, &proof_bytes); + let mut verifier = test_verifier(nu, sigma, &proof_bytes); let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, @@ -156,10 +163,13 @@ fn test_non_square_matrix_very_rectangular() { .commit::(nu, sigma, &prover_setup) .expect("Commitment should succeed"); - let mut prover = test_prover(sigma); + 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, @@ -169,10 +179,9 @@ fn test_non_square_matrix_very_rectangular() { ) .expect("Proof generation should succeed"); - let evaluation = poly.evaluate(&point); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = test_verifier(sigma, &proof_bytes); + let mut verifier = test_verifier(nu, sigma, &proof_bytes); let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, evaluation, diff --git a/tests/arkworks/serialization.rs b/tests/arkworks/serialization.rs index 5b60f01..79fd4d9 100644 --- a/tests/arkworks/serialization.rs +++ b/tests/arkworks/serialization.rs @@ -14,14 +14,17 @@ fn make_transparent_proof_bytes(nu: usize, sigma: usize) -> Vec { let poly_size = 1 << (nu + sigma); let poly = random_polynomial(poly_size); let point = random_point(nu + sigma); - let (_, tier_1, commit_blind) = poly + let (tier_2, tier_1, commit_blind) = poly .commit::(nu, sigma, &setup) .unwrap(); - let mut prover = test_prover(sigma); + 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, @@ -31,7 +34,7 @@ fn make_transparent_proof_bytes(nu: usize, sigma: usize) -> Vec { ) .unwrap(); - prover.check_complete().narg_string().to_vec() + prover.narg_string().to_vec() } #[test] @@ -85,10 +88,13 @@ fn test_proof_bytes_roundtrip_through_verify() { .commit::(nu, sigma, &setup) .unwrap(); - let mut prover = test_prover(sigma); + 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, @@ -97,13 +103,12 @@ fn test_proof_bytes_roundtrip_through_verify() { &mut prover, ) .unwrap(); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let eval = poly.evaluate(&point); - let mut verifier = test_verifier(sigma, &proof_bytes); + let mut verifier = test_verifier(nu, sigma, &proof_bytes); verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, - eval, + evaluation, &point, nu, sigma, @@ -135,14 +140,17 @@ mod zk_narg { let poly_size = 1 << (nu + sigma); let poly = random_polynomial(poly_size); let point = random_point(nu + sigma); - let (_, tier_1, commit_blind) = poly + let (tier_2, tier_1, commit_blind) = poly .commit::(nu, sigma, &setup) .unwrap(); - let mut prover = test_prover_zk(sigma); + 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, nu, @@ -152,7 +160,7 @@ mod zk_narg { ) .unwrap(); - prover.check_complete().narg_string().to_vec() + prover.narg_string().to_vec() } #[test] @@ -179,10 +187,13 @@ mod zk_narg { .commit::(nu, sigma, &setup) .unwrap(); - let mut prover = test_prover_zk(sigma); + 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, nu, @@ -191,13 +202,12 @@ mod zk_narg { &mut prover, ) .unwrap(); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let eval = poly.evaluate(&point); - let mut verifier = test_verifier_zk(sigma, &proof_bytes); + let mut verifier = test_verifier_zk(nu, sigma, &proof_bytes); verify::<_, BN254, TestG1Routines, TestG2Routines, _, ZK>( tier_2, - eval, + evaluation, &point, nu, sigma, diff --git a/tests/arkworks/soundness.rs b/tests/arkworks/soundness.rs index 7417eb1..990cb62 100644 --- a/tests/arkworks/soundness.rs +++ b/tests/arkworks/soundness.rs @@ -32,10 +32,13 @@ fn create_valid_proof( .commit::(nu, sigma, &prover_setup) .unwrap(); - let mut prover = test_prover(sigma); + 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, @@ -44,8 +47,7 @@ fn create_valid_proof( &mut prover, ) .unwrap(); - let evaluation = poly.evaluate(&point); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); ( prover_setup, @@ -68,7 +70,7 @@ fn try_verify( verifier_setup: VerifierSetup, proof_bytes: &[u8], ) -> Result<(), dory_pcs::DoryError> { - let mut verifier = test_verifier(sigma, proof_bytes); + let mut verifier = test_verifier(nu, sigma, proof_bytes); verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( commitment, evaluation, @@ -101,14 +103,16 @@ fn test_soundness_wrong_commitment() { .commit::(nu, sigma, &prover_setup) .unwrap(); - let (_, tier_1_poly2, commit_blind2) = poly2 + let (commitment2, tier_1_poly2, commit_blind2) = poly2 .commit::(nu, sigma, &prover_setup) .unwrap(); - - let mut prover = test_prover(sigma); + let evaluation = poly2.evaluate(&point); + let mut prover = test_prover(nu, sigma); prove::<_, BN254, TestG1Routines, TestG2Routines, _, _, Transparent>( &poly2, &point, + &commitment2, + &evaluation, tier_1_poly2, commit_blind2, nu, @@ -117,8 +121,7 @@ fn test_soundness_wrong_commitment() { &mut prover, ) .unwrap(); - let evaluation = poly2.evaluate(&point); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); let result = try_verify( commitment1, @@ -271,7 +274,7 @@ fn test_soundness_extra_bytes_rejected_by_length_check() { create_valid_proof(256, nu, sigma); // Valid proof passes check_eof - let mut verifier = test_verifier(sigma, &proof_bytes); + let mut verifier = test_verifier(nu, sigma, &proof_bytes); verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, eval, @@ -287,7 +290,7 @@ fn test_soundness_extra_bytes_rejected_by_length_check() { // Appending even one byte fails check_eof let mut extended = proof_bytes.clone(); extended.push(0x00); - let mut verifier = test_verifier(sigma, &extended); + let mut verifier = test_verifier(nu, sigma, &extended); verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, eval, @@ -302,7 +305,7 @@ fn test_soundness_extra_bytes_rejected_by_length_check() { // Truncated also fails let truncated = &proof_bytes[..proof_bytes.len() - 1]; - let mut verifier = test_verifier(sigma, truncated); + let mut verifier = test_verifier(nu, sigma, truncated); let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, Transparent>( tier_2, eval, diff --git a/tests/arkworks/zk.rs b/tests/arkworks/zk.rs index 9e23485..2df1a8a 100644 --- a/tests/arkworks/zk.rs +++ b/tests/arkworks/zk.rs @@ -21,11 +21,14 @@ fn test_zk_full_workflow() { let point = random_point(8); let expected_evaluation = poly.evaluate(&point); + let evaluation = poly.evaluate(&point); - let mut prover = test_prover_zk(sigma); + 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, @@ -34,11 +37,10 @@ fn test_zk_full_workflow() { &mut prover, ) .unwrap(); - let evaluation = poly.evaluate(&point); assert_eq!(evaluation, expected_evaluation); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = test_verifier_zk(sigma, &proof_bytes); + let mut verifier = test_verifier_zk(nu, sigma, &proof_bytes); let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, ZK>( tier_2, evaluation, @@ -68,10 +70,12 @@ fn test_zk_small_polynomial() { let point = random_point(2); let evaluation = poly.evaluate(&point); - let mut prover = test_prover_zk(sigma); + 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, @@ -80,9 +84,9 @@ fn test_zk_small_polynomial() { &mut prover, ) .unwrap(); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = test_verifier_zk(sigma, &proof_bytes); + let mut verifier = test_verifier_zk(nu, sigma, &proof_bytes); let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, ZK>( tier_2, evaluation, @@ -116,10 +120,12 @@ fn test_zk_larger_polynomial() { let point = random_point(10); let evaluation = poly.evaluate(&point); - let mut prover = test_prover_zk(sigma); + 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, @@ -128,9 +134,9 @@ fn test_zk_larger_polynomial() { &mut prover, ) .unwrap(); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = test_verifier_zk(sigma, &proof_bytes); + let mut verifier = test_verifier_zk(nu, sigma, &proof_bytes); let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, ZK>( tier_2, evaluation, @@ -165,10 +171,12 @@ fn test_zk_non_square_matrix() { let point = random_point(7); // nu + sigma = 7 let evaluation = poly.evaluate(&point); - let mut prover = test_prover_zk(sigma); + 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, @@ -177,9 +185,9 @@ fn test_zk_non_square_matrix() { &mut prover, ) .unwrap(); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = test_verifier_zk(sigma, &proof_bytes); + let mut verifier = test_verifier_zk(nu, sigma, &proof_bytes); let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, ZK>( tier_2, evaluation, @@ -214,10 +222,12 @@ fn test_zk_hidden_evaluation() { let evaluation = poly.evaluate(&point); // Create ZK proof using unified API with ZK mode - let mut prover = test_prover_zk(sigma); + 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, @@ -226,7 +236,7 @@ fn test_zk_hidden_evaluation() { &mut prover, ) .unwrap(); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); // Verify that ZK proof bytes are non-empty assert!( @@ -234,7 +244,7 @@ fn test_zk_hidden_evaluation() { "ZK proof should produce non-empty bytes" ); - let mut verifier = test_verifier_zk(sigma, &proof_bytes); + let mut verifier = test_verifier_zk(nu, sigma, &proof_bytes); let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, ZK>( tier_2, evaluation, @@ -269,10 +279,12 @@ fn test_zk_hidden_evaluation_larger() { let point = random_point(8); let evaluation = poly.evaluate(&point); - let mut prover = test_prover_zk(sigma); + 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, @@ -281,9 +293,9 @@ fn test_zk_hidden_evaluation_larger() { &mut prover, ) .unwrap(); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); - let mut verifier = test_verifier_zk(sigma, &proof_bytes); + let mut verifier = test_verifier_zk(nu, sigma, &proof_bytes); let result = verify::<_, BN254, TestG1Routines, TestG2Routines, _, ZK>( tier_2, evaluation, @@ -329,10 +341,14 @@ fn create_valid_zk_proof_bytes( .commit::(nu, sigma, &prover_setup) .unwrap(); - let mut prover = test_prover_zk(sigma); + 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, @@ -341,8 +357,7 @@ fn create_valid_zk_proof_bytes( &mut prover, ) .unwrap(); - let evaluation = poly.evaluate(&point); - let proof_bytes = prover.check_complete().narg_string().to_vec(); + let proof_bytes = prover.narg_string().to_vec(); ( proof_bytes, @@ -364,7 +379,7 @@ fn verify_tampered_zk_proof( proof_bytes: &[u8], verifier_setup: VerifierSetup, ) -> Result<(), dory_pcs::DoryError> { - let mut verifier = test_verifier_zk(sigma, proof_bytes); + let mut verifier = test_verifier_zk(nu, sigma, proof_bytes); verify::<_, BN254, TestG1Routines, TestG2Routines, _, ZK>( commitment, evaluation, @@ -523,19 +538,19 @@ fn test_zk_soundness_empty_proof() { assert!(result.is_err(), "Should fail with empty ZK proof"); } -/// In ZK mode, the evaluation is hidden inside the commitment and verified -/// via sigma proofs. The `evaluation` parameter to `verify()` is not used -/// in ZK mode — the proof itself binds the correct evaluation. This is -/// correct ZK behavior: the evaluation is private to the prover. +/// 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_evaluation_is_hidden() { +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); - // In ZK mode, any evaluation value can be passed — the proof is - // self-contained and the sigma proofs bind the correct evaluation. + // 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( @@ -548,8 +563,8 @@ fn test_zk_evaluation_is_hidden() { verifier_setup, ); assert!( - result.is_ok(), - "ZK verify ignores evaluation param — proof is self-contained" + result.is_err(), + "Wrong evaluation should fail even in ZK mode — evaluation is transcript-bound" ); } @@ -589,10 +604,14 @@ fn test_zk_soundness_transparent_proof_as_zk() { .commit::(nu, sigma, &prover_setup) .unwrap(); - let mut prover = test_prover(sigma); + 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, @@ -601,8 +620,7 @@ fn test_zk_soundness_transparent_proof_as_zk() { &mut prover, ) .unwrap(); - let transparent_bytes = prover.check_complete().narg_string().to_vec(); - let evaluation = poly.evaluate(&point); + let transparent_bytes = prover.narg_string().to_vec(); // Try to verify transparent proof bytes using ZK domain let result = verify_tampered_zk_proof( @@ -630,14 +648,18 @@ fn test_zk_proof_bytes_larger_than_transparent() { let (prover_setup, _) = test_setup_pair(nu + sigma + 2); let poly = random_polynomial(256); let point = random_point(nu + sigma); - let (_, tier_1, commit_blind) = poly + let (tier_2, tier_1, commit_blind) = poly .commit::(nu, sigma, &prover_setup) .unwrap(); - let mut prover = test_prover(sigma); + 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, @@ -646,7 +668,7 @@ fn test_zk_proof_bytes_larger_than_transparent() { &mut prover, ) .unwrap(); - let transparent_bytes = prover.check_complete().narg_string().to_vec(); + let transparent_bytes = prover.narg_string().to_vec(); assert!( zk_bytes.len() > transparent_bytes.len(),