From e7e2bdbd546fe501e817db17fa21761088384884 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Mon, 14 Apr 2025 17:53:14 +0200 Subject: [PATCH 01/25] te_cond_add evaluated at 2 points --- w3f-plonk-common/src/gadgets/ec/mod.rs | 9 + .../src/gadgets/ec/te_cond_add.rs | 193 ++++++++++++------ 2 files changed, 140 insertions(+), 62 deletions(-) diff --git a/w3f-plonk-common/src/gadgets/ec/mod.rs b/w3f-plonk-common/src/gadgets/ec/mod.rs index 048548c..ed8e26d 100644 --- a/w3f-plonk-common/src/gadgets/ec/mod.rs +++ b/w3f-plonk-common/src/gadgets/ec/mod.rs @@ -117,3 +117,12 @@ pub struct CondAddValues> { pub acc: (F, F), pub _phantom: PhantomData

, } + +pub struct CondAddEvals> { + pub bitmask_at_z: F, + pub points_at_z: (F, F), + pub not_last_at_z: F, + pub acc_at_z: (F, F), + pub acc_at_zw: (F, F), + pub _phantom: PhantomData

, +} diff --git a/w3f-plonk-common/src/gadgets/ec/te_cond_add.rs b/w3f-plonk-common/src/gadgets/ec/te_cond_add.rs index 9eb327f..c635937 100644 --- a/w3f-plonk-common/src/gadgets/ec/te_cond_add.rs +++ b/w3f-plonk-common/src/gadgets/ec/te_cond_add.rs @@ -1,13 +1,83 @@ use ark_ec::twisted_edwards::{Affine, TECurveConfig}; use ark_ff::{FftField, Field}; use ark_poly::univariate::DensePolynomial; -use ark_poly::{Evaluations, GeneralEvaluationDomain}; +use ark_poly::{EvaluationDomain, Evaluations, GeneralEvaluationDomain}; +use ark_std::ops::{AddAssign, MulAssign, SubAssign}; use ark_std::{vec, vec::Vec}; -use crate::gadgets::ec::{CondAdd, CondAddValues}; +use crate::gadgets::ec::{CondAdd, CondAddEvals, CondAddValues}; use crate::gadgets::{ProverGadget, VerifierGadget}; use crate::{const_evals, Column}; +// cx = {[(a.x1.x2 + y1.y2).x3 - x1.y1 - x2.y2].b + (x3 - x1).(1 - b)}.not_last +// cy = {[(x1.y2 - x2.y1).y3 - x1.y1 + x2.y2].b + (y3 - y1).(1 - b)}.not_last +fn cond_te_addition( + te_coeff_a: F, + b: &F, + x1: &F, + y1: &F, + x2: &F, + y2: &F, + x3: F, + y3: F, + not_last: &F, + one: F, +) -> Vec +where + F: Clone, + F: for<'a> AddAssign<&'a F>, + F: for<'a> SubAssign<&'a F>, + F: for<'a> MulAssign<&'a F>, +{ + let mut x1y1 = x1.clone(); + x1y1 *= y1; + let mut x2y2 = x2.clone(); + x2y2 *= y2; + let mut y1y2 = y1.clone(); + y1y2 *= y2; + let mut x2y1 = x2.clone(); + x2y1 *= y1; + + // lx = [(a.x1.x2 + y1.y2).x3 - x1.y1 - x2.y2].b + let mut lx = te_coeff_a; + lx *= x1; + lx *= x2; + lx += &y1y2; + lx *= &x3; + lx -= &x1y1; + lx -= &x2y2; + lx *= b; + + // ly = [(x1.y2 - x2.y1).y3 - x1.y1 + x2.y2].b + let mut ly = x1.clone(); + ly *= y2; + ly -= &x2y1; + ly *= &y3; + ly -= &x1y1; + ly += &x2y2; + ly *= b; + + // rx = (x3 - x1).(1 - b) + // ry = (y3 - y1).(1 - b) + let mut one_minus_b = one; + one_minus_b -= b; + let mut rx = x3; + rx -= x1; + rx *= &one_minus_b; + let mut ry = y3; + ry -= y1; + ry *= &one_minus_b; + + // cx = {lx + rx}.not_last + // cy = {ly + ry}.not_last + lx += ℞ + lx *= ¬_last; + ly += &ry; + ly *= ¬_last; + + vec![lx, ly] +} + impl ProverGadget for CondAdd> where F: FftField, @@ -20,59 +90,13 @@ where fn constraints(&self) -> Vec> { let domain = self.bitmask.domain_4x(); let b = &self.bitmask.col.evals_4x; - let one = &const_evals(F::one(), domain); - let te_a_coeff = &const_evals(Curve::COEFF_A, domain); + let one = const_evals(F::one(), domain); + let te_coeff_a = const_evals(Curve::COEFF_A, domain); + let not_last = &self.not_last.evals_4x; let (x1, y1) = (&self.acc.xs.evals_4x, &self.acc.ys.evals_4x); let (x2, y2) = (&self.points.xs.evals_4x, &self.points.ys.evals_4x); - let (x3, y3) = (&self.acc.xs.shifted_4x(), &self.acc.ys.shifted_4x()); - - //b (x_3 (y_1 y_2 + ax_1 x_2) - x_1 y_1 - y_2 x_2) + (1 - b) (x_3 - x_1) = 0 - #[rustfmt::skip] - let mut c1 = - &( - b * - &( - &( - x3 * - &( - &(y1 * y2) + - - &(te_a_coeff * - &(x1 * x2) - ) - ) - ) - - - &( - &(x1 * y1) + &(y2* x2) - ) - ) - ) + - &( - &(one - b) * &(x3 - x1) - ); - - //b (y_3 (x_1 y_2 - x_2 y_1) - x_1 y_1 + x_2 y_2) + (1 - b) (y_3 - y_1) = 0 - #[rustfmt::skip] - let mut c2 = - &( - b * - &( &(y3 * - &( - &(x1 * y2) - &(x2 * y1))) - - &(&(x1 * y1) - &(x2 * y2)) - ) - ) - + - &( - &(one - b) * &(y3 - y1) - ); - - let not_last = &self.not_last.evals_4x; - c1 *= not_last; - c2 *= not_last; - - vec![c1, c2] + let (x3, y3) = (self.acc.xs.shifted_4x(), self.acc.ys.shifted_4x()); + cond_te_addition(te_coeff_a, b, x1, y1, x2, y2, x3, y3, not_last, one) } /// Mary-Oana Linearization technique. See: https://hackmd.io/0kdBl3GVSmmcB7QJe1NTuw?view#Linearization @@ -95,6 +119,41 @@ where } } +impl CondAdd> +where + F: FftField, + Curve: TECurveConfig, +{ + pub fn evaluate_at_z_and_zw(&self, zeta: &F) -> CondAddEvals> { + let zeta_omega = self.bitmask.domain().group_gen() * zeta; + CondAddEvals { + bitmask_at_z: self.bitmask.evaluate(zeta), + points_at_z: self.points.evaluate(zeta), + not_last_at_z: self.not_last.evaluate(zeta), + acc_at_z: self.acc.evaluate(zeta), + acc_at_zw: self.acc.evaluate(&zeta_omega), + _phantom: Default::default(), + } + } +} + +impl> CondAddEvals> { + pub fn constraints_at_z(&self) -> Vec { + cond_te_addition( + C::COEFF_A, + &self.bitmask_at_z, + &self.acc_at_z.0, + &self.acc_at_z.1, + &self.points_at_z.0, + &self.points_at_z.1, + self.acc_at_zw.0, + self.acc_at_zw.1, + &self.not_last_at_z, + F::one(), + ) + } +} + impl> CondAddValues> { pub fn acc_coeffs_1(&self) -> (F, F) { let b = self.bitmask; @@ -155,10 +214,10 @@ mod tests { use crate::gadgets::ec::BitColumn; use crate::gadgets::ec::Domain; use ark_ec::AffineRepr; - use ark_ed_on_bls12_381_bandersnatch::EdwardsAffine; + use ark_ed_on_bls12_381_bandersnatch::{EdwardsAffine, Fq}; use ark_poly::Polynomial; - use ark_std::test_rng; + use ark_std::{test_rng, UniformRand}; use crate::test_helpers::cond_sum; use crate::test_helpers::*; @@ -184,16 +243,26 @@ mod tests { assert_eq!(res, &expected_res); let cs = gadget.constraints(); - let (c1, c2) = (&cs[0], &cs[1]); - let c1 = c1.interpolate_by_ref(); - let c2 = c2.interpolate_by_ref(); + let c1 = cs[0].interpolate_by_ref(); + let c2 = cs[1].interpolate_by_ref(); assert_eq!(c1.degree(), 4 * n - 3); assert_eq!(c2.degree(), 4 * n - 3); - domain.divide_by_vanishing_poly(&c1); - domain.divide_by_vanishing_poly(&c2); - - // test_gadget(gadget); + let q1 = domain.divide_by_vanishing_poly(&c1); + let q2 = domain.divide_by_vanishing_poly(&c2); + + let zeta = Fq::rand(rng); + let domain_at_z = domain.evaluate(zeta); + let evals = gadget.evaluate_at_z_and_zw(&zeta); + let cs_at_z = evals.constraints_at_z(); + assert_eq!( + domain_at_z.divide_by_vanishing_poly_in_zeta(cs_at_z[0]), + q1.evaluate(&zeta) + ); + assert_eq!( + domain_at_z.divide_by_vanishing_poly_in_zeta(cs_at_z[1]), + q2.evaluate(&zeta) + ); } #[test] From d767582e20901f9d870b3b9348becc0fa61d0480 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Wed, 16 Apr 2025 15:33:22 +0200 Subject: [PATCH 02/25] comment --- w3f-plonk-common/src/gadgets/ec/te_cond_add.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/w3f-plonk-common/src/gadgets/ec/te_cond_add.rs b/w3f-plonk-common/src/gadgets/ec/te_cond_add.rs index c635937..6141b44 100644 --- a/w3f-plonk-common/src/gadgets/ec/te_cond_add.rs +++ b/w3f-plonk-common/src/gadgets/ec/te_cond_add.rs @@ -9,9 +9,16 @@ use crate::gadgets::ec::{CondAdd, CondAddEvals, CondAddValues}; use crate::gadgets::{ProverGadget, VerifierGadget}; use crate::{const_evals, Column}; -// cx = {[(a.x1.x2 + y1.y2).x3 - x1.y1 - x2.y2].b + (x3 - x1).(1 - b)}.not_last -// cy = {[(x1.y2 - x2.y1).y3 - x1.y1 + x2.y2].b + (y3 - y1).(1 - b)}.not_last -fn cond_te_addition( +/// These constraints are deduced from +/// "Affine addition formulae (independent of d) for twisted Edwards curves", +/// see e.g. formula (3) in https://eprint.iacr.org/2008/522.pdf. +/// Works for distinct prime-order points. +/// `cx = {[(a.x1.x2 + y1.y2).x3 - x1.y1 - x2.y2].b + (x3 - x1).(1 - b)}.not_last` +/// `cy = {[(x1.y2 - x2.y1).y3 - x1.y1 + x2.y2].b + (y3 - y1).(1 - b)}.not_last` +// Where: +/// `(x3, y3) = (x1, y1) + (x2, y2)`, if `b = 1`, and +/// `(x3, y3) = (x1, y1)` otherwise. +pub fn cond_te_addition( te_coeff_a: F, b: &F, x1: &F, From ae2a5d5eed8f2f027132459c521d61c1b924de13 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Wed, 16 Apr 2025 15:33:52 +0200 Subject: [PATCH 03/25] cond_te_addition in solidity --- evm-vrfier/Cargo.toml | 2 + evm-vrfier/contracts/src/Constraints.sol | 35 +++++++++++++ evm-vrfier/src/constraints.rs | 63 ++++++++++++++++++++++++ evm-vrfier/src/lib.rs | 1 + 4 files changed, 101 insertions(+) create mode 100644 evm-vrfier/contracts/src/Constraints.sol create mode 100644 evm-vrfier/src/constraints.rs diff --git a/evm-vrfier/Cargo.toml b/evm-vrfier/Cargo.toml index bbf642b..a6166b6 100644 --- a/evm-vrfier/Cargo.toml +++ b/evm-vrfier/Cargo.toml @@ -7,11 +7,13 @@ edition = "2021" alloy = { version = "0.14", default-features = false, features = ["contract", "provider-anvil-node"] } ark-ff = { workspace = true } ark-bls12-381 = { version = "0.5", default-features = false, features = ["curve"] } +ark-ed-on-bls12-381-bandersnatch = { version = "0.5", default-features = false } [dev-dependencies] tokio = { version = "1.44", default-features = false } ark-std = { workspace = true } ark-ec = { workspace = true } +w3f-plonk-common = { path = "../w3f-plonk-common", default-features = false } w3f-pcs = { workspace = true } [build-dependencies] diff --git a/evm-vrfier/contracts/src/Constraints.sol b/evm-vrfier/contracts/src/Constraints.sol new file mode 100644 index 0000000..beb4a4f --- /dev/null +++ b/evm-vrfier/contracts/src/Constraints.sol @@ -0,0 +1,35 @@ +pragma solidity ^0.8.24; + +library Constraints { + uint256 constant r = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001; + uint256 constant te_coeff_a = r - 5; + + function add(uint256 a, uint256 b) internal pure returns (uint256 c) { + c = addmod(a, b, r); + } + + function mul(uint256 a, uint256 b) internal pure returns (uint256 c) { + c = mulmod(a, b, r); + } + + function cond_te_addition( + uint256 b, + uint256 x1, + uint256 y1, + uint256 x2, + uint256 y2, + uint256 x3, + uint256 y3, + uint256 not_last + ) public pure returns (uint256 cx) { + /// `cx = {[(a.x1.x2 + y1.y2).x3 - x1.y1 - x2.y2].b + (x3 - x1).(1 - b)}.not_last` + uint256 x1y1 = mul(x1, y1); + uint256 x2y2 = mul(x2, y2); + uint256 lx = mul(add(mul(add(mul(te_coeff_a, mul(x1, x2)), mul(y1, y2)), x3), r - add(x1y1, x2y2)), b); + cx = add(lx, mul(add(x3, r - x1), add(1, r - b))); + } + +// function not_last_row(unit256 domain_size) internal pure returns uint256 { +// +// } +} diff --git a/evm-vrfier/src/constraints.rs b/evm-vrfier/src/constraints.rs new file mode 100644 index 0000000..74a18b4 --- /dev/null +++ b/evm-vrfier/src/constraints.rs @@ -0,0 +1,63 @@ +#[cfg(test)] +mod tests { + use ark_bls12_381::Fr; + use ark_ec::twisted_edwards::TECurveConfig; + use ark_ed_on_bls12_381_bandersnatch::BandersnatchConfig; + use ark_ff::One; + use ark_std::{test_rng, UniformRand}; + use w3f_plonk_common::gadgets::ec::te_cond_add::cond_te_addition; + use crate::plonk_kzg::bls_scalar_field_to_uint256; + + alloy::sol!( + #[sol(rpc)] + Constraints, + "contracts/out/Constraints.sol/Constraints.json" + ); + + #[tokio::test] + async fn constraints() -> Result<(), Box> { + let provider = alloy::providers::builder() + .with_recommended_fillers() + .on_anvil_with_wallet(); + let constraints = Constraints::deploy(&provider).await?; + + let rng = &mut test_rng(); + + let te_coeff_a = BandersnatchConfig::COEFF_A; + let b = Fr::rand(rng); + let x1 = Fr::rand(rng); + let y1 = Fr::rand(rng); + let x2 = Fr::rand(rng); + let y2 = Fr::rand(rng); + let x3 = Fr::rand(rng); + let y3 = Fr::rand(rng); + + let cx_rust= cond_te_addition( + te_coeff_a, + &b, + &x1, + &y1, + &x2, + &y2, + x3, + y3, + &Fr::one(), + Fr::one(), + )[0]; + + let cx_sol = constraints.cond_te_addition( + bls_scalar_field_to_uint256(b), + bls_scalar_field_to_uint256(x1), + bls_scalar_field_to_uint256(y1), + bls_scalar_field_to_uint256(x2), + bls_scalar_field_to_uint256(y2), + bls_scalar_field_to_uint256(x3), + bls_scalar_field_to_uint256(y3), + bls_scalar_field_to_uint256(Fr::one()), + ).call().await?; + + assert_eq!(bls_scalar_field_to_uint256(cx_rust), cx_sol); + + Ok(()) + } +} diff --git a/evm-vrfier/src/lib.rs b/evm-vrfier/src/lib.rs index 07d116b..69afdc8 100644 --- a/evm-vrfier/src/lib.rs +++ b/evm-vrfier/src/lib.rs @@ -1,4 +1,5 @@ pub mod plonk_kzg; +mod constraints; #[cfg(test)] mod tests { From a52b5c4f2aaa6c99095fd5ecf43c3f72a153270f Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Wed, 16 Apr 2025 16:09:20 +0200 Subject: [PATCH 04/25] mod_exp --- evm-vrfier/contracts/src/Constraints.sol | 7 +++++++ evm-vrfier/src/constraints.rs | 14 ++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/evm-vrfier/contracts/src/Constraints.sol b/evm-vrfier/contracts/src/Constraints.sol index beb4a4f..374ec05 100644 --- a/evm-vrfier/contracts/src/Constraints.sol +++ b/evm-vrfier/contracts/src/Constraints.sol @@ -29,6 +29,13 @@ library Constraints { cx = add(lx, mul(add(x3, r - x1), add(1, r - b))); } + function mod_exp(uint256 base, uint256 exp) public view returns (uint256) { + bytes memory precompileData = abi.encode(32, 32, 32, base, exp, r); + (bool ok, bytes memory data) = address(5).staticcall(precompileData); + require(ok, "expMod failed"); + return abi.decode(data, (uint256)); + } + // function not_last_row(unit256 domain_size) internal pure returns uint256 { // // } diff --git a/evm-vrfier/src/constraints.rs b/evm-vrfier/src/constraints.rs index 74a18b4..373f4dc 100644 --- a/evm-vrfier/src/constraints.rs +++ b/evm-vrfier/src/constraints.rs @@ -1,9 +1,10 @@ #[cfg(test)] mod tests { + use alloy::primitives::U256; use ark_bls12_381::Fr; use ark_ec::twisted_edwards::TECurveConfig; use ark_ed_on_bls12_381_bandersnatch::BandersnatchConfig; - use ark_ff::One; + use ark_ff::{Field, One}; use ark_std::{test_rng, UniformRand}; use w3f_plonk_common::gadgets::ec::te_cond_add::cond_te_addition; use crate::plonk_kzg::bls_scalar_field_to_uint256; @@ -23,6 +24,14 @@ mod tests { let rng = &mut test_rng(); + let z = Fr::rand(rng); + let z_n_rust = z.pow([256]); + let z_n_sol = constraints.mod_exp( + bls_scalar_field_to_uint256(z), + U256::from(256), + ).call().await?; + assert_eq!(bls_scalar_field_to_uint256(z_n_rust), z_n_sol); + let te_coeff_a = BandersnatchConfig::COEFF_A; let b = Fr::rand(rng); let x1 = Fr::rand(rng); @@ -31,7 +40,6 @@ mod tests { let y2 = Fr::rand(rng); let x3 = Fr::rand(rng); let y3 = Fr::rand(rng); - let cx_rust= cond_te_addition( te_coeff_a, &b, @@ -44,7 +52,6 @@ mod tests { &Fr::one(), Fr::one(), )[0]; - let cx_sol = constraints.cond_te_addition( bls_scalar_field_to_uint256(b), bls_scalar_field_to_uint256(x1), @@ -55,7 +62,6 @@ mod tests { bls_scalar_field_to_uint256(y3), bls_scalar_field_to_uint256(Fr::one()), ).call().await?; - assert_eq!(bls_scalar_field_to_uint256(cx_rust), cx_sol); Ok(()) From 962230821cce549385541542bb6a9e2afc3c9e04 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Wed, 16 Apr 2025 17:57:28 +0200 Subject: [PATCH 05/25] vanishing poly in solidity --- evm-vrfier/contracts/src/Constraints.sol | 30 ++++++++++++++-- evm-vrfier/src/constraints.rs | 46 +++++++++++++++++++++++- w3f-plonk-common/src/domain.rs | 4 +++ 3 files changed, 76 insertions(+), 4 deletions(-) diff --git a/evm-vrfier/contracts/src/Constraints.sol b/evm-vrfier/contracts/src/Constraints.sol index 374ec05..7da706b 100644 --- a/evm-vrfier/contracts/src/Constraints.sol +++ b/evm-vrfier/contracts/src/Constraints.sol @@ -1,6 +1,14 @@ pragma solidity ^0.8.24; library Constraints { + uint256 constant domain_size = 256; + + uint256 constant w = 36007022166693598376559747923784822035233416720563672082740011604939309541707; + uint256 constant w_inv = 11184958699465346337974417366548385058372410568086779736245770566382283753344; + uint256 constant w_inv_2 = 43775291915288810309377910988321681322896939416379112495208008906206324170002; + uint256 constant w_inv_3 = 24824062393296269928157607240610716359041681219294130923310247842219009400878; + uint256 constant w_inv_4 = 45254319123522011116259460062854627366454101350769349111320208945036885998124; + uint256 constant r = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001; uint256 constant te_coeff_a = r - 5; @@ -36,7 +44,23 @@ library Constraints { return abi.decode(data, (uint256)); } -// function not_last_row(unit256 domain_size) internal pure returns uint256 { -// -// } + function inv(uint256 x) public view returns (uint256) { + return mod_exp(x, r - 2); + } + + function v_at(uint256 z) public view returns (uint256) { + return mod_exp(z, domain_size) - 1; + } + + function v_inv_at(uint256 z) public view returns (uint256) { + return inv(v_at(z)); + } + + function v_inv_hiding_at(uint256 z) public view returns (uint256) { + return mul(mul(mul(v_inv_at(z), add(z, r - w_inv)), add(z, r - w_inv_2)), add(z, r - w_inv_3)); + } + + function not_last_row(uint256 z) public pure returns (uint256) { + return add(z, r - w_inv_4); + } } diff --git a/evm-vrfier/src/constraints.rs b/evm-vrfier/src/constraints.rs index 373f4dc..af03314 100644 --- a/evm-vrfier/src/constraints.rs +++ b/evm-vrfier/src/constraints.rs @@ -3,9 +3,10 @@ mod tests { use alloy::primitives::U256; use ark_bls12_381::Fr; use ark_ec::twisted_edwards::TECurveConfig; - use ark_ed_on_bls12_381_bandersnatch::BandersnatchConfig; + use ark_ed_on_bls12_381_bandersnatch::{BandersnatchConfig, Fq}; use ark_ff::{Field, One}; use ark_std::{test_rng, UniformRand}; + use w3f_plonk_common::domain::Domain; use w3f_plonk_common::gadgets::ec::te_cond_add::cond_te_addition; use crate::plonk_kzg::bls_scalar_field_to_uint256; @@ -66,4 +67,47 @@ mod tests { Ok(()) } + + #[tokio::test] + async fn domain_at_zeta() -> Result<(), Box> { + let provider = alloy::providers::builder() + .with_recommended_fillers() + .on_anvil_with_wallet(); + + let constraints = Constraints::deploy(&provider).await?; + + let rng = &mut test_rng(); + + let domain = Domain::new(256, true); + let z = Fq::rand(rng); + let domain_at_z = domain.evaluate(z); + + let w = domain.omega(); + let w_inv = domain.omega_inv(); + let w_inv_2 = w_inv * w_inv; + let w_inv_3 = w_inv_2 * w_inv; + let w_inv_4 = w_inv_3 * w_inv; + println!("\tuint256 constant w = {};", bls_scalar_field_to_uint256(w)); + println!("\tuint256 constant w_inv = {};", bls_scalar_field_to_uint256(w_inv)); + println!("\tuint256 constant w_inv_2 = {};", bls_scalar_field_to_uint256(w_inv_2)); + println!("\tuint256 constant w_inv_3 = {};", bls_scalar_field_to_uint256(w_inv_3)); + println!("\tuint256 constant w_inv_4 = {};", bls_scalar_field_to_uint256(w_inv_4)); + + let z_inv = constraints.inv( + bls_scalar_field_to_uint256(z), + ).call().await?; + assert_eq!(z_inv, bls_scalar_field_to_uint256(z.inverse().unwrap())); + + let v_inv_hiding_at = constraints.v_inv_hiding_at( + bls_scalar_field_to_uint256(z), + ).call().await?; + assert_eq!(v_inv_hiding_at, bls_scalar_field_to_uint256(domain_at_z.vanishing_polynomial_inv)); + + let not_last_row = constraints.not_last_row( + bls_scalar_field_to_uint256(z), + ).call().await?; + assert_eq!(not_last_row, bls_scalar_field_to_uint256(domain_at_z.not_last_row)); + + Ok(()) + } } diff --git a/w3f-plonk-common/src/domain.rs b/w3f-plonk-common/src/domain.rs index 42241dc..e91cdbc 100644 --- a/w3f-plonk-common/src/domain.rs +++ b/w3f-plonk-common/src/domain.rs @@ -133,6 +133,10 @@ impl Domain { self.domains.x1.group_gen() } + pub fn omega_inv(&self) -> F { + self.domains.x1.group_gen_inv() + } + pub fn domain(&self) -> GeneralEvaluationDomain { self.domains.x1 } From 0cbd5c4a093a08418e1c8d1a92310ce49a7f1db5 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Thu, 17 Apr 2025 15:12:31 +0200 Subject: [PATCH 06/25] tests cleaned up --- evm-vrfier/src/constraints.rs | 31 +++++++++++++++---------------- evm-vrfier/src/plonk_kzg.rs | 4 ++++ 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/evm-vrfier/src/constraints.rs b/evm-vrfier/src/constraints.rs index af03314..5cca0a6 100644 --- a/evm-vrfier/src/constraints.rs +++ b/evm-vrfier/src/constraints.rs @@ -3,12 +3,12 @@ mod tests { use alloy::primitives::U256; use ark_bls12_381::Fr; use ark_ec::twisted_edwards::TECurveConfig; - use ark_ed_on_bls12_381_bandersnatch::{BandersnatchConfig, Fq}; + use ark_ed_on_bls12_381_bandersnatch::BandersnatchConfig; use ark_ff::{Field, One}; use ark_std::{test_rng, UniformRand}; use w3f_plonk_common::domain::Domain; use w3f_plonk_common::gadgets::ec::te_cond_add::cond_te_addition; - use crate::plonk_kzg::bls_scalar_field_to_uint256; + use crate::plonk_kzg::{bls_scalar_field_to_uint256, from_uint}; alloy::sol!( #[sol(rpc)] @@ -25,14 +25,6 @@ mod tests { let rng = &mut test_rng(); - let z = Fr::rand(rng); - let z_n_rust = z.pow([256]); - let z_n_sol = constraints.mod_exp( - bls_scalar_field_to_uint256(z), - U256::from(256), - ).call().await?; - assert_eq!(bls_scalar_field_to_uint256(z_n_rust), z_n_sol); - let te_coeff_a = BandersnatchConfig::COEFF_A; let b = Fr::rand(rng); let x1 = Fr::rand(rng); @@ -63,7 +55,7 @@ mod tests { bls_scalar_field_to_uint256(y3), bls_scalar_field_to_uint256(Fr::one()), ).call().await?; - assert_eq!(bls_scalar_field_to_uint256(cx_rust), cx_sol); + assert_eq!(from_uint(cx_sol), cx_rust); Ok(()) } @@ -79,8 +71,6 @@ mod tests { let rng = &mut test_rng(); let domain = Domain::new(256, true); - let z = Fq::rand(rng); - let domain_at_z = domain.evaluate(z); let w = domain.omega(); let w_inv = domain.omega_inv(); @@ -93,20 +83,29 @@ mod tests { println!("\tuint256 constant w_inv_3 = {};", bls_scalar_field_to_uint256(w_inv_3)); println!("\tuint256 constant w_inv_4 = {};", bls_scalar_field_to_uint256(w_inv_4)); + let z = Fr::rand(rng); + let domain_at_z = domain.evaluate(z); + + let z_n = constraints.mod_exp( + bls_scalar_field_to_uint256(z), + U256::from(123), + ).call().await?; + assert_eq!(from_uint(z_n), z.pow([123])); + let z_inv = constraints.inv( bls_scalar_field_to_uint256(z), ).call().await?; - assert_eq!(z_inv, bls_scalar_field_to_uint256(z.inverse().unwrap())); + assert_eq!(from_uint(z_inv), z.inverse().unwrap()); let v_inv_hiding_at = constraints.v_inv_hiding_at( bls_scalar_field_to_uint256(z), ).call().await?; - assert_eq!(v_inv_hiding_at, bls_scalar_field_to_uint256(domain_at_z.vanishing_polynomial_inv)); + assert_eq!(from_uint(v_inv_hiding_at), domain_at_z.vanishing_polynomial_inv); let not_last_row = constraints.not_last_row( bls_scalar_field_to_uint256(z), ).call().await?; - assert_eq!(not_last_row, bls_scalar_field_to_uint256(domain_at_z.not_last_row)); + assert_eq!(from_uint(not_last_row), domain_at_z.not_last_row); Ok(()) } diff --git a/evm-vrfier/src/plonk_kzg.rs b/evm-vrfier/src/plonk_kzg.rs index 84ed236..288862d 100644 --- a/evm-vrfier/src/plonk_kzg.rs +++ b/evm-vrfier/src/plonk_kzg.rs @@ -25,6 +25,10 @@ pub fn bls_scalar_field_to_uint256(fr: ark_bls12_381::Fr) -> U256 { U256::from_be_slice(&be_bytes) } +pub fn from_uint(f: U256) -> ark_bls12_381::Fr { + ark_bls12_381::Fr::from_le_bytes_mod_order(&f.as_le_bytes()) +} + pub fn bls_scalar_field_to_bytes32(fr: ark_bls12_381::Fr) -> FixedBytes<32> { let be_bytes = fr.into_bigint().to_bytes_be(); FixedBytes::left_padding_from(&be_bytes) From 227575b24baa2daafa3fdf37261606a258542879 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Thu, 17 Apr 2025 17:22:44 +0200 Subject: [PATCH 07/25] forge fmt ignores SoladyBls.sol --- evm-vrfier/contracts/foundry.toml | 3 + evm-vrfier/contracts/src/SoladyBls.sol | 83 ++++++++++++++++++++------ 2 files changed, 67 insertions(+), 19 deletions(-) diff --git a/evm-vrfier/contracts/foundry.toml b/evm-vrfier/contracts/foundry.toml index a40938d..afeb7e9 100644 --- a/evm-vrfier/contracts/foundry.toml +++ b/evm-vrfier/contracts/foundry.toml @@ -6,4 +6,7 @@ libs = ["lib"] evm_version = "prague" via_ir = true +[fmt] +ignore = ["src/SoladyBls.sol"] + # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/evm-vrfier/contracts/src/SoladyBls.sol b/evm-vrfier/contracts/src/SoladyBls.sol index 4b6aa4c..ada0bea 100644 --- a/evm-vrfier/contracts/src/SoladyBls.sol +++ b/evm-vrfier/contracts/src/SoladyBls.sol @@ -118,11 +118,20 @@ library BLS { /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Adds two G1 points. Returns a new G1 point. - function add(G1Point memory point0, G1Point memory point1) internal view returns (G1Point memory result) { + function add(G1Point memory point0, G1Point memory point1) + internal + view + returns (G1Point memory result) + { assembly ("memory-safe") { mcopy(result, point0, 0x80) mcopy(add(result, 0x80), point1, 0x80) - if iszero(and(eq(returndatasize(), 0x80), staticcall(gas(), BLS12_G1ADD, result, 0x100, result, 0x80))) { + if iszero( + and( + eq(returndatasize(), 0x80), + staticcall(gas(), BLS12_G1ADD, result, 0x100, result, 0x80) + ) + ) { mstore(0x00, 0xd6cc76eb) // `G1AddFailed()`. revert(0x1c, 0x04) } @@ -130,7 +139,11 @@ library BLS { } /// @dev Multi-scalar multiplication of G1 points with scalars. Returns a new G1 point. - function msm(G1Point[] memory points, bytes32[] memory scalars) internal view returns (G1Point memory result) { + function msm(G1Point[] memory points, bytes32[] memory scalars) + internal + view + returns (G1Point memory result) + { assembly ("memory-safe") { let k := mload(points) let d := sub(scalars, points) @@ -153,11 +166,20 @@ library BLS { } /// @dev Adds two G2 points. Returns a new G2 point. - function add(G2Point memory point0, G2Point memory point1) internal view returns (G2Point memory result) { + function add(G2Point memory point0, G2Point memory point1) + internal + view + returns (G2Point memory result) + { assembly ("memory-safe") { mcopy(result, point0, 0x100) mcopy(add(result, 0x100), point1, 0x100) - if iszero(and(eq(returndatasize(), 0x100), staticcall(gas(), BLS12_G2ADD, result, 0x200, result, 0x100))) { + if iszero( + and( + eq(returndatasize(), 0x100), + staticcall(gas(), BLS12_G2ADD, result, 0x200, result, 0x100) + ) + ) { mstore(0x00, 0xc55e5e33) // `G2AddFailed()`. revert(0x1c, 0x04) } @@ -165,7 +187,11 @@ library BLS { } /// @dev Multi-scalar multiplication of G2 points with scalars. Returns a new G2 point. - function msm(G2Point[] memory points, bytes32[] memory scalars) internal view returns (G2Point memory result) { + function msm(G2Point[] memory points, bytes32[] memory scalars) + internal + view + returns (G2Point memory result) + { assembly ("memory-safe") { let k := mload(points) let d := sub(scalars, points) @@ -188,7 +214,11 @@ library BLS { } /// @dev Checks the pairing of G1 points with G2 points. Returns whether the pairing is valid. - function pairing(G1Point[] memory g1Points, G2Point[] memory g2Points) internal view returns (bool result) { + function pairing(G1Point[] memory g1Points, G2Point[] memory g2Points) + internal + view + returns (bool result) + { assembly ("memory-safe") { let k := mload(g1Points) let m := mload(0x40) @@ -216,7 +246,10 @@ library BLS { function toG1(Fp memory element) internal view returns (G1Point memory result) { assembly ("memory-safe") { if iszero( - and(eq(returndatasize(), 0x80), staticcall(gas(), BLS12_MAP_FP_TO_G1, element, 0x40, result, 0x80)) + and( + eq(returndatasize(), 0x80), + staticcall(gas(), BLS12_MAP_FP_TO_G1, element, 0x40, result, 0x80) + ) ) { mstore(0x00, 0x24a289fc) // `MapFpToG1Failed()`. revert(0x1c, 0x04) @@ -228,7 +261,10 @@ library BLS { function toG2(Fp2 memory element) internal view returns (G2Point memory result) { assembly ("memory-safe") { if iszero( - and(eq(returndatasize(), 0x100), staticcall(gas(), BLS12_MAP_FP2_TO_G2, element, 0x80, result, 0x100)) + and( + eq(returndatasize(), 0x100), + staticcall(gas(), BLS12_MAP_FP2_TO_G2, element, 0x80, result, 0x100) + ) ) { mstore(0x00, 0x89083b91) // `MapFp2ToG2Failed()`. revert(0x1c, 0x04) @@ -247,22 +283,26 @@ library BLS { } function sha2(data_, n_) -> _h { - if iszero(and(eq(returndatasize(), 0x20), staticcall(gas(), 2, data_, n_, 0x00, 0x20))) { - revert(calldatasize(), 0x00) - } + if iszero( + and(eq(returndatasize(), 0x20), staticcall(gas(), 2, data_, n_, 0x00, 0x20)) + ) { revert(calldatasize(), 0x00) } _h := mload(0x00) } function modfield(s_, b_) { mcopy(add(s_, 0x60), b_, 0x40) - if iszero(and(eq(returndatasize(), 0x40), staticcall(gas(), 5, s_, 0x100, b_, 0x40))) { - revert(calldatasize(), 0x00) - } + if iszero( + and(eq(returndatasize(), 0x40), staticcall(gas(), 5, s_, 0x100, b_, 0x40)) + ) { revert(calldatasize(), 0x00) } } function mapToG2(s_, r_) { - if iszero(and(eq(returndatasize(), 0x100), staticcall(gas(), BLS12_MAP_FP2_TO_G2, s_, 0x80, r_, 0x100))) - { + if iszero( + and( + eq(returndatasize(), 0x100), + staticcall(gas(), BLS12_MAP_FP2_TO_G2, s_, 0x80, r_, 0x100) + ) + ) { mstore(0x00, 0x89083b91) // `MapFp2ToG2Failed()`. revert(0x1c, 0x04) } @@ -301,10 +341,15 @@ library BLS { mapToG2(b, result) mapToG2(add(0x80, b), add(0x100, result)) - if iszero(and(eq(returndatasize(), 0x100), staticcall(gas(), BLS12_G2ADD, result, 0x200, result, 0x100))) { + if iszero( + and( + eq(returndatasize(), 0x100), + staticcall(gas(), BLS12_G2ADD, result, 0x200, result, 0x100) + ) + ) { mstore(0x00, 0xc55e5e33) // `G2AddFailed()`. revert(0x1c, 0x04) } } } -} +} \ No newline at end of file From dc2348aa3efe34182feb8995b4ec9121f21cbac3 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Thu, 17 Apr 2025 17:34:12 +0200 Subject: [PATCH 08/25] forgefmt ignore --- evm-vrfier/contracts/src/Constraints.sol | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/evm-vrfier/contracts/src/Constraints.sol b/evm-vrfier/contracts/src/Constraints.sol index 7da706b..32dec16 100644 --- a/evm-vrfier/contracts/src/Constraints.sol +++ b/evm-vrfier/contracts/src/Constraints.sol @@ -33,7 +33,17 @@ library Constraints { /// `cx = {[(a.x1.x2 + y1.y2).x3 - x1.y1 - x2.y2].b + (x3 - x1).(1 - b)}.not_last` uint256 x1y1 = mul(x1, y1); uint256 x2y2 = mul(x2, y2); - uint256 lx = mul(add(mul(add(mul(te_coeff_a, mul(x1, x2)), mul(y1, y2)), x3), r - add(x1y1, x2y2)), b); + // forgefmt: disable-next-item + uint256 lx = mul( + add( + mul( + add(mul(te_coeff_a, mul(x1, x2)), mul(y1, y2)), + x3 + ), + r - add(x1y1, x2y2) + ), + b + ); cx = add(lx, mul(add(x3, r - x1), add(1, r - b))); } From 939f92ab1327095599e9c436de00cd6001027ced Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Thu, 17 Apr 2025 18:56:17 +0200 Subject: [PATCH 09/25] PlonkKzg made an internal lib --- evm-vrfier/contracts/src/PlonkKzg.sol | 32 +++++++------ evm-vrfier/contracts/test/PlonkKzg.t.sol | 60 +++++++++++++++++------- evm-vrfier/src/plonk_kzg.rs | 2 +- 3 files changed, 62 insertions(+), 32 deletions(-) diff --git a/evm-vrfier/contracts/src/PlonkKzg.sol b/evm-vrfier/contracts/src/PlonkKzg.sol index f9d6761..a7fc81c 100644 --- a/evm-vrfier/contracts/src/PlonkKzg.sol +++ b/evm-vrfier/contracts/src/PlonkKzg.sol @@ -2,14 +2,7 @@ pragma solidity ^0.8.24; import "./BlsGenerators.sol"; -contract PlonkKzg { - // The trapdoor `tau` in G2, part of the standard KZG verification key. - BLS.G2Point tau_g2; - - constructor(BLS.G2Point memory tau_g2_) { - tau_g2 = tau_g2_; - } - +library PlonkKzg { function verify_plonk_kzg( BLS.G1Point[] memory polys_z1, BLS.G1Point memory poly_z2, @@ -20,8 +13,9 @@ contract PlonkKzg { BLS.G1Point memory proof_z1, BLS.G1Point memory proof_z2, bytes32[] memory nus, - uint256 r - ) public view returns (bool) { + uint256 r, + BLS.G2Point memory tau_g2 + ) internal view returns (bool) { assert(evals_at_z1.length == polys_z1.length); assert(nus.length == polys_z1.length); @@ -57,10 +51,14 @@ contract PlonkKzg { BLS.G1Point memory agg_acc = BLS.msm(msm_bases, msm_scalars); BLS.G1Point memory acc_proof = BLS.add(proof_z1, BlsGenerators.g1_mul(proof_z2, bytes32(r))); - return verify_acc(agg_acc, acc_proof); + return verify_acc(agg_acc, acc_proof, tau_g2); } - function verify(BLS.G1Point memory c, uint256 z, uint256 v, BLS.G1Point memory proof) public view returns (bool) { + function verify(BLS.G1Point memory c, uint256 z, uint256 v, BLS.G1Point memory proof, BLS.G2Point memory tau_g2) + internal + view + returns (bool) + { bytes32[] memory msm_scalars = new bytes32[](2); BLS.G1Point[] memory msm_bases = new BLS.G1Point[](2); msm_scalars[0] = bytes32(z); @@ -69,10 +67,14 @@ contract PlonkKzg { msm_bases[1] = BlsGenerators.G1(); BLS.G1Point memory acc = BLS.msm(msm_bases, msm_scalars); acc = BLS.add(acc, c); - return verify_acc(acc, proof); + return verify_acc(acc, proof, tau_g2); } - function verify_acc(BLS.G1Point memory acc, BLS.G1Point memory acc_proof) public view returns (bool) { + function verify_acc(BLS.G1Point memory acc, BLS.G1Point memory acc_proof, BLS.G2Point memory tau_g2) + internal + view + returns (bool) + { return pairing2(acc, BlsGenerators.G2_NEG(), acc_proof, tau_g2); } @@ -81,7 +83,7 @@ contract PlonkKzg { BLS.G2Point memory g2_1, BLS.G1Point memory g1_2, BLS.G2Point memory g2_2 - ) public view returns (bool result) { + ) internal view returns (bool result) { BLS.G1Point[] memory g1_points = new BLS.G1Point[](2); BLS.G2Point[] memory g2_points = new BLS.G2Point[](2); g1_points[0] = g1_1; diff --git a/evm-vrfier/contracts/test/PlonkKzg.t.sol b/evm-vrfier/contracts/test/PlonkKzg.t.sol index 60576de..8592216 100644 --- a/evm-vrfier/contracts/test/PlonkKzg.t.sol +++ b/evm-vrfier/contracts/test/PlonkKzg.t.sol @@ -1,14 +1,50 @@ pragma solidity ^0.8.24; -import {Test, console} from "forge-std/Test.sol"; -import "../src/SoladyBls.sol"; -import "../src/BlsGenerators.sol"; -import "../src/PlonkKzg.sol"; +import {Test} from "forge-std/Test.sol"; +import {BLS, BlsGenerators, PlonkKzg} from "src/PlonkKzg.sol"; + +contract KzgVerifier { + // The trapdoor `tau` in G2, part of the standard KZG verification key. + BLS.G2Point tau_g2; + + constructor(BLS.G2Point memory tau_g2_) { + tau_g2 = tau_g2_; + } + + function verify(BLS.G1Point memory c, uint256 z, uint256 v, BLS.G1Point memory proof) public view returns (bool) { + return PlonkKzg.verify(c, z, v, proof, tau_g2); + } + + function verify_plonk_kzg( + BLS.G1Point[] memory polys_z1, + BLS.G1Point memory poly_z2, + uint256 z1, + uint256 z2, + uint256[] memory evals_at_z1, + uint256 eval_at_z2, + BLS.G1Point memory proof_z1, + BLS.G1Point memory proof_z2, + bytes32[] memory nus, + uint256 r + ) public view returns (bool) { + return PlonkKzg.verify_plonk_kzg( + polys_z1, poly_z2, z1, z2, evals_at_z1, eval_at_z2, proof_z1, proof_z2, nus, r, tau_g2 + ); + } + + function pairing2( + BLS.G1Point memory g1_1, + BLS.G2Point memory g2_1, + BLS.G1Point memory g1_2, + BLS.G2Point memory g2_2 + ) public view returns (bool) { + return PlonkKzg.pairing2(g1_1, g2_1, g1_2, g2_2); + } +} contract PlonkKzgTest is Test { - PlonkKzg kzg; + KzgVerifier kzg; BLS.G1Point[] srs_g1; - BLS.G2Point tau_g2; function setUp() public { uint256 n = 3; @@ -17,8 +53,8 @@ contract PlonkKzgTest is Test { for (uint256 i = 1; i < n; i++) { srs_g1.push(BlsGenerators.g1_mul(srs_g1[i - 1], tau)); } - tau_g2 = BlsGenerators.g2_mul(BlsGenerators.G2(), tau); - kzg = new PlonkKzg(tau_g2); + BLS.G2Point memory tau_g2 = BlsGenerators.g2_mul(BlsGenerators.G2(), tau); + kzg = new KzgVerifier(tau_g2); } function commit(bytes32[] memory coeffs) internal view returns (BLS.G1Point memory c) { @@ -50,14 +86,6 @@ contract PlonkKzgTest is Test { } } - function verify_single(BLS.G1Point memory c, uint256 z, uint256 v, BLS.G1Point memory proof) - internal - view - returns (bool) - { - return kzg.verify(c, z, v, proof); - } - function test_div() public pure { // 3x^2 + 2x + 1 = [1, 2, 3] bytes32[] memory poly = new bytes32[](3); diff --git a/evm-vrfier/src/plonk_kzg.rs b/evm-vrfier/src/plonk_kzg.rs index 288862d..b4e54b0 100644 --- a/evm-vrfier/src/plonk_kzg.rs +++ b/evm-vrfier/src/plonk_kzg.rs @@ -5,7 +5,7 @@ use ark_ff::BigInteger; alloy::sol!( #[sol(rpc)] PlonkKzg, - "contracts/out/PlonkKzg.sol/PlonkKzg.json" + "contracts/out/PlonkKzg.t.sol/KzgVerifier.json" ); /// Encodes a BLS12-381 base field element (381 bits) as specified in From b202095f76671427d4695a9f73116a991f114004 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Sun, 20 Apr 2025 16:49:40 +0200 Subject: [PATCH 10/25] eth encoding moved to lib --- evm-vrfier/src/constraints.rs | 47 ++++++++++++++--------------- evm-vrfier/src/lib.rs | 30 +++++++++++++++++++ evm-vrfier/src/plonk_kzg.rs | 56 +++++++++-------------------------- 3 files changed, 67 insertions(+), 66 deletions(-) diff --git a/evm-vrfier/src/constraints.rs b/evm-vrfier/src/constraints.rs index 5cca0a6..6415c18 100644 --- a/evm-vrfier/src/constraints.rs +++ b/evm-vrfier/src/constraints.rs @@ -8,8 +8,7 @@ mod tests { use ark_std::{test_rng, UniformRand}; use w3f_plonk_common::domain::Domain; use w3f_plonk_common::gadgets::ec::te_cond_add::cond_te_addition; - use crate::plonk_kzg::{bls_scalar_field_to_uint256, from_uint}; - + use crate::{fr_to_uint, unit_to_fr}; alloy::sol!( #[sol(rpc)] Constraints, @@ -46,16 +45,16 @@ mod tests { Fr::one(), )[0]; let cx_sol = constraints.cond_te_addition( - bls_scalar_field_to_uint256(b), - bls_scalar_field_to_uint256(x1), - bls_scalar_field_to_uint256(y1), - bls_scalar_field_to_uint256(x2), - bls_scalar_field_to_uint256(y2), - bls_scalar_field_to_uint256(x3), - bls_scalar_field_to_uint256(y3), - bls_scalar_field_to_uint256(Fr::one()), + fr_to_uint(b), + fr_to_uint(x1), + fr_to_uint(y1), + fr_to_uint(x2), + fr_to_uint(y2), + fr_to_uint(x3), + fr_to_uint(y3), + fr_to_uint(Fr::one()), ).call().await?; - assert_eq!(from_uint(cx_sol), cx_rust); + assert_eq!(unit_to_fr(cx_sol), cx_rust); Ok(()) } @@ -77,35 +76,35 @@ mod tests { let w_inv_2 = w_inv * w_inv; let w_inv_3 = w_inv_2 * w_inv; let w_inv_4 = w_inv_3 * w_inv; - println!("\tuint256 constant w = {};", bls_scalar_field_to_uint256(w)); - println!("\tuint256 constant w_inv = {};", bls_scalar_field_to_uint256(w_inv)); - println!("\tuint256 constant w_inv_2 = {};", bls_scalar_field_to_uint256(w_inv_2)); - println!("\tuint256 constant w_inv_3 = {};", bls_scalar_field_to_uint256(w_inv_3)); - println!("\tuint256 constant w_inv_4 = {};", bls_scalar_field_to_uint256(w_inv_4)); + println!("\tuint256 constant w = {};", fr_to_uint(w)); + println!("\tuint256 constant w_inv = {};", fr_to_uint(w_inv)); + println!("\tuint256 constant w_inv_2 = {};", fr_to_uint(w_inv_2)); + println!("\tuint256 constant w_inv_3 = {};", fr_to_uint(w_inv_3)); + println!("\tuint256 constant w_inv_4 = {};", fr_to_uint(w_inv_4)); let z = Fr::rand(rng); let domain_at_z = domain.evaluate(z); let z_n = constraints.mod_exp( - bls_scalar_field_to_uint256(z), + fr_to_uint(z), U256::from(123), ).call().await?; - assert_eq!(from_uint(z_n), z.pow([123])); + assert_eq!(unit_to_fr(z_n), z.pow([123])); let z_inv = constraints.inv( - bls_scalar_field_to_uint256(z), + fr_to_uint(z), ).call().await?; - assert_eq!(from_uint(z_inv), z.inverse().unwrap()); + assert_eq!(unit_to_fr(z_inv), z.inverse().unwrap()); let v_inv_hiding_at = constraints.v_inv_hiding_at( - bls_scalar_field_to_uint256(z), + fr_to_uint(z), ).call().await?; - assert_eq!(from_uint(v_inv_hiding_at), domain_at_z.vanishing_polynomial_inv); + assert_eq!(unit_to_fr(v_inv_hiding_at), domain_at_z.vanishing_polynomial_inv); let not_last_row = constraints.not_last_row( - bls_scalar_field_to_uint256(z), + fr_to_uint(z), ).call().await?; - assert_eq!(from_uint(not_last_row), domain_at_z.not_last_row); + assert_eq!(unit_to_fr(not_last_row), domain_at_z.not_last_row); Ok(()) } diff --git a/evm-vrfier/src/lib.rs b/evm-vrfier/src/lib.rs index 69afdc8..e5a00a8 100644 --- a/evm-vrfier/src/lib.rs +++ b/evm-vrfier/src/lib.rs @@ -1,6 +1,35 @@ +use alloy::primitives::{FixedBytes, U256}; +use ark_ff::{BigInteger, PrimeField}; + pub mod plonk_kzg; mod constraints; +/// Encodes a BLS12-381 base field element (381 bits) into 2 bytes32 as specified in +/// [eip-2537](https://eips.ethereum.org/EIPS/eip-2537#fine-points-and-encoding-of-base-elements): +/// > A base field element (Fp) is encoded as 64 bytes +/// > by performing the BigEndian encoding of the corresponding (unsigned) integer. +/// > Due to the size of p, the top 16 bytes are always zeroes. +pub fn fq_to_bytes(fq: ark_bls12_381::Fq) -> [FixedBytes<32>; 2] { + let be_bytes = fq.into_bigint().to_bytes_be(); // 48 bytes + let high_bytes = FixedBytes::left_padding_from(&be_bytes[..16]); // 16 bytes + let low_bytes = FixedBytes::from_slice(&be_bytes[16..]); // 32 bytes + [high_bytes, low_bytes] +} + +pub fn fr_to_bytes(fr: ark_bls12_381::Fr) -> FixedBytes<32> { + let be_bytes = fr.into_bigint().to_bytes_be(); + FixedBytes::left_padding_from(&be_bytes) +} + +pub fn fr_to_uint(fr: ark_bls12_381::Fr) -> U256 { + let be_bytes = fr.into_bigint().to_bytes_be(); + U256::from_be_slice(&be_bytes) +} + +pub fn unit_to_fr(f: U256) -> ark_bls12_381::Fr { + ark_bls12_381::Fr::from_le_bytes_mod_order(&f.as_le_bytes()) +} + #[cfg(test)] mod tests { use alloy::primitives::Uint; @@ -26,3 +55,4 @@ mod tests { Ok(()) } } + diff --git a/evm-vrfier/src/plonk_kzg.rs b/evm-vrfier/src/plonk_kzg.rs index b4e54b0..d4bdf30 100644 --- a/evm-vrfier/src/plonk_kzg.rs +++ b/evm-vrfier/src/plonk_kzg.rs @@ -1,50 +1,20 @@ -use alloy::primitives::{FixedBytes, U256}; -use ark_ff::fields::PrimeField; -use ark_ff::BigInteger; - alloy::sol!( #[sol(rpc)] PlonkKzg, "contracts/out/PlonkKzg.t.sol/KzgVerifier.json" ); -/// Encodes a BLS12-381 base field element (381 bits) as specified in -/// [eip-2537](https://eips.ethereum.org/EIPS/eip-2537#fine-points-and-encoding-of-base-elements): -/// > A base field element (Fp) is encoded as 64 bytes -/// > by performing the BigEndian encoding of the corresponding (unsigned) integer. -/// > Due to the size of p, the top 16 bytes are always zeroes. -pub fn bls_base_field_to_bytes(fq: ark_bls12_381::Fq) -> [FixedBytes<32>; 2] { - let be_bytes = fq.into_bigint().to_bytes_be(); // 48 bytes - let high_bytes = FixedBytes::left_padding_from(&be_bytes[..16]); // 16 bytes - let low_bytes = FixedBytes::from_slice(&be_bytes[16..]); // 32 bytes - [high_bytes, low_bytes] -} - -pub fn bls_scalar_field_to_uint256(fr: ark_bls12_381::Fr) -> U256 { - let be_bytes = fr.into_bigint().to_bytes_be(); - U256::from_be_slice(&be_bytes) -} - -pub fn from_uint(f: U256) -> ark_bls12_381::Fr { - ark_bls12_381::Fr::from_le_bytes_mod_order(&f.as_le_bytes()) -} - -pub fn bls_scalar_field_to_bytes32(fr: ark_bls12_381::Fr) -> FixedBytes<32> { - let be_bytes = fr.into_bigint().to_bytes_be(); - FixedBytes::left_padding_from(&be_bytes) -} - pub fn encode_bls_g1(p: ark_bls12_381::G1Affine) -> BLS::G1Point { - let [x_a, x_b] = bls_base_field_to_bytes(p.x); - let [y_a, y_b] = bls_base_field_to_bytes(p.y); + let [x_a, x_b] = crate::fq_to_bytes(p.x); + let [y_a, y_b] = crate::fq_to_bytes(p.y); BLS::G1Point { x_a, x_b, y_a, y_b } } pub fn encode_bls_g2(p: ark_bls12_381::G2Affine) -> BLS::G2Point { - let [x_c0_a, x_c0_b] = bls_base_field_to_bytes(p.x.c0); - let [x_c1_a, x_c1_b] = bls_base_field_to_bytes(p.x.c1); - let [y_c0_a, y_c0_b] = bls_base_field_to_bytes(p.y.c0); - let [y_c1_a, y_c1_b] = bls_base_field_to_bytes(p.y.c1); + let [x_c0_a, x_c0_b] = crate::fq_to_bytes(p.x.c0); + let [x_c1_a, x_c1_b] = crate::fq_to_bytes(p.x.c1); + let [y_c0_a, y_c0_b] = crate::fq_to_bytes(p.y.c0); + let [y_c1_a, y_c1_b] = crate::fq_to_bytes(p.y.c1); BLS::G2Point { x_c0_a, x_c0_b, @@ -59,6 +29,7 @@ pub fn encode_bls_g2(p: ark_bls12_381::G2Affine) -> BLS::G2Point { #[cfg(test)] mod tests { + use alloy::primitives::U256; use super::*; use crate::plonk_kzg::PlonkKzg; use ark_bls12_381::{Bls12_381, Fr, G2Affine}; @@ -74,6 +45,7 @@ mod tests { use w3f_pcs::DenseUVPolynomial; use w3f_pcs::Poly; use w3f_pcs::Polynomial; + use crate::{fr_to_bytes, fr_to_uint}; struct ArksBatchKzgOpenning { polys_z1: Vec, @@ -102,14 +74,14 @@ mod tests { EthBatchKzgOpenning { polys_z1: self.polys_z1.into_iter().map(encode_bls_g1).collect(), poly_z2: encode_bls_g1(self.poly_z2), - z1: bls_scalar_field_to_uint256(self.z1), - z2: bls_scalar_field_to_uint256(self.z2), + z1: fr_to_uint(self.z1), + z2: fr_to_uint(self.z2), evals_at_z1: self .evals_at_z1 .into_iter() - .map(bls_scalar_field_to_uint256) + .map(fr_to_uint) .collect(), - eval_at_z2: bls_scalar_field_to_uint256(self.eval_at_z2), + eval_at_z2: fr_to_uint(self.eval_at_z2), kzg_proof_at_z1: encode_bls_g1(self.kzg_proof_at_z1), kzg_proof_at_z2: encode_bls_g1(self.kzg_proof_at_z2), } @@ -201,8 +173,8 @@ mod tests { test_openning.eval_at_z2, test_openning.kzg_proof_at_z1, test_openning.kzg_proof_at_z2, - nus.into_iter().map(bls_scalar_field_to_bytes32).collect(), - bls_scalar_field_to_uint256(Fr::from(1)), + nus.into_iter().map(fr_to_bytes).collect(), + fr_to_uint(Fr::from(1)), ) .call() .await?; From fa68bed5476052f931cf0a298703fc759a51adcc Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Sun, 20 Apr 2025 16:53:20 +0200 Subject: [PATCH 11/25] fmt --- evm-vrfier/src/constraints.rs | 52 +++++++++++++++++------------------ evm-vrfier/src/lib.rs | 3 +- evm-vrfier/src/plonk_kzg.rs | 10 ++----- 3 files changed, 30 insertions(+), 35 deletions(-) diff --git a/evm-vrfier/src/constraints.rs b/evm-vrfier/src/constraints.rs index 6415c18..c2db0cd 100644 --- a/evm-vrfier/src/constraints.rs +++ b/evm-vrfier/src/constraints.rs @@ -1,5 +1,6 @@ #[cfg(test)] mod tests { + use crate::{fr_to_uint, unit_to_fr}; use alloy::primitives::U256; use ark_bls12_381::Fr; use ark_ec::twisted_edwards::TECurveConfig; @@ -8,7 +9,6 @@ mod tests { use ark_std::{test_rng, UniformRand}; use w3f_plonk_common::domain::Domain; use w3f_plonk_common::gadgets::ec::te_cond_add::cond_te_addition; - use crate::{fr_to_uint, unit_to_fr}; alloy::sol!( #[sol(rpc)] Constraints, @@ -32,7 +32,7 @@ mod tests { let y2 = Fr::rand(rng); let x3 = Fr::rand(rng); let y3 = Fr::rand(rng); - let cx_rust= cond_te_addition( + let cx_rust = cond_te_addition( te_coeff_a, &b, &x1, @@ -44,16 +44,19 @@ mod tests { &Fr::one(), Fr::one(), )[0]; - let cx_sol = constraints.cond_te_addition( - fr_to_uint(b), - fr_to_uint(x1), - fr_to_uint(y1), - fr_to_uint(x2), - fr_to_uint(y2), - fr_to_uint(x3), - fr_to_uint(y3), - fr_to_uint(Fr::one()), - ).call().await?; + let cx_sol = constraints + .cond_te_addition( + fr_to_uint(b), + fr_to_uint(x1), + fr_to_uint(y1), + fr_to_uint(x2), + fr_to_uint(y2), + fr_to_uint(x3), + fr_to_uint(y3), + fr_to_uint(Fr::one()), + ) + .call() + .await?; assert_eq!(unit_to_fr(cx_sol), cx_rust); Ok(()) @@ -85,25 +88,22 @@ mod tests { let z = Fr::rand(rng); let domain_at_z = domain.evaluate(z); - let z_n = constraints.mod_exp( - fr_to_uint(z), - U256::from(123), - ).call().await?; + let z_n = constraints + .mod_exp(fr_to_uint(z), U256::from(123)) + .call() + .await?; assert_eq!(unit_to_fr(z_n), z.pow([123])); - let z_inv = constraints.inv( - fr_to_uint(z), - ).call().await?; + let z_inv = constraints.inv(fr_to_uint(z)).call().await?; assert_eq!(unit_to_fr(z_inv), z.inverse().unwrap()); - let v_inv_hiding_at = constraints.v_inv_hiding_at( - fr_to_uint(z), - ).call().await?; - assert_eq!(unit_to_fr(v_inv_hiding_at), domain_at_z.vanishing_polynomial_inv); + let v_inv_hiding_at = constraints.v_inv_hiding_at(fr_to_uint(z)).call().await?; + assert_eq!( + unit_to_fr(v_inv_hiding_at), + domain_at_z.vanishing_polynomial_inv + ); - let not_last_row = constraints.not_last_row( - fr_to_uint(z), - ).call().await?; + let not_last_row = constraints.not_last_row(fr_to_uint(z)).call().await?; assert_eq!(unit_to_fr(not_last_row), domain_at_z.not_last_row); Ok(()) diff --git a/evm-vrfier/src/lib.rs b/evm-vrfier/src/lib.rs index e5a00a8..551d1d3 100644 --- a/evm-vrfier/src/lib.rs +++ b/evm-vrfier/src/lib.rs @@ -1,8 +1,8 @@ use alloy::primitives::{FixedBytes, U256}; use ark_ff::{BigInteger, PrimeField}; -pub mod plonk_kzg; mod constraints; +pub mod plonk_kzg; /// Encodes a BLS12-381 base field element (381 bits) into 2 bytes32 as specified in /// [eip-2537](https://eips.ethereum.org/EIPS/eip-2537#fine-points-and-encoding-of-base-elements): @@ -55,4 +55,3 @@ mod tests { Ok(()) } } - diff --git a/evm-vrfier/src/plonk_kzg.rs b/evm-vrfier/src/plonk_kzg.rs index d4bdf30..324a9f8 100644 --- a/evm-vrfier/src/plonk_kzg.rs +++ b/evm-vrfier/src/plonk_kzg.rs @@ -29,9 +29,10 @@ pub fn encode_bls_g2(p: ark_bls12_381::G2Affine) -> BLS::G2Point { #[cfg(test)] mod tests { - use alloy::primitives::U256; use super::*; use crate::plonk_kzg::PlonkKzg; + use crate::{fr_to_bytes, fr_to_uint}; + use alloy::primitives::U256; use ark_bls12_381::{Bls12_381, Fr, G2Affine}; use ark_ec::pairing::Pairing; use ark_ec::{AffineRepr, PrimeGroup}; @@ -45,7 +46,6 @@ mod tests { use w3f_pcs::DenseUVPolynomial; use w3f_pcs::Poly; use w3f_pcs::Polynomial; - use crate::{fr_to_bytes, fr_to_uint}; struct ArksBatchKzgOpenning { polys_z1: Vec, @@ -76,11 +76,7 @@ mod tests { poly_z2: encode_bls_g1(self.poly_z2), z1: fr_to_uint(self.z1), z2: fr_to_uint(self.z2), - evals_at_z1: self - .evals_at_z1 - .into_iter() - .map(fr_to_uint) - .collect(), + evals_at_z1: self.evals_at_z1.into_iter().map(fr_to_uint).collect(), eval_at_z2: fr_to_uint(self.eval_at_z2), kzg_proof_at_z1: encode_bls_g1(self.kzg_proof_at_z1), kzg_proof_at_z2: encode_bls_g1(self.kzg_proof_at_z2), From bad3e25c751dcda6b2b95356697527cfc585c02a Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Sun, 20 Apr 2025 16:54:04 +0200 Subject: [PATCH 12/25] PlonkKzg moved to test --- evm-vrfier/src/plonk_kzg.rs | 60 ++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/evm-vrfier/src/plonk_kzg.rs b/evm-vrfier/src/plonk_kzg.rs index 324a9f8..7d0555c 100644 --- a/evm-vrfier/src/plonk_kzg.rs +++ b/evm-vrfier/src/plonk_kzg.rs @@ -1,36 +1,5 @@ -alloy::sol!( - #[sol(rpc)] - PlonkKzg, - "contracts/out/PlonkKzg.t.sol/KzgVerifier.json" -); - -pub fn encode_bls_g1(p: ark_bls12_381::G1Affine) -> BLS::G1Point { - let [x_a, x_b] = crate::fq_to_bytes(p.x); - let [y_a, y_b] = crate::fq_to_bytes(p.y); - BLS::G1Point { x_a, x_b, y_a, y_b } -} - -pub fn encode_bls_g2(p: ark_bls12_381::G2Affine) -> BLS::G2Point { - let [x_c0_a, x_c0_b] = crate::fq_to_bytes(p.x.c0); - let [x_c1_a, x_c1_b] = crate::fq_to_bytes(p.x.c1); - let [y_c0_a, y_c0_b] = crate::fq_to_bytes(p.y.c0); - let [y_c1_a, y_c1_b] = crate::fq_to_bytes(p.y.c1); - BLS::G2Point { - x_c0_a, - x_c0_b, - x_c1_a, - x_c1_b, - y_c0_a, - y_c0_b, - y_c1_a, - y_c1_b, - } -} - #[cfg(test)] mod tests { - use super::*; - use crate::plonk_kzg::PlonkKzg; use crate::{fr_to_bytes, fr_to_uint}; use alloy::primitives::U256; use ark_bls12_381::{Bls12_381, Fr, G2Affine}; @@ -47,6 +16,35 @@ mod tests { use w3f_pcs::Poly; use w3f_pcs::Polynomial; + alloy::sol!( + #[sol(rpc)] + PlonkKzg, + "contracts/out/PlonkKzg.t.sol/KzgVerifier.json" + ); + + pub fn encode_bls_g1(p: ark_bls12_381::G1Affine) -> BLS::G1Point { + let [x_a, x_b] = crate::fq_to_bytes(p.x); + let [y_a, y_b] = crate::fq_to_bytes(p.y); + BLS::G1Point { x_a, x_b, y_a, y_b } + } + + pub fn encode_bls_g2(p: ark_bls12_381::G2Affine) -> BLS::G2Point { + let [x_c0_a, x_c0_b] = crate::fq_to_bytes(p.x.c0); + let [x_c1_a, x_c1_b] = crate::fq_to_bytes(p.x.c1); + let [y_c0_a, y_c0_b] = crate::fq_to_bytes(p.y.c0); + let [y_c1_a, y_c1_b] = crate::fq_to_bytes(p.y.c1); + BLS::G2Point { + x_c0_a, + x_c0_b, + x_c1_a, + x_c1_b, + y_c0_a, + y_c0_b, + y_c1_a, + y_c1_b, + } + } + struct ArksBatchKzgOpenning { polys_z1: Vec, poly_z2: E::G1Affine, From 93b31cbd8ff70659f5c31b6794ccf050643d039c Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Sun, 20 Apr 2025 18:07:20 +0200 Subject: [PATCH 13/25] sol points encoding moved to lib --- evm-vrfier/src/lib.rs | 43 +++++++++++++++++++++++ evm-vrfier/src/plonk_kzg.rs | 69 ++++++++++++++++++++----------------- 2 files changed, 81 insertions(+), 31 deletions(-) diff --git a/evm-vrfier/src/lib.rs b/evm-vrfier/src/lib.rs index 551d1d3..f214d26 100644 --- a/evm-vrfier/src/lib.rs +++ b/evm-vrfier/src/lib.rs @@ -4,6 +4,26 @@ use ark_ff::{BigInteger, PrimeField}; mod constraints; pub mod plonk_kzg; +alloy::sol! { + struct G1Point { + bytes32 x_a; + bytes32 x_b; + bytes32 y_a; + bytes32 y_b; + } + + struct G2Point { + bytes32 x_c0_a; + bytes32 x_c0_b; + bytes32 x_c1_a; + bytes32 x_c1_b; + bytes32 y_c0_a; + bytes32 y_c0_b; + bytes32 y_c1_a; + bytes32 y_c1_b; + } +} + /// Encodes a BLS12-381 base field element (381 bits) into 2 bytes32 as specified in /// [eip-2537](https://eips.ethereum.org/EIPS/eip-2537#fine-points-and-encoding-of-base-elements): /// > A base field element (Fp) is encoded as 64 bytes @@ -30,6 +50,29 @@ pub fn unit_to_fr(f: U256) -> ark_bls12_381::Fr { ark_bls12_381::Fr::from_le_bytes_mod_order(&f.as_le_bytes()) } +pub fn encode_g1(p: ark_bls12_381::G1Affine) -> G1Point { + let [x_a, x_b] = fq_to_bytes(p.x); + let [y_a, y_b] = fq_to_bytes(p.y); + G1Point { x_a, x_b, y_a, y_b } +} + +pub fn encode_g2(p: ark_bls12_381::G2Affine) -> G2Point { + let [x_c0_a, x_c0_b] = fq_to_bytes(p.x.c0); + let [x_c1_a, x_c1_b] = fq_to_bytes(p.x.c1); + let [y_c0_a, y_c0_b] = fq_to_bytes(p.y.c0); + let [y_c1_a, y_c1_b] = fq_to_bytes(p.y.c1); + G2Point { + x_c0_a, + x_c0_b, + x_c1_a, + x_c1_b, + y_c0_a, + y_c0_b, + y_c1_a, + y_c1_b, + } +} + #[cfg(test)] mod tests { use alloy::primitives::Uint; diff --git a/evm-vrfier/src/plonk_kzg.rs b/evm-vrfier/src/plonk_kzg.rs index 7d0555c..7961239 100644 --- a/evm-vrfier/src/plonk_kzg.rs +++ b/evm-vrfier/src/plonk_kzg.rs @@ -1,8 +1,8 @@ #[cfg(test)] mod tests { - use crate::{fr_to_bytes, fr_to_uint}; + use crate::{encode_g1, encode_g2, fr_to_bytes, fr_to_uint, G1Point, G2Point}; use alloy::primitives::U256; - use ark_bls12_381::{Bls12_381, Fr, G2Affine}; + use ark_bls12_381::{Bls12_381, Fr, G1Affine, G2Affine}; use ark_ec::pairing::Pairing; use ark_ec::{AffineRepr, PrimeGroup}; use ark_std::rand::Rng; @@ -22,26 +22,29 @@ mod tests { "contracts/out/PlonkKzg.t.sol/KzgVerifier.json" ); - pub fn encode_bls_g1(p: ark_bls12_381::G1Affine) -> BLS::G1Point { - let [x_a, x_b] = crate::fq_to_bytes(p.x); - let [y_a, y_b] = crate::fq_to_bytes(p.y); - BLS::G1Point { x_a, x_b, y_a, y_b } + impl From for BLS::G1Point { + fn from(p: G1Point) -> Self { + Self { + x_a: p.x_a, + x_b: p.x_b, + y_a: p.y_a, + y_b: p.y_b, + } + } } - pub fn encode_bls_g2(p: ark_bls12_381::G2Affine) -> BLS::G2Point { - let [x_c0_a, x_c0_b] = crate::fq_to_bytes(p.x.c0); - let [x_c1_a, x_c1_b] = crate::fq_to_bytes(p.x.c1); - let [y_c0_a, y_c0_b] = crate::fq_to_bytes(p.y.c0); - let [y_c1_a, y_c1_b] = crate::fq_to_bytes(p.y.c1); - BLS::G2Point { - x_c0_a, - x_c0_b, - x_c1_a, - x_c1_b, - y_c0_a, - y_c0_b, - y_c1_a, - y_c1_b, + impl From for BLS::G2Point { + fn from(p: G2Point) -> Self { + Self { + x_c0_a: p.x_c0_a, + x_c0_b: p.x_c0_b, + x_c1_a: p.x_c1_a, + x_c1_b: p.x_c1_b, + y_c0_a: p.y_c0_a, + y_c0_b: p.y_c0_b, + y_c1_a: p.y_c1_a, + y_c1_b: p.y_c1_b, + } } } @@ -70,14 +73,18 @@ mod tests { impl ArksBatchKzgOpenning { fn encode(self) -> EthBatchKzgOpenning { EthBatchKzgOpenning { - polys_z1: self.polys_z1.into_iter().map(encode_bls_g1).collect(), - poly_z2: encode_bls_g1(self.poly_z2), + polys_z1: self + .polys_z1 + .into_iter() + .map(|p| encode_g1(p).into()) + .collect(), + poly_z2: encode_g1(self.poly_z2).into(), z1: fr_to_uint(self.z1), z2: fr_to_uint(self.z2), evals_at_z1: self.evals_at_z1.into_iter().map(fr_to_uint).collect(), eval_at_z2: fr_to_uint(self.eval_at_z2), - kzg_proof_at_z1: encode_bls_g1(self.kzg_proof_at_z1), - kzg_proof_at_z2: encode_bls_g1(self.kzg_proof_at_z2), + kzg_proof_at_z1: encode_g1(self.kzg_proof_at_z1).into(), + kzg_proof_at_z2: encode_g1(self.kzg_proof_at_z2).into(), } } } @@ -155,7 +162,7 @@ mod tests { let (test_openning, nus, kzg_vk) = random_opening::(123, 1, &mut test_rng()); let test_openning = test_openning.encode(); - let plonk_kzg = PlonkKzg::deploy(&provider, encode_bls_g2(kzg_vk.tau_in_g2)).await?; + let plonk_kzg = PlonkKzg::deploy(&provider, encode_g2(kzg_vk.tau_in_g2).into()).await?; let res = plonk_kzg .verify_plonk_kzg( @@ -186,7 +193,7 @@ mod tests { let (test_openning, _, kzg_vk) = random_opening::(123, 0, &mut test_rng()); let test_openning = test_openning.encode(); - let plonk_kzg = PlonkKzg::deploy(&provider, encode_bls_g2(kzg_vk.tau_in_g2)).await?; + let plonk_kzg = PlonkKzg::deploy(&provider, encode_g2(kzg_vk.tau_in_g2).into()).await?; let res = plonk_kzg .verify( @@ -209,14 +216,14 @@ mod tests { .on_anvil_with_wallet_and_config(|anvil| anvil.prague())?; let _tau_g2 = G2Affine::generator(); - let plonk_kzg = PlonkKzg::deploy(&provider, encode_bls_g2(_tau_g2)).await?; + let plonk_kzg = PlonkKzg::deploy(&provider, encode_g2(_tau_g2).into()).await?; let res = plonk_kzg .pairing2( - encode_bls_g1(-ark_bls12_381::G1Affine::generator()), - encode_bls_g2(ark_bls12_381::G2Affine::generator()), - encode_bls_g1(ark_bls12_381::G1Affine::generator()), - encode_bls_g2(ark_bls12_381::G2Affine::generator()), + encode_g1(-G1Affine::generator()).into(), + encode_g2(G2Affine::generator()).into(), + encode_g1(G1Affine::generator()).into(), + encode_g2(G2Affine::generator()).into(), ) .call() .await?; From fb282ed0d68d6724cda9faab3b75cf7bcb44abd6 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Sun, 20 Apr 2025 19:06:23 +0200 Subject: [PATCH 14/25] toy plonk verifier --- evm-vrfier/Cargo.toml | 2 +- evm-vrfier/contracts/src/Constraints.sol | 20 ++- evm-vrfier/contracts/src/Plonk.sol | 63 +++++++ evm-vrfier/src/lib.rs | 5 +- evm-vrfier/src/plonk_test.rs | 205 +++++++++++++++++++++++ w3f-plonk-common/src/domain.rs | 2 +- 6 files changed, 285 insertions(+), 12 deletions(-) create mode 100644 evm-vrfier/contracts/src/Plonk.sol create mode 100644 evm-vrfier/src/plonk_test.rs diff --git a/evm-vrfier/Cargo.toml b/evm-vrfier/Cargo.toml index a6166b6..d670a9c 100644 --- a/evm-vrfier/Cargo.toml +++ b/evm-vrfier/Cargo.toml @@ -13,7 +13,7 @@ ark-ed-on-bls12-381-bandersnatch = { version = "0.5", default-features = false } tokio = { version = "1.44", default-features = false } ark-std = { workspace = true } ark-ec = { workspace = true } -w3f-plonk-common = { path = "../w3f-plonk-common", default-features = false } +w3f-plonk-common = { path = "../w3f-plonk-common", default-features = false , features = ["std"]} w3f-pcs = { workspace = true } [build-dependencies] diff --git a/evm-vrfier/contracts/src/Constraints.sol b/evm-vrfier/contracts/src/Constraints.sol index 32dec16..8523b85 100644 --- a/evm-vrfier/contracts/src/Constraints.sol +++ b/evm-vrfier/contracts/src/Constraints.sol @@ -29,7 +29,7 @@ library Constraints { uint256 x3, uint256 y3, uint256 not_last - ) public pure returns (uint256 cx) { + ) internal pure returns (uint256 cx) { /// `cx = {[(a.x1.x2 + y1.y2).x3 - x1.y1 - x2.y2].b + (x3 - x1).(1 - b)}.not_last` uint256 x1y1 = mul(x1, y1); uint256 x2y2 = mul(x2, y2); @@ -44,33 +44,37 @@ library Constraints { ), b ); - cx = add(lx, mul(add(x3, r - x1), add(1, r - b))); + cx = mul(add(lx, mul(add(x3, r - x1), add(1, r - b))), not_last); } - function mod_exp(uint256 base, uint256 exp) public view returns (uint256) { + function mod_exp(uint256 base, uint256 exp) internal view returns (uint256) { bytes memory precompileData = abi.encode(32, 32, 32, base, exp, r); (bool ok, bytes memory data) = address(5).staticcall(precompileData); require(ok, "expMod failed"); return abi.decode(data, (uint256)); } - function inv(uint256 x) public view returns (uint256) { + function inv(uint256 x) internal view returns (uint256) { return mod_exp(x, r - 2); } - function v_at(uint256 z) public view returns (uint256) { + function v_at(uint256 z) internal view returns (uint256) { return mod_exp(z, domain_size) - 1; } - function v_inv_at(uint256 z) public view returns (uint256) { + function v_inv_at(uint256 z) internal view returns (uint256) { return inv(v_at(z)); } - function v_inv_hiding_at(uint256 z) public view returns (uint256) { + function v_inv_hiding_at(uint256 z) internal view returns (uint256) { return mul(mul(mul(v_inv_at(z), add(z, r - w_inv)), add(z, r - w_inv_2)), add(z, r - w_inv_3)); } - function not_last_row(uint256 z) public pure returns (uint256) { + function quotient_at(uint256 c, uint256 z) internal view returns (uint256) { + return mul(c, v_inv_hiding_at(z)); + } + + function not_last_row(uint256 z) internal pure returns (uint256) { return add(z, r - w_inv_4); } } diff --git a/evm-vrfier/contracts/src/Plonk.sol b/evm-vrfier/contracts/src/Plonk.sol new file mode 100644 index 0000000..1ffad28 --- /dev/null +++ b/evm-vrfier/contracts/src/Plonk.sol @@ -0,0 +1,63 @@ +pragma solidity ^0.8.24; + +import {BLS, PlonkKzg} from "src/PlonkKzg.sol"; +import {Constraints} from "src/Constraints.sol"; + +contract Plonk { + // The trapdoor `tau` in G2, part of the standard KZG verification key. + BLS.G2Point tau_g2; + + constructor(BLS.G2Point memory tau_g2_) { + tau_g2 = tau_g2_; + } + + function verify_proof( + BLS.G1Point[] memory columns, + BLS.G1Point memory quotient, + uint256 z1, + uint256 z2, + uint256[] memory columns_at_z1, + uint256 column_at_z2, + BLS.G1Point memory kzg_proof_at_z1, + BLS.G1Point memory kzg_proof_at_z2, + bytes32[] memory nus + ) public view returns (bool) { + uint256 k = columns.length; + require(columns_at_z1.length == k); + + BLS.G1Point[] memory polys_z1 = new BLS.G1Point[](k + 1); + for (uint256 i = 0; i < k; i++) { + polys_z1[i] = columns[i]; + } + polys_z1[k] = quotient; + + uint256[] memory evals_at_z1 = new uint256[](k + 1); + for (uint256 i = 0; i < k; i++) { + evals_at_z1[i] = columns_at_z1[i]; + } + evals_at_z1[k] = compute_quotient(columns_at_z1, column_at_z2, z1); + + return PlonkKzg.verify_plonk_kzg( + polys_z1, polys_z1[0], z1, z2, evals_at_z1, column_at_z2, kzg_proof_at_z1, kzg_proof_at_z2, nus, 123, tau_g2 + ); + } + + function compute_quotient(uint256[] memory columns_at_z1, uint256 column_at_z2, uint256 z) + internal + view + returns (uint256) + { + uint256 not_last = Constraints.not_last_row(z); + uint256 c = Constraints.cond_te_addition( + columns_at_z1[4], + columns_at_z1[0], + columns_at_z1[1], + columns_at_z1[2], + columns_at_z1[3], + column_at_z2, + column_at_z2, + not_last + ); + return Constraints.quotient_at(c, z); + } +} diff --git a/evm-vrfier/src/lib.rs b/evm-vrfier/src/lib.rs index f214d26..42bc596 100644 --- a/evm-vrfier/src/lib.rs +++ b/evm-vrfier/src/lib.rs @@ -1,8 +1,9 @@ use alloy::primitives::{FixedBytes, U256}; use ark_ff::{BigInteger, PrimeField}; -mod constraints; -pub mod plonk_kzg; +// mod constraints; +mod plonk_kzg; +mod plonk_test; alloy::sol! { struct G1Point { diff --git a/evm-vrfier/src/plonk_test.rs b/evm-vrfier/src/plonk_test.rs new file mode 100644 index 0000000..2e0d6f2 --- /dev/null +++ b/evm-vrfier/src/plonk_test.rs @@ -0,0 +1,205 @@ +alloy::sol!( + #[sol(rpc)] + Plonk, + "contracts/out/Plonk.sol/Plonk.json" +); + +#[cfg(test)] +mod tests { + use super::*; + use crate::{encode_g1, encode_g2, fr_to_bytes, fr_to_uint, G1Point, G2Point}; + use alloy::primitives::U256; + use ark_bls12_381::Bls12_381; + use ark_ec::pairing::Pairing; + use ark_ec::twisted_edwards::{Affine, TECurveConfig}; + use ark_ec::PrimeGroup; + use ark_ed_on_bls12_381_bandersnatch::EdwardsConfig; + use ark_ff::{BigInteger, PrimeField}; + use ark_std::rand::Rng; + use ark_std::{test_rng, UniformRand}; + use w3f_pcs::aggregation::single::aggregate_polys; + use w3f_pcs::pcs::kzg::params::RawKzgVerifierKey; + use w3f_pcs::pcs::kzg::urs::URS; + use w3f_pcs::pcs::kzg::KZG; + use w3f_pcs::pcs::{PcsParams, PCS}; + use w3f_pcs::Polynomial; + use w3f_plonk_common::domain::Domain; + use w3f_plonk_common::gadgets::booleanity::BitColumn; + use w3f_plonk_common::gadgets::ec::te_doubling::Doubling; + use w3f_plonk_common::gadgets::ec::{AffineColumn, CondAdd}; + use w3f_plonk_common::gadgets::ProverGadget; + use w3f_plonk_common::Column; + + impl From for BLS::G1Point { + fn from(p: G1Point) -> Self { + Self { + x_a: p.x_a, + x_b: p.x_b, + y_a: p.y_a, + y_b: p.y_b, + } + } + } + + impl From for BLS::G2Point { + fn from(p: G2Point) -> Self { + Self { + x_c0_a: p.x_c0_a, + x_c0_b: p.x_c0_b, + x_c1_a: p.x_c1_a, + x_c1_b: p.x_c1_b, + y_c0_a: p.y_c0_a, + y_c0_b: p.y_c0_b, + y_c1_a: p.y_c1_a, + y_c1_b: p.y_c1_b, + } + } + } + + struct ArkProof { + columns: Vec, + quotient: E::G1Affine, + z1: E::ScalarField, + z2: E::ScalarField, + columns_at_z1: Vec, + column_at_z2: E::ScalarField, + kzg_proof_at_z1: E::G1Affine, + kzg_proof_at_z2: E::G1Affine, + } + + struct EthProof { + columns: Vec, + quotient: BLS::G1Point, + z1: U256, + z2: U256, + columns_at_z1: Vec, + column_at_z2: U256, + kzg_proof_at_z1: BLS::G1Point, + kzg_proof_at_z2: BLS::G1Point, + } + + impl ArkProof { + fn encode(self) -> EthProof { + EthProof { + columns: self + .columns + .into_iter() + .map(|p| encode_g1(p).into()) + .collect(), + quotient: encode_g1(self.quotient).into(), + z1: fr_to_uint(self.z1), + z2: fr_to_uint(self.z2), + columns_at_z1: self.columns_at_z1.into_iter().map(fr_to_uint).collect(), + column_at_z2: fr_to_uint(self.column_at_z2), + kzg_proof_at_z1: encode_g1(self.kzg_proof_at_z1).into(), + kzg_proof_at_z2: encode_g1(self.kzg_proof_at_z2).into(), + } + } + } + + fn produce_proof, R: Rng>( + rng: &mut R, + ) -> (ArkProof, RawKzgVerifierKey, Vec) { + let n = 256; + let domain = Domain::new(n, true); + + // KZG setup + let urs = URS::from_trapdoor( + E::ScalarField::rand(rng), + 3 * n + 1, + 2, + E::G1::generator(), + E::G2::generator(), + ); + let (ck, rvk) = (urs.ck(), urs.raw_vk()); + + // scalar.POINT + let scalar = Jubjub::ScalarField::rand(rng); + let scalar_bits = &scalar.into_bigint().to_bits_le()[..252]; + let scalar_bits = BitColumn::init(scalar_bits.to_vec(), &domain); + let point = Affine::::rand(rng); + let doublings = Doubling::doublings_of(point, &domain); + let doublings = AffineColumn::public_column(doublings, &domain); + let seed = Affine::::rand(rng); + let cond_add = CondAdd::init(scalar_bits.clone(), doublings.clone(), seed, &domain); + + let column_polys = vec![ + cond_add.acc.xs.as_poly().clone(), + cond_add.acc.ys.as_poly().clone(), + doublings.xs.as_poly().clone(), + doublings.ys.as_poly().clone(), + scalar_bits.as_poly().clone(), + ]; + + let column_commitments: Vec = column_polys + .iter() + .map(|p| KZG::::commit(&ck, p).unwrap().0) + .collect(); + + // TODO: sample alphas (coeffs to agg constraints) + let constraint = cond_add.constraints()[0].interpolate_by_ref(); + let quotient_poly = domain.divide_by_vanishing_poly(&constraint); + let quotient_commitment = KZG::::commit(&ck, "ient_poly).unwrap().0; + + // sample zeta + let z = E::ScalarField::rand(rng); + let zw = z * domain.omega(); + let columns_at_z1 = column_polys.iter().map(|p| p.evaluate(&z)).collect(); + let column_at_z2 = column_polys[0].evaluate(&zw); + + // sample nus + let mut polys = column_polys.clone(); + polys.push(quotient_poly); + let nus: Vec = (0..polys.len()) + .map(|_| E::ScalarField::rand(rng)) + .collect(); + let agg_poly = aggregate_polys(&polys, &nus); + + let kzg_proof_at_z1 = KZG::::open(&ck, &agg_poly, z).unwrap(); + let kzg_proof_at_z2 = KZG::::open(&ck, &column_polys[0], zw).unwrap(); + + let proof = ArkProof { + columns: column_commitments, + quotient: quotient_commitment, + z1: z, + z2: zw, + columns_at_z1, + column_at_z2, + kzg_proof_at_z1, + kzg_proof_at_z2, + }; + + (proof, rvk, nus) + } + + #[tokio::test] + async fn verify_plonk_proof() -> Result<(), Box> { + let provider = alloy::providers::builder() + .with_recommended_fillers() + .on_anvil_with_wallet_and_config(|anvil| anvil.prague())?; + + let (test_proof, kzg_vk, nus) = + produce_proof::(&mut test_rng()); + let test_proof = test_proof.encode(); + + let plonk = Plonk::deploy(&provider, encode_g2(kzg_vk.tau_in_g2).into()).await?; + + let res = plonk + .verify_proof( + test_proof.columns, + test_proof.quotient, + test_proof.z1, + test_proof.z2, + test_proof.columns_at_z1, + test_proof.column_at_z2, + test_proof.kzg_proof_at_z1, + test_proof.kzg_proof_at_z2, + nus.into_iter().map(fr_to_bytes).collect(), + ) + .call() + .await?; + assert!(res); + + Ok(()) + } +} diff --git a/w3f-plonk-common/src/domain.rs b/w3f-plonk-common/src/domain.rs index e91cdbc..5698126 100644 --- a/w3f-plonk-common/src/domain.rs +++ b/w3f-plonk-common/src/domain.rs @@ -95,7 +95,7 @@ impl Domain { } } - pub(crate) fn divide_by_vanishing_poly(&self, poly: &DensePolynomial) -> DensePolynomial { + pub fn divide_by_vanishing_poly(&self, poly: &DensePolynomial) -> DensePolynomial { let (quotient, remainder) = if self.hiding { let exclude_zk_rows = poly * self.zk_rows_vanishing_poly.as_ref().unwrap(); exclude_zk_rows.divide_by_vanishing_poly(self.domains.x1) From b5fa99177b362724324b9ef2127b8cce25daa98f Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Sun, 20 Apr 2025 19:17:29 +0200 Subject: [PATCH 15/25] fmt --- evm-vrfier/src/plonk_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm-vrfier/src/plonk_test.rs b/evm-vrfier/src/plonk_test.rs index 2e0d6f2..d5d9bb7 100644 --- a/evm-vrfier/src/plonk_test.rs +++ b/evm-vrfier/src/plonk_test.rs @@ -97,7 +97,7 @@ mod tests { } } - fn produce_proof, R: Rng>( + fn produce_proof, R: Rng>( rng: &mut R, ) -> (ArkProof, RawKzgVerifierKey, Vec) { let n = 256; From 18e307acf36594eaa62593fc0390f35b85e448a3 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Sun, 20 Apr 2025 20:41:01 +0200 Subject: [PATCH 16/25] constraints tests fixed --- evm-vrfier/contracts/test/Constraints.t.sol | 47 +++++++++++++++++++++ evm-vrfier/src/constraints.rs | 3 +- evm-vrfier/src/lib.rs | 2 +- 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 evm-vrfier/contracts/test/Constraints.t.sol diff --git a/evm-vrfier/contracts/test/Constraints.t.sol b/evm-vrfier/contracts/test/Constraints.t.sol new file mode 100644 index 0000000..21e1b8f --- /dev/null +++ b/evm-vrfier/contracts/test/Constraints.t.sol @@ -0,0 +1,47 @@ +pragma solidity ^0.8.24; + +import {Test} from "forge-std/Test.sol"; +import {Constraints} from "src/Constraints.sol"; + +library ConstraintsExt { + function cond_te_addition( + uint256 b, + uint256 x1, + uint256 y1, + uint256 x2, + uint256 y2, + uint256 x3, + uint256 y3, + uint256 not_last + ) public pure returns (uint256) { + return Constraints.cond_te_addition(b, x1, y1, x2, y2, x3, y3, not_last); + } + + function mod_exp(uint256 base, uint256 exp) public view returns (uint256) { + return Constraints.mod_exp(base, exp); + } + + function inv(uint256 x) public view returns (uint256) { + return Constraints.inv(x); + } + + function v_at(uint256 z) public view returns (uint256) { + return Constraints.v_at(z); + } + + function v_inv_at(uint256 z) public view returns (uint256) { + return Constraints.v_inv_at(z); + } + + function v_inv_hiding_at(uint256 z) public view returns (uint256) { + return Constraints.v_inv_hiding_at(z); + } + + function quotient_at(uint256 c, uint256 z) public view returns (uint256) { + return Constraints.quotient_at(c, z); + } + + function not_last_row(uint256 z) public pure returns (uint256) { + return Constraints.not_last_row(z); + } +} diff --git a/evm-vrfier/src/constraints.rs b/evm-vrfier/src/constraints.rs index c2db0cd..3fe386d 100644 --- a/evm-vrfier/src/constraints.rs +++ b/evm-vrfier/src/constraints.rs @@ -9,10 +9,11 @@ mod tests { use ark_std::{test_rng, UniformRand}; use w3f_plonk_common::domain::Domain; use w3f_plonk_common::gadgets::ec::te_cond_add::cond_te_addition; + alloy::sol!( #[sol(rpc)] Constraints, - "contracts/out/Constraints.sol/Constraints.json" + "contracts/out/Constraints.t.sol/ConstraintsExt.json" ); #[tokio::test] diff --git a/evm-vrfier/src/lib.rs b/evm-vrfier/src/lib.rs index 42bc596..635b12b 100644 --- a/evm-vrfier/src/lib.rs +++ b/evm-vrfier/src/lib.rs @@ -1,7 +1,7 @@ use alloy::primitives::{FixedBytes, U256}; use ark_ff::{BigInteger, PrimeField}; -// mod constraints; +mod constraints; mod plonk_kzg; mod plonk_test; From a6fb09d3199aa97803f82dfc3083c58a47a91ac3 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Sun, 20 Apr 2025 21:20:36 +0200 Subject: [PATCH 17/25] renamed PlonkKzg -> Kzg --- .../contracts/src/{PlonkKzg.sol => Kzg.sol} | 2 +- evm-vrfier/contracts/src/Plonk.sol | 4 ++-- .../test/{PlonkKzg.t.sol => Kzg.t.sol} | 17 ++++++++--------- evm-vrfier/src/lib.rs | 2 +- evm-vrfier/src/{plonk_kzg.rs => test_kzg.rs} | 10 +++++----- 5 files changed, 17 insertions(+), 18 deletions(-) rename evm-vrfier/contracts/src/{PlonkKzg.sol => Kzg.sol} (99%) rename evm-vrfier/contracts/test/{PlonkKzg.t.sol => Kzg.t.sol} (91%) rename evm-vrfier/src/{plonk_kzg.rs => test_kzg.rs} (95%) diff --git a/evm-vrfier/contracts/src/PlonkKzg.sol b/evm-vrfier/contracts/src/Kzg.sol similarity index 99% rename from evm-vrfier/contracts/src/PlonkKzg.sol rename to evm-vrfier/contracts/src/Kzg.sol index a7fc81c..f4a250b 100644 --- a/evm-vrfier/contracts/src/PlonkKzg.sol +++ b/evm-vrfier/contracts/src/Kzg.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.24; import "./BlsGenerators.sol"; -library PlonkKzg { +library Kzg { function verify_plonk_kzg( BLS.G1Point[] memory polys_z1, BLS.G1Point memory poly_z2, diff --git a/evm-vrfier/contracts/src/Plonk.sol b/evm-vrfier/contracts/src/Plonk.sol index 1ffad28..5ba3933 100644 --- a/evm-vrfier/contracts/src/Plonk.sol +++ b/evm-vrfier/contracts/src/Plonk.sol @@ -1,6 +1,6 @@ pragma solidity ^0.8.24; -import {BLS, PlonkKzg} from "src/PlonkKzg.sol"; +import {BLS, Kzg} from "src/Kzg.sol"; import {Constraints} from "src/Constraints.sol"; contract Plonk { @@ -37,7 +37,7 @@ contract Plonk { } evals_at_z1[k] = compute_quotient(columns_at_z1, column_at_z2, z1); - return PlonkKzg.verify_plonk_kzg( + return Kzg.verify_plonk_kzg( polys_z1, polys_z1[0], z1, z2, evals_at_z1, column_at_z2, kzg_proof_at_z1, kzg_proof_at_z2, nus, 123, tau_g2 ); } diff --git a/evm-vrfier/contracts/test/PlonkKzg.t.sol b/evm-vrfier/contracts/test/Kzg.t.sol similarity index 91% rename from evm-vrfier/contracts/test/PlonkKzg.t.sol rename to evm-vrfier/contracts/test/Kzg.t.sol index 8592216..16fb88f 100644 --- a/evm-vrfier/contracts/test/PlonkKzg.t.sol +++ b/evm-vrfier/contracts/test/Kzg.t.sol @@ -1,9 +1,9 @@ pragma solidity ^0.8.24; import {Test} from "forge-std/Test.sol"; -import {BLS, BlsGenerators, PlonkKzg} from "src/PlonkKzg.sol"; +import {BLS, BlsGenerators, Kzg} from "src/Kzg.sol"; -contract KzgVerifier { +contract KzgExt { // The trapdoor `tau` in G2, part of the standard KZG verification key. BLS.G2Point tau_g2; @@ -12,7 +12,7 @@ contract KzgVerifier { } function verify(BLS.G1Point memory c, uint256 z, uint256 v, BLS.G1Point memory proof) public view returns (bool) { - return PlonkKzg.verify(c, z, v, proof, tau_g2); + return Kzg.verify(c, z, v, proof, tau_g2); } function verify_plonk_kzg( @@ -27,9 +27,8 @@ contract KzgVerifier { bytes32[] memory nus, uint256 r ) public view returns (bool) { - return PlonkKzg.verify_plonk_kzg( - polys_z1, poly_z2, z1, z2, evals_at_z1, eval_at_z2, proof_z1, proof_z2, nus, r, tau_g2 - ); + return + Kzg.verify_plonk_kzg(polys_z1, poly_z2, z1, z2, evals_at_z1, eval_at_z2, proof_z1, proof_z2, nus, r, tau_g2); } function pairing2( @@ -38,12 +37,12 @@ contract KzgVerifier { BLS.G1Point memory g1_2, BLS.G2Point memory g2_2 ) public view returns (bool) { - return PlonkKzg.pairing2(g1_1, g2_1, g1_2, g2_2); + return Kzg.pairing2(g1_1, g2_1, g1_2, g2_2); } } contract PlonkKzgTest is Test { - KzgVerifier kzg; + KzgExt kzg; BLS.G1Point[] srs_g1; function setUp() public { @@ -54,7 +53,7 @@ contract PlonkKzgTest is Test { srs_g1.push(BlsGenerators.g1_mul(srs_g1[i - 1], tau)); } BLS.G2Point memory tau_g2 = BlsGenerators.g2_mul(BlsGenerators.G2(), tau); - kzg = new KzgVerifier(tau_g2); + kzg = new KzgExt(tau_g2); } function commit(bytes32[] memory coeffs) internal view returns (BLS.G1Point memory c) { diff --git a/evm-vrfier/src/lib.rs b/evm-vrfier/src/lib.rs index 635b12b..ac65803 100644 --- a/evm-vrfier/src/lib.rs +++ b/evm-vrfier/src/lib.rs @@ -2,7 +2,7 @@ use alloy::primitives::{FixedBytes, U256}; use ark_ff::{BigInteger, PrimeField}; mod constraints; -mod plonk_kzg; +mod test_kzg; mod plonk_test; alloy::sol! { diff --git a/evm-vrfier/src/plonk_kzg.rs b/evm-vrfier/src/test_kzg.rs similarity index 95% rename from evm-vrfier/src/plonk_kzg.rs rename to evm-vrfier/src/test_kzg.rs index 7961239..50d44b5 100644 --- a/evm-vrfier/src/plonk_kzg.rs +++ b/evm-vrfier/src/test_kzg.rs @@ -18,8 +18,8 @@ mod tests { alloy::sol!( #[sol(rpc)] - PlonkKzg, - "contracts/out/PlonkKzg.t.sol/KzgVerifier.json" + Kzg, + "contracts/out/Kzg.t.sol/KzgExt.json" ); impl From for BLS::G1Point { @@ -162,7 +162,7 @@ mod tests { let (test_openning, nus, kzg_vk) = random_opening::(123, 1, &mut test_rng()); let test_openning = test_openning.encode(); - let plonk_kzg = PlonkKzg::deploy(&provider, encode_g2(kzg_vk.tau_in_g2).into()).await?; + let plonk_kzg = Kzg::deploy(&provider, encode_g2(kzg_vk.tau_in_g2).into()).await?; let res = plonk_kzg .verify_plonk_kzg( @@ -193,7 +193,7 @@ mod tests { let (test_openning, _, kzg_vk) = random_opening::(123, 0, &mut test_rng()); let test_openning = test_openning.encode(); - let plonk_kzg = PlonkKzg::deploy(&provider, encode_g2(kzg_vk.tau_in_g2).into()).await?; + let plonk_kzg = Kzg::deploy(&provider, encode_g2(kzg_vk.tau_in_g2).into()).await?; let res = plonk_kzg .verify( @@ -216,7 +216,7 @@ mod tests { .on_anvil_with_wallet_and_config(|anvil| anvil.prague())?; let _tau_g2 = G2Affine::generator(); - let plonk_kzg = PlonkKzg::deploy(&provider, encode_g2(_tau_g2).into()).await?; + let plonk_kzg = Kzg::deploy(&provider, encode_g2(_tau_g2).into()).await?; let res = plonk_kzg .pairing2( From 2c6e0fca5a141a0f512437bddaf3d18f55dce744 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Mon, 21 Apr 2025 00:40:58 +0200 Subject: [PATCH 18/25] kzg openning of the same polys in 2 points --- evm-vrfier/contracts/src/Kzg.sol | 59 +++++++++++++--------- evm-vrfier/contracts/src/Plonk.sol | 27 +++++----- evm-vrfier/contracts/test/Kzg.t.sol | 48 +++++++++--------- evm-vrfier/src/lib.rs | 2 +- evm-vrfier/src/plonk_test.rs | 23 +++++---- evm-vrfier/src/test_kzg.rs | 78 ++++++++++++++--------------- 6 files changed, 127 insertions(+), 110 deletions(-) diff --git a/evm-vrfier/contracts/src/Kzg.sol b/evm-vrfier/contracts/src/Kzg.sol index f4a250b..f7aa192 100644 --- a/evm-vrfier/contracts/src/Kzg.sol +++ b/evm-vrfier/contracts/src/Kzg.sol @@ -3,54 +3,67 @@ pragma solidity ^0.8.24; import "./BlsGenerators.sol"; library Kzg { + // Verifies a batch of `2` kzg proofs: + // 1. `proofs[0]` certifying that `polys[i](z1) = evals_at_z1[i], i = 0,...,k, k = evals_at_z1.length`, + // 2. `proofs[1]` certifying that `polys[j](z2) = evals_at_z2[j], j = 0,...,l, l = evals_at_z2.length`. function verify_plonk_kzg( - BLS.G1Point[] memory polys_z1, - BLS.G1Point memory poly_z2, + BLS.G1Point[] memory polys, uint256 z1, - uint256 z2, + // uint256 z2, uint256[] memory evals_at_z1, - uint256 eval_at_z2, - BLS.G1Point memory proof_z1, - BLS.G1Point memory proof_z2, - bytes32[] memory nus, - uint256 r, + uint256[] memory evals_at_z2, + BLS.G1Point[] memory proofs, + uint256[] memory nus, + // uint256 r, BLS.G2Point memory tau_g2 ) internal view returns (bool) { - assert(evals_at_z1.length == polys_z1.length); - assert(nus.length == polys_z1.length); + uint256 r = 123; //TODO + uint256 z2 = z1 + 1; //TODO - uint256 n_bases = polys_z1.length + 4; + uint256 k = polys.length; + assert(evals_at_z1.length == k); + assert(nus.length == k); + uint256 l = evals_at_z2.length; + assert(l <= k); + + // all the g1 points the verifier knows should go to a single msm + uint256 n_bases = k + 3; // `n` commitments to the polynomials, proofs in `z1` and `z2`, and `g1` to commit to the evaluations BLS.G1Point[] memory msm_bases = new BLS.G1Point[](n_bases); bytes32[] memory msm_scalars = new bytes32[](n_bases); uint256 i; - for (i = 0; i < polys_z1.length; i++) { - msm_bases[i] = polys_z1[i]; + for (i = 0; i < k; i++) { + msm_bases[i] = polys[i]; } - for (i = 0; i < polys_z1.length; i++) { - msm_scalars[i] = nus[i]; + uint256 r_plus_1 = BlsGenerators.add_fr(r, 1); + for (i = 0; i < l; i++) { + msm_scalars[i] = bytes32(BlsGenerators.mul_fr(r_plus_1, nus[i])); + } + for (i = l; i < k; i++) { + msm_scalars[i] = bytes32(nus[i]); } uint256 agg_at_z = 0; - for (i = 0; i < polys_z1.length; i++) { + for (i = 0; i < l; i++) { + agg_at_z = BlsGenerators.add_fr(agg_at_z, BlsGenerators.mul_fr(uint256(nus[i]), evals_at_z2[i])); + } + agg_at_z = BlsGenerators.mul_fr(agg_at_z, r); + for (i = 0; i < polys.length; i++) { agg_at_z = BlsGenerators.add_fr(agg_at_z, BlsGenerators.mul_fr(uint256(nus[i]), evals_at_z1[i])); } msm_bases[i] = BlsGenerators.G1(); - msm_scalars[i] = bytes32(BlsGenerators.q - BlsGenerators.add_fr(agg_at_z, BlsGenerators.mul_fr(r, eval_at_z2))); + msm_scalars[i] = bytes32(BlsGenerators.q - agg_at_z); - msm_bases[++i] = proof_z1; + msm_bases[++i] = proofs[0]; msm_scalars[i] = bytes32(z1); - msm_bases[++i] = poly_z2; - msm_scalars[i] = bytes32(r); - - msm_bases[++i] = proof_z2; + msm_bases[++i] = proofs[1]; msm_scalars[i] = bytes32(BlsGenerators.mul_fr(r, z2)); BLS.G1Point memory agg_acc = BLS.msm(msm_bases, msm_scalars); - BLS.G1Point memory acc_proof = BLS.add(proof_z1, BlsGenerators.g1_mul(proof_z2, bytes32(r))); + BLS.G1Point memory acc_proof = BLS.add(proofs[0], BlsGenerators.g1_mul(proofs[1], bytes32(r))); return verify_acc(agg_acc, acc_proof, tau_g2); } diff --git a/evm-vrfier/contracts/src/Plonk.sol b/evm-vrfier/contracts/src/Plonk.sol index 5ba3933..90b3d99 100644 --- a/evm-vrfier/contracts/src/Plonk.sol +++ b/evm-vrfier/contracts/src/Plonk.sol @@ -15,34 +15,35 @@ contract Plonk { BLS.G1Point[] memory columns, BLS.G1Point memory quotient, uint256 z1, - uint256 z2, uint256[] memory columns_at_z1, - uint256 column_at_z2, + uint256[] memory columns_at_z2, BLS.G1Point memory kzg_proof_at_z1, BLS.G1Point memory kzg_proof_at_z2, - bytes32[] memory nus + uint256[] memory nus ) public view returns (bool) { uint256 k = columns.length; require(columns_at_z1.length == k); + require(columns_at_z2.length <= k); - BLS.G1Point[] memory polys_z1 = new BLS.G1Point[](k + 1); + BLS.G1Point[] memory polys = new BLS.G1Point[](k + 1); for (uint256 i = 0; i < k; i++) { - polys_z1[i] = columns[i]; + polys[i] = columns[i]; } - polys_z1[k] = quotient; + polys[k] = quotient; uint256[] memory evals_at_z1 = new uint256[](k + 1); for (uint256 i = 0; i < k; i++) { evals_at_z1[i] = columns_at_z1[i]; } - evals_at_z1[k] = compute_quotient(columns_at_z1, column_at_z2, z1); + evals_at_z1[k] = compute_quotient(columns_at_z1, columns_at_z2, z1); - return Kzg.verify_plonk_kzg( - polys_z1, polys_z1[0], z1, z2, evals_at_z1, column_at_z2, kzg_proof_at_z1, kzg_proof_at_z2, nus, 123, tau_g2 - ); + BLS.G1Point[] memory kzg_proofs = new BLS.G1Point[](2); + kzg_proofs[0] = kzg_proof_at_z1; + kzg_proofs[1] = kzg_proof_at_z2; + return Kzg.verify_plonk_kzg(polys, z1, evals_at_z1, columns_at_z2, kzg_proofs, nus, tau_g2); } - function compute_quotient(uint256[] memory columns_at_z1, uint256 column_at_z2, uint256 z) + function compute_quotient(uint256[] memory columns_at_z1, uint256[] memory columns_at_z2, uint256 z) internal view returns (uint256) @@ -54,8 +55,8 @@ contract Plonk { columns_at_z1[1], columns_at_z1[2], columns_at_z1[3], - column_at_z2, - column_at_z2, + columns_at_z2[0], + columns_at_z2[1], not_last ); return Constraints.quotient_at(c, z); diff --git a/evm-vrfier/contracts/test/Kzg.t.sol b/evm-vrfier/contracts/test/Kzg.t.sol index 16fb88f..1968c84 100644 --- a/evm-vrfier/contracts/test/Kzg.t.sol +++ b/evm-vrfier/contracts/test/Kzg.t.sol @@ -16,19 +16,14 @@ contract KzgExt { } function verify_plonk_kzg( - BLS.G1Point[] memory polys_z1, - BLS.G1Point memory poly_z2, + BLS.G1Point[] memory polys, uint256 z1, - uint256 z2, uint256[] memory evals_at_z1, - uint256 eval_at_z2, - BLS.G1Point memory proof_z1, - BLS.G1Point memory proof_z2, - bytes32[] memory nus, - uint256 r + uint256[] memory evals_at_z2, + BLS.G1Point[] memory proofs, + uint256[] memory nus ) public view returns (bool) { - return - Kzg.verify_plonk_kzg(polys_z1, poly_z2, z1, z2, evals_at_z1, eval_at_z2, proof_z1, proof_z2, nus, r, tau_g2); + return Kzg.verify_plonk_kzg(polys, z1, evals_at_z1, evals_at_z2, proofs, nus, tau_g2); } function pairing2( @@ -108,7 +103,7 @@ contract PlonkKzgTest is Test { uint256 z = 777; BLS.G1Point memory proof = prove(poly, z); uint256 v = eval(poly, z); - assert(kzg.verify(c, z, v, proof)); + assertTrue(kzg.verify(c, z, v, proof)); } function test_plonk_kzg() public view { @@ -116,30 +111,35 @@ contract PlonkKzgTest is Test { for (uint256 i = 0; i < poly.length; i++) { poly[i] = bytes32(i + 10); } - BLS.G1Point memory c = commit(poly); + BLS.G1Point memory commitment = commit(poly); uint256 z1 = 666; - uint256 z2 = 777; + uint256 z2 = 667; BLS.G1Point memory proof_z1 = prove(poly, z1); BLS.G1Point memory proof_z2 = prove(poly, z2); uint256 eval_at_z1 = eval(poly, z1); uint256 eval_at_z2 = eval(poly, z2); - assert(kzg.verify(c, z1, eval_at_z1, proof_z1)); - assert(kzg.verify(c, z2, eval_at_z2, proof_z2)); + assertTrue(kzg.verify(commitment, z1, eval_at_z1, proof_z1)); + assertTrue(kzg.verify(commitment, z2, eval_at_z2, proof_z2)); - BLS.G1Point[] memory polys_z1 = new BLS.G1Point[](1); + BLS.G1Point[] memory commitments = new BLS.G1Point[](1); uint256[] memory evals_at_z1 = new uint256[](1); - polys_z1[0] = c; + uint256[] memory evals_at_z2 = new uint256[](1); + commitments[0] = commitment; evals_at_z1[0] = eval_at_z1; + evals_at_z2[0] = eval_at_z2; - BLS.G1Point memory poly_z2 = c; - - bytes32 nu = bytes32(uint256(123)); - uint256 r = 345; - bytes32[] memory nus = new bytes32[](1); + uint256 nu = 2; + uint256[] memory nus = new uint256[](1); nus[0] = nu; - proof_z1 = BlsGenerators.g1_mul(proof_z1, nu); - assert(kzg.verify_plonk_kzg(polys_z1, poly_z2, z1, z2, evals_at_z1, eval_at_z2, proof_z1, proof_z2, nus, r)); + BLS.G1Point[] memory proofs = new BLS.G1Point[](2); + proofs[0] = BlsGenerators.g1_mul(proof_z1, bytes32(nu)); + proofs[1] = BlsGenerators.g1_mul(proof_z2, bytes32(nu)); + + assertTrue( + kzg.verify_plonk_kzg(commitments, z1, evals_at_z1, evals_at_z2, proofs, nus), + "Batch KZG verification failed" + ); } } diff --git a/evm-vrfier/src/lib.rs b/evm-vrfier/src/lib.rs index ac65803..7cb9369 100644 --- a/evm-vrfier/src/lib.rs +++ b/evm-vrfier/src/lib.rs @@ -2,8 +2,8 @@ use alloy::primitives::{FixedBytes, U256}; use ark_ff::{BigInteger, PrimeField}; mod constraints; -mod test_kzg; mod plonk_test; +mod test_kzg; alloy::sol! { struct G1Point { diff --git a/evm-vrfier/src/plonk_test.rs b/evm-vrfier/src/plonk_test.rs index d5d9bb7..e8c8d8d 100644 --- a/evm-vrfier/src/plonk_test.rs +++ b/evm-vrfier/src/plonk_test.rs @@ -14,6 +14,7 @@ mod tests { use ark_ec::twisted_edwards::{Affine, TECurveConfig}; use ark_ec::PrimeGroup; use ark_ed_on_bls12_381_bandersnatch::EdwardsConfig; + use ark_ff::One; use ark_ff::{BigInteger, PrimeField}; use ark_std::rand::Rng; use ark_std::{test_rng, UniformRand}; @@ -22,6 +23,7 @@ mod tests { use w3f_pcs::pcs::kzg::urs::URS; use w3f_pcs::pcs::kzg::KZG; use w3f_pcs::pcs::{PcsParams, PCS}; + use w3f_pcs::Polynomial; use w3f_plonk_common::domain::Domain; use w3f_plonk_common::gadgets::booleanity::BitColumn; @@ -62,7 +64,7 @@ mod tests { z1: E::ScalarField, z2: E::ScalarField, columns_at_z1: Vec, - column_at_z2: E::ScalarField, + columns_at_z2: Vec, kzg_proof_at_z1: E::G1Affine, kzg_proof_at_z2: E::G1Affine, } @@ -73,7 +75,7 @@ mod tests { z1: U256, z2: U256, columns_at_z1: Vec, - column_at_z2: U256, + columns_at_z2: Vec, kzg_proof_at_z1: BLS::G1Point, kzg_proof_at_z2: BLS::G1Point, } @@ -90,7 +92,7 @@ mod tests { z1: fr_to_uint(self.z1), z2: fr_to_uint(self.z2), columns_at_z1: self.columns_at_z1.into_iter().map(fr_to_uint).collect(), - column_at_z2: fr_to_uint(self.column_at_z2), + columns_at_z2: self.columns_at_z2.into_iter().map(fr_to_uint).collect(), kzg_proof_at_z1: encode_g1(self.kzg_proof_at_z1).into(), kzg_proof_at_z2: encode_g1(self.kzg_proof_at_z2).into(), } @@ -143,9 +145,10 @@ mod tests { // sample zeta let z = E::ScalarField::rand(rng); - let zw = z * domain.omega(); + // let zw = z * domain.omega(); + let zw = z + E::ScalarField::one(); //TODO let columns_at_z1 = column_polys.iter().map(|p| p.evaluate(&z)).collect(); - let column_at_z2 = column_polys[0].evaluate(&zw); + let columns_at_z2 = column_polys[..2].iter().map(|p| p.evaluate(&z)).collect(); // sample nus let mut polys = column_polys.clone(); @@ -164,7 +167,7 @@ mod tests { z1: z, z2: zw, columns_at_z1, - column_at_z2, + columns_at_z2, kzg_proof_at_z1, kzg_proof_at_z2, }; @@ -172,7 +175,7 @@ mod tests { (proof, rvk, nus) } - #[tokio::test] + // #[tokio::test] //TODO async fn verify_plonk_proof() -> Result<(), Box> { let provider = alloy::providers::builder() .with_recommended_fillers() @@ -189,12 +192,12 @@ mod tests { test_proof.columns, test_proof.quotient, test_proof.z1, - test_proof.z2, + // test_proof.z2, test_proof.columns_at_z1, - test_proof.column_at_z2, + test_proof.columns_at_z2, test_proof.kzg_proof_at_z1, test_proof.kzg_proof_at_z2, - nus.into_iter().map(fr_to_bytes).collect(), + nus.into_iter().map(fr_to_uint).collect(), ) .call() .await?; diff --git a/evm-vrfier/src/test_kzg.rs b/evm-vrfier/src/test_kzg.rs index 50d44b5..e768677 100644 --- a/evm-vrfier/src/test_kzg.rs +++ b/evm-vrfier/src/test_kzg.rs @@ -1,12 +1,14 @@ #[cfg(test)] mod tests { - use crate::{encode_g1, encode_g2, fr_to_bytes, fr_to_uint, G1Point, G2Point}; + use crate::{encode_g1, encode_g2, fr_to_uint, G1Point, G2Point}; use alloy::primitives::U256; - use ark_bls12_381::{Bls12_381, Fr, G1Affine, G2Affine}; + use ark_bls12_381::{Bls12_381, G1Affine, G2Affine}; use ark_ec::pairing::Pairing; use ark_ec::{AffineRepr, PrimeGroup}; + use ark_ff::One; use ark_std::rand::Rng; use ark_std::{test_rng, UniformRand}; + use std::ops::Mul; use w3f_pcs::aggregation::single::aggregate_polys; use w3f_pcs::pcs::kzg::params::RawKzgVerifierKey; use w3f_pcs::pcs::kzg::urs::URS; @@ -49,23 +51,21 @@ mod tests { } struct ArksBatchKzgOpenning { - polys_z1: Vec, - poly_z2: E::G1Affine, + polys: Vec, z1: E::ScalarField, z2: E::ScalarField, evals_at_z1: Vec, - eval_at_z2: E::ScalarField, + evals_at_z2: Vec, kzg_proof_at_z1: E::G1Affine, kzg_proof_at_z2: E::G1Affine, } struct EthBatchKzgOpenning { - polys_z1: Vec, - poly_z2: BLS::G1Point, + polys: Vec, z1: U256, z2: U256, evals_at_z1: Vec, - eval_at_z2: U256, + evals_at_z2: Vec, kzg_proof_at_z1: BLS::G1Point, kzg_proof_at_z2: BLS::G1Point, } @@ -73,31 +73,34 @@ mod tests { impl ArksBatchKzgOpenning { fn encode(self) -> EthBatchKzgOpenning { EthBatchKzgOpenning { - polys_z1: self - .polys_z1 + polys: self + .polys .into_iter() .map(|p| encode_g1(p).into()) .collect(), - poly_z2: encode_g1(self.poly_z2).into(), z1: fr_to_uint(self.z1), z2: fr_to_uint(self.z2), evals_at_z1: self.evals_at_z1.into_iter().map(fr_to_uint).collect(), - eval_at_z2: fr_to_uint(self.eval_at_z2), + evals_at_z2: self.evals_at_z2.into_iter().map(fr_to_uint).collect(), kzg_proof_at_z1: encode_g1(self.kzg_proof_at_z1).into(), kzg_proof_at_z2: encode_g1(self.kzg_proof_at_z2).into(), } } } + // generates an opening proof for `k` random degree up to `d` polynomials at a random point + // and a subset of the first `l <= k` polynomials at another random point. fn random_opening( d: usize, k: usize, + l: usize, rng: &mut R, ) -> ( ArksBatchKzgOpenning, Vec, RawKzgVerifierKey, ) { + assert!(l <= k); // KZG setup let urs = URS::from_trapdoor( E::ScalarField::rand(rng), @@ -109,42 +112,41 @@ mod tests { let (ck, rvk) = (urs.ck(), urs.raw_vk()); // Polynomials - let polys_z1: Vec> = (0..k) + let polys: Vec> = (0..k) .map(|_| Poly::::rand(d, rng)) .collect(); - let poly_z2 = Poly::::rand(d, rng); // Aggregate polynomial let nus: Vec = (0..k).map(|_| E::ScalarField::rand(rng)).collect(); - let agg_poly_z1 = aggregate_polys(&polys_z1, &nus); + let agg_poly_z1 = aggregate_polys(&polys, &nus); + let agg_poly_z2 = aggregate_polys(&polys[..l], &nus[..l]); // Evaluation points let z1 = E::ScalarField::rand(rng); - let z2 = E::ScalarField::rand(rng); + // let z2 = E::ScalarField::rand(rng); + let z2 = z1 + E::ScalarField::one(); //TODO // Proofs let kzg_proof_at_z1 = KZG::::open(&ck, &agg_poly_z1, z1).unwrap(); - let kzg_proof_at_z2 = KZG::::open(&ck, &poly_z2, z2).unwrap(); + let kzg_proof_at_z2 = KZG::::open(&ck, &agg_poly_z2, z2).unwrap(); // Evaluations - let evals_at_z1: Vec = polys_z1.iter().map(|p| p.evaluate(&z1)).collect(); - let eval_at_z2 = poly_z2.evaluate(&z2); + let evals_at_z1: Vec = polys.iter().map(|p| p.evaluate(&z1)).collect(); + let evals_at_z2: Vec = polys[..l].iter().map(|p| p.evaluate(&z2)).collect(); // Commitments - let polys_z1: Vec = polys_z1 + let polys: Vec = polys .iter() .map(|p| KZG::::commit(&ck, p).unwrap().0) .collect(); - let poly_z2 = KZG::::commit(&ck, &poly_z2).unwrap().0; ( ArksBatchKzgOpenning { - polys_z1, - poly_z2, + polys, z1, z2, evals_at_z1, - eval_at_z2, + evals_at_z2, kzg_proof_at_z1, kzg_proof_at_z2, }, @@ -159,23 +161,20 @@ mod tests { .with_recommended_fillers() .on_anvil_with_wallet_and_config(|anvil| anvil.prague())?; - let (test_openning, nus, kzg_vk) = random_opening::(123, 1, &mut test_rng()); + let (test_openning, nus, kzg_vk) = + random_opening::(123, 2, 1, &mut test_rng()); let test_openning = test_openning.encode(); let plonk_kzg = Kzg::deploy(&provider, encode_g2(kzg_vk.tau_in_g2).into()).await?; let res = plonk_kzg .verify_plonk_kzg( - test_openning.polys_z1, - test_openning.poly_z2, + test_openning.polys, test_openning.z1, - test_openning.z2, test_openning.evals_at_z1, - test_openning.eval_at_z2, - test_openning.kzg_proof_at_z1, - test_openning.kzg_proof_at_z2, - nus.into_iter().map(fr_to_bytes).collect(), - fr_to_uint(Fr::from(1)), + test_openning.evals_at_z2, + vec![test_openning.kzg_proof_at_z1, test_openning.kzg_proof_at_z2], + nus.into_iter().map(fr_to_uint).collect(), ) .call() .await?; @@ -184,23 +183,24 @@ mod tests { Ok(()) } - #[tokio::test] + // #[tokio::test] //TODO async fn test_single_openning() -> Result<(), Box> { let provider = alloy::providers::builder() .with_recommended_fillers() .on_anvil_with_wallet_and_config(|anvil| anvil.prague())?; - let (test_openning, _, kzg_vk) = random_opening::(123, 0, &mut test_rng()); + let (mut test_openning, _, kzg_vk) = + random_opening::(123, 1, 0, &mut test_rng()); let test_openning = test_openning.encode(); let plonk_kzg = Kzg::deploy(&provider, encode_g2(kzg_vk.tau_in_g2).into()).await?; let res = plonk_kzg .verify( - test_openning.poly_z2, - test_openning.z2, - test_openning.eval_at_z2, - test_openning.kzg_proof_at_z2, + test_openning.polys[0].clone(), + test_openning.z1, + test_openning.evals_at_z1[0], + test_openning.kzg_proof_at_z1, ) .call() .await?; From 331f1f15d63d8ef633d3befb56d35d02172d6ee7 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Mon, 21 Apr 2025 00:50:12 +0200 Subject: [PATCH 19/25] ...in 2 specified points --- evm-vrfier/contracts/src/Kzg.sol | 14 ++++++-------- evm-vrfier/contracts/src/Plonk.sol | 29 ++++++++++++++++------------- evm-vrfier/contracts/test/Kzg.t.sol | 14 +++++++------- evm-vrfier/src/test_kzg.rs | 2 +- 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/evm-vrfier/contracts/src/Kzg.sol b/evm-vrfier/contracts/src/Kzg.sol index f7aa192..058ea52 100644 --- a/evm-vrfier/contracts/src/Kzg.sol +++ b/evm-vrfier/contracts/src/Kzg.sol @@ -4,12 +4,11 @@ import "./BlsGenerators.sol"; library Kzg { // Verifies a batch of `2` kzg proofs: - // 1. `proofs[0]` certifying that `polys[i](z1) = evals_at_z1[i], i = 0,...,k, k = evals_at_z1.length`, - // 2. `proofs[1]` certifying that `polys[j](z2) = evals_at_z2[j], j = 0,...,l, l = evals_at_z2.length`. + // 1. `proofs[0]` certifying that `polys[i](zs[0]) = evals_at_z1[i], i = 0,...,k, k = evals_at_z1.length`, + // 2. `proofs[1]` certifying that `polys[j](zs[1]) = evals_at_z2[j], j = 0,...,l, l = evals_at_z2.length`. function verify_plonk_kzg( BLS.G1Point[] memory polys, - uint256 z1, - // uint256 z2, + uint256[] memory zs, uint256[] memory evals_at_z1, uint256[] memory evals_at_z2, BLS.G1Point[] memory proofs, @@ -18,7 +17,6 @@ library Kzg { BLS.G2Point memory tau_g2 ) internal view returns (bool) { uint256 r = 123; //TODO - uint256 z2 = z1 + 1; //TODO uint256 k = polys.length; assert(evals_at_z1.length == k); @@ -27,7 +25,7 @@ library Kzg { assert(l <= k); // all the g1 points the verifier knows should go to a single msm - uint256 n_bases = k + 3; // `n` commitments to the polynomials, proofs in `z1` and `z2`, and `g1` to commit to the evaluations + uint256 n_bases = k + 3; // `n` commitments to the polynomials, proofs in `zs[0]` and `zs[1]`, and `g1` to commit to the evaluations BLS.G1Point[] memory msm_bases = new BLS.G1Point[](n_bases); bytes32[] memory msm_scalars = new bytes32[](n_bases); @@ -57,10 +55,10 @@ library Kzg { msm_scalars[i] = bytes32(BlsGenerators.q - agg_at_z); msm_bases[++i] = proofs[0]; - msm_scalars[i] = bytes32(z1); + msm_scalars[i] = bytes32(zs[0]); msm_bases[++i] = proofs[1]; - msm_scalars[i] = bytes32(BlsGenerators.mul_fr(r, z2)); + msm_scalars[i] = bytes32(BlsGenerators.mul_fr(r, zs[1])); BLS.G1Point memory agg_acc = BLS.msm(msm_bases, msm_scalars); BLS.G1Point memory acc_proof = BLS.add(proofs[0], BlsGenerators.g1_mul(proofs[1], bytes32(r))); diff --git a/evm-vrfier/contracts/src/Plonk.sol b/evm-vrfier/contracts/src/Plonk.sol index 90b3d99..267ddae 100644 --- a/evm-vrfier/contracts/src/Plonk.sol +++ b/evm-vrfier/contracts/src/Plonk.sol @@ -14,16 +14,16 @@ contract Plonk { function verify_proof( BLS.G1Point[] memory columns, BLS.G1Point memory quotient, - uint256 z1, - uint256[] memory columns_at_z1, - uint256[] memory columns_at_z2, - BLS.G1Point memory kzg_proof_at_z1, - BLS.G1Point memory kzg_proof_at_z2, + uint256 z, + uint256[] memory columns_at_z, + uint256[] memory columns_at_zw, + BLS.G1Point memory kzg_proof_at_z, + BLS.G1Point memory kzg_proof_at_zw, uint256[] memory nus ) public view returns (bool) { uint256 k = columns.length; - require(columns_at_z1.length == k); - require(columns_at_z2.length <= k); + require(columns_at_z.length == k); + require(columns_at_zw.length <= k); BLS.G1Point[] memory polys = new BLS.G1Point[](k + 1); for (uint256 i = 0; i < k; i++) { @@ -31,16 +31,19 @@ contract Plonk { } polys[k] = quotient; - uint256[] memory evals_at_z1 = new uint256[](k + 1); + uint256[] memory evals_at_z = new uint256[](k + 1); for (uint256 i = 0; i < k; i++) { - evals_at_z1[i] = columns_at_z1[i]; + evals_at_z[i] = columns_at_z[i]; } - evals_at_z1[k] = compute_quotient(columns_at_z1, columns_at_z2, z1); + evals_at_z[k] = compute_quotient(columns_at_z, columns_at_zw, z); BLS.G1Point[] memory kzg_proofs = new BLS.G1Point[](2); - kzg_proofs[0] = kzg_proof_at_z1; - kzg_proofs[1] = kzg_proof_at_z2; - return Kzg.verify_plonk_kzg(polys, z1, evals_at_z1, columns_at_z2, kzg_proofs, nus, tau_g2); + kzg_proofs[0] = kzg_proof_at_z; + kzg_proofs[1] = kzg_proof_at_zw; + uint256[] memory zs = new uint256[](2); + zs[0] = z; + zs[1] = Constraints.mul(z, Constraints.w); + return Kzg.verify_plonk_kzg(polys, zs, evals_at_z, columns_at_zw, kzg_proofs, nus, tau_g2); } function compute_quotient(uint256[] memory columns_at_z1, uint256[] memory columns_at_z2, uint256 z) diff --git a/evm-vrfier/contracts/test/Kzg.t.sol b/evm-vrfier/contracts/test/Kzg.t.sol index 1968c84..cf207c4 100644 --- a/evm-vrfier/contracts/test/Kzg.t.sol +++ b/evm-vrfier/contracts/test/Kzg.t.sol @@ -17,13 +17,13 @@ contract KzgExt { function verify_plonk_kzg( BLS.G1Point[] memory polys, - uint256 z1, + uint256[] memory zs, uint256[] memory evals_at_z1, uint256[] memory evals_at_z2, BLS.G1Point[] memory proofs, uint256[] memory nus ) public view returns (bool) { - return Kzg.verify_plonk_kzg(polys, z1, evals_at_z1, evals_at_z2, proofs, nus, tau_g2); + return Kzg.verify_plonk_kzg(polys, zs, evals_at_z1, evals_at_z2, proofs, nus, tau_g2); } function pairing2( @@ -113,7 +113,7 @@ contract PlonkKzgTest is Test { } BLS.G1Point memory commitment = commit(poly); uint256 z1 = 666; - uint256 z2 = 667; + uint256 z2 = 668; BLS.G1Point memory proof_z1 = prove(poly, z1); BLS.G1Point memory proof_z2 = prove(poly, z2); uint256 eval_at_z1 = eval(poly, z1); @@ -128,17 +128,17 @@ contract PlonkKzgTest is Test { commitments[0] = commitment; evals_at_z1[0] = eval_at_z1; evals_at_z2[0] = eval_at_z2; - uint256 nu = 2; uint256[] memory nus = new uint256[](1); nus[0] = nu; - BLS.G1Point[] memory proofs = new BLS.G1Point[](2); proofs[0] = BlsGenerators.g1_mul(proof_z1, bytes32(nu)); proofs[1] = BlsGenerators.g1_mul(proof_z2, bytes32(nu)); - + uint256[] memory zs = new uint256[](2); + zs[0] = z1; + zs[1] = z2; assertTrue( - kzg.verify_plonk_kzg(commitments, z1, evals_at_z1, evals_at_z2, proofs, nus), + kzg.verify_plonk_kzg(commitments, zs, evals_at_z1, evals_at_z2, proofs, nus), "Batch KZG verification failed" ); } diff --git a/evm-vrfier/src/test_kzg.rs b/evm-vrfier/src/test_kzg.rs index e768677..b168836 100644 --- a/evm-vrfier/src/test_kzg.rs +++ b/evm-vrfier/src/test_kzg.rs @@ -170,7 +170,7 @@ mod tests { let res = plonk_kzg .verify_plonk_kzg( test_openning.polys, - test_openning.z1, + vec![test_openning.z1, test_openning.z2], test_openning.evals_at_z1, test_openning.evals_at_z2, vec![test_openning.kzg_proof_at_z1, test_openning.kzg_proof_at_z2], From 2e7147a839b7163fd1a3cf6a52a6c2916c0b2687 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Mon, 21 Apr 2025 01:00:38 +0200 Subject: [PATCH 20/25] verify_plonk_proof works --- evm-vrfier/src/plonk_test.rs | 80 +++++++++++++++++------------------- evm-vrfier/src/test_kzg.rs | 7 +--- 2 files changed, 39 insertions(+), 48 deletions(-) diff --git a/evm-vrfier/src/plonk_test.rs b/evm-vrfier/src/plonk_test.rs index e8c8d8d..fdb0f0a 100644 --- a/evm-vrfier/src/plonk_test.rs +++ b/evm-vrfier/src/plonk_test.rs @@ -7,14 +7,13 @@ alloy::sol!( #[cfg(test)] mod tests { use super::*; - use crate::{encode_g1, encode_g2, fr_to_bytes, fr_to_uint, G1Point, G2Point}; + use crate::{encode_g1, encode_g2, fr_to_uint, G1Point, G2Point}; use alloy::primitives::U256; use ark_bls12_381::Bls12_381; use ark_ec::pairing::Pairing; use ark_ec::twisted_edwards::{Affine, TECurveConfig}; use ark_ec::PrimeGroup; use ark_ed_on_bls12_381_bandersnatch::EdwardsConfig; - use ark_ff::One; use ark_ff::{BigInteger, PrimeField}; use ark_std::rand::Rng; use ark_std::{test_rng, UniformRand}; @@ -61,23 +60,21 @@ mod tests { struct ArkProof { columns: Vec, quotient: E::G1Affine, - z1: E::ScalarField, - z2: E::ScalarField, - columns_at_z1: Vec, - columns_at_z2: Vec, - kzg_proof_at_z1: E::G1Affine, - kzg_proof_at_z2: E::G1Affine, + z: E::ScalarField, + columns_at_z: Vec, + columns_at_zw: Vec, + kzg_proof_at_z: E::G1Affine, + kzg_proof_at_zw: E::G1Affine, } struct EthProof { columns: Vec, quotient: BLS::G1Point, - z1: U256, - z2: U256, - columns_at_z1: Vec, - columns_at_z2: Vec, - kzg_proof_at_z1: BLS::G1Point, - kzg_proof_at_z2: BLS::G1Point, + z: U256, + columns_at_z: Vec, + columns_at_zw: Vec, + kzg_proof_at_z: BLS::G1Point, + kzg_proof_at_zw: BLS::G1Point, } impl ArkProof { @@ -89,12 +86,11 @@ mod tests { .map(|p| encode_g1(p).into()) .collect(), quotient: encode_g1(self.quotient).into(), - z1: fr_to_uint(self.z1), - z2: fr_to_uint(self.z2), - columns_at_z1: self.columns_at_z1.into_iter().map(fr_to_uint).collect(), - columns_at_z2: self.columns_at_z2.into_iter().map(fr_to_uint).collect(), - kzg_proof_at_z1: encode_g1(self.kzg_proof_at_z1).into(), - kzg_proof_at_z2: encode_g1(self.kzg_proof_at_z2).into(), + z: fr_to_uint(self.z), + columns_at_z: self.columns_at_z.into_iter().map(fr_to_uint).collect(), + columns_at_zw: self.columns_at_zw.into_iter().map(fr_to_uint).collect(), + kzg_proof_at_z: encode_g1(self.kzg_proof_at_z).into(), + kzg_proof_at_zw: encode_g1(self.kzg_proof_at_zw).into(), } } } @@ -145,37 +141,36 @@ mod tests { // sample zeta let z = E::ScalarField::rand(rng); - // let zw = z * domain.omega(); - let zw = z + E::ScalarField::one(); //TODO - let columns_at_z1 = column_polys.iter().map(|p| p.evaluate(&z)).collect(); - let columns_at_z2 = column_polys[..2].iter().map(|p| p.evaluate(&z)).collect(); + let zw = z * domain.omega(); + let columns_at_z = column_polys.iter().map(|p| p.evaluate(&z)).collect(); + let columns_at_zw = column_polys[..2].iter().map(|p| p.evaluate(&zw)).collect(); // sample nus - let mut polys = column_polys.clone(); - polys.push(quotient_poly); - let nus: Vec = (0..polys.len()) + let mut polys_at_z = column_polys.clone(); + polys_at_z.push(quotient_poly); + let nus: Vec = (0..polys_at_z.len()) .map(|_| E::ScalarField::rand(rng)) .collect(); - let agg_poly = aggregate_polys(&polys, &nus); + let agg_poly_z = aggregate_polys(&polys_at_z, &nus); + let agg_columns_zw = aggregate_polys(&column_polys[..2], &nus[..2]); - let kzg_proof_at_z1 = KZG::::open(&ck, &agg_poly, z).unwrap(); - let kzg_proof_at_z2 = KZG::::open(&ck, &column_polys[0], zw).unwrap(); + let kzg_proof_at_z = KZG::::open(&ck, &agg_poly_z, z).unwrap(); + let kzg_proof_at_zw = KZG::::open(&ck, &agg_columns_zw, zw).unwrap(); let proof = ArkProof { columns: column_commitments, quotient: quotient_commitment, - z1: z, - z2: zw, - columns_at_z1, - columns_at_z2, - kzg_proof_at_z1, - kzg_proof_at_z2, + z, + columns_at_z, + columns_at_zw, + kzg_proof_at_z, + kzg_proof_at_zw, }; (proof, rvk, nus) } - // #[tokio::test] //TODO + #[tokio::test] async fn verify_plonk_proof() -> Result<(), Box> { let provider = alloy::providers::builder() .with_recommended_fillers() @@ -191,12 +186,11 @@ mod tests { .verify_proof( test_proof.columns, test_proof.quotient, - test_proof.z1, - // test_proof.z2, - test_proof.columns_at_z1, - test_proof.columns_at_z2, - test_proof.kzg_proof_at_z1, - test_proof.kzg_proof_at_z2, + test_proof.z, + test_proof.columns_at_z, + test_proof.columns_at_zw, + test_proof.kzg_proof_at_z, + test_proof.kzg_proof_at_zw, nus.into_iter().map(fr_to_uint).collect(), ) .call() diff --git a/evm-vrfier/src/test_kzg.rs b/evm-vrfier/src/test_kzg.rs index b168836..e918441 100644 --- a/evm-vrfier/src/test_kzg.rs +++ b/evm-vrfier/src/test_kzg.rs @@ -5,10 +5,8 @@ mod tests { use ark_bls12_381::{Bls12_381, G1Affine, G2Affine}; use ark_ec::pairing::Pairing; use ark_ec::{AffineRepr, PrimeGroup}; - use ark_ff::One; use ark_std::rand::Rng; use ark_std::{test_rng, UniformRand}; - use std::ops::Mul; use w3f_pcs::aggregation::single::aggregate_polys; use w3f_pcs::pcs::kzg::params::RawKzgVerifierKey; use w3f_pcs::pcs::kzg::urs::URS; @@ -123,8 +121,7 @@ mod tests { // Evaluation points let z1 = E::ScalarField::rand(rng); - // let z2 = E::ScalarField::rand(rng); - let z2 = z1 + E::ScalarField::one(); //TODO + let z2 = E::ScalarField::rand(rng); // Proofs let kzg_proof_at_z1 = KZG::::open(&ck, &agg_poly_z1, z1).unwrap(); @@ -189,7 +186,7 @@ mod tests { .with_recommended_fillers() .on_anvil_with_wallet_and_config(|anvil| anvil.prague())?; - let (mut test_openning, _, kzg_vk) = + let (test_openning, _, kzg_vk) = random_opening::(123, 1, 0, &mut test_rng()); let test_openning = test_openning.encode(); From b3bdd07e369acdd1e20b4ea040f16b307b86f071 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Mon, 21 Apr 2025 01:03:05 +0200 Subject: [PATCH 21/25] cosmetic --- evm-vrfier/src/lib.rs | 4 ++-- evm-vrfier/src/{constraints.rs => test_constraints.rs} | 0 evm-vrfier/src/test_kzg.rs | 3 +-- evm-vrfier/src/{plonk_test.rs => test_plonk.rs} | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) rename evm-vrfier/src/{constraints.rs => test_constraints.rs} (100%) rename evm-vrfier/src/{plonk_test.rs => test_plonk.rs} (98%) diff --git a/evm-vrfier/src/lib.rs b/evm-vrfier/src/lib.rs index 7cb9369..8f8d178 100644 --- a/evm-vrfier/src/lib.rs +++ b/evm-vrfier/src/lib.rs @@ -1,9 +1,9 @@ use alloy::primitives::{FixedBytes, U256}; use ark_ff::{BigInteger, PrimeField}; -mod constraints; -mod plonk_test; +mod test_constraints; mod test_kzg; +mod test_plonk; alloy::sol! { struct G1Point { diff --git a/evm-vrfier/src/constraints.rs b/evm-vrfier/src/test_constraints.rs similarity index 100% rename from evm-vrfier/src/constraints.rs rename to evm-vrfier/src/test_constraints.rs diff --git a/evm-vrfier/src/test_kzg.rs b/evm-vrfier/src/test_kzg.rs index e918441..0841228 100644 --- a/evm-vrfier/src/test_kzg.rs +++ b/evm-vrfier/src/test_kzg.rs @@ -186,8 +186,7 @@ mod tests { .with_recommended_fillers() .on_anvil_with_wallet_and_config(|anvil| anvil.prague())?; - let (test_openning, _, kzg_vk) = - random_opening::(123, 1, 0, &mut test_rng()); + let (test_openning, _, kzg_vk) = random_opening::(123, 1, 0, &mut test_rng()); let test_openning = test_openning.encode(); let plonk_kzg = Kzg::deploy(&provider, encode_g2(kzg_vk.tau_in_g2).into()).await?; diff --git a/evm-vrfier/src/plonk_test.rs b/evm-vrfier/src/test_plonk.rs similarity index 98% rename from evm-vrfier/src/plonk_test.rs rename to evm-vrfier/src/test_plonk.rs index fdb0f0a..2d3af31 100644 --- a/evm-vrfier/src/plonk_test.rs +++ b/evm-vrfier/src/test_plonk.rs @@ -171,7 +171,7 @@ mod tests { } #[tokio::test] - async fn verify_plonk_proof() -> Result<(), Box> { + async fn verify_proof() -> Result<(), Box> { let provider = alloy::providers::builder() .with_recommended_fillers() .on_anvil_with_wallet_and_config(|anvil| anvil.prague())?; From 653b00e0e748dc4f0f28a6876bebf91d086887e9 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Mon, 21 Apr 2025 01:31:51 +0200 Subject: [PATCH 22/25] cond_te_addition: `y` constraint added --- evm-vrfier/contracts/src/Constraints.sol | 25 +++++++++++++++++---- evm-vrfier/contracts/src/Plonk.sol | 4 ++-- evm-vrfier/contracts/test/Constraints.t.sol | 2 +- evm-vrfier/src/test_constraints.rs | 14 +++++++----- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/evm-vrfier/contracts/src/Constraints.sol b/evm-vrfier/contracts/src/Constraints.sol index 8523b85..3c66c53 100644 --- a/evm-vrfier/contracts/src/Constraints.sol +++ b/evm-vrfier/contracts/src/Constraints.sol @@ -29,22 +29,39 @@ library Constraints { uint256 x3, uint256 y3, uint256 not_last - ) internal pure returns (uint256 cx) { + ) internal pure returns (uint256 cx, uint256 cy) { /// `cx = {[(a.x1.x2 + y1.y2).x3 - x1.y1 - x2.y2].b + (x3 - x1).(1 - b)}.not_last` + /// `cy = {[(x1.y2 - x2.y1).y3 - x1.y1 + x2.y2].b + (y3 - y1).(1 - b)}.not_last` uint256 x1y1 = mul(x1, y1); uint256 x2y2 = mul(x2, y2); + uint256 one_minus_b = add(1, r - b); // forgefmt: disable-next-item - uint256 lx = mul( + cx = mul( // [(a.x1.x2 + y1.y2).x3 - x1.y1 - x2.y2].b add( mul( - add(mul(te_coeff_a, mul(x1, x2)), mul(y1, y2)), + add(mul(te_coeff_a, mul(x1, x2)), mul(y1, y2)), //a.x1.x2 + y1.y2 x3 ), r - add(x1y1, x2y2) ), b ); - cx = mul(add(lx, mul(add(x3, r - x1), add(1, r - b))), not_last); + cx = mul(add(cx, mul(add(x3, r - x1), one_minus_b)), not_last); + // forgefmt: disable-next-item + cy = mul( // [(x1.y2 - x2.y1).y3 - x1.y1 + x2.y2].b + add( + add( + mul( + + add(mul(x1, y2), r - mul(x2,y1)), //x1.y2 - x2.y1 + y3 + ), + r - x1y1), + x2y2 + ), + b + ); + cy = mul(add(cy, mul(add(y3, r - y1), one_minus_b)), not_last); } function mod_exp(uint256 base, uint256 exp) internal view returns (uint256) { diff --git a/evm-vrfier/contracts/src/Plonk.sol b/evm-vrfier/contracts/src/Plonk.sol index 267ddae..736231e 100644 --- a/evm-vrfier/contracts/src/Plonk.sol +++ b/evm-vrfier/contracts/src/Plonk.sol @@ -52,7 +52,7 @@ contract Plonk { returns (uint256) { uint256 not_last = Constraints.not_last_row(z); - uint256 c = Constraints.cond_te_addition( + (uint256 cx, uint256 cy) = Constraints.cond_te_addition( columns_at_z1[4], columns_at_z1[0], columns_at_z1[1], @@ -62,6 +62,6 @@ contract Plonk { columns_at_z2[1], not_last ); - return Constraints.quotient_at(c, z); + return Constraints.quotient_at(cx, z); } } diff --git a/evm-vrfier/contracts/test/Constraints.t.sol b/evm-vrfier/contracts/test/Constraints.t.sol index 21e1b8f..4b157d1 100644 --- a/evm-vrfier/contracts/test/Constraints.t.sol +++ b/evm-vrfier/contracts/test/Constraints.t.sol @@ -13,7 +13,7 @@ library ConstraintsExt { uint256 x3, uint256 y3, uint256 not_last - ) public pure returns (uint256) { + ) public pure returns (uint256, uint256) { return Constraints.cond_te_addition(b, x1, y1, x2, y2, x3, y3, not_last); } diff --git a/evm-vrfier/src/test_constraints.rs b/evm-vrfier/src/test_constraints.rs index 3fe386d..9a39e3a 100644 --- a/evm-vrfier/src/test_constraints.rs +++ b/evm-vrfier/src/test_constraints.rs @@ -33,7 +33,8 @@ mod tests { let y2 = Fr::rand(rng); let x3 = Fr::rand(rng); let y3 = Fr::rand(rng); - let cx_rust = cond_te_addition( + let not_last = Fr::rand(rng); + let cs_rust = cond_te_addition( te_coeff_a, &b, &x1, @@ -42,10 +43,10 @@ mod tests { &y2, x3, y3, - &Fr::one(), + ¬_last, Fr::one(), - )[0]; - let cx_sol = constraints + ); + let cs_sol = constraints .cond_te_addition( fr_to_uint(b), fr_to_uint(x1), @@ -54,11 +55,12 @@ mod tests { fr_to_uint(y2), fr_to_uint(x3), fr_to_uint(y3), - fr_to_uint(Fr::one()), + fr_to_uint(not_last), ) .call() .await?; - assert_eq!(unit_to_fr(cx_sol), cx_rust); + assert_eq!(unit_to_fr(cs_sol._0), cs_rust[0]); + assert_eq!(unit_to_fr(cs_sol._1), cs_rust[1]); Ok(()) } From a96fcc756c95f871d0352b07ee7e8a986b296061 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Mon, 21 Apr 2025 01:39:38 +0200 Subject: [PATCH 23/25] cond_te_addition: `y` constraint employed --- evm-vrfier/contracts/src/Plonk.sol | 2 +- evm-vrfier/src/test_plonk.rs | 11 ++++++++--- w3f-plonk-common/src/prover.rs | 22 +++++++++++----------- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/evm-vrfier/contracts/src/Plonk.sol b/evm-vrfier/contracts/src/Plonk.sol index 736231e..e999d71 100644 --- a/evm-vrfier/contracts/src/Plonk.sol +++ b/evm-vrfier/contracts/src/Plonk.sol @@ -62,6 +62,6 @@ contract Plonk { columns_at_z2[1], not_last ); - return Constraints.quotient_at(cx, z); + return Constraints.quotient_at(Constraints.add(cx, cy), z); //TODO: alphas } } diff --git a/evm-vrfier/src/test_plonk.rs b/evm-vrfier/src/test_plonk.rs index 2d3af31..57691bb 100644 --- a/evm-vrfier/src/test_plonk.rs +++ b/evm-vrfier/src/test_plonk.rs @@ -14,7 +14,7 @@ mod tests { use ark_ec::twisted_edwards::{Affine, TECurveConfig}; use ark_ec::PrimeGroup; use ark_ed_on_bls12_381_bandersnatch::EdwardsConfig; - use ark_ff::{BigInteger, PrimeField}; + use ark_ff::{BigInteger, One, PrimeField}; use ark_std::rand::Rng; use ark_std::{test_rng, UniformRand}; use w3f_pcs::aggregation::single::aggregate_polys; @@ -29,6 +29,7 @@ mod tests { use w3f_plonk_common::gadgets::ec::te_doubling::Doubling; use w3f_plonk_common::gadgets::ec::{AffineColumn, CondAdd}; use w3f_plonk_common::gadgets::ProverGadget; + use w3f_plonk_common::prover::aggregate_evaluations; use w3f_plonk_common::Column; impl From for BLS::G1Point { @@ -135,8 +136,12 @@ mod tests { .collect(); // TODO: sample alphas (coeffs to agg constraints) - let constraint = cond_add.constraints()[0].interpolate_by_ref(); - let quotient_poly = domain.divide_by_vanishing_poly(&constraint); + let constraints = cond_add.constraints(); + let agg_constraint = aggregate_evaluations( + &constraints, + &[E::ScalarField::one(), E::ScalarField::one()], + ); + let quotient_poly = domain.divide_by_vanishing_poly(&agg_constraint.interpolate()); let quotient_commitment = KZG::::commit(&ck, "ient_poly).unwrap().0; // sample zeta diff --git a/w3f-plonk-common/src/prover.rs b/w3f-plonk-common/src/prover.rs index 6dc96be..5d368cc 100644 --- a/w3f-plonk-common/src/prover.rs +++ b/w3f-plonk-common/src/prover.rs @@ -1,4 +1,4 @@ -use ark_ff::PrimeField; +use ark_ff::{FftField, PrimeField}; use ark_poly::{Evaluations, Polynomial}; use ark_serialize::CanonicalSerialize; use ark_std::vec; @@ -47,7 +47,7 @@ impl, T: PlonkTranscript> PlonkProver let constraint_polys = piop.constraints(); let alphas = transcript.get_constraints_aggregation_coeffs(constraint_polys.len()); // Aggregate constraint polynomials in evaluation form... - let agg_constraint_poly = Self::aggregate_evaluations(&constraint_polys, &alphas); + let agg_constraint_poly = aggregate_evaluations(&constraint_polys, &alphas); // ...and then interpolate (to save some FFTs). let agg_constraint_poly = agg_constraint_poly.interpolate(); let quotient_poly = piop.domain().divide_by_vanishing_poly(&agg_constraint_poly); @@ -82,14 +82,14 @@ impl, T: PlonkTranscript> PlonkProver lin_at_zeta_omega_proof, } } +} - fn aggregate_evaluations(polys: &[Evaluations], coeffs: &[F]) -> Evaluations { - assert_eq!(coeffs.len(), polys.len()); - polys - .iter() - .zip(coeffs.iter()) - .map(|(p, &c)| p * c) - .reduce(|acc, p| &acc + &p) - .unwrap() - } +pub fn aggregate_evaluations(polys: &[Evaluations], coeffs: &[F]) -> Evaluations { + assert_eq!(coeffs.len(), polys.len()); + polys + .iter() + .zip(coeffs.iter()) + .map(|(p, &c)| p * c) + .reduce(|acc, p| &acc + &p) + .unwrap() } From 682cc5d863a9f5c8b20b122e2f73f5be4e046d06 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Mon, 21 Apr 2025 01:53:10 +0200 Subject: [PATCH 24/25] fmt --- w3f-plonk-common/src/prover.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/w3f-plonk-common/src/prover.rs b/w3f-plonk-common/src/prover.rs index 5d368cc..c50d463 100644 --- a/w3f-plonk-common/src/prover.rs +++ b/w3f-plonk-common/src/prover.rs @@ -84,7 +84,10 @@ impl, T: PlonkTranscript> PlonkProver } } -pub fn aggregate_evaluations(polys: &[Evaluations], coeffs: &[F]) -> Evaluations { +pub fn aggregate_evaluations( + polys: &[Evaluations], + coeffs: &[F], +) -> Evaluations { assert_eq!(coeffs.len(), polys.len()); polys .iter() From 0478b7be52dd4263f1164d934991889cd6613ff1 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Mon, 21 Apr 2025 01:57:54 +0200 Subject: [PATCH 25/25] test_single_openning fixed --- evm-vrfier/src/test_kzg.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/evm-vrfier/src/test_kzg.rs b/evm-vrfier/src/test_kzg.rs index 0841228..d64a049 100644 --- a/evm-vrfier/src/test_kzg.rs +++ b/evm-vrfier/src/test_kzg.rs @@ -5,6 +5,7 @@ mod tests { use ark_bls12_381::{Bls12_381, G1Affine, G2Affine}; use ark_ec::pairing::Pairing; use ark_ec::{AffineRepr, PrimeGroup}; + use ark_ff::One; use ark_std::rand::Rng; use ark_std::{test_rng, UniformRand}; use w3f_pcs::aggregation::single::aggregate_polys; @@ -115,7 +116,9 @@ mod tests { .collect(); // Aggregate polynomial - let nus: Vec = (0..k).map(|_| E::ScalarField::rand(rng)).collect(); + let nus: Vec = ark_std::iter::once(E::ScalarField::one()) + .chain((0..k - 1).map(|_| E::ScalarField::rand(rng))) + .collect(); let agg_poly_z1 = aggregate_polys(&polys, &nus); let agg_poly_z2 = aggregate_polys(&polys[..l], &nus[..l]); @@ -180,7 +183,7 @@ mod tests { Ok(()) } - // #[tokio::test] //TODO + #[tokio::test] async fn test_single_openning() -> Result<(), Box> { let provider = alloy::providers::builder() .with_recommended_fillers()