diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ab25ed..1b83b4c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,3 +71,5 @@ jobs: run: cargo run --example homomorphic --features backends - name: Run non_square example run: cargo run --example non_square --features backends + - name: Run homomorphic_mixed_sizes example + run: cargo run --example homomorphic_mixed_sizes --features backends diff --git a/Cargo.lock b/Cargo.lock index 0c2ce1c..ad7749d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -190,12 +190,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bitflags" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" - [[package]] name = "blake2" version = "0.10.6" @@ -372,27 +366,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - [[package]] name = "dory-derive" version = "0.1.0" @@ -415,14 +388,13 @@ dependencies = [ "blake2", "criterion", "digest", - "dirs", "dory-derive", "once_cell", "rand", "rand_core", "rayon", "serde", - "thiserror 2.0.17", + "thiserror", "tracing", ] @@ -525,7 +497,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -568,16 +540,6 @@ version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" -[[package]] -name = "libredox" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" -dependencies = [ - "bitflags", - "libc", -] - [[package]] name = "memchr" version = "2.7.6" @@ -624,12 +586,6 @@ version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - [[package]] name = "paste" version = "1.0.15" @@ -747,17 +703,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom", - "libredox", - "thiserror 1.0.69", -] - [[package]] name = "regex" version = "1.12.2" @@ -868,33 +813,13 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - [[package]] name = "thiserror" version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.17", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "thiserror-impl", ] [[package]] @@ -1044,7 +969,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -1053,15 +978,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -1071,63 +987,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "zerocopy" version = "0.8.27" diff --git a/Cargo.toml b/Cargo.toml index 19195a2..5f00df1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,14 +47,13 @@ arkworks = [ ] parallel = ["dep:rayon", "ark-ec?/parallel", "ark-ff?/parallel"] cache = ["arkworks", "dep:once_cell", "parallel"] -disk-persistence = ["dep:dirs"] +disk-persistence = [] [dependencies] thiserror = "2.0" rand_core = "0.6" dory-derive = { version = "0.1.0", path = "derive" } tracing = "0.1" -dirs = { version = "5.0", optional = true } # Arkworks backend ark-bn254 = { version = "0.5.0", optional = true } @@ -85,6 +84,10 @@ required-features = ["backends"] name = "non_square" required-features = ["backends"] +[[example]] +name = "homomorphic_mixed_sizes" +required-features = ["backends"] + [[bench]] name = "arkworks_proof" harness = false diff --git a/examples/homomorphic_mixed_sizes.rs b/examples/homomorphic_mixed_sizes.rs new file mode 100644 index 0000000..90fb474 --- /dev/null +++ b/examples/homomorphic_mixed_sizes.rs @@ -0,0 +1,136 @@ +//! Mixed-size homomorphic combination example for Dory commitments. +//! +//! Demonstrates how to homomorphically combine two polynomials that only use a +//! subset of the coefficient domain (sizes 5 and 20, padded to 32) and then +//! produce and verify an evaluation proof for the combined commitment. + +use dory_pcs::backends::arkworks::{ + ArkFr, ArkG1, ArkworksPolynomial, Blake2bTranscript, G1Routines, G2Routines, BN254, +}; +use dory_pcs::primitives::arithmetic::{Field, Group}; +use dory_pcs::primitives::poly::Polynomial; +use dory_pcs::{prove, setup, verify}; +use rand::thread_rng; +use tracing::info; + +fn main() -> Result<(), Box> { + info!("Dory PCS - Mixed-size Homomorphic Combination Example"); + let mut rng = thread_rng(); + + let (prover_setup, verifier_setup) = setup::(&mut rng, 4); + + info!("Creating two polynomials with logical sizes 16 and 4..."); + let mut coeffs_poly1 = vec![ArkFr::zero(); 16]; + let mut coeffs_poly2 = vec![ArkFr::zero(); 4]; + for coeff in coeffs_poly1.iter_mut() { + *coeff = ArkFr::random(&mut rng); + } + for coeff in coeffs_poly2.iter_mut() { + *coeff = ArkFr::random(&mut rng); + } + let poly1 = ArkworksPolynomial::new(coeffs_poly1.clone()); + let poly2 = ArkworksPolynomial::new(coeffs_poly2.clone()); + + info!("Poly1: {:?}", poly1); + info!("Poly2: {:?}", poly2); + + let commitment1 = poly1 + .commit::(2, 2, &prover_setup) + .unwrap(); + let commitment2 = poly2 + .commit::(1, 1, &prover_setup) + .unwrap(); + info!("✓ Commitments ready\n"); + + info!("Sampling random combination scalars r1, r2..."); + let coeff_scalars = [ArkFr::random(&mut rng), ArkFr::random(&mut rng)]; + + info!("Combining tier-2 commitments (GT)..."); + let combined_tier2 = coeff_scalars[0] * commitment1.0 + coeff_scalars[1] * commitment2.0; + + info!("Combining tier-1 commitments (G1 rows)..."); + + let mut combined_tier1 = vec![ArkG1::identity(); 4]; + for (row_idx, row_commit) in commitment1.1.iter().enumerate() { + combined_tier1[row_idx] = combined_tier1[row_idx] + (coeff_scalars[0] * row_commit); + } + for (row_idx, row_commit) in commitment2.1.iter().enumerate() { + combined_tier1[row_idx] = combined_tier1[row_idx] + (coeff_scalars[1] * row_commit); + } + + info!("Building combined polynomial coefficients..."); + let mut combined_coeffs = vec![ArkFr::zero(); 16]; + for idx in 0..16 { + let term1 = coeff_scalars[0].mul(&coeffs_poly1[idx]); + combined_coeffs[idx] = combined_coeffs[idx] + term1; + if idx < 2 { + let term2 = coeff_scalars[1].mul(&coeffs_poly2[idx]); + combined_coeffs[idx] = combined_coeffs[idx] + term2; + } + if idx > 3 && idx < 6 { + let term3 = coeff_scalars[1].mul(&coeffs_poly2[idx - 2]); + combined_coeffs[idx] = combined_coeffs[idx] + term3; + } + } + let combined_poly = ArkworksPolynomial::new(combined_coeffs); + + info!("Combined polynomial: {:?}", combined_poly); + + let mut padded_poly2_coefficients = vec![ArkFr::zero(); 16]; + padded_poly2_coefficients[0] = coeffs_poly2[0]; + padded_poly2_coefficients[1] = coeffs_poly2[1]; + padded_poly2_coefficients[4] = coeffs_poly2[2]; + padded_poly2_coefficients[5] = coeffs_poly2[3]; + let padded_poly2 = ArkworksPolynomial::new(padded_poly2_coefficients); + + info!("Evaluating combined polynomial at a random point..."); + let point: Vec = (0..4).map(|_| ArkFr::random(&mut rng)).collect(); + let evaluation = combined_poly.evaluate(&point); + + info!("Checking that evaluation matches r1·P1(x) + r2·P2(x)..."); + let eval1 = poly1.evaluate(&point); + let eval2 = padded_poly2.evaluate(&point); + let eval3 = poly2.evaluate(&[point[0], point[2]]) + * (ArkFr::one() - point[1]) + * (ArkFr::one() - point[3]); + assert_eq!(eval3, eval2); + let mut expected = ArkFr::zero(); + expected = expected + coeff_scalars[0].mul(&eval1); + expected = expected + coeff_scalars[1].mul(&eval2); + assert_eq!(evaluation, expected); + info!("✓ Evaluation matches linear combination\n"); + + info!("Generating evaluation proof with combined commitment..."); + let mut prover_transcript = Blake2bTranscript::new(b"dory-homomorphic-mixed"); + let proof = prove::<_, BN254, G1Routines, G2Routines, _, _>( + &combined_poly, + &point, + combined_tier1, + 2, + 2, + &prover_setup, + &mut prover_transcript, + )?; + info!("✓ Proof generated\n"); + + info!("Verifying proof against combined tier-2 commitment..."); + let mut verifier_transcript = Blake2bTranscript::new(b"dory-homomorphic-mixed"); + verify::<_, BN254, G1Routines, G2Routines, _>( + combined_tier2, + evaluation, + &point, + &proof, + verifier_setup, + &mut verifier_transcript, + )?; + info!("✓ Proof verified!"); + + info!("==========================================="); + let padded_poly_commitment = padded_poly2 + .commit::(2, 2, &prover_setup) + .unwrap(); + assert_eq!(padded_poly_commitment.0, commitment2.0); + info!("✓ Padded poly commitment matches original poly2 commitment"); + + Ok(()) +} diff --git a/src/setup.rs b/src/setup.rs index 9007ff7..759165b 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -196,26 +196,46 @@ impl ProverSetup { } } -/// Get the storage directory for Dory setup files +/// Get the full path to the setup file for a given max_log_n /// -/// Returns the appropriate storage directory based on the OS: +/// Determines the appropriate storage directory based on the OS: /// - Linux: `~/.cache/dory/` /// - macOS: `~/Library/Caches/dory/` /// - Windows: `{FOLDERID_LocalAppData}\dory\` /// -/// Note: Uses XDG cache directory for persistent storage. -#[cfg(feature = "disk-persistence")] -fn get_storage_dir() -> Option { - dirs::cache_dir().map(|mut path| { - path.push("dory"); - path - }) -} - -/// Get the full path to the setup file for a given max_log_n +/// Note: Detects OS at runtime by checking environment variables then chooses XDG cache directory for persistent storage. #[cfg(feature = "disk-persistence")] fn get_storage_path(max_log_n: usize) -> Option { - get_storage_dir().map(|mut path| { + let cache_directory = { + // Check for Windows first (LOCALAPPDATA is Windows-specific) + if let Ok(local_app_data) = std::env::var("LOCALAPPDATA") { + Some(PathBuf::from(local_app_data)) + } else if let Ok(home) = std::env::var("HOME") { + let mut path = PathBuf::from(&home); + + // Check if Library/Caches exists (macOS indicator) + let macos_cache = { + let mut test_path = PathBuf::from(&home); + test_path.push("Library"); + test_path.push("Caches"); + test_path.exists() + }; + + if macos_cache { + path.push("Library"); + path.push("Caches"); + } else { + // Linux and other Unix-like systems + path.push(".cache"); + } + Some(path) + } else { + None + } + }; + + cache_directory.map(|mut path| { + path.push("dory"); path.push(format!("dory_{}.urs", max_log_n)); path }) diff --git a/tests/arkworks/setup.rs b/tests/arkworks/setup.rs index 9b89dd5..4b9495c 100644 --- a/tests/arkworks/setup.rs +++ b/tests/arkworks/setup.rs @@ -156,10 +156,41 @@ fn test_arkworks_setup_new_from_urs() { let max_log_n = 14; // Clean up any existing cache file first - if let Some(cache_dir) = dirs::cache_dir() { - let cache_file = cache_dir - .join("dory") - .join(format!("dory_{}.urs", max_log_n)); + let cache_directory = { + // Check for Windows first (LOCALAPPDATA is Windows-specific) + if let Ok(local_app_data) = std::env::var("LOCALAPPDATA") { + Some(std::path::PathBuf::from(local_app_data)) + } else if let Ok(home) = std::env::var("HOME") { + let mut path = std::path::PathBuf::from(&home); + + // Check if Library/Caches exists (macOS indicator) + let macos_cache = { + let mut test_path = std::path::PathBuf::from(&home); + test_path.push("Library"); + test_path.push("Caches"); + test_path.exists() + }; + + if macos_cache { + path.push("Library"); + path.push("Caches"); + } else { + // Linux and other Unix-like systems + path.push(".cache"); + } + Some(path) + } else { + None + } + }; + + let cache_file = cache_directory.map(|mut path| { + path.push("dory"); + path.push(format!("dory_{}.urs", max_log_n)); + path + }); + + if let Some(cache_file) = cache_file { let _ = std::fs::remove_file(&cache_file); }