From 187b7f8dfb0392e455dbc190f4b3cf6e75f967dc Mon Sep 17 00:00:00 2001 From: curiecrypt Date: Wed, 9 Oct 2024 15:05:28 +0300 Subject: [PATCH] proptest fft --- rust_accumulator/Cargo.lock | 239 ++++++++++++++++++++++++++++++++++++ rust_accumulator/Cargo.toml | 3 +- rust_accumulator/src/lib.rs | 133 +++++++++++++++++++- 3 files changed, 368 insertions(+), 7 deletions(-) diff --git a/rust_accumulator/Cargo.lock b/rust_accumulator/Cargo.lock index 821616a..98f8de7 100644 --- a/rust_accumulator/Cargo.lock +++ b/rust_accumulator/Cargo.lock @@ -14,6 +14,33 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + [[package]] name = "bitvec" version = "1.0.1" @@ -129,6 +156,22 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + [[package]] name = "ff" version = "0.13.0" @@ -140,6 +183,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "funty" version = "2.0.0" @@ -212,6 +261,18 @@ version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "maybe-rayon" version = "0.1.1" @@ -222,6 +283,16 @@ dependencies = [ "rayon", ] +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + [[package]] name = "num_cpus" version = "1.16.0" @@ -286,6 +357,32 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proptest" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" version = "1.0.37" @@ -360,6 +457,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "rust_accumulator" version = "0.1.0" @@ -369,10 +472,36 @@ dependencies = [ "group", "halo2_proofs", "libc", + "proptest", "rand", "rayon", ] +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + [[package]] name = "serde" version = "1.0.210" @@ -434,6 +563,19 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "threadpool" version = "1.8.1" @@ -474,18 +616,115 @@ dependencies = [ "once_cell", ] +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicode-ident" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "wyz" version = "0.5.1" diff --git a/rust_accumulator/Cargo.toml b/rust_accumulator/Cargo.toml index 9e69fea..8cd7d89 100644 --- a/rust_accumulator/Cargo.toml +++ b/rust_accumulator/Cargo.toml @@ -15,4 +15,5 @@ rand = "0.8" rayon = "1.10.0" halo2_proofs = "0.3.0" # flint-sys = "0.7.3" -libc = "0.2.154" \ No newline at end of file +libc = "0.2.154" +proptest = "1.0" diff --git a/rust_accumulator/src/lib.rs b/rust_accumulator/src/lib.rs index fb3f52d..2e2e4a9 100644 --- a/rust_accumulator/src/lib.rs +++ b/rust_accumulator/src/lib.rs @@ -136,12 +136,133 @@ pub extern "C" fn get_poly_commitment_g2( } #[cfg(test)] -mod test_get_poly_commitments { +mod tests { use super::*; - // in this test we will calculate the polynomial commitment a = g1^f(tau) and b = g2^f(tau) - // of the polynomial f = (x + 1)^5. Then do the pairing check - // e(a,g2) = e(g1,b) - // where e is the bilinear pairing + use proptest::prelude::*; + + /// Helper function: Schoolbook multiplication of two polynomials + fn direct_mul(left: &[Scalar], right: &[Scalar]) -> Vec { + let mut result = vec![Scalar::ZERO; left.len() + right.len() - 1]; + + for (i, l) in left.iter().enumerate() { + for (j, r) in right.iter().enumerate() { + result[i + j] += *l * *r; + } + } + + result + } + + // Strategy for generating random `Scalar`s + fn random_scalar() -> impl Strategy { + any::<[u8; 32]>().prop_map(|b| { + Scalar::from_bytes_be(&b).unwrap_or(Scalar::ZERO) // Fallback to zero on failure + }) + } + + // Strategy for generating zero-filled `Scalar` vectors + fn zero_scalar() -> impl Strategy { + Just(Scalar::ZERO) + } + + // Test: Single coefficient polynomials + proptest! { + #[test] + fn test_fft_mul_single_coeff_polys(left in random_scalar(), right in random_scalar()) { + let fft_result = fft_mul(&[left], &[right]); + let expected = vec![left * right]; + prop_assert_eq!(fft_result, expected, "Failed on single coefficient polynomials"); + } + } + + // Test: Polynomials with different lengths + proptest! { + #[test] + fn test_fft_mul_diff_lengths(left in prop::collection::vec(random_scalar(), 1..10), + right in prop::collection::vec(random_scalar(), 1..5)) { + // Perform FFT-based multiplication on the two polynomials + let fft_result = fft_mul(&left, &right); + + // Perform direct (schoolbook) multiplication on the same polynomials + let direct_result = direct_mul(&left, &right); + + // Expected length of the result + let expected_len = left.len() + right.len() - 1; + + // Ensure the length is correct + prop_assert_eq!(fft_result.len(), expected_len, "Failed on mismatched polynomial lengths"); + + // Ensure the content is correct by comparing FFT result with direct multiplication result + prop_assert_eq!(fft_result, direct_result, "Failed on mismatched polynomials with different lengths: content mismatch"); + } + } + + // Test: Empty polynomials + proptest! { + #[test] + fn test_fft_mul_empty_polys( + non_empty_vec in prop::collection::vec(random_scalar(), 1..100) // Non-empty + ) { + // Case 1: left = empty, right = non-empty + let fft_result = fft_mul(&[], &non_empty_vec); + let expected = vec![Scalar::ZERO; non_empty_vec.len()]; // Expected result is a zero-filled vector of length right + prop_assert_eq!(fft_result, expected, "Failed on case left = empty, right = non-empty with right.len() = {}", non_empty_vec.len()); + + // Case 2: left = non-empty, right = empty + let fft_result = fft_mul(&non_empty_vec, &[]); + let expected = vec![Scalar::ZERO; non_empty_vec.len()]; // Expected result is a zero-filled vector of length left + prop_assert_eq!(fft_result, expected, "Failed on case left = non-empty, right = empty with left.len() = {}", non_empty_vec.len()); + + // Case 3: left = empty, right = empty + let fft_result = fft_mul(&[], &[]); + let expected = Vec::::new(); // Expected result is an empty vector + prop_assert_eq!(fft_result, expected, "Failed on case left = empty, right = empty"); + } + } + + // Test: Zero and Non-zero polynomials with varying sizes + proptest! { + #[test] + fn test_fft_mul_zero_nonzero_cases( + // Randomly generate zero-filled and non-zero polynomials of varying lengths + left_nonzero in prop::collection::vec(random_scalar(), 1..100), + right_nonzero in prop::collection::vec(random_scalar(), 1..100), + left_zero in prop::collection::vec(zero_scalar(), 1..100), + right_zero in prop::collection::vec(zero_scalar(), 1..100) + ) { + // Case 1: left = 0 (zero vector), right = non-zero + let fft_result = fft_mul(&left_zero, &right_nonzero); + let expected = vec![Scalar::ZERO; left_zero.len() + right_nonzero.len() - 1]; + prop_assert_eq!(fft_result, expected, "Failed on case left = 0, right = non-zero with left.len() = {} and right.len() = {}", left_zero.len(), right_nonzero.len()); + + // Case 2: left = non-zero, right = 0 (zero polynomial) + let fft_result = fft_mul(&left_nonzero, &right_zero); + let expected = vec![Scalar::ZERO; left_nonzero.len() + right_zero.len() - 1]; + prop_assert_eq!(fft_result, expected, "Failed on case left = non-zero, right = 0 with left.len() = {} and right.len() = {}", left_nonzero.len(), right_zero.len()); + + // Case 3: left = 0, right = 0 (both polynomials are zero) + let fft_result = fft_mul(&left_zero, &right_zero); + let expected = vec![Scalar::ZERO; left_zero.len() + right_zero.len() - 1]; + prop_assert_eq!(fft_result, expected, "Failed on case left = 0, right = 0 with left.len() = {} and right.len() = {}", left_zero.len(), right_zero.len()); + } + } + + // Test: Polynomial multiplication with random inputs and large sizes + proptest! { + #[test] + fn test_fft_mul_large_polys(left in prop::collection::vec(random_scalar(), 500..700), + right in prop::collection::vec(random_scalar(), 500..700)) { + + // Perform FFT-based multiplication + let fft_result = fft_mul(&left, &right); + + // Perform direct multiplication + let direct_result = direct_mul(&left, &right); + + // Ensure the results match + prop_assert_eq!(fft_result, direct_result, "Failed on random polynomial multiplication"); + } + } #[test] fn test_get_coeff_from_roots() { @@ -161,7 +282,7 @@ mod test_get_poly_commitments { bytes[31] = 10; bytes }) - .unwrap(); + .unwrap(); // Initialize a vector with the "zero't" power of tau (tau^0 = 1) let mut scalar_power_of_tau: Vec = vec![Scalar::ONE];