diff --git a/Cargo.lock b/Cargo.lock index e3b902d..36db7ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,18 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "aho-corasick" version = "1.1.3" @@ -23,128 +11,11 @@ dependencies = [ "memchr", ] -[[package]] -name = "ark-bn254" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-std", -] - -[[package]] -name = "ark-ec" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" -dependencies = [ - "ark-ff", - "ark-poly", - "ark-serialize", - "ark-std", - "derivative", - "hashbrown", - "itertools", - "num-traits", - "zeroize", -] - -[[package]] -name = "ark-ff" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" -dependencies = [ - "ark-ff-asm", - "ark-ff-macros", - "ark-serialize", - "ark-std", - "derivative", - "digest", - "itertools", - "num-bigint", - "num-traits", - "paste", - "rustc_version", - "zeroize", -] - -[[package]] -name = "ark-ff-asm" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-ff-macros" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" -dependencies = [ - "num-bigint", - "num-traits", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-poly" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" -dependencies = [ - "ark-ff", - "ark-serialize", - "ark-std", - "derivative", - "hashbrown", -] - -[[package]] -name = "ark-serialize" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" -dependencies = [ - "ark-serialize-derive", - "ark-std", - "digest", - "num-bigint", -] - -[[package]] -name = "ark-serialize-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-std" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" -dependencies = [ - "num-traits", - "rand", -] - [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "cfg-if" @@ -152,48 +23,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "crypto-common", -] - -[[package]] -name = "either" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" - [[package]] name = "futures" version = "0.3.30" @@ -250,7 +85,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn", ] [[package]] @@ -289,21 +124,11 @@ dependencies = [ "slab", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -316,29 +141,11 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - [[package]] name = "libc" -version = "0.2.154" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "memchr" @@ -367,25 +174,13 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - [[package]] name = "pin-project-lite" version = "0.2.14" @@ -416,9 +211,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" dependencies = [ "unicode-ident", ] @@ -501,11 +296,6 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" name = "ronkathon" version = "0.1.0" dependencies = [ - "ark-bn254", - "ark-ec", - "ark-ff", - "ark-poly", - "ark-std", "num-bigint", "pretty_assertions", "rand", @@ -537,7 +327,7 @@ dependencies = [ "regex", "relative-path", "rustc_version", - "syn 2.0.60", + "syn", "unicode-ident", ] @@ -552,9 +342,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "slab" @@ -567,44 +357,21 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.60" +version = "2.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -616,43 +383,3 @@ name = "yansi" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" - -[[package]] -name = "zerocopy" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.60", -] - -[[package]] -name = "zeroize" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.60", -] diff --git a/Cargo.toml b/Cargo.toml index 04224fe..514e6fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,12 +9,6 @@ version ="0.1.0" [dependencies] rand ="0.8.5" -num-bigint={ version="0.4.5", default-features=false } -ark-std ={ version="0.4.0", default-features=false } -ark-bn254 ="0.4.0" -ark-poly ="0.4.0" -ark-ff ="0.4.0" -ark-ec ="0.4.0" [dev-dependencies] rstest ="0.19.0" diff --git a/src/curve/mod.rs b/src/curve/mod.rs index 41999eb..164ee19 100644 --- a/src/curve/mod.rs +++ b/src/curve/mod.rs @@ -1,5 +1,6 @@ //! Elliptic curve operations and types. +use self::field::prime::PlutoScalarField; use super::*; pub mod pairing; @@ -93,6 +94,12 @@ impl AddAssign for AffinePoint { fn add_assign(&mut self, rhs: Self) { *self = *self + rhs; } } +impl Sum for AffinePoint { + fn sum>(iter: I) -> Self { + iter.reduce(|x, y| x + y).unwrap_or(AffinePoint::Infinity) + } +} + impl Neg for AffinePoint { type Output = AffinePoint; @@ -105,8 +112,6 @@ impl Neg for AffinePoint { } } -// TODO: This should likely use a `Self::ScalarField` instead of `u32`. -/// Scalar multiplication on the rhs: P*(u32) /// This is the niave implementation of scalar multiplication /// There is a faster way to do this but this is simpler to reason about for now #[allow(clippy::suspicious_arithmetic_impl)] @@ -114,6 +119,9 @@ impl Mul for AffinePoint { type Output = AffinePoint; fn mul(mut self, scalar: u32) -> Self::Output { + if scalar == 0 { + return AffinePoint::Infinity; + } let val = self; for _ in 1..scalar { self += val; @@ -122,12 +130,26 @@ impl Mul for AffinePoint { } } -/// Scalar multiplication on the Lhs (u32)*P +impl Sub for AffinePoint { + type Output = AffinePoint; + + fn sub(self, rhs: Self) -> Self::Output { self + -rhs } +} + +impl Mul for AffinePoint { + type Output = AffinePoint; + + fn mul(self, scalar: PlutoScalarField) -> Self::Output { scalar.value as u32 * self } +} + #[allow(clippy::suspicious_arithmetic_impl)] impl std::ops::Mul> for u32 { type Output = AffinePoint; fn mul(self, val: AffinePoint) -> Self::Output { + if self == 0 { + return AffinePoint::Infinity; + } let mut out = val; for _ in 1..self { out += val; diff --git a/src/curve/pairing.rs b/src/curve/pairing.rs index a88c8ed..5e97e53 100644 --- a/src/curve/pairing.rs +++ b/src/curve/pairing.rs @@ -249,4 +249,36 @@ mod tests { assert_eq!(result.pow(17), PlutoBaseFieldExtension::ONE); } + + // test the bilinearity of the pairing + #[test] + fn bilinearity() { + let a = PlutoScalarField::new(3); + let b = PlutoScalarField::new(5); + + let p = AffinePoint::::from(AffinePoint::::generator()); + let cube_root_of_unity = PlutoBaseFieldExtension::primitive_root_of_unity(3); + let q = if let AffinePoint::::Point(x, y) = + AffinePoint::::generator() + { + AffinePoint::::new( + cube_root_of_unity * PlutoBaseFieldExtension::from(x), + PlutoBaseFieldExtension::from(y), + ) + } else { + panic!("Generator is not a point"); + }; + + let a_p = p * a; + let b_q = q * b; + + let lhs = pairing::(a_p, b_q); + let ab = a * b; + let rhs = pairing::(p, q).pow(ab.value); + + println!("LHS: {:?}", lhs); + println!("RHS: {:?}", rhs); + + assert_eq!(lhs, rhs); + } } diff --git a/src/field/prime/mod.rs b/src/field/prime/mod.rs index 956cd76..fd87de3 100644 --- a/src/field/prime/mod.rs +++ b/src/field/prime/mod.rs @@ -29,7 +29,7 @@ pub type PlutoScalarField = PrimeField<{ PlutoPrime::Scalar as usize }>; /// by a prime number `P`, and the elements are integers modulo `P`. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default, PartialOrd)] pub struct PrimeField { - value: usize, + pub(crate) value: usize, } impl PrimeField

{ diff --git a/src/kzg.rs b/src/kzg.rs deleted file mode 100644 index 7691c0e..0000000 --- a/src/kzg.rs +++ /dev/null @@ -1,139 +0,0 @@ -//! KZG10 is a polynomial commitment scheme that allows for efficient verification of polynomial -//! evaluations. - -use ark_bn254::{Bn254, Fr, G1Affine, G1Projective, G2Affine, G2Projective}; -use ark_ec::{AffineRepr, CurveGroup, VariableBaseMSM}; -use ark_ff::Field; -use ark_poly::DenseUVPolynomial; -use num_bigint::BigUint; -use rand::thread_rng; - -use super::*; - -/// simple setup to get params. -pub fn setup(d: u64) -> (Vec, Vec) { - // NOTE: For demonstration purposes only. - // Create some toxic waste, typically done in MPC and discarded. - let mut rng = thread_rng(); - let mut tau_bytes = vec![0u8; 32]; - rng.fill(&mut tau_bytes[..]); - println!("tau_toxic_waste={:?}", BigUint::from_bytes_le(&tau_bytes)); - - let tau = Fr::from(BigUint::from_bytes_le(&tau_bytes)); - // let tau = Fr::rand(&mut rng); - - // NOTE: Just sample the d of both for now. - // - g1 and g2 SRS have variable sizes for diff kzg uses - // - in eth blobs, g1 is 4096 elements, g2 is 16 elements - // - in plonk, we need d+5 g1 elements and one g2 element - let mut srs_g1_points: Vec = vec![]; - let mut srs_g2_points: Vec = vec![]; - for i in 1..d + 1 { - // G1 Group - let result = G1Projective::from(G1Affine::generator()) * tau.pow([i]); - srs_g1_points.push(result.into_affine()); - - // G2 Group - let result = G2Projective::from(G2Affine::generator()) * tau.pow([i]); - srs_g2_points.push(result.into_affine()); - } - - (srs_g1_points, srs_g2_points) -} - -/// kzg commit -pub fn commit(coefs: Vec, g1_srs: Vec) -> G1Affine { - assert!(g1_srs.len() >= coefs.len()); - - // fiddle with arkworks too - if MSM { - return ::msm(&g1_srs[..coefs.len()], &coefs) - .expect("failed msm") - .into_affine(); - } - - return g1_srs - .iter() - .zip(coefs) - .map(|x| G1Projective::from(*x.0) * x.1) - .reduce(|x, y| x + y) - .expect("srs not large enough") - .into_affine(); -} - -/// divide the poly by the point to find the witness poly -pub fn open(coefs: Vec, point: Fr, g1_srs: Vec) -> G1Affine { - use std::ops::Div; - - use ark_poly::univariate::DensePolynomial; - - // arkworks for now - let poly = DensePolynomial::from_coefficients_vec(coefs); - let divisor = DensePolynomial::from_coefficients_vec(vec![-point, Fr::ONE]); - - commit::(poly.div(&divisor).coeffs, g1_srs) -} - -/// Verify the polynomial evaluation. -pub fn check( - p: G1Affine, - q: G1Affine, - point: Fr, - value: Fr, - g1_srs: Vec, - g2_srs: Vec, -) -> bool { - use ark_ec::pairing::Pairing; - - let g1 = *g1_srs.first().expect("has g1 srs"); - let g2 = *g2_srs.first().expect("has g2 srs"); - - let lhs = Bn254::pairing(q, g2.into_group() - G2Affine::generator() * point); - let rhs = Bn254::pairing(p.into_group() - g1 * value, G2Affine::generator()); - - lhs == rhs -} - -/// Evaluate a polynomial at a point. -pub fn evaluate(coefs: Vec, point: Fr) -> Fr { - coefs - .iter() - .enumerate() - .map(|x| { - let exp = x.0 as u64; - x.1 * &point.pow([exp]) - }) - .reduce(|x, y| x + y) - .unwrap() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn e2e() { - // A univariate polynomial with roots 1,2,3 converted into coefficient form for simplicity. - // - // p(x) = (x-1)(x-2)(x-3) - // p(x) = x^3 - 6x^2 + 11x - 6 - // - // Now, for example, prove that p(4) = 6 via witness commit q(x) = x^2 - 2x + 3 - // - - // Poor man's polynomial - let p = vec![Fr::from(-6), Fr::from(11), Fr::from(-6), Fr::from(1)]; - let x = Fr::from(4); - let y = evaluate(p.clone(), x); - - let (g1_srs, g2_srs) = setup(10); - let p_commit = commit::(p.clone(), g1_srs.clone()); - let q_commit = open(p.clone(), x, g1_srs.clone()); - let valid = check(p_commit, q_commit, x, y, g1_srs.clone(), g2_srs.clone()); - - println!("p_commit={}", p_commit); - println!("q_commit={}", q_commit); - - assert!(valid); - } -} diff --git a/src/kzg/mod.rs b/src/kzg/mod.rs new file mode 100644 index 0000000..2002e8c --- /dev/null +++ b/src/kzg/mod.rs @@ -0,0 +1,7 @@ +//! KZG implementation for polynomial commitments +pub mod setup; +#[cfg(test)] mod tests; + +pub use setup::*; + +use super::*; diff --git a/src/kzg/setup.rs b/src/kzg/setup.rs new file mode 100644 index 0000000..4f3ceb2 --- /dev/null +++ b/src/kzg/setup.rs @@ -0,0 +1,112 @@ +//! Does the SRS setup for the KZG10 scheme. + +use self::{curve::pairing::pairing, field::prime::PlutoScalarField}; +use super::*; + +/// simple setup to get params. +#[allow(dead_code, clippy::type_complexity)] +pub fn setup() -> (Vec>, Vec>) { + // NOTE: For demonstration purposes only. + + // This is just tau from plonk by hand, it is not actually secure + let tau: PlutoScalarField = PlutoScalarField::new(2); + + let g1 = AffinePoint::::from(AffinePoint::::generator()); + let g2 = AffinePoint::::generator(); + // NOTE: Just sample the d of both for now. + // - g1 and g2 SRS have variable sizes for diff kzg uses + // - in eth blobs, g1 is 4096 elements, g2 is 16 elements + // - in plonk, we need d+5 g1 elements and one g2 element + let mut srs_g1_points: Vec> = vec![]; + let mut srs_g2_points: Vec> = vec![]; + for i in 0..7 { + // G1 Group + + // degree seven commitment poly + // g1srs = {g1^tau^0, g1^tau^1, g1^tau^2, g1^tau^3, g1^tau^4, g1^tau^5, g1^tau^6} + let result = g1 * tau.pow(i); + + srs_g1_points.push(result); + // G2 Group + + // degree two divisor poly + if i < 2 { + // g2srs = {g2^tau^0, g2^tau^1} + let result = g2 * tau.pow(i); + srs_g2_points.push(result); + } + } + + (srs_g1_points, srs_g2_points) +} + +/// kzg poly commit +/// Both binding and hiding commitment +#[allow(dead_code)] +pub fn commit( + coefs: Vec, + g1_srs: Vec>, +) -> AffinePoint { + // check srs is longer than coefs + assert!(g1_srs.len() >= coefs.len()); + // SUM_{i=0}^{n} (g1^tau^i * coef_i) + g1_srs.into_iter().zip(coefs).map(|(g1, coef)| g1 * coef).sum::>() +} + +/// Open the commitment +pub fn open( + coefs: Vec, + eval_point: PlutoScalarField, + g1_srs: Vec>, +) -> AffinePoint { + let poly = Polynomial::::new(coefs.clone()); + let divisor = + Polynomial::::new(vec![-eval_point, PlutoScalarField::ONE]); + + let result = poly.div(divisor).coefficients; + println!("resulting polynomial {:?}", result); + + commit(result, g1_srs) +} + +/// Verify the polynomial evaluation. +pub fn check( + p: AffinePoint, + q: AffinePoint, + point: PlutoScalarField, + value: PlutoScalarField, + g1_srs: Vec>, + g2_srs: Vec>, +) -> bool { + let g1 = *g1_srs.first().expect("has g1 srs"); + + // This was the seeming bug, It now works for all polynomials, but am not sure why yet. + let g2 = g2_srs[1]; + + // e(pi, g2 - gen * point) + let lhs = pairing::( + q, + g2 - AffinePoint::::generator() * point, + ); + + // e(p - g1 * value, gen) + let rhs = pairing::( + p - g1 * value, + AffinePoint::::generator(), + ); + println!("lhs {:?}", lhs); + println!("rhs {:?}", rhs); + + lhs == rhs +} + +// p = 101 +// k = 2 (embedding degree, determines your extension field) +// base field = GF_101 +// base scaler is 17 (number of points in the group) +// pairing output is in Extension field = GF_101^2? +// (all petals are in this base extension field: has two cyclic groups of order 17) + +// Asymmetric means G1 and G2 are different subgroups +// This is a little confusing teminology because all pairing friendly subgroups are isomorphic +// Symmetric means G1 and G2 are the same subgroup diff --git a/src/kzg/tests.rs b/src/kzg/tests.rs new file mode 100644 index 0000000..2ba30fd --- /dev/null +++ b/src/kzg/tests.rs @@ -0,0 +1,332 @@ +use super::*; +use crate::{curve::pairing::pairing, field::prime::PlutoScalarField}; + +#[test] +fn test_setup() { + let (g1srs, g2srs) = setup(); + assert!(g1srs.len() == 7); + assert!(g2srs.len() == 2); + let expected_g1srs = vec![ + AffinePoint::::new( + PlutoBaseFieldExtension::from(1usize), + PlutoBaseFieldExtension::from(2usize), + ), + AffinePoint::::new( + PlutoBaseFieldExtension::from(68usize), + PlutoBaseFieldExtension::from(74usize), + ), + AffinePoint::::new( + PlutoBaseFieldExtension::from(65usize), + PlutoBaseFieldExtension::from(98usize), + ), + AffinePoint::::new( + PlutoBaseFieldExtension::from(18usize), + PlutoBaseFieldExtension::from(49usize), + ), + AffinePoint::::new( + PlutoBaseFieldExtension::from(1usize), + PlutoBaseFieldExtension::from(99usize), + ), + AffinePoint::::new( + PlutoBaseFieldExtension::from(68usize), + PlutoBaseFieldExtension::from(27usize), + ), + AffinePoint::::new( + PlutoBaseFieldExtension::from(65usize), + PlutoBaseFieldExtension::from(3usize), + ), + ]; + + assert_eq!(g1srs, expected_g1srs); + + let expected_2g = AffinePoint::::new( + PlutoBaseFieldExtension::new([PlutoBaseField::new(90), PlutoBaseField::ZERO]), + PlutoBaseFieldExtension::new([PlutoBaseField::ZERO, PlutoBaseField::new(82)]), + ); + let g2_gen = AffinePoint::::generator(); + let expected_g2srs = vec![g2_gen, expected_2g]; + + assert_eq!(g2srs, expected_g2srs); +} + +#[fixture] +fn poly_1() -> Polynomial { + // p(x) = (x-1)(x-2)(x-3) + // p(x) = - 6 + 11x -6x^2 + x^3 + // p(x) = 11 + 11x + 11x^2 + x^3 mod 17 + // -> -6 mod 17 is 11 so this is [11, 11, 11, 1] + let a = PlutoScalarField::from(11usize); + let b = PlutoScalarField::from(11usize); + let c = PlutoScalarField::from(11usize); + let d = PlutoScalarField::from(1usize); + Polynomial::::new(vec![a, b, c, d]) +} + +#[fixture] +fn poly_2() -> Polynomial { + // p(x) = (x-1)(x-2)(x-3)(x-4) + // p(x) = 24 - 50x + 35x^2 - 10x^3 + // -> 24 mod 17 is 7 + // -> 50 mod 17 is 16 + // -> 35 mod 17 is 1 + // coefficients = [7, 16, 1, 11, 1] + let a = PlutoScalarField::from(7usize); + let b = PlutoScalarField::from(16usize); + let c = PlutoScalarField::from(1usize); + let d = PlutoScalarField::from(11usize); + let e = PlutoScalarField::from(1usize); + Polynomial::::new(vec![a, b, c, d, e]) +} + +#[fixture] +fn poly_3() -> Polynomial { + // p(x) = 3 + 2x + x^2 + let a = PlutoScalarField::from(3usize); + let b = PlutoScalarField::from(2usize); + let c = PlutoScalarField::from(1usize); + Polynomial::::new(vec![a, b, c]) +} + +#[test] +fn test_commit() { + println!("FIRST COMMIT"); + let (g1srs, _) = setup(); + // p(x) = (x-1)(x-2)(x-3) + // p(x) = - 6 + 11x -6x^2 + x^3 + // p(x) = 11 + 11x + 11x^2 + x^3 mod 17 + + // -> -6 mod 17 is 11 so this is [11, 11, 11, 1] + let coefficients = poly_1().coefficients.clone(); + // g1srs[0] * 11 + g1srs[1] * 11 + g1srs[2] * 11 + g1srs[3] * 1 + let commit_1 = commit(coefficients, g1srs.clone()); + assert_eq!(commit_1, AffinePoint::::Infinity); + + println!("\n\nSECOND COMMIT"); + // p(x) = (x-1)(x-2)(x-3)(x-4) + // p(x) = 24 - 50x + 35x^2 - 10x^3 + // -> 24 mod 17 is 7 + // -> 50 mod 17 is 16 + // -> 35 mod 17 is 1 + // coefficients = [7, 16, 1, 11, 1] + let coefficients = poly_2().coefficients.clone(); + // g1srs[0] * 7 + g1srs[1] * 16 + g1srs[2] * 1 + g1srs[3] * 11 + g1srs[4] * 1 + let commit_2 = commit(coefficients, g1srs.clone()); + + assert_eq!( + commit_2, + AffinePoint::::new( + PlutoBaseFieldExtension::from(32usize), + PlutoBaseFieldExtension::from(59usize), + ) + ); + + println!("\n\nTHIRD COMMIT"); + // p(x) = 3 + 2x + x^2 + let coefficients = poly_3().coefficients.clone(); + // g1srs[0] * 3 + g1srs[1] * 2 + g1srs[2] * 1 + let commit_3 = commit(coefficients, g1srs); + + assert_eq!( + commit_3, + AffinePoint::::new( + PlutoBaseFieldExtension::from(32usize), + PlutoBaseFieldExtension::from(59usize), + ) + ); +} + +#[test] +fn srs_open() { + let (g1srs, _) = setup(); + let result = g1srs[0] * PlutoScalarField::new(3); + let result_2 = g1srs[1] * PlutoScalarField::new(15); + let result_3 = g1srs[2] * PlutoScalarField::new(1); + let sum = result + result_2 + result_3; + dbg!(sum); + assert_eq!( + sum, + AffinePoint::::new( + PlutoBaseFieldExtension::from(26usize), + PlutoBaseFieldExtension::from(45usize), + ) + ); +} + +#[test] +fn opening() { + let (g1srs, _) = setup(); + println!("g1srs[0]:{:?}, g1srs[1]:{:?}, g1srs[2]:{:?}", g1srs[0], g1srs[1], g1srs[2]); + let poly = poly_1(); + let eval_point = PlutoScalarField::new(4); + // let eval_result = poly.evaluate(eval_point); + let commit = commit(poly.coefficients.clone(), g1srs.clone()); + assert_eq!(commit, AffinePoint::::Infinity); + // p(x) = (x-1)(x-2)(x-3) + // p(x) = - 6 + 11x -6x^2 + x^3 + + // divisor poly q(x) = x - 4 + // result = p(x) / q(x) = x^2 - 2x + 3 + // multiplying (1,2) * 3 + (68, 74) * 15 + (65, 98) * 1 + let open_commit = open(poly.coefficients, eval_point, g1srs.clone()); + + assert_eq!( + open_commit, + AffinePoint::::new( + PlutoBaseFieldExtension::from(26usize), + PlutoBaseFieldExtension::from(45usize), + ) + ); +} + +// this test was for debugging purposes +#[test] +fn all_srs_combinations() { + let paring_params = commit_and_open(poly_1(), PlutoScalarField::new(4)); + + let mut g1_index = 0; + let mut g2_index = 0; + let mut pairings: Vec<(GaloisField<2, 101>, GaloisField<2, 101>)> = vec![]; + #[allow(clippy::explicit_counter_loop)] + for g1 in paring_params.g1srs { + println!("Loop for g1 {:?}", g1); + for g2 in &paring_params.g2srs { + println!("Loop for g2 {:?}", g2); + let lhs = pairing::( + paring_params.q, + *g2 - AffinePoint::::generator() * paring_params.point, + ); + + let rhs = pairing::( + paring_params.p - g1 * paring_params.value, + AffinePoint::::generator(), + ); + if lhs == rhs { + println!( + "Pairing match! for g1: {:?} and g2: {:?} with g1 index {:?} and g2 index {:?}", + g1, g2, g1_index, g2_index + ); + println!("LHS {:?}", lhs); + println!("RHS {:?}", rhs); + pairings.push((lhs, rhs)); + assert_eq!(lhs, rhs); + } + g2_index += 1; + } + g1_index += 1; + } + assert!(!pairings.is_empty()); +} + +#[rstest] +#[case(poly_1(), PlutoScalarField::new(4))] +#[case(poly_2(), PlutoScalarField::new(3))] +#[case(poly_3(), PlutoScalarField::new(5))] +fn e2e(#[case] poly: Polynomial, #[case] eval_point: PlutoScalarField) { + let paring_params = commit_and_open(poly, eval_point); + + // Both `p_commit` and `q_commit` are in the same group so this is good. + // We can look at `g1srs` and see it is in `G1` and `g2srs` is in `G2` + dbg!(paring_params.g1srs.first().unwrap()); + for i in 0..17 { + println!("{}: {:?}", i, *paring_params.g1srs.first().unwrap() * i); + } + assert_eq!( + *paring_params.g1srs.first().unwrap() * 17u32, + AffinePoint::::Infinity + ); + dbg!(paring_params.g2srs.first().unwrap()); + for i in 0..17 { + println!("{}: {:?}", i, *paring_params.g2srs.first().unwrap() * i); + } + assert_eq!( + *paring_params.g2srs.first().unwrap() * 17u32, + AffinePoint::::Infinity + ); + + let valid = check( + paring_params.p, + paring_params.q, + paring_params.point, + paring_params.value, + paring_params.g1srs.clone(), + paring_params.g2srs.clone(), + ); + assert!(valid); +} + +#[rstest] +#[case(poly_1(), PlutoScalarField::new(4))] +#[case(poly_2(), PlutoScalarField::new(3))] +#[case(poly_3(), PlutoScalarField::new(5))] +#[should_panic] +fn invalid_check( + #[case] poly: Polynomial, + #[case] eval_point: PlutoScalarField, +) { + let paring_params = commit_and_open(poly, eval_point); + let valid = check( + paring_params.p, + paring_params.q, + paring_params.point, + PlutoScalarField::new(10), // fake evaluation point + paring_params.g1srs.clone(), + paring_params.g2srs.clone(), + ); + assert!(valid); +} + +#[rstest] +#[case(poly_1(), PlutoScalarField::new(4))] +#[case(poly_2(), PlutoScalarField::new(3))] +#[case(poly_3(), PlutoScalarField::new(5))] +#[should_panic] +fn fake_proof( + #[case] poly: Polynomial, + #[case] eval_point: PlutoScalarField, +) { + let paring_params = commit_and_open(poly, eval_point); + let valid = check( + paring_params.p, + AffinePoint::::Infinity, // fake proof + paring_params.point, + paring_params.point, + paring_params.g1srs.clone(), + paring_params.g2srs.clone(), + ); + assert!(valid); +} + +/// Pairing params for testing pairing +pub struct PairingParams { + pub p: AffinePoint, + pub q: AffinePoint, + pub point: PlutoScalarField, + pub value: PlutoScalarField, + pub g1srs: Vec>, + pub g2srs: Vec>, +} + +/// given a polynomial and eval point return the pairing params +pub fn commit_and_open( + poly: Polynomial, + eval_point: PlutoScalarField, +) -> PairingParams { + let (g1srs, g2srs) = setup(); + let eval_result = poly.evaluate(eval_point); + let p_commit = commit(poly.coefficients.clone(), g1srs.clone()); + let q_commit = open(poly.coefficients, eval_point, g1srs.clone()); + PairingParams { p: p_commit, q: q_commit, point: eval_point, value: eval_result, g1srs, g2srs } +} + +#[test] +fn pairing_params() { + let params = commit_and_open(poly_1(), PlutoScalarField::new(4)); + assert_eq!(params.p, AffinePoint::::Infinity); + assert_eq!( + params.q, + AffinePoint::::new( + PlutoBaseFieldExtension::from(26usize), + PlutoBaseFieldExtension::from(45usize) + ) + ); +} diff --git a/src/lib.rs b/src/lib.rs index 4fda8be..07d1b84 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,6 @@ pub mod curve; pub mod field; pub mod kzg; pub mod polynomial; -pub mod setup; use core::{ fmt::{self, Display, Formatter}, diff --git a/src/setup.rs b/src/setup.rs deleted file mode 100644 index 7d3b77a..0000000 --- a/src/setup.rs +++ /dev/null @@ -1,126 +0,0 @@ -//! Does the SRS setup for the KZG10 scheme. - -use super::*; - -/// simple setup to get params. -#[allow(dead_code, clippy::type_complexity)] -fn setup() -> (Vec>, Vec>) { - // NOTE: For demonstration purposes only. - - // This is just tau from plonk by hand, it is not actually secure - let tau: u32 = 2; - - // NOTE: Just sample the d of both for now. - // - g1 and g2 SRS have variable sizes for diff kzg uses - // - in eth blobs, g1 is 4096 elements, g2 is 16 elements - // - in plonk, we need d+5 g1 elements and one g2 element - let mut srs_g1_points: Vec> = vec![]; - let mut srs_g2_points: Vec> = vec![]; - for i in 0..7 { - // G1 Group - - // degree seven commitment poly - let result = AffinePoint::::generator() * tau.pow(i); - srs_g1_points.push(result); - // G2 Group - - // degree two divisor poly - if i < 2 { - let result = AffinePoint::::generator() * tau.pow(i); - srs_g2_points.push(result); - } - } - - (srs_g1_points, srs_g2_points) -} - -/// kzg poly commit -#[allow(dead_code)] -fn commit( - coefs: Vec, - g1_srs: Vec>, -) -> AffinePoint { - // commit to a polynomial - // - given a polynomial, commit to it - assert!(g1_srs.len() >= coefs.len()); - // Todo implement multiplication with field elements as scalar mult. - // Maybe having the scalar mult be around the base field like colin suggested is better - - let mut commitment = AffinePoint::Infinity; - for (coef, point) in coefs.iter().zip(g1_srs) { - let res = point * *coef; - // println!("res {:?}, of multiplying point {:?}, and coef {:?}", res, point, coef); - println!("commitment {:?} before addition with {:?}", commitment, res); - commitment += res; - } - commitment -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_setup() { - let (g1srs, g2srs) = setup(); - assert!(g1srs.len() == 7); - assert!(g2srs.len() == 2); - let expected_g1srs = vec![ - AffinePoint::::new(PlutoBaseField::new(1), PlutoBaseField::new(2)), - AffinePoint::::new(PlutoBaseField::new(68), PlutoBaseField::new(74)), - AffinePoint::::new(PlutoBaseField::new(65), PlutoBaseField::new(98)), - AffinePoint::::new(PlutoBaseField::new(18), PlutoBaseField::new(49)), - AffinePoint::::new(PlutoBaseField::new(1), PlutoBaseField::new(99)), - AffinePoint::::new(PlutoBaseField::new(68), PlutoBaseField::new(27)), - AffinePoint::::new(PlutoBaseField::new(65), PlutoBaseField::new(3)), - ]; - - assert_eq!(g1srs, expected_g1srs); - - println!("g2srs {:?}", g2srs); - let expected_2g = AffinePoint::::new( - PlutoBaseFieldExtension::new([PlutoBaseField::new(90), PlutoBaseField::ZERO]), - PlutoBaseFieldExtension::new([PlutoBaseField::ZERO, PlutoBaseField::new(82)]), - ); - - let g2_gen = AffinePoint::::generator(); - let expected_g2srs = vec![g2_gen, expected_2g]; - - assert_eq!(g2srs, expected_g2srs); - } - - #[test] - fn test_commit() { - let (g1srs, _) = setup(); - // p(x) = (x-1)(x-2)(x-3) - // p(x) = x^3 - 6x^2 + 11x - 6 - // -> -6 mod 17 is 11 so this is [1, 11, 11, 1] - let coefficients = vec![11, 11, 11, 1]; - // g1srs[0] * 11 + g1srs[1] * 11 + g1srs[2] * 11 + g1srs[3] * 1 - let commit_1 = commit(coefficients, g1srs.clone()); - assert_eq!(commit_1, AffinePoint::::Infinity); - - // p(x) = (x-1)(x-2)(x-3)(x-4) - // p(x) = x^4 - 10x^3 + 35x^2 - 50x + 24 - // -> 24 mod 17 is 7 - // -> 50 mod 17 is 16 - // -> 35 mod 17 is 1 - // coefficients = [7, 16, 1, 11, 1] - let coefficients = vec![7, 16, 1, 11, 1]; - // g1srs[0] * 7 + g1srs[1] * 16 + g1srs[2] * 1 + g1srs[3] * 11 + g1srs[4] * 1 - let commit_2 = commit(coefficients, g1srs.clone()); - assert_eq!( - commit_2, - AffinePoint::::new(PlutoBaseField::new(32), PlutoBaseField::new(59)) - ); - - // p(x) = x^2 + 2x + 3 - let coefficients = vec![3, 2, 1]; - // g1srs[0] * 3 + g1srs[1] * 2 + g1srs[2] * 1 - let commit_3 = commit(coefficients, g1srs); - assert_eq!( - commit_3, - AffinePoint::::new(PlutoBaseField::new(32), PlutoBaseField::new(59)) - ); - } -}