From 5e183e3fae6836873870ca4022cef81922df279a Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Sat, 1 Mar 2025 21:21:36 +0100 Subject: [PATCH 01/23] pk_index and related columns added --- w3f-ring-proof/src/piop/prover.rs | 1 + w3f-ring-vrf-snark/src/lib.rs | 52 +++++----- w3f-ring-vrf-snark/src/piop/mod.rs | 61 +++++++----- w3f-ring-vrf-snark/src/piop/params.rs | 124 ++++++++++-------------- w3f-ring-vrf-snark/src/piop/prover.rs | 41 +++++++- w3f-ring-vrf-snark/src/piop/verifier.rs | 2 +- 6 files changed, 151 insertions(+), 130 deletions(-) diff --git a/w3f-ring-proof/src/piop/prover.rs b/w3f-ring-proof/src/piop/prover.rs index 23ac074..3b78890 100644 --- a/w3f-ring-proof/src/piop/prover.rs +++ b/w3f-ring-proof/src/piop/prover.rs @@ -75,6 +75,7 @@ impl> PiopProver { } } + // TODO: move to params? fn bits_column( params: &PiopParams, index_in_keys: usize, diff --git a/w3f-ring-vrf-snark/src/lib.rs b/w3f-ring-vrf-snark/src/lib.rs index a9c5669..1029632 100644 --- a/w3f-ring-vrf-snark/src/lib.rs +++ b/w3f-ring-vrf-snark/src/lib.rs @@ -14,7 +14,7 @@ pub use w3f_pcs::pcs; pub use w3f_plonk_common::domain::Domain; mod piop; -mod ring_vrf; +// mod ring_vrf; pub mod ring_vrf_prover; pub mod ring_vrf_verifier; @@ -46,18 +46,14 @@ impl ArkTranscript { #[cfg(test)] mod tests { - use ark_bls12_381::Bls12_381; use ark_ec::CurveGroup; use ark_ed_on_bls12_381_bandersnatch::{BandersnatchConfig, EdwardsAffine, Fq, Fr}; use ark_std::ops::Mul; use ark_std::rand::Rng; use ark_std::{end_timer, start_timer, test_rng, UniformRand}; - use w3f_pcs::pcs::kzg::KZG; use w3f_plonk_common::test_helpers::random_vec; - use crate::piop::FixedColumnsCommitted; - use crate::ring_vrf::{Ring, RingBuilderKey}; use crate::ring_vrf_prover::RingProver; use crate::ring_vrf_verifier::RingVerifier; @@ -101,29 +97,29 @@ mod tests { assert!(res); } - #[test] - fn test_lagrangian_commitment() { - let rng = &mut test_rng(); - - let domain_size = 2usize.pow(9); - - let (pcs_params, piop_params) = setup::<_, KZG>(rng, domain_size); - let ring_builder_key = RingBuilderKey::from_srs(&pcs_params, domain_size); - - let max_keyset_size = piop_params.keyset_part_size; - let keyset_size: usize = rng.gen_range(0..max_keyset_size); - let pks = random_vec::(keyset_size, rng); - - let (_, verifier_key) = index::<_, KZG, _>(&pcs_params, &piop_params, &pks); - - let ring = Ring::<_, Bls12_381, _>::with_keys(&piop_params, &pks, &ring_builder_key); - - let fixed_columns_committed = FixedColumnsCommitted::from_ring(&ring); - assert_eq!( - fixed_columns_committed, - verifier_key.fixed_columns_committed - ); - } + // #[test] + // fn test_lagrangian_commitment() { + // let rng = &mut test_rng(); + // + // let domain_size = 2usize.pow(9); + // + // let (pcs_params, piop_params) = setup::<_, KZG>(rng, domain_size); + // let ring_builder_key = RingBuilderKey::from_srs(&pcs_params, domain_size); + // + // let max_keyset_size = piop_params.keyset_part_size; + // let keyset_size: usize = rng.gen_range(0..max_keyset_size); + // let pks = random_vec::(keyset_size, rng); + // + // let (_, verifier_key) = index::<_, KZG, _>(&pcs_params, &piop_params, &pks); + // + // let ring = Ring::<_, Bls12_381, _>::with_keys(&piop_params, &pks, &ring_builder_key); + // + // let fixed_columns_committed = FixedColumnsCommitted::from_ring(&ring); + // assert_eq!( + // fixed_columns_committed, + // verifier_key.fixed_columns_committed + // ); + // } fn setup>( rng: &mut R, diff --git a/w3f-ring-vrf-snark/src/piop/mod.rs b/w3f-ring-vrf-snark/src/piop/mod.rs index b2a206c..76e0acf 100644 --- a/w3f-ring-vrf-snark/src/piop/mod.rs +++ b/w3f-ring-vrf-snark/src/piop/mod.rs @@ -15,7 +15,6 @@ pub(crate) use verifier::PiopVerifier; use w3f_plonk_common::gadgets::ec::AffineColumn; use w3f_plonk_common::{Column, ColumnsCommited, ColumnsEvaluated}; -use crate::ring_vrf::Ring; use crate::PiopParams; pub mod params; @@ -24,8 +23,9 @@ mod verifier; #[derive(Clone, CanonicalSerialize, CanonicalDeserialize)] pub struct RingCommitments> { - // doublings_of_g are prepended by the verifier + // `pks` and `doublings_of_g` are prepended by the verifier pub(crate) sk_bits: C, + pub(crate) pk_index: C, pub(crate) pk_from_sk: [C; 2], pub(crate) doublings_of_in: [C; 2], pub(crate) out_from_in: [C; 2], @@ -36,6 +36,7 @@ impl> ColumnsCommited for RingCommitments< fn to_vec(self) -> Vec { vec![ self.sk_bits, + self.pk_index, self.pk_from_sk[0].clone(), self.pk_from_sk[1].clone(), self.doublings_of_in[0].clone(), @@ -48,8 +49,10 @@ impl> ColumnsCommited for RingCommitments< #[derive(Clone, CanonicalSerialize, CanonicalDeserialize)] pub struct RingEvaluations { + pub(crate) pks: [F; 2], pub(crate) doublings_of_g: [F; 2], pub(crate) sk_bits: F, + pub(crate) pk_index: F, pub(crate) pk_from_sk: [F; 2], pub(crate) doublings_of_in: [F; 2], pub(crate) out_from_in: [F; 2], @@ -58,9 +61,12 @@ pub struct RingEvaluations { impl ColumnsEvaluated for RingEvaluations { fn to_vec(self) -> Vec { vec![ + self.pks[0], + self.pks[1], self.doublings_of_g[0], self.doublings_of_g[1], self.sk_bits, + self.pk_index, self.pk_from_sk[0], self.pk_from_sk[1], self.doublings_of_in[0], @@ -76,12 +82,14 @@ impl ColumnsEvaluated for RingEvaluations { // #[derive(Clone, CanonicalSerialize, CanonicalDeserialize)] #[derive(Clone)] pub struct FixedColumns> { + pks: AffineColumn, doublings_of_g: AffineColumn, } // Commitments to the fixed columns (see above). #[derive(Clone, CanonicalSerialize, CanonicalDeserialize, PartialEq, Eq, Debug)] pub struct FixedColumnsCommitted> { + pub pks: [C; 2], pub doublings_of_g: [C; 2], pub phantom: PhantomData, } @@ -89,33 +97,40 @@ pub struct FixedColumnsCommitted> { impl> FixedColumnsCommitted { fn as_vec(&self) -> Vec { vec![ + self.pks[0].clone(), + self.pks[1].clone(), self.doublings_of_g[0].clone(), self.doublings_of_g[1].clone(), ] } } -impl FixedColumnsCommitted> { - pub fn from_ring>( - ring: &Ring, - ) -> Self { - let cx = KzgCommitment(ring.cx); - let cy = KzgCommitment(ring.cy); - Self { - doublings_of_g: [cx, cy], - phantom: Default::default(), - } - } -} +// impl FixedColumnsCommitted> { +// pub fn from_ring>( +// ring: &Ring, +// ) -> Self { +// let cx = KzgCommitment(ring.cx); +// let cy = KzgCommitment(ring.cy); +// Self { +// doublings_of_g: [cx, cy], +// phantom: Default::default(), +// } +// } +// } impl> FixedColumns { fn commit>(&self, ck: &CS::CK) -> FixedColumnsCommitted { - let points = [ + let pks = [ + CS::commit(ck, self.pks.xs.as_poly()).unwrap(), + CS::commit(ck, self.pks.ys.as_poly()).unwrap(), + ]; + let doublings_of_g = [ CS::commit(ck, self.doublings_of_g.xs.as_poly()).unwrap(), CS::commit(ck, self.doublings_of_g.ys.as_poly()).unwrap(), ]; FixedColumnsCommitted { - doublings_of_g: points, + pks, + doublings_of_g, phantom: Default::default(), } } @@ -136,12 +151,12 @@ pub struct VerifierKey> { } impl VerifierKey> { - pub fn from_ring_and_kzg_vk>( - ring: &Ring, - kzg_vk: RawKzgVerifierKey, - ) -> Self { - Self::from_commitment_and_kzg_vk(FixedColumnsCommitted::from_ring(ring), kzg_vk) - } + // pub fn from_ring_and_kzg_vk>( + // ring: &Ring, + // kzg_vk: RawKzgVerifierKey, + // ) -> Self { + // Self::from_commitment_and_kzg_vk(FixedColumnsCommitted::from_ring(ring), kzg_vk) + // } pub fn from_commitment_and_kzg_vk( commitment: FixedColumnsCommitted>, @@ -165,7 +180,7 @@ pub fn index, Curve: TECurveConfig>( ) -> (ProverKey>, VerifierKey) { let pcs_ck = pcs_params.ck(); let pcs_raw_vk = pcs_params.raw_vk(); - let fixed_columns = piop_params.fixed_columns(&keys); + let fixed_columns = piop_params.fixed_columns(keys.to_vec()); //TODO let fixed_columns_committed = fixed_columns.commit::(&pcs_ck); let verifier_key = VerifierKey { pcs_raw_vk: pcs_raw_vk.clone(), diff --git a/w3f-ring-vrf-snark/src/piop/params.rs b/w3f-ring-vrf-snark/src/piop/params.rs index 2e09203..7b6352c 100644 --- a/w3f-ring-vrf-snark/src/piop/params.rs +++ b/w3f-ring-vrf-snark/src/piop/params.rs @@ -4,13 +4,14 @@ use ark_ff::{BigInteger, PrimeField}; use ark_std::{vec, vec::Vec}; use w3f_plonk_common::domain::Domain; +use w3f_plonk_common::gadgets::booleanity::BitColumn; use w3f_plonk_common::gadgets::ec::AffineColumn; use crate::piop::FixedColumns; /// Plonk Interactive Oracle Proofs (PIOP) parameters. #[derive(Clone)] -pub struct PiopParams> { +pub struct PiopParams> { /// Domain over which the piop is represented. pub(crate) domain: Domain, /// Number of bits used to represent a jubjub scalar. @@ -27,7 +28,7 @@ pub struct PiopParams> { pub(crate) padding: Affine, } -impl> PiopParams { +impl> PiopParams { /// Initialize PIOP parameters. /// /// - `domain`: polynomials evaluation domain. @@ -56,19 +57,37 @@ impl> PiopParams { } } - pub fn fixed_columns(&self, _keys: &[Affine]) -> FixedColumns> { - let doublings_of_g = self.doublings_of_g(); + pub fn fixed_columns(&self, pks: Vec>) -> FixedColumns> { + let doublings_of_g = self.doublings_of_g_col(); + let mut pks = pks; + assert!(pks.len() <= self.domain.capacity - 1); + pks.resize(self.domain.capacity - 1, self.padding); + let pks = AffineColumn::public_column(pks, &self.domain); // TODO: here we assume pk.len() > 256 FixedColumns { + pks, doublings_of_g, } } - fn doublings_of_g(&self) -> AffineColumn> { + fn doublings_of_g_col(&self) -> AffineColumn> { let mut doublings_of_g = self.doublings_of(self.g); doublings_of_g.resize(self.domain.capacity - 1, self.g); //TODO: eh, may be zeros AffineColumn::public_column(doublings_of_g, &self.domain) } + pub fn pk_index_col(&self, index: usize) -> BitColumn { + assert!(index <= self.domain.capacity - 1); + let mut col = vec![false; self.domain.capacity - 1]; + col[index] = true; + BitColumn::init(col, &self.domain) + } + + pub fn sk_bits(&self, sk: Curve::ScalarField) -> Vec { + let bits_with_trailing_zeroes = sk.into_bigint().to_bits_le(); + let significant_bits = &bits_with_trailing_zeroes[..self.scalar_bitlen]; + significant_bits.to_vec() + } + pub fn doublings_of(&self, p: Affine) -> Vec> { let mut p = p.into_group(); let mut doublings = Vec::with_capacity(self.domain.capacity); @@ -79,74 +98,31 @@ impl> PiopParams { } CurveGroup::normalize_batch(&doublings) } - - pub fn points_column(&self, keys: &[Affine]) -> AffineColumn> { - assert!(keys.len() <= self.keyset_part_size); - let padding_len = self.keyset_part_size - keys.len(); - let padding = vec![self.padding; padding_len]; - let points = [keys, &padding, &self.power_of_2_multiples_of_h()].concat(); - assert_eq!(points.len(), self.domain.capacity - 1); - AffineColumn::public_column(points, &self.domain) - } - - pub fn pubkey_points_column(&self, keys: &[Affine]) -> AffineColumn> { - assert!(keys.len() <= self.keyset_part_size); - let padding_len = self.keyset_part_size - keys.len(); - let padding = vec![self.padding; padding_len]; - let points = [keys, &padding].concat(); - assert!(points.len() < self.domain.capacity); //check if it fits the domain. - AffineColumn::public_column(points, &self.domain) - } - - pub fn power_of_2_multiples_of_h(&self) -> Vec> { - let mut h = self.h.into_group(); - let mut multiples = Vec::with_capacity(self.scalar_bitlen); - multiples.push(h); - for _ in 1..self.scalar_bitlen { - h.double_in_place(); - multiples.push(h); - } - CurveGroup::normalize_batch(&multiples) - } - - pub fn scalar_part(&self, e: Curve::ScalarField) -> Vec { - let bits_with_trailing_zeroes = e.into_bigint().to_bits_le(); - let significant_bits = &bits_with_trailing_zeroes[..self.scalar_bitlen]; - significant_bits.to_vec() - } - - pub fn keyset_part_selector(&self) -> Vec { - [ - vec![F::one(); self.keyset_part_size], - vec![F::zero(); self.scalar_bitlen], - ] - .concat() - } } -#[cfg(test)] -mod tests { - use ark_ed_on_bls12_381_bandersnatch::{BandersnatchConfig, EdwardsAffine, Fq, Fr}; - use ark_std::ops::Mul; - use ark_std::{test_rng, UniformRand}; - - use w3f_plonk_common::domain::Domain; - use w3f_plonk_common::test_helpers::cond_sum; - - use crate::piop::params::PiopParams; - - #[test] - fn test_powers_of_h() { - let rng = &mut test_rng(); - let h = EdwardsAffine::rand(rng); - let seed = EdwardsAffine::rand(rng); - let padding = EdwardsAffine::rand(rng); - let domain = Domain::new(1024, false); - - let params = PiopParams::::setup(domain, h, seed, padding); - let t = Fr::rand(rng); - let t_bits = params.scalar_part(t); - let th = cond_sum(&t_bits, ¶ms.power_of_2_multiples_of_h()); - assert_eq!(th, params.h.mul(t)); - } -} +// #[cfg(test)] +// mod tests { +// use ark_ed_on_bls12_381_bandersnatch::{BandersnatchConfig, EdwardsAffine, Fq, Fr}; +// use ark_std::ops::Mul; +// use ark_std::{test_rng, UniformRand}; +// +// use w3f_plonk_common::domain::Domain; +// use w3f_plonk_common::test_helpers::cond_sum; +// +// use crate::piop::params::PiopParams; +// +// #[test] +// fn test_powers_of_h() { +// let rng = &mut test_rng(); +// let h = EdwardsAffine::rand(rng); +// let seed = EdwardsAffine::rand(rng); +// let padding = EdwardsAffine::rand(rng); +// let domain = Domain::new(1024, false); +// +// let params = PiopParams::::setup(domain, h, seed, padding); +// let t = Fr::rand(rng); +// let t_bits = params.scalar_part(t); +// let th = cond_sum(&t_bits, ¶ms.power_of_2_multiples_of_h()); +// assert_eq!(th, params.h.mul(t)); +// } +// } diff --git a/w3f-ring-vrf-snark/src/piop/prover.rs b/w3f-ring-vrf-snark/src/piop/prover.rs index e98aec8..025ab36 100644 --- a/w3f-ring-vrf-snark/src/piop/prover.rs +++ b/w3f-ring-vrf-snark/src/piop/prover.rs @@ -19,6 +19,7 @@ use w3f_plonk_common::piop::ProverPiop; use w3f_plonk_common::gadgets::ec::te_doubling::Doubling; use w3f_plonk_common::Column; +use w3f_plonk_common::gadgets::inner_prod::InnerProd; /// The prover's private input is its secret key `sk`. /// The public inputs are: @@ -53,8 +54,10 @@ use w3f_plonk_common::Column; /// | signer's index | x of pubkey | y of pubkey | $\sum k_ipk_x$ | $\sum k_ipk_y$ | binary rep of secret key | | $\sum sk_i2^iG$ | | $\sum sk_i2^iH$ | pub struct PiopProver> { domain: Domain, + pks: Rc>>, doublings_of_g: Rc>>, sk_bits: Rc>, + pk_index: Rc>, // gadgets sk_bits_bool: Booleanity, pk_from_sk: CondAdd>, @@ -62,30 +65,40 @@ pub struct PiopProver> { out_from_in: CondAdd>, out_from_in_x: FixedCells, out_from_in_y: FixedCells, + pk_index_bool: Booleanity, + // pk_index_unique: InnerProd, //TODO: + pk_from_index_x: InnerProd, + pk_from_index_y: InnerProd, + // pks_equal // TODO } impl> PiopProver { pub fn build( params: &PiopParams, fixed_columns: FixedColumns>, // TODO: rename to AdviceColumns - _signer_index: usize, //TODO + pk_index: usize, sk: Curve::ScalarField, vrf_in: Affine, ) -> Self { let domain = params.domain.clone(); - let doublings_of_g = fixed_columns.doublings_of_g; + let FixedColumns { + pks, + doublings_of_g, + } = fixed_columns; + let pks = Rc::new(pks); let doublings_of_g = Rc::new(doublings_of_g); let sk_bits = { - let mut sk_bits = params.scalar_part(sk); //TODO: return right thing + let mut sk_bits = params.sk_bits(sk); //TODO: return right thing assert!(sk_bits.len() <= domain.capacity - 1); sk_bits.resize(domain.capacity - 1, false); let sk_bits = BitColumn::init(sk_bits, ¶ms.domain); Rc::new(sk_bits) }; - let sk_bits_bool = Booleanity::init(sk_bits.clone()); + let pk_index = Rc::new(params.pk_index_col(pk_index)); + let sk_bits_bool = Booleanity::init(sk_bits.clone()); // `PK_sk := sk.G` let pk_from_sk = CondAdd::init( sk_bits.clone(), @@ -94,6 +107,10 @@ impl> PiopProver { &domain, ); + let pk_index_bool = Booleanity::init(pk_index.clone()); + let pk_from_index_x = InnerProd::init(pks.xs.clone(), pk_index.col.clone(), &domain); + let pk_from_index_y = InnerProd::init(pks.ys.clone(), pk_index.col.clone(), &domain); + let doublings_of_in_gadget = Doubling::init(vrf_in, &domain); let doublings_of_in = doublings_of_in_gadget.doublings.clone(); let out_from_in = CondAdd::init( @@ -107,14 +124,19 @@ impl> PiopProver { Self { domain, + pks, doublings_of_g, sk_bits, + pk_index, sk_bits_bool, pk_from_sk, doublings_of_in_gadget, out_from_in, out_from_in_x, out_from_in_y, + pk_index_bool, + pk_from_index_x, + pk_from_index_y, } } } @@ -134,6 +156,7 @@ where commit: Fun, ) -> Self::Commitments { let sk_bits = commit(self.sk_bits.as_poly()); + let pk_index = commit(self.pk_index.as_poly()); let pk_from_sk = [ commit(self.pk_from_sk.acc.xs.as_poly()), commit(self.pk_from_sk.acc.ys.as_poly()), @@ -148,6 +171,7 @@ where ]; RingCommitments { sk_bits, + pk_index, pk_from_sk, doublings_of_in, out_from_in, @@ -160,6 +184,7 @@ where fn columns(&self) -> Vec> { vec![ self.sk_bits.as_poly().clone(), + self.pk_index.as_poly().clone(), self.pk_from_sk.acc.xs.as_poly().clone(), self.pk_from_sk.acc.ys.as_poly().clone(), self.doublings_of_in_gadget.doublings.xs.as_poly().clone(), @@ -170,11 +195,16 @@ where } fn columns_evaluated(&self, zeta: &F) -> RingEvaluations { + let pks = [ + self.pks.xs.evaluate(zeta), + self.pks.ys.evaluate(zeta), + ]; let doublings_of_g = [ self.doublings_of_g.xs.evaluate(zeta), self.doublings_of_g.ys.evaluate(zeta), ]; let sk_bits = self.sk_bits.evaluate(zeta); + let pk_index = self.pk_index.evaluate(zeta); let pk_from_sk = [ self.pk_from_sk.acc.xs.evaluate(zeta), self.pk_from_sk.acc.ys.evaluate(zeta), @@ -188,8 +218,10 @@ where self.out_from_in.acc.ys.evaluate(zeta), ]; RingEvaluations { + pks, doublings_of_g, sk_bits, + pk_index, pk_from_sk, doublings_of_in, out_from_in, @@ -224,6 +256,7 @@ where &self.domain } + // TODO: full instance fn result(&self) -> Self::Instance { self.out_from_in.result } diff --git a/w3f-ring-vrf-snark/src/piop/verifier.rs b/w3f-ring-vrf-snark/src/piop/verifier.rs index 6a34b81..ee98c95 100644 --- a/w3f-ring-vrf-snark/src/piop/verifier.rs +++ b/w3f-ring-vrf-snark/src/piop/verifier.rs @@ -111,7 +111,7 @@ impl, Jubjub: TECurveConfig> Veri for PiopVerifier> { const N_CONSTRAINTS: usize = 9; - const N_COLUMNS: usize = 9; + const N_COLUMNS: usize = 12; fn precommitted_columns(&self) -> Vec { self.fixed_columns_committed.as_vec() From 1da991dcadc3e7dcb55c29b0d0e30d0f8c0d9b95 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Sat, 1 Mar 2025 21:35:42 +0100 Subject: [PATCH 02/23] pk_index booleanity constrained --- w3f-ring-vrf-snark/src/piop/mod.rs | 1 + w3f-ring-vrf-snark/src/piop/prover.rs | 15 +++++++++---- w3f-ring-vrf-snark/src/piop/verifier.rs | 29 ++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/w3f-ring-vrf-snark/src/piop/mod.rs b/w3f-ring-vrf-snark/src/piop/mod.rs index 76e0acf..c86fcb5 100644 --- a/w3f-ring-vrf-snark/src/piop/mod.rs +++ b/w3f-ring-vrf-snark/src/piop/mod.rs @@ -56,6 +56,7 @@ pub struct RingEvaluations { pub(crate) pk_from_sk: [F; 2], pub(crate) doublings_of_in: [F; 2], pub(crate) out_from_in: [F; 2], + pub(crate) pk_from_index: [F; 2], } impl ColumnsEvaluated for RingEvaluations { diff --git a/w3f-ring-vrf-snark/src/piop/prover.rs b/w3f-ring-vrf-snark/src/piop/prover.rs index 025ab36..dcb418b 100644 --- a/w3f-ring-vrf-snark/src/piop/prover.rs +++ b/w3f-ring-vrf-snark/src/piop/prover.rs @@ -107,10 +107,6 @@ impl> PiopProver { &domain, ); - let pk_index_bool = Booleanity::init(pk_index.clone()); - let pk_from_index_x = InnerProd::init(pks.xs.clone(), pk_index.col.clone(), &domain); - let pk_from_index_y = InnerProd::init(pks.ys.clone(), pk_index.col.clone(), &domain); - let doublings_of_in_gadget = Doubling::init(vrf_in, &domain); let doublings_of_in = doublings_of_in_gadget.doublings.clone(); let out_from_in = CondAdd::init( @@ -122,6 +118,10 @@ impl> PiopProver { let out_from_in_x = FixedCells::init(out_from_in.acc.xs.clone(), &domain); let out_from_in_y = FixedCells::init(out_from_in.acc.ys.clone(), &domain); + let pk_index_bool = Booleanity::init(pk_index.clone()); + let pk_from_index_x = InnerProd::init(pks.xs.clone(), pk_index.col.clone(), &domain); + let pk_from_index_y = InnerProd::init(pks.ys.clone(), pk_index.col.clone(), &domain); + Self { domain, pks, @@ -217,6 +217,10 @@ where self.out_from_in.acc.xs.evaluate(zeta), self.out_from_in.acc.ys.evaluate(zeta), ]; + let pk_from_index = [ + self.pk_from_index_x.acc.evaluate(zeta), + self.pk_from_index_y.acc.evaluate(zeta), + ]; RingEvaluations { pks, doublings_of_g, @@ -225,6 +229,7 @@ where pk_from_sk, doublings_of_in, out_from_in, + pk_from_index, } } @@ -236,6 +241,7 @@ where self.out_from_in.constraints(), self.out_from_in_x.constraints(), self.out_from_in_y.constraints(), + self.pk_index_bool.constraints(), ] .concat() } @@ -248,6 +254,7 @@ where self.out_from_in.constraints_linearized(zeta), self.out_from_in_x.constraints_linearized(zeta), self.out_from_in_y.constraints_linearized(zeta), + self.pk_index_bool.constraints_linearized(zeta), ] .concat() } diff --git a/w3f-ring-vrf-snark/src/piop/verifier.rs b/w3f-ring-vrf-snark/src/piop/verifier.rs index ee98c95..5ce50ef 100644 --- a/w3f-ring-vrf-snark/src/piop/verifier.rs +++ b/w3f-ring-vrf-snark/src/piop/verifier.rs @@ -9,6 +9,7 @@ use w3f_plonk_common::gadgets::booleanity::BooleanityValues; use w3f_plonk_common::gadgets::ec::te_doubling::DoublingValues; use w3f_plonk_common::gadgets::ec::CondAddValues; use w3f_plonk_common::gadgets::fixed_cells::FixedCellsValues; +use w3f_plonk_common::gadgets::inner_prod::InnerProdValues; use w3f_plonk_common::gadgets::VerifierGadget; use w3f_plonk_common::piop::VerifierPiop; @@ -27,6 +28,11 @@ pub struct PiopVerifier, P: AffineRepr, out_from_in_x: FixedCellsValues, out_from_in_y: FixedCellsValues, + pk_index_bool: BooleanityValues, + // pk_index_unique: InnerProd, //TODO: + pk_from_index_x: InnerProdValues, + pk_from_index_y: InnerProdValues, + // pks_equal // TODO } impl, P: AffineRepr> PiopVerifier { @@ -93,6 +99,23 @@ impl, P: AffineRepr> PiopVerifier l_last: domain_evals.l_last, }; + let pk_index_bool = BooleanityValues { + bits: all_columns_evaluated.pk_index, + }; + // pk_index_unique: InnerProd, //TODO: + let pk_from_index_x = InnerProdValues { + a: all_columns_evaluated.pks[0], + b: all_columns_evaluated.pk_index, + not_last: domain_evals.not_last_row, + acc: all_columns_evaluated.pk_from_index[0], + }; + let pk_from_index_y = InnerProdValues { + a: all_columns_evaluated.pks[1], + b: all_columns_evaluated.pk_index, + not_last: domain_evals.not_last_row, + acc: all_columns_evaluated.pk_from_index[1], + }; + Self { domain_evals, fixed_columns_committed, @@ -103,6 +126,9 @@ impl, P: AffineRepr> PiopVerifier out_from_in, out_from_in_x, out_from_in_y, + pk_index_bool, + pk_from_index_x, + pk_from_index_y, } } } @@ -110,7 +136,7 @@ impl, P: AffineRepr> PiopVerifier impl, Jubjub: TECurveConfig> VerifierPiop for PiopVerifier> { - const N_CONSTRAINTS: usize = 9; + const N_CONSTRAINTS: usize = 10; const N_COLUMNS: usize = 12; fn precommitted_columns(&self) -> Vec { @@ -125,6 +151,7 @@ impl, Jubjub: TECurveConfig> Veri self.out_from_in.evaluate_constraints_main(), self.out_from_in_x.evaluate_constraints_main(), self.out_from_in_y.evaluate_constraints_main(), + self.pk_index_bool.evaluate_constraints_main(), ] .concat() } From fa99361b5e8c70de879b04d8aca4cadfed186f67 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Sat, 1 Mar 2025 22:11:34 +0100 Subject: [PATCH 03/23] pk_from_index constrained --- w3f-ring-vrf-snark/src/piop/mod.rs | 5 +++++ w3f-ring-vrf-snark/src/piop/prover.rs | 14 ++++++++++++-- w3f-ring-vrf-snark/src/piop/verifier.rs | 15 ++++++++++++--- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/w3f-ring-vrf-snark/src/piop/mod.rs b/w3f-ring-vrf-snark/src/piop/mod.rs index c86fcb5..ba1f1bd 100644 --- a/w3f-ring-vrf-snark/src/piop/mod.rs +++ b/w3f-ring-vrf-snark/src/piop/mod.rs @@ -29,6 +29,7 @@ pub struct RingCommitments> { pub(crate) pk_from_sk: [C; 2], pub(crate) doublings_of_in: [C; 2], pub(crate) out_from_in: [C; 2], + pub(crate) pk_from_index: [C; 2], pub(crate) phantom: PhantomData, } @@ -43,6 +44,8 @@ impl> ColumnsCommited for RingCommitments< self.doublings_of_in[1].clone(), self.out_from_in[0].clone(), self.out_from_in[1].clone(), + self.pk_from_index[0].clone(), + self.pk_from_index[1].clone(), ] } } @@ -74,6 +77,8 @@ impl ColumnsEvaluated for RingEvaluations { self.doublings_of_in[1], self.out_from_in[0], self.out_from_in[1], + self.pk_from_index[0], + self.pk_from_index[1], ] } } diff --git a/w3f-ring-vrf-snark/src/piop/prover.rs b/w3f-ring-vrf-snark/src/piop/prover.rs index dcb418b..4c7337f 100644 --- a/w3f-ring-vrf-snark/src/piop/prover.rs +++ b/w3f-ring-vrf-snark/src/piop/prover.rs @@ -169,12 +169,17 @@ where commit(self.out_from_in.acc.xs.as_poly()), commit(self.out_from_in.acc.ys.as_poly()), ]; + let pk_from_index = [ + commit(self.pk_from_index_x.acc.as_poly()), + commit(self.pk_from_index_y.acc.as_poly()), + ]; RingCommitments { sk_bits, pk_index, pk_from_sk, doublings_of_in, out_from_in, + pk_from_index, phantom: Default::default(), } } @@ -236,12 +241,15 @@ where fn constraints(&self) -> Vec> { [ self.sk_bits_bool.constraints(), + self.pk_index_bool.constraints(), self.pk_from_sk.constraints(), self.doublings_of_in_gadget.constraints(), self.out_from_in.constraints(), + self.pk_from_index_x.constraints(), + self.pk_from_index_y.constraints(), self.out_from_in_x.constraints(), self.out_from_in_y.constraints(), - self.pk_index_bool.constraints(), + ] .concat() } @@ -249,12 +257,14 @@ where fn constraints_lin(&self, zeta: &F) -> Vec> { [ self.sk_bits_bool.constraints_linearized(zeta), + self.pk_index_bool.constraints_linearized(zeta), self.pk_from_sk.constraints_linearized(zeta), self.doublings_of_in_gadget.constraints_linearized(zeta), self.out_from_in.constraints_linearized(zeta), + self.pk_from_index_x.constraints_linearized(zeta), + self.pk_from_index_y.constraints_linearized(zeta), self.out_from_in_x.constraints_linearized(zeta), self.out_from_in_y.constraints_linearized(zeta), - self.pk_index_bool.constraints_linearized(zeta), ] .concat() } diff --git a/w3f-ring-vrf-snark/src/piop/verifier.rs b/w3f-ring-vrf-snark/src/piop/verifier.rs index 5ce50ef..bd930ba 100644 --- a/w3f-ring-vrf-snark/src/piop/verifier.rs +++ b/w3f-ring-vrf-snark/src/piop/verifier.rs @@ -136,8 +136,8 @@ impl, P: AffineRepr> PiopVerifier impl, Jubjub: TECurveConfig> VerifierPiop for PiopVerifier> { - const N_CONSTRAINTS: usize = 10; - const N_COLUMNS: usize = 12; + const N_CONSTRAINTS: usize = 12; + const N_COLUMNS: usize = 14; fn precommitted_columns(&self) -> Vec { self.fixed_columns_committed.as_vec() @@ -146,12 +146,14 @@ impl, Jubjub: TECurveConfig> Veri fn evaluate_constraints_main(&self) -> Vec { [ self.sk_bits_bool.evaluate_constraints_main(), + self.pk_index_bool.evaluate_constraints_main(), self.pk_from_sk.evaluate_constraints_main(), self.doublings_of_in_gadget.evaluate_constraints_main(), self.out_from_in.evaluate_constraints_main(), + self.pk_from_index_x.evaluate_constraints_main(), + self.pk_from_index_y.evaluate_constraints_main(), self.out_from_in_x.evaluate_constraints_main(), self.out_from_in_y.evaluate_constraints_main(), - self.pk_index_bool.evaluate_constraints_main(), ] .concat() } @@ -177,6 +179,11 @@ impl, Jubjub: TECurveConfig> Veri let (out_x_coeff, out_y_coeff) = self.out_from_in.acc_coeffs_2(); let out_from_in_c2_lin = out_x.mul(out_x_coeff) + out_y.mul(out_y_coeff); + let pk_from_index_x = &self.witness_columns_committed.pk_from_index[0]; + let pk_from_index_y = &self.witness_columns_committed.pk_from_index[1]; + let pk_from_index_x_lin = pk_from_index_x.mul(self.domain_evals.not_last_row); + let pk_from_index_y_lin = pk_from_index_y.mul(self.domain_evals.not_last_row); + vec![ pk_from_sk_c1_lin, pk_from_sk_c2_lin, @@ -184,6 +191,8 @@ impl, Jubjub: TECurveConfig> Veri doublings_of_in_lin[1].clone(), out_from_in_c1_lin, out_from_in_c2_lin, + pk_from_index_x_lin, + pk_from_index_y_lin, ] } From 4daec3b32210d1a62efbc7d4cde1d7f04545fdf7 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Fri, 14 Mar 2025 12:10:58 +0300 Subject: [PATCH 04/23] warning fixed --- w3f-ring-vrf-snark/src/piop/params.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/w3f-ring-vrf-snark/src/piop/params.rs b/w3f-ring-vrf-snark/src/piop/params.rs index 95bb274..77b053e 100644 --- a/w3f-ring-vrf-snark/src/piop/params.rs +++ b/w3f-ring-vrf-snark/src/piop/params.rs @@ -20,8 +20,6 @@ pub struct PiopParams> { pub keyset_part_size: usize, /// The generator used to compute public keys, `pk=sk.G`. pub(crate) g: Affine, - /// Blinding base point. - pub(crate) h: Affine, /// Summation base point. pub(crate) seed: Affine, /// The point used to pad the list of public keys. @@ -39,7 +37,7 @@ impl> PiopParams { /// All points should be of an unknown discrete log. pub fn setup( domain: Domain, - h: Affine, + _h: Affine, //TODO: cleanup seed: Affine, padding: Affine, ) -> Self { @@ -51,7 +49,6 @@ impl> PiopParams { scalar_bitlen, keyset_part_size, g: Affine::::generator(), - h, seed, padding, } From c46e8852c83574837f50f9260e8a9662b6a07d9a Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Fri, 14 Mar 2025 12:13:49 +0300 Subject: [PATCH 05/23] warning fixed - 2 --- w3f-plonk-common/src/prover.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/w3f-plonk-common/src/prover.rs b/w3f-plonk-common/src/prover.rs index 2eff75f..6dc96be 100644 --- a/w3f-plonk-common/src/prover.rs +++ b/w3f-plonk-common/src/prover.rs @@ -68,8 +68,6 @@ impl, T: PlonkTranscript> PlonkProver let lin_at_zeta_omega = lin.evaluate(&zeta_omega); transcript.add_evaluations(&columns_at_zeta, &lin_at_zeta_omega); - println!("z= {}", zeta); - let polys_at_zeta = [columns_to_open, vec![quotient_poly]].concat(); let nus = transcript.get_kzg_aggregation_challenges(polys_at_zeta.len()); let agg_at_zeta = aggregate_polys(&polys_at_zeta, &nus); From a7e117c996022d1c776e83b80a4cd487e97c547a Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Fri, 14 Mar 2025 13:27:05 +0300 Subject: [PATCH 06/23] nostd build fixed --- w3f-ring-vrf-snark/src/piop/prover.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/w3f-ring-vrf-snark/src/piop/prover.rs b/w3f-ring-vrf-snark/src/piop/prover.rs index ddc6414..ad97094 100644 --- a/w3f-ring-vrf-snark/src/piop/prover.rs +++ b/w3f-ring-vrf-snark/src/piop/prover.rs @@ -3,7 +3,7 @@ use ark_ff::PrimeField; use ark_poly::univariate::DensePolynomial; use ark_poly::Evaluations; use ark_std::{vec, vec::Vec}; -use std::rc::Rc; +use ark_std::rc::Rc; use w3f_pcs::pcs::Commitment; use crate::piop::params::PiopParams; From 6ff0a5d1d8329fa8297572d1cc3002753bdede3f Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Fri, 14 Mar 2025 13:45:56 +0300 Subject: [PATCH 07/23] fmt --- w3f-ring-vrf-snark/src/piop/prover.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/w3f-ring-vrf-snark/src/piop/prover.rs b/w3f-ring-vrf-snark/src/piop/prover.rs index ad97094..549f493 100644 --- a/w3f-ring-vrf-snark/src/piop/prover.rs +++ b/w3f-ring-vrf-snark/src/piop/prover.rs @@ -2,8 +2,8 @@ use ark_ec::twisted_edwards::{Affine, TECurveConfig}; use ark_ff::PrimeField; use ark_poly::univariate::DensePolynomial; use ark_poly::Evaluations; -use ark_std::{vec, vec::Vec}; use ark_std::rc::Rc; +use ark_std::{vec, vec::Vec}; use w3f_pcs::pcs::Commitment; use crate::piop::params::PiopParams; From baff1caa2b2195bc8aab5febab76ccf0f82043d0 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Sun, 16 Mar 2025 23:35:27 +0300 Subject: [PATCH 08/23] pk_from_index computed using ec add --- w3f-ring-vrf-snark/src/piop/prover.rs | 31 ++++++++-------- w3f-ring-vrf-snark/src/piop/verifier.rs | 49 ++++++++++++------------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/w3f-ring-vrf-snark/src/piop/prover.rs b/w3f-ring-vrf-snark/src/piop/prover.rs index 549f493..2337822 100644 --- a/w3f-ring-vrf-snark/src/piop/prover.rs +++ b/w3f-ring-vrf-snark/src/piop/prover.rs @@ -18,7 +18,6 @@ use w3f_plonk_common::gadgets::ProverGadget; use w3f_plonk_common::piop::ProverPiop; use w3f_plonk_common::gadgets::ec::te_doubling::Doubling; -use w3f_plonk_common::gadgets::inner_prod::InnerProd; use w3f_plonk_common::Column; /// The prover's private input is its secret key `sk`. @@ -67,8 +66,7 @@ pub struct PiopProver> { out_from_in_y: FixedCells, pk_index_bool: Booleanity, // pk_index_unique: InnerProd, //TODO: - pk_from_index_x: InnerProd, - pk_from_index_y: InnerProd, + pk_from_index: CondAdd>, // TODO: constrain the seed // pks_equal // TODO } @@ -114,8 +112,12 @@ impl> PiopProver { let out_from_in_y = FixedCells::init(out_from_in.acc.ys.clone(), &domain); let pk_index_bool = Booleanity::init(pk_index.clone()); - let pk_from_index_x = InnerProd::init(pks.xs.clone(), pk_index.col.clone(), &domain); - let pk_from_index_y = InnerProd::init(pks.ys.clone(), pk_index.col.clone(), &domain); + let pk_from_index = CondAdd::init( + pk_index.clone(), + pks.clone(), + params.seed, + &domain, + ); Self { domain, @@ -130,8 +132,7 @@ impl> PiopProver { out_from_in_x, out_from_in_y, pk_index_bool, - pk_from_index_x, - pk_from_index_y, + pk_from_index, } } } @@ -165,8 +166,8 @@ where commit(self.out_from_in.acc.ys.as_poly()), ]; let pk_from_index = [ - commit(self.pk_from_index_x.acc.as_poly()), - commit(self.pk_from_index_y.acc.as_poly()), + commit(self.pk_from_index.acc.xs.as_poly()), + commit(self.pk_from_index.acc.ys.as_poly()), ]; RingCommitments { sk_bits, @@ -191,6 +192,8 @@ where self.doublings_of_in_gadget.doublings.ys.as_poly().clone(), self.out_from_in.acc.xs.as_poly().clone(), self.out_from_in.acc.ys.as_poly().clone(), + self.pk_from_index.acc.xs.as_poly().clone(), + self.pk_from_index.acc.ys.as_poly().clone(), ] } @@ -215,8 +218,8 @@ where self.out_from_in.acc.ys.evaluate(zeta), ]; let pk_from_index = [ - self.pk_from_index_x.acc.evaluate(zeta), - self.pk_from_index_y.acc.evaluate(zeta), + self.pk_from_index.acc.xs.evaluate(zeta), + self.pk_from_index.acc.ys.evaluate(zeta), ]; RingEvaluations { pks, @@ -237,8 +240,7 @@ where self.pk_from_sk.constraints(), self.doublings_of_in_gadget.constraints(), self.out_from_in.constraints(), - self.pk_from_index_x.constraints(), - self.pk_from_index_y.constraints(), + self.pk_from_index.constraints(), self.out_from_in_x.constraints(), self.out_from_in_y.constraints(), ] @@ -252,8 +254,7 @@ where self.pk_from_sk.constraints_linearized(zeta), self.doublings_of_in_gadget.constraints_linearized(zeta), self.out_from_in.constraints_linearized(zeta), - self.pk_from_index_x.constraints_linearized(zeta), - self.pk_from_index_y.constraints_linearized(zeta), + self.pk_from_index.constraints_linearized(zeta), self.out_from_in_x.constraints_linearized(zeta), self.out_from_in_y.constraints_linearized(zeta), ] diff --git a/w3f-ring-vrf-snark/src/piop/verifier.rs b/w3f-ring-vrf-snark/src/piop/verifier.rs index 86b6b46..b63ce6e 100644 --- a/w3f-ring-vrf-snark/src/piop/verifier.rs +++ b/w3f-ring-vrf-snark/src/piop/verifier.rs @@ -9,7 +9,6 @@ use w3f_plonk_common::gadgets::booleanity::BooleanityValues; use w3f_plonk_common::gadgets::ec::te_doubling::DoublingValues; use w3f_plonk_common::gadgets::ec::CondAddValues; use w3f_plonk_common::gadgets::fixed_cells::FixedCellsValues; -use w3f_plonk_common::gadgets::inner_prod::InnerProdValues; use w3f_plonk_common::gadgets::VerifierGadget; use w3f_plonk_common::piop::VerifierPiop; @@ -30,8 +29,7 @@ pub struct PiopVerifier, P: AffineRepr, pk_index_bool: BooleanityValues, // pk_index_unique: InnerProd, //TODO: - pk_from_index_x: InnerProdValues, - pk_from_index_y: InnerProdValues, + pk_from_index: CondAddValues, // pks_equal // TODO } @@ -103,17 +101,18 @@ impl, P: AffineRepr> PiopVerifier bits: all_columns_evaluated.pk_index, }; // pk_index_unique: InnerProd, //TODO: - let pk_from_index_x = InnerProdValues { - a: all_columns_evaluated.pks[0], - b: all_columns_evaluated.pk_index, - not_last: domain_evals.not_last_row, - acc: all_columns_evaluated.pk_from_index[0], - }; - let pk_from_index_y = InnerProdValues { - a: all_columns_evaluated.pks[1], - b: all_columns_evaluated.pk_index, + let pk_from_index = CondAddValues { + bitmask: all_columns_evaluated.pk_index, + points: ( + all_columns_evaluated.pks[0], + all_columns_evaluated.pks[1], + ), not_last: domain_evals.not_last_row, - acc: all_columns_evaluated.pk_from_index[1], + acc: ( + all_columns_evaluated.pk_from_index[0], + all_columns_evaluated.pk_from_index[1], + ), + _phantom: Default::default(), }; Self { @@ -127,8 +126,7 @@ impl, P: AffineRepr> PiopVerifier out_from_in_x, out_from_in_y, pk_index_bool, - pk_from_index_x, - pk_from_index_y, + pk_from_index, } } } @@ -150,8 +148,7 @@ impl, Jubjub: TECurveConfig> Veri self.pk_from_sk.evaluate_constraints_main(), self.doublings_of_in_gadget.evaluate_constraints_main(), self.out_from_in.evaluate_constraints_main(), - self.pk_from_index_x.evaluate_constraints_main(), - self.pk_from_index_y.evaluate_constraints_main(), + self.pk_from_index.evaluate_constraints_main(), self.out_from_in_x.evaluate_constraints_main(), self.out_from_in_y.evaluate_constraints_main(), ] @@ -159,12 +156,12 @@ impl, Jubjub: TECurveConfig> Veri } fn lin_poly_commitment(&self, agg_coeffs: &[F]) -> C { - let pk_x = &self.witness_columns_committed.pk_from_sk[0]; - let pk_y = &self.witness_columns_committed.pk_from_sk[1]; + let pk_from_sk_x = &self.witness_columns_committed.pk_from_sk[0]; + let pk_from_sk_y = &self.witness_columns_committed.pk_from_sk[1]; let (pk_x_coeff, pk_y_coeff) = self.pk_from_sk.acc_coeffs_1(); - let pk_from_sk_c1_lin = pk_x.mul(pk_x_coeff) + pk_y.mul(pk_y_coeff); + let pk_from_sk_c1_lin = pk_from_sk_x.mul(pk_x_coeff) + pk_from_sk_y.mul(pk_y_coeff); let (pk_x_coeff, pk_y_coeff) = self.pk_from_sk.acc_coeffs_2(); - let pk_from_sk_c2_lin = pk_x.mul(pk_x_coeff) + pk_y.mul(pk_y_coeff); + let pk_from_sk_c2_lin = pk_from_sk_x.mul(pk_x_coeff) + pk_from_sk_y.mul(pk_y_coeff); let doublings_of_in_x = self.witness_columns_committed.doublings_of_in[0].clone(); let doublings_of_in_y = self.witness_columns_committed.doublings_of_in[1].clone(); @@ -181,8 +178,10 @@ impl, Jubjub: TECurveConfig> Veri let pk_from_index_x = &self.witness_columns_committed.pk_from_index[0]; let pk_from_index_y = &self.witness_columns_committed.pk_from_index[1]; - let pk_from_index_x_lin = pk_from_index_x.mul(self.domain_evals.not_last_row); - let pk_from_index_y_lin = pk_from_index_y.mul(self.domain_evals.not_last_row); + let (pk_x_coeff, pk_y_coeff) = self.pk_from_index.acc_coeffs_1(); + let pk_from_index_c1_lin = pk_from_index_x.mul(pk_x_coeff) + pk_from_index_y.mul(pk_y_coeff); + let (pk_x_coeff, pk_y_coeff) = self.pk_from_index.acc_coeffs_2(); + let pk_from_index_c2_lin = pk_from_index_x.mul(pk_x_coeff) + pk_from_index_y.mul(pk_y_coeff); let per_constraint = vec![ pk_from_sk_c1_lin, @@ -191,8 +190,8 @@ impl, Jubjub: TECurveConfig> Veri doublings_of_in_lin[1].clone(), out_from_in_c1_lin, out_from_in_c2_lin, - pk_from_index_x_lin, - pk_from_index_y_lin, + pk_from_index_c1_lin, + pk_from_index_c2_lin, ]; // TODO: optimize muls From 94ce2e2bc0f99b61a06fd26ad578d43f66c2e54c Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Mon, 17 Mar 2025 05:47:00 +0300 Subject: [PATCH 09/23] test fixed --- w3f-ring-vrf-snark/src/lib.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/w3f-ring-vrf-snark/src/lib.rs b/w3f-ring-vrf-snark/src/lib.rs index 1029632..e20414d 100644 --- a/w3f-ring-vrf-snark/src/lib.rs +++ b/w3f-ring-vrf-snark/src/lib.rs @@ -66,15 +66,18 @@ mod tests { let max_keyset_size = piop_params.keyset_part_size; let keyset_size: usize = rng.gen_range(0..max_keyset_size); - let pks = random_vec::(keyset_size, rng); + let mut pks = random_vec::(keyset_size, rng); let k = rng.gen_range(0..keyset_size); // prover's secret index + let sk = Fr::rand(rng); // prover's secret scalar + let pk = piop_params.g.mul(sk).into_affine(); + pks[k] = pk; let (prover_key, verifier_key) = index::<_, CS, _>(&pcs_params, &piop_params, &pks); // PROOF generation - let secret = Fr::rand(rng); // prover's secret scalar + let vrf_input = EdwardsAffine::rand(rng); - let vrf_output = vrf_input.mul(secret).into_affine(); + let vrf_output = vrf_input.mul(sk).into_affine(); let ring_prover = RingProver::init( prover_key, piop_params.clone(), @@ -82,7 +85,7 @@ mod tests { ArkTranscript::new(b"w3f-ring-vrf-snark-test"), ); let t_prove = start_timer!(|| "Prove"); - let (proof, res) = ring_prover.prove(secret, vrf_input); + let (proof, res) = ring_prover.prove(sk, vrf_input); end_timer!(t_prove); assert_eq!(res, vrf_output); From a7021834c9cfae975752621855da33bf0b488a1f Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Mon, 17 Mar 2025 05:47:19 +0300 Subject: [PATCH 10/23] FieldColumn fields made pub --- w3f-plonk-common/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/w3f-plonk-common/src/lib.rs b/w3f-plonk-common/src/lib.rs index 26ac351..87208d9 100644 --- a/w3f-plonk-common/src/lib.rs +++ b/w3f-plonk-common/src/lib.rs @@ -31,10 +31,10 @@ pub trait Column { #[derive(Clone)] pub struct FieldColumn { // actual (constrained) len of the input in evaluation form - len: usize, - poly: DensePolynomial, - evals: Evaluations, - evals_4x: Evaluations, + pub len: usize, + pub poly: DensePolynomial, + pub evals: Evaluations, + pub evals_4x: Evaluations, } impl FieldColumn { From 7b6df3318cb8b369d86eab30d9ba2f8d1cf07fc3 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Mon, 17 Mar 2025 05:47:33 +0300 Subject: [PATCH 11/23] cell equality gadget added --- w3f-ring-vrf-snark/src/piop/cell_equality.rs | 56 ++++++++++++++++++++ w3f-ring-vrf-snark/src/piop/mod.rs | 1 + w3f-ring-vrf-snark/src/piop/prover.rs | 21 +++++++- w3f-ring-vrf-snark/src/piop/verifier.rs | 21 +++++++- 4 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 w3f-ring-vrf-snark/src/piop/cell_equality.rs diff --git a/w3f-ring-vrf-snark/src/piop/cell_equality.rs b/w3f-ring-vrf-snark/src/piop/cell_equality.rs new file mode 100644 index 0000000..eb4e385 --- /dev/null +++ b/w3f-ring-vrf-snark/src/piop/cell_equality.rs @@ -0,0 +1,56 @@ +use ark_ff::{FftField, Field, Zero}; +use ark_poly::univariate::DensePolynomial; +use ark_poly::Evaluations; +use ark_std::rc::Rc; +use ark_std::{vec, vec::Vec}; + +use w3f_plonk_common::domain::Domain; +use w3f_plonk_common::gadgets::VerifierGadget; +use w3f_plonk_common::FieldColumn; + +pub struct CellEqualityPolys { + a: Rc>, + b: Rc>, + l_last: FieldColumn, +} + +pub struct CellEqualityEvals { + pub a: F, + pub b: F, + pub l_last: F, +} + +impl CellEqualityPolys { + pub fn init(a: Rc>, b: Rc>, domain: &Domain) -> Self { + assert_eq!(a.len, domain.capacity); + assert_eq!(b.len, domain.capacity); + let a_last = a.evals.evals[domain.capacity - 1]; + let b_last = b.evals.evals[domain.capacity - 1]; + assert_eq!(a_last, b_last); + let l_last = domain.l_last.clone(); + Self { + a, + b, + l_last, + } + } + + pub fn constraints(&self) -> Vec> { + let a = &self.a.evals_4x; + let b = &self.b.evals_4x; + let l_last = &self.l_last.evals_4x; + let c = l_last * &(a - b); + vec![c] + } + + pub fn constraints_linearized(&self, _z: &F) -> Vec> { + vec![DensePolynomial::zero()] + } +} + +impl VerifierGadget for CellEqualityEvals { + fn evaluate_constraints_main(&self) -> Vec { + let c = self.l_last * (self.a - self.b); + vec![c] + } +} diff --git a/w3f-ring-vrf-snark/src/piop/mod.rs b/w3f-ring-vrf-snark/src/piop/mod.rs index ba1f1bd..abec210 100644 --- a/w3f-ring-vrf-snark/src/piop/mod.rs +++ b/w3f-ring-vrf-snark/src/piop/mod.rs @@ -20,6 +20,7 @@ use crate::PiopParams; pub mod params; mod prover; mod verifier; +mod cell_equality; #[derive(Clone, CanonicalSerialize, CanonicalDeserialize)] pub struct RingCommitments> { diff --git a/w3f-ring-vrf-snark/src/piop/prover.rs b/w3f-ring-vrf-snark/src/piop/prover.rs index 2337822..1460b67 100644 --- a/w3f-ring-vrf-snark/src/piop/prover.rs +++ b/w3f-ring-vrf-snark/src/piop/prover.rs @@ -19,6 +19,7 @@ use w3f_plonk_common::piop::ProverPiop; use w3f_plonk_common::gadgets::ec::te_doubling::Doubling; use w3f_plonk_common::Column; +use crate::piop::cell_equality::CellEqualityPolys; /// The prover's private input is its secret key `sk`. /// The public inputs are: @@ -67,7 +68,8 @@ pub struct PiopProver> { pk_index_bool: Booleanity, // pk_index_unique: InnerProd, //TODO: pk_from_index: CondAdd>, // TODO: constrain the seed - // pks_equal // TODO + pks_equal_x: CellEqualityPolys, + pks_equal_y: CellEqualityPolys, } impl> PiopProver { @@ -119,6 +121,17 @@ impl> PiopProver { &domain, ); + let pks_equal_x = CellEqualityPolys::init( + pk_from_index.acc.xs.clone(), + pk_from_sk.acc.xs.clone(), + &domain + ); + let pks_equal_y = CellEqualityPolys::init( + pk_from_index.acc.ys.clone(), + pk_from_sk.acc.ys.clone(), + &domain + ); + Self { domain, pks, @@ -133,6 +146,8 @@ impl> PiopProver { out_from_in_y, pk_index_bool, pk_from_index, + pks_equal_x, + pks_equal_y, } } } @@ -243,6 +258,8 @@ where self.pk_from_index.constraints(), self.out_from_in_x.constraints(), self.out_from_in_y.constraints(), + self.pks_equal_x.constraints(), + self.pks_equal_y.constraints(), ] .concat() } @@ -257,6 +274,8 @@ where self.pk_from_index.constraints_linearized(zeta), self.out_from_in_x.constraints_linearized(zeta), self.out_from_in_y.constraints_linearized(zeta), + self.pks_equal_x.constraints_linearized(zeta), + self.pks_equal_y.constraints_linearized(zeta), ] .concat() } diff --git a/w3f-ring-vrf-snark/src/piop/verifier.rs b/w3f-ring-vrf-snark/src/piop/verifier.rs index b63ce6e..f7d816e 100644 --- a/w3f-ring-vrf-snark/src/piop/verifier.rs +++ b/w3f-ring-vrf-snark/src/piop/verifier.rs @@ -13,6 +13,7 @@ use w3f_plonk_common::gadgets::VerifierGadget; use w3f_plonk_common::piop::VerifierPiop; use crate::piop::{FixedColumnsCommitted, RingCommitments}; +use crate::piop::cell_equality::CellEqualityEvals; use crate::RingEvaluations; pub struct PiopVerifier, P: AffineRepr> { @@ -30,7 +31,8 @@ pub struct PiopVerifier, P: AffineRepr, // pk_index_unique: InnerProd, //TODO: pk_from_index: CondAddValues, - // pks_equal // TODO + pks_equal_x: CellEqualityEvals, + pks_equal_y: CellEqualityEvals, } impl, P: AffineRepr> PiopVerifier { @@ -115,6 +117,17 @@ impl, P: AffineRepr> PiopVerifier _phantom: Default::default(), }; + let pks_equal_x = CellEqualityEvals { + a: all_columns_evaluated.pk_from_index[0], + b: all_columns_evaluated.pk_from_sk[0], + l_last: domain_evals.l_last, + }; + let pks_equal_y = CellEqualityEvals { + a: all_columns_evaluated.pk_from_index[1], + b: all_columns_evaluated.pk_from_sk[1], + l_last: domain_evals.l_last, + }; + Self { domain_evals, fixed_columns_committed, @@ -127,6 +140,8 @@ impl, P: AffineRepr> PiopVerifier out_from_in_y, pk_index_bool, pk_from_index, + pks_equal_x, + pks_equal_y, } } } @@ -134,7 +149,7 @@ impl, P: AffineRepr> PiopVerifier impl, Jubjub: TECurveConfig> VerifierPiop for PiopVerifier> { - const N_CONSTRAINTS: usize = 12; + const N_CONSTRAINTS: usize = 14; const N_COLUMNS: usize = 14; fn precommitted_columns(&self) -> Vec { @@ -151,6 +166,8 @@ impl, Jubjub: TECurveConfig> Veri self.pk_from_index.evaluate_constraints_main(), self.out_from_in_x.evaluate_constraints_main(), self.out_from_in_y.evaluate_constraints_main(), + self.pks_equal_x.evaluate_constraints_main(), + self.pks_equal_y.evaluate_constraints_main(), ] .concat() } From 3f68c4d3f048a79d8b74df5d5a7488a8a473986e Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Mon, 17 Mar 2025 05:47:57 +0300 Subject: [PATCH 12/23] fmt --- w3f-ring-vrf-snark/src/piop/cell_equality.rs | 6 +----- w3f-ring-vrf-snark/src/piop/mod.rs | 2 +- w3f-ring-vrf-snark/src/piop/prover.rs | 13 ++++--------- w3f-ring-vrf-snark/src/piop/verifier.rs | 13 ++++++------- 4 files changed, 12 insertions(+), 22 deletions(-) diff --git a/w3f-ring-vrf-snark/src/piop/cell_equality.rs b/w3f-ring-vrf-snark/src/piop/cell_equality.rs index eb4e385..00b9c6e 100644 --- a/w3f-ring-vrf-snark/src/piop/cell_equality.rs +++ b/w3f-ring-vrf-snark/src/piop/cell_equality.rs @@ -28,11 +28,7 @@ impl CellEqualityPolys { let b_last = b.evals.evals[domain.capacity - 1]; assert_eq!(a_last, b_last); let l_last = domain.l_last.clone(); - Self { - a, - b, - l_last, - } + Self { a, b, l_last } } pub fn constraints(&self) -> Vec> { diff --git a/w3f-ring-vrf-snark/src/piop/mod.rs b/w3f-ring-vrf-snark/src/piop/mod.rs index abec210..9dc47c1 100644 --- a/w3f-ring-vrf-snark/src/piop/mod.rs +++ b/w3f-ring-vrf-snark/src/piop/mod.rs @@ -17,10 +17,10 @@ use w3f_plonk_common::{Column, ColumnsCommited, ColumnsEvaluated}; use crate::PiopParams; +mod cell_equality; pub mod params; mod prover; mod verifier; -mod cell_equality; #[derive(Clone, CanonicalSerialize, CanonicalDeserialize)] pub struct RingCommitments> { diff --git a/w3f-ring-vrf-snark/src/piop/prover.rs b/w3f-ring-vrf-snark/src/piop/prover.rs index 1460b67..f24ce23 100644 --- a/w3f-ring-vrf-snark/src/piop/prover.rs +++ b/w3f-ring-vrf-snark/src/piop/prover.rs @@ -17,9 +17,9 @@ use w3f_plonk_common::gadgets::fixed_cells::FixedCells; use w3f_plonk_common::gadgets::ProverGadget; use w3f_plonk_common::piop::ProverPiop; +use crate::piop::cell_equality::CellEqualityPolys; use w3f_plonk_common::gadgets::ec::te_doubling::Doubling; use w3f_plonk_common::Column; -use crate::piop::cell_equality::CellEqualityPolys; /// The prover's private input is its secret key `sk`. /// The public inputs are: @@ -114,22 +114,17 @@ impl> PiopProver { let out_from_in_y = FixedCells::init(out_from_in.acc.ys.clone(), &domain); let pk_index_bool = Booleanity::init(pk_index.clone()); - let pk_from_index = CondAdd::init( - pk_index.clone(), - pks.clone(), - params.seed, - &domain, - ); + let pk_from_index = CondAdd::init(pk_index.clone(), pks.clone(), params.seed, &domain); let pks_equal_x = CellEqualityPolys::init( pk_from_index.acc.xs.clone(), pk_from_sk.acc.xs.clone(), - &domain + &domain, ); let pks_equal_y = CellEqualityPolys::init( pk_from_index.acc.ys.clone(), pk_from_sk.acc.ys.clone(), - &domain + &domain, ); Self { diff --git a/w3f-ring-vrf-snark/src/piop/verifier.rs b/w3f-ring-vrf-snark/src/piop/verifier.rs index f7d816e..de7f4df 100644 --- a/w3f-ring-vrf-snark/src/piop/verifier.rs +++ b/w3f-ring-vrf-snark/src/piop/verifier.rs @@ -12,8 +12,8 @@ use w3f_plonk_common::gadgets::fixed_cells::FixedCellsValues; use w3f_plonk_common::gadgets::VerifierGadget; use w3f_plonk_common::piop::VerifierPiop; -use crate::piop::{FixedColumnsCommitted, RingCommitments}; use crate::piop::cell_equality::CellEqualityEvals; +use crate::piop::{FixedColumnsCommitted, RingCommitments}; use crate::RingEvaluations; pub struct PiopVerifier, P: AffineRepr> { @@ -105,10 +105,7 @@ impl, P: AffineRepr> PiopVerifier // pk_index_unique: InnerProd, //TODO: let pk_from_index = CondAddValues { bitmask: all_columns_evaluated.pk_index, - points: ( - all_columns_evaluated.pks[0], - all_columns_evaluated.pks[1], - ), + points: (all_columns_evaluated.pks[0], all_columns_evaluated.pks[1]), not_last: domain_evals.not_last_row, acc: ( all_columns_evaluated.pk_from_index[0], @@ -196,9 +193,11 @@ impl, Jubjub: TECurveConfig> Veri let pk_from_index_x = &self.witness_columns_committed.pk_from_index[0]; let pk_from_index_y = &self.witness_columns_committed.pk_from_index[1]; let (pk_x_coeff, pk_y_coeff) = self.pk_from_index.acc_coeffs_1(); - let pk_from_index_c1_lin = pk_from_index_x.mul(pk_x_coeff) + pk_from_index_y.mul(pk_y_coeff); + let pk_from_index_c1_lin = + pk_from_index_x.mul(pk_x_coeff) + pk_from_index_y.mul(pk_y_coeff); let (pk_x_coeff, pk_y_coeff) = self.pk_from_index.acc_coeffs_2(); - let pk_from_index_c2_lin = pk_from_index_x.mul(pk_x_coeff) + pk_from_index_y.mul(pk_y_coeff); + let pk_from_index_c2_lin = + pk_from_index_x.mul(pk_x_coeff) + pk_from_index_y.mul(pk_y_coeff); let per_constraint = vec![ pk_from_sk_c1_lin, From 86c6b8ceac053c103ac47981566f387869f6fda2 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Wed, 19 Mar 2025 03:31:13 +0300 Subject: [PATCH 13/23] FixedCells gadget refactored --- w3f-plonk-common/src/gadgets/fixed_cells.rs | 39 ++++++++++++--------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/w3f-plonk-common/src/gadgets/fixed_cells.rs b/w3f-plonk-common/src/gadgets/fixed_cells.rs index a08c2c9..4d646f6 100644 --- a/w3f-plonk-common/src/gadgets/fixed_cells.rs +++ b/w3f-plonk-common/src/gadgets/fixed_cells.rs @@ -10,8 +10,6 @@ use crate::{const_evals, Column, FieldColumn}; pub struct FixedCells { col: Rc>, - col_first: F, - col_last: F, l_first: FieldColumn, l_last: FieldColumn, } @@ -27,40 +25,49 @@ pub struct FixedCellsValues { impl FixedCells { pub fn init(col: Rc>, domain: &Domain) -> Self { assert_eq!(col.len, domain.capacity); - let col_first = col.evals.evals[0]; - let col_last = col.evals.evals[domain.capacity - 1]; let l_first = domain.l_first.clone(); let l_last = domain.l_last.clone(); Self { col, - col_first, - col_last, l_first, l_last, } } pub fn constraints(&self) -> Vec> { - let col = &self.col; - let domain = col.domain_4x(); - let first = &const_evals(self.col_first, domain); - let last = &const_evals(self.col_last, domain); - let col = &self.col.evals_4x; - let l_first = &self.l_first.evals_4x; - let l_last = &self.l_last.evals_4x; - let c = &(l_first * &(col - first)) + &(l_last * &(col - last)); + let domain_capacity = self.col.len; // that's an ugly way to learn the capacity, but we've asserted it above. + let c = &Self::constraint_cell(&self.col, &self.l_first, 0) + + &Self::constraint_cell(&self.col, &self.l_last, domain_capacity - 1); vec![c] } pub fn constraints_linearized(&self, _z: &F) -> Vec> { vec![DensePolynomial::zero()] } + + /// Constraints the column `col` to have the value `col[i]` at index `i`. + /// `li` should be the `i-th` Lagrange basis polynomial `li = L_i(X)`. + /// The constraint polynomial is `c(X) = L_i(X).col(X) - col[i].L_i(X)`. + pub fn constraint_cell(col: &FieldColumn, li: &FieldColumn, i: usize) -> Evaluations { + let cell_val = col.evals[i]; + let domain = col.domain_4x(); + let cell_val = &const_evals(cell_val, domain); + let col = &col.evals_4x; + let li = &li.evals_4x; + li * &(col - cell_val) + } +} + +impl FixedCellsValues { + pub fn evaluate_for_cell(col_eval: F, li_eval: F, cell_val: F) -> F { + li_eval * (col_eval - cell_val) + } } impl VerifierGadget for FixedCellsValues { fn evaluate_constraints_main(&self) -> Vec { - let c = - (self.col - self.col_first) * self.l_first + (self.col - self.col_last) * self.l_last; + let c = Self::evaluate_for_cell(self.col, self.l_first, self.col_first) + + Self::evaluate_for_cell(self.col, self.l_last, self.col_last); vec![c] } } From 150f551a8ee4c8f3f17b50bf06848207faf85bb2 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Wed, 19 Mar 2025 03:32:06 +0300 Subject: [PATCH 14/23] input cells constrained --- w3f-ring-vrf-snark/src/piop/prover.rs | 24 ++++++++++++++++++++++-- w3f-ring-vrf-snark/src/piop/verifier.rs | 16 ++++++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/w3f-ring-vrf-snark/src/piop/prover.rs b/w3f-ring-vrf-snark/src/piop/prover.rs index f24ce23..3f77556 100644 --- a/w3f-ring-vrf-snark/src/piop/prover.rs +++ b/w3f-ring-vrf-snark/src/piop/prover.rs @@ -1,5 +1,5 @@ use ark_ec::twisted_edwards::{Affine, TECurveConfig}; -use ark_ff::PrimeField; +use ark_ff::{PrimeField, Zero}; use ark_poly::univariate::DensePolynomial; use ark_poly::Evaluations; use ark_std::rc::Rc; @@ -52,6 +52,14 @@ use w3f_plonk_common::Column; /// | $k$ | $pk_x$ | $pk_y$ | $acc_{pk_x}$ | $acc_{pk_y}$ | $sk$ | $2^iG$x2 | $acc_{sk}$x2 | $2^iH$x2 | $acc_{out}$x2 | /// | -------- | -------- | -------- | -- | - | - | - | - | - | - | /// | signer's index | x of pubkey | y of pubkey | $\sum k_ipk_x$ | $\sum k_ipk_y$ | binary rep of secret key | | $\sum sk_i2^iG$ | | $\sum sk_i2^iH$ | + +/// Verifier's input is: +/// - the `seed` to seed the `pk_from_sk`, `pk_from_index`, and `out_from_in` additions, +/// - `vrf_in` to seed the `doublings_of_in_gadget`, +/// - `vrf_out = sk.vrf_in + seed` -- the result of `out_from_in`, +/// and also the commitments to +/// - the ring -- the list of public keys enrolled, +/// - the doublings `g, 2g, 4g, ...` of the generator `g` (such that `pk = sk.g`). pub struct PiopProver> { domain: Domain, pks: Rc>>, @@ -93,7 +101,7 @@ impl> PiopProver { let mut sk_bits = params.sk_bits(sk); //TODO: return right thing assert!(sk_bits.len() <= domain.capacity - 1); sk_bits.resize(domain.capacity - 1, false); - let sk_bits = BitColumn::init(sk_bits, ¶ms.domain); + let sk_bits = BitColumn::init(sk_bits, &domain); Rc::new(sk_bits) }; let pk_index = Rc::new(params.pk_index_col(pk_index)); @@ -255,6 +263,12 @@ where self.out_from_in_y.constraints(), self.pks_equal_x.constraints(), self.pks_equal_y.constraints(), + vec![FixedCells::constraint_cell(&self.pk_from_sk.acc.xs, &self.domain.l_first, 0)], + vec![FixedCells::constraint_cell(&self.pk_from_sk.acc.ys, &self.domain.l_first, 0)], + vec![FixedCells::constraint_cell(&self.pk_from_index.acc.xs, &self.domain.l_first, 0)], + vec![FixedCells::constraint_cell(&self.pk_from_index.acc.ys, &self.domain.l_first, 0)], + vec![FixedCells::constraint_cell(&self.doublings_of_in_gadget.doublings.xs, &self.domain.l_first, 0)], + vec![FixedCells::constraint_cell(&self.doublings_of_in_gadget.doublings.ys, &self.domain.l_first, 0)], ] .concat() } @@ -271,6 +285,12 @@ where self.out_from_in_y.constraints_linearized(zeta), self.pks_equal_x.constraints_linearized(zeta), self.pks_equal_y.constraints_linearized(zeta), + vec![DensePolynomial::zero()], + vec![DensePolynomial::zero()], + vec![DensePolynomial::zero()], + vec![DensePolynomial::zero()], + vec![DensePolynomial::zero()], + vec![DensePolynomial::zero()], ] .concat() } diff --git a/w3f-ring-vrf-snark/src/piop/verifier.rs b/w3f-ring-vrf-snark/src/piop/verifier.rs index de7f4df..1236081 100644 --- a/w3f-ring-vrf-snark/src/piop/verifier.rs +++ b/w3f-ring-vrf-snark/src/piop/verifier.rs @@ -33,6 +33,10 @@ pub struct PiopVerifier, P: AffineRepr, pks_equal_x: CellEqualityEvals, pks_equal_y: CellEqualityEvals, + + //TODO: params? + seed: (F, F), + vrf_in: (F, F), } impl, P: AffineRepr> PiopVerifier { @@ -42,7 +46,7 @@ impl, P: AffineRepr> PiopVerifier witness_columns_committed: RingCommitments, all_columns_evaluated: RingEvaluations, seed: (F, F), - _vrf_in: (F, F), //TODO + vrf_in: (F, F), vrf_out: (F, F), ) -> Self { let sk_bits_bool = BooleanityValues { @@ -139,6 +143,8 @@ impl, P: AffineRepr> PiopVerifier pk_from_index, pks_equal_x, pks_equal_y, + seed, + vrf_in, } } } @@ -146,7 +152,7 @@ impl, P: AffineRepr> PiopVerifier impl, Jubjub: TECurveConfig> VerifierPiop for PiopVerifier> { - const N_CONSTRAINTS: usize = 14; + const N_CONSTRAINTS: usize = 20; const N_COLUMNS: usize = 14; fn precommitted_columns(&self) -> Vec { @@ -165,6 +171,12 @@ impl, Jubjub: TECurveConfig> Veri self.out_from_in_y.evaluate_constraints_main(), self.pks_equal_x.evaluate_constraints_main(), self.pks_equal_y.evaluate_constraints_main(), + vec![FixedCellsValues::evaluate_for_cell(self.pk_from_sk.acc.0, self.domain_evals.l_first, self.seed.0)], + vec![FixedCellsValues::evaluate_for_cell(self.pk_from_sk.acc.1, self.domain_evals.l_first, self.seed.1)], + vec![FixedCellsValues::evaluate_for_cell(self.pk_from_index.acc.0, self.domain_evals.l_first, self.seed.0)], + vec![FixedCellsValues::evaluate_for_cell(self.pk_from_index.acc.1, self.domain_evals.l_first, self.seed.1)], + vec![FixedCellsValues::evaluate_for_cell(self.doublings_of_in_gadget.doublings.0, self.domain_evals.l_first, self.vrf_in.0)], + vec![FixedCellsValues::evaluate_for_cell(self.doublings_of_in_gadget.doublings.1, self.domain_evals.l_first, self.vrf_in.1)], ] .concat() } From 1d56701f2154e37a1b4d9380c10479c2af7f91d7 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Thu, 20 Mar 2025 00:48:22 +0300 Subject: [PATCH 15/23] column_sum gadget --- w3f-plonk-common/src/gadgets/column_sum.rs | 122 +++++++++++++++++++++ w3f-plonk-common/src/gadgets/mod.rs | 1 + 2 files changed, 123 insertions(+) create mode 100644 w3f-plonk-common/src/gadgets/column_sum.rs diff --git a/w3f-plonk-common/src/gadgets/column_sum.rs b/w3f-plonk-common/src/gadgets/column_sum.rs new file mode 100644 index 0000000..4a3e263 --- /dev/null +++ b/w3f-plonk-common/src/gadgets/column_sum.rs @@ -0,0 +1,122 @@ +use ark_ff::{FftField, Field}; +use ark_poly::univariate::DensePolynomial; +use ark_poly::{Evaluations, GeneralEvaluationDomain}; +use ark_std::rc::Rc; +use ark_std::{vec, vec::Vec}; + +use crate::domain::Domain; +use crate::gadgets::{ProverGadget, VerifierGadget}; +use crate::{Column, FieldColumn}; + +pub struct ColumnSumPolys { + /// Input column. + /// Should have length `n-1`, where `n` is the domain "capacity" (domain.size - ZK_ROWS): + /// `col[0], ..., col[n-2]` + col: Rc>, + /// Partial sums of `col`: `acc[0] = 0, acc[i] = col[0] + ... + col[i-1], i = 1,...,n-1` + acc: Rc>, + not_last: FieldColumn, +} + +pub struct ColumnSumEvals { + pub col: F, + pub acc: F, + pub not_last: F, +} + +impl ColumnSumPolys { + pub fn init(col: Rc>, domain: &Domain) -> Self { + assert_eq!(col.len, domain.capacity - 1); // last element is not constrained + let partial_sums = Self::partial_sums(col.vals()); + let mut acc = vec![F::zero()]; + acc.extend(partial_sums); + let acc = domain.private_column(acc); + let acc = Rc::new(acc); + Self { + col, + acc, + not_last: domain.not_last_row.clone(), + } + } + + /// Returns `col[0], col[0] + col[1], ..., col[0] + col[1] + ... + col[n-1]`. + fn partial_sums(col: &[F]) -> Vec { + col.iter().scan(F::zero(), |state, &x| { + *state += x; + Some(*state) + }).collect() + } +} + +impl ProverGadget for ColumnSumPolys { + fn witness_columns(&self) -> Vec> { + vec![self.acc.poly.clone()] + } + + fn constraints(&self) -> Vec> { + let col = &self.col.evals_4x; + let acc = &self.acc.evals_4x; + let acc_shifted = &self.acc.shifted_4x(); + let not_last = &self.not_last.evals_4x; + let c = &(&(acc_shifted - acc) - col) * not_last; + vec![c] + } + + fn constraints_linearized(&self, z: &F) -> Vec> { + let c = &self.acc.poly * self.not_last.evaluate(z); + vec![c] + } + + fn domain(&self) -> GeneralEvaluationDomain { + self.col.evals.domain() + } +} + +impl VerifierGadget for ColumnSumEvals { + fn evaluate_constraints_main(&self) -> Vec { + let c = (-self.acc - self.col) * self.not_last; + vec![c] + } +} + +#[cfg(test)] +mod tests { + use ark_ed_on_bls12_381_bandersnatch::Fq; + use ark_ff::Zero; + use ark_poly::Polynomial; + use ark_std::test_rng; + + use crate::domain::Domain; + use crate::test_helpers::random_vec; + + use super::*; + + fn _test_column_sum_gadget(hiding: bool) { + let rng = &mut test_rng(); + + let log_n = 10; + let n = 2usize.pow(log_n); + let domain = Domain::new(n, hiding); + + let col = random_vec(domain.capacity - 1, rng); + let sum = col.iter().sum(); + let col = Rc::new(domain.private_column(col)); + + let gadget = ColumnSumPolys::::init(col, &domain); + + let acc = &gadget.acc.evals.evals; + assert!(acc[0].is_zero()); + assert_eq!(acc[domain.capacity - 1], sum); + + let constraint_poly = gadget.constraints()[0].interpolate_by_ref(); + + assert_eq!(constraint_poly.degree(), n); + domain.divide_by_vanishing_poly(&constraint_poly); + } + + #[test] + fn test_column_sum_gadget() { + _test_column_sum_gadget(false); + _test_column_sum_gadget(true); + } +} diff --git a/w3f-plonk-common/src/gadgets/mod.rs b/w3f-plonk-common/src/gadgets/mod.rs index efb21a3..4024276 100644 --- a/w3f-plonk-common/src/gadgets/mod.rs +++ b/w3f-plonk-common/src/gadgets/mod.rs @@ -8,6 +8,7 @@ pub mod booleanity; pub mod ec; pub mod fixed_cells; pub mod inner_prod; +pub mod column_sum; pub trait ProverGadget { // Columns populated by the gadget. From d97828c108e57f6bc79540788f12d298add6d0e8 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Thu, 20 Mar 2025 01:11:09 +0300 Subject: [PATCH 16/23] pk_index constrained to be unique --- w3f-plonk-common/src/gadgets/column_sum.rs | 6 ++-- w3f-ring-vrf-snark/src/piop/mod.rs | 4 +++ w3f-ring-vrf-snark/src/piop/prover.rs | 21 ++++++++++++-- w3f-ring-vrf-snark/src/piop/verifier.rs | 32 ++++++++++++++++++---- 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/w3f-plonk-common/src/gadgets/column_sum.rs b/w3f-plonk-common/src/gadgets/column_sum.rs index 4a3e263..d19177a 100644 --- a/w3f-plonk-common/src/gadgets/column_sum.rs +++ b/w3f-plonk-common/src/gadgets/column_sum.rs @@ -12,10 +12,10 @@ pub struct ColumnSumPolys { /// Input column. /// Should have length `n-1`, where `n` is the domain "capacity" (domain.size - ZK_ROWS): /// `col[0], ..., col[n-2]` - col: Rc>, + pub col: Rc>, /// Partial sums of `col`: `acc[0] = 0, acc[i] = col[0] + ... + col[i-1], i = 1,...,n-1` - acc: Rc>, - not_last: FieldColumn, + pub acc: Rc>, + pub not_last: FieldColumn, } pub struct ColumnSumEvals { diff --git a/w3f-ring-vrf-snark/src/piop/mod.rs b/w3f-ring-vrf-snark/src/piop/mod.rs index 9dc47c1..ca040cc 100644 --- a/w3f-ring-vrf-snark/src/piop/mod.rs +++ b/w3f-ring-vrf-snark/src/piop/mod.rs @@ -31,6 +31,7 @@ pub struct RingCommitments> { pub(crate) doublings_of_in: [C; 2], pub(crate) out_from_in: [C; 2], pub(crate) pk_from_index: [C; 2], + pub(crate) pk_index_sum: C, pub(crate) phantom: PhantomData, } @@ -47,6 +48,7 @@ impl> ColumnsCommited for RingCommitments< self.out_from_in[1].clone(), self.pk_from_index[0].clone(), self.pk_from_index[1].clone(), + self.pk_index_sum.clone(), ] } } @@ -61,6 +63,7 @@ pub struct RingEvaluations { pub(crate) doublings_of_in: [F; 2], pub(crate) out_from_in: [F; 2], pub(crate) pk_from_index: [F; 2], + pub(crate) pk_index_sum: F, } impl ColumnsEvaluated for RingEvaluations { @@ -80,6 +83,7 @@ impl ColumnsEvaluated for RingEvaluations { self.out_from_in[1], self.pk_from_index[0], self.pk_from_index[1], + self.pk_index_sum, ] } } diff --git a/w3f-ring-vrf-snark/src/piop/prover.rs b/w3f-ring-vrf-snark/src/piop/prover.rs index 3f77556..f7b0500 100644 --- a/w3f-ring-vrf-snark/src/piop/prover.rs +++ b/w3f-ring-vrf-snark/src/piop/prover.rs @@ -20,6 +20,7 @@ use w3f_plonk_common::piop::ProverPiop; use crate::piop::cell_equality::CellEqualityPolys; use w3f_plonk_common::gadgets::ec::te_doubling::Doubling; use w3f_plonk_common::Column; +use w3f_plonk_common::gadgets::column_sum::ColumnSumPolys; /// The prover's private input is its secret key `sk`. /// The public inputs are: @@ -74,8 +75,11 @@ pub struct PiopProver> { out_from_in_x: FixedCells, out_from_in_y: FixedCells, pk_index_bool: Booleanity, - // pk_index_unique: InnerProd, //TODO: - pk_from_index: CondAdd>, // TODO: constrain the seed + /// The following 2 together guarantee that the single bit is set in `pk_index[0..domain.capacity - 1]`. + /// `pk_index[domain.capacity - 1]` is not constrained. That's ok because the ec addition gadget ignores the last bit. + pk_index_sum: ColumnSumPolys, + pk_index_sum_val: FixedCells, + pk_from_index: CondAdd>, pks_equal_x: CellEqualityPolys, pks_equal_y: CellEqualityPolys, } @@ -122,6 +126,8 @@ impl> PiopProver { let out_from_in_y = FixedCells::init(out_from_in.acc.ys.clone(), &domain); let pk_index_bool = Booleanity::init(pk_index.clone()); + let pk_index_sum = ColumnSumPolys::init(pk_index.col.clone(), &domain); + let pk_index_sum_val = FixedCells::init(pk_index_sum.acc.clone(), &domain); let pk_from_index = CondAdd::init(pk_index.clone(), pks.clone(), params.seed, &domain); let pks_equal_x = CellEqualityPolys::init( @@ -148,6 +154,8 @@ impl> PiopProver { out_from_in_x, out_from_in_y, pk_index_bool, + pk_index_sum, + pk_index_sum_val, pk_from_index, pks_equal_x, pks_equal_y, @@ -187,6 +195,7 @@ where commit(self.pk_from_index.acc.xs.as_poly()), commit(self.pk_from_index.acc.ys.as_poly()), ]; + let pk_index_sum = commit(self.pk_index_sum.acc.as_poly()); RingCommitments { sk_bits, pk_index, @@ -194,6 +203,7 @@ where doublings_of_in, out_from_in, pk_from_index, + pk_index_sum, phantom: Default::default(), } } @@ -212,6 +222,7 @@ where self.out_from_in.acc.ys.as_poly().clone(), self.pk_from_index.acc.xs.as_poly().clone(), self.pk_from_index.acc.ys.as_poly().clone(), + self.pk_index_sum.acc.as_poly().clone(), ] } @@ -239,6 +250,7 @@ where self.pk_from_index.acc.xs.evaluate(zeta), self.pk_from_index.acc.ys.evaluate(zeta), ]; + let pk_index_unique = self.pk_index_sum.acc.evaluate(zeta); RingEvaluations { pks, doublings_of_g, @@ -248,6 +260,7 @@ where doublings_of_in, out_from_in, pk_from_index, + pk_index_sum: pk_index_unique, } } @@ -259,6 +272,8 @@ where self.doublings_of_in_gadget.constraints(), self.out_from_in.constraints(), self.pk_from_index.constraints(), + self.pk_index_sum.constraints(), + self.pk_index_sum_val.constraints(), self.out_from_in_x.constraints(), self.out_from_in_y.constraints(), self.pks_equal_x.constraints(), @@ -281,6 +296,8 @@ where self.doublings_of_in_gadget.constraints_linearized(zeta), self.out_from_in.constraints_linearized(zeta), self.pk_from_index.constraints_linearized(zeta), + self.pk_index_sum.constraints_linearized(zeta), + self.pk_index_sum_val.constraints_linearized(zeta), self.out_from_in_x.constraints_linearized(zeta), self.out_from_in_y.constraints_linearized(zeta), self.pks_equal_x.constraints_linearized(zeta), diff --git a/w3f-ring-vrf-snark/src/piop/verifier.rs b/w3f-ring-vrf-snark/src/piop/verifier.rs index 1236081..75d62b0 100644 --- a/w3f-ring-vrf-snark/src/piop/verifier.rs +++ b/w3f-ring-vrf-snark/src/piop/verifier.rs @@ -6,6 +6,7 @@ use w3f_pcs::pcs::Commitment; use w3f_plonk_common::domain::EvaluatedDomain; use w3f_plonk_common::gadgets::booleanity::BooleanityValues; +use w3f_plonk_common::gadgets::column_sum::ColumnSumEvals; use w3f_plonk_common::gadgets::ec::te_doubling::DoublingValues; use w3f_plonk_common::gadgets::ec::CondAddValues; use w3f_plonk_common::gadgets::fixed_cells::FixedCellsValues; @@ -29,7 +30,8 @@ pub struct PiopVerifier, P: AffineRepr, out_from_in_y: FixedCellsValues, pk_index_bool: BooleanityValues, - // pk_index_unique: InnerProd, //TODO: + pk_index_sum: ColumnSumEvals, + pk_index_sum_val: FixedCellsValues, pk_from_index: CondAddValues, pks_equal_x: CellEqualityEvals, pks_equal_y: CellEqualityEvals, @@ -106,7 +108,19 @@ impl, P: AffineRepr> PiopVerifier let pk_index_bool = BooleanityValues { bits: all_columns_evaluated.pk_index, }; - // pk_index_unique: InnerProd, //TODO: + let pk_index_sum = ColumnSumEvals { + col: all_columns_evaluated.pk_index, + acc: all_columns_evaluated.pk_index_sum, + not_last: domain_evals.not_last_row, + }; + let pk_index_sum_val = FixedCellsValues { + col: all_columns_evaluated.pk_index_sum, + col_first: F::zero(), + col_last: F::one(), + l_first: domain_evals.l_first, + l_last: domain_evals.l_last, + }; + let pk_from_index = CondAddValues { bitmask: all_columns_evaluated.pk_index, points: (all_columns_evaluated.pks[0], all_columns_evaluated.pks[1]), @@ -140,6 +154,8 @@ impl, P: AffineRepr> PiopVerifier out_from_in_x, out_from_in_y, pk_index_bool, + pk_index_sum, + pk_index_sum_val, pk_from_index, pks_equal_x, pks_equal_y, @@ -152,8 +168,8 @@ impl, P: AffineRepr> PiopVerifier impl, Jubjub: TECurveConfig> VerifierPiop for PiopVerifier> { - const N_CONSTRAINTS: usize = 20; - const N_COLUMNS: usize = 14; + const N_CONSTRAINTS: usize = 22; + const N_COLUMNS: usize = 15; fn precommitted_columns(&self) -> Vec { self.fixed_columns_committed.as_vec() @@ -167,6 +183,8 @@ impl, Jubjub: TECurveConfig> Veri self.doublings_of_in_gadget.evaluate_constraints_main(), self.out_from_in.evaluate_constraints_main(), self.pk_from_index.evaluate_constraints_main(), + self.pk_index_sum.evaluate_constraints_main(), + self.pk_index_sum_val.evaluate_constraints_main(), self.out_from_in_x.evaluate_constraints_main(), self.out_from_in_y.evaluate_constraints_main(), self.pks_equal_x.evaluate_constraints_main(), @@ -211,6 +229,9 @@ impl, Jubjub: TECurveConfig> Veri let pk_from_index_c2_lin = pk_from_index_x.mul(pk_x_coeff) + pk_from_index_y.mul(pk_y_coeff); + let pk_index_sum_lin = (&self.witness_columns_committed.pk_index_sum) + .mul(self.pk_index_sum.not_last); + let per_constraint = vec![ pk_from_sk_c1_lin, pk_from_sk_c2_lin, @@ -220,10 +241,11 @@ impl, Jubjub: TECurveConfig> Veri out_from_in_c2_lin, pk_from_index_c1_lin, pk_from_index_c2_lin, + pk_index_sum_lin, ]; // TODO: optimize muls - C::combine(&agg_coeffs[2..10], &per_constraint) //TODO + C::combine(&agg_coeffs[2..11], &per_constraint) //TODO } fn domain_evaluated(&self) -> &EvaluatedDomain { From b2f4b7f2cbad4027bfe8d3463d94b6ce13455aa0 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Thu, 20 Mar 2025 01:11:28 +0300 Subject: [PATCH 17/23] fmt --- w3f-plonk-common/src/gadgets/column_sum.rs | 10 +++--- w3f-plonk-common/src/gadgets/mod.rs | 2 +- w3f-ring-vrf-snark/src/piop/prover.rs | 38 ++++++++++++++++---- w3f-ring-vrf-snark/src/piop/verifier.rs | 40 +++++++++++++++++----- 4 files changed, 70 insertions(+), 20 deletions(-) diff --git a/w3f-plonk-common/src/gadgets/column_sum.rs b/w3f-plonk-common/src/gadgets/column_sum.rs index d19177a..d596936 100644 --- a/w3f-plonk-common/src/gadgets/column_sum.rs +++ b/w3f-plonk-common/src/gadgets/column_sum.rs @@ -41,10 +41,12 @@ impl ColumnSumPolys { /// Returns `col[0], col[0] + col[1], ..., col[0] + col[1] + ... + col[n-1]`. fn partial_sums(col: &[F]) -> Vec { - col.iter().scan(F::zero(), |state, &x| { - *state += x; - Some(*state) - }).collect() + col.iter() + .scan(F::zero(), |state, &x| { + *state += x; + Some(*state) + }) + .collect() } } diff --git a/w3f-plonk-common/src/gadgets/mod.rs b/w3f-plonk-common/src/gadgets/mod.rs index 4024276..9d8bdf2 100644 --- a/w3f-plonk-common/src/gadgets/mod.rs +++ b/w3f-plonk-common/src/gadgets/mod.rs @@ -5,10 +5,10 @@ use ark_std::vec::Vec; pub mod booleanity; // pub mod inner_prod_pub; +pub mod column_sum; pub mod ec; pub mod fixed_cells; pub mod inner_prod; -pub mod column_sum; pub trait ProverGadget { // Columns populated by the gadget. diff --git a/w3f-ring-vrf-snark/src/piop/prover.rs b/w3f-ring-vrf-snark/src/piop/prover.rs index f7b0500..fe453b3 100644 --- a/w3f-ring-vrf-snark/src/piop/prover.rs +++ b/w3f-ring-vrf-snark/src/piop/prover.rs @@ -18,9 +18,9 @@ use w3f_plonk_common::gadgets::ProverGadget; use w3f_plonk_common::piop::ProverPiop; use crate::piop::cell_equality::CellEqualityPolys; +use w3f_plonk_common::gadgets::column_sum::ColumnSumPolys; use w3f_plonk_common::gadgets::ec::te_doubling::Doubling; use w3f_plonk_common::Column; -use w3f_plonk_common::gadgets::column_sum::ColumnSumPolys; /// The prover's private input is its secret key `sk`. /// The public inputs are: @@ -278,12 +278,36 @@ where self.out_from_in_y.constraints(), self.pks_equal_x.constraints(), self.pks_equal_y.constraints(), - vec![FixedCells::constraint_cell(&self.pk_from_sk.acc.xs, &self.domain.l_first, 0)], - vec![FixedCells::constraint_cell(&self.pk_from_sk.acc.ys, &self.domain.l_first, 0)], - vec![FixedCells::constraint_cell(&self.pk_from_index.acc.xs, &self.domain.l_first, 0)], - vec![FixedCells::constraint_cell(&self.pk_from_index.acc.ys, &self.domain.l_first, 0)], - vec![FixedCells::constraint_cell(&self.doublings_of_in_gadget.doublings.xs, &self.domain.l_first, 0)], - vec![FixedCells::constraint_cell(&self.doublings_of_in_gadget.doublings.ys, &self.domain.l_first, 0)], + vec![FixedCells::constraint_cell( + &self.pk_from_sk.acc.xs, + &self.domain.l_first, + 0, + )], + vec![FixedCells::constraint_cell( + &self.pk_from_sk.acc.ys, + &self.domain.l_first, + 0, + )], + vec![FixedCells::constraint_cell( + &self.pk_from_index.acc.xs, + &self.domain.l_first, + 0, + )], + vec![FixedCells::constraint_cell( + &self.pk_from_index.acc.ys, + &self.domain.l_first, + 0, + )], + vec![FixedCells::constraint_cell( + &self.doublings_of_in_gadget.doublings.xs, + &self.domain.l_first, + 0, + )], + vec![FixedCells::constraint_cell( + &self.doublings_of_in_gadget.doublings.ys, + &self.domain.l_first, + 0, + )], ] .concat() } diff --git a/w3f-ring-vrf-snark/src/piop/verifier.rs b/w3f-ring-vrf-snark/src/piop/verifier.rs index 75d62b0..d478573 100644 --- a/w3f-ring-vrf-snark/src/piop/verifier.rs +++ b/w3f-ring-vrf-snark/src/piop/verifier.rs @@ -189,12 +189,36 @@ impl, Jubjub: TECurveConfig> Veri self.out_from_in_y.evaluate_constraints_main(), self.pks_equal_x.evaluate_constraints_main(), self.pks_equal_y.evaluate_constraints_main(), - vec![FixedCellsValues::evaluate_for_cell(self.pk_from_sk.acc.0, self.domain_evals.l_first, self.seed.0)], - vec![FixedCellsValues::evaluate_for_cell(self.pk_from_sk.acc.1, self.domain_evals.l_first, self.seed.1)], - vec![FixedCellsValues::evaluate_for_cell(self.pk_from_index.acc.0, self.domain_evals.l_first, self.seed.0)], - vec![FixedCellsValues::evaluate_for_cell(self.pk_from_index.acc.1, self.domain_evals.l_first, self.seed.1)], - vec![FixedCellsValues::evaluate_for_cell(self.doublings_of_in_gadget.doublings.0, self.domain_evals.l_first, self.vrf_in.0)], - vec![FixedCellsValues::evaluate_for_cell(self.doublings_of_in_gadget.doublings.1, self.domain_evals.l_first, self.vrf_in.1)], + vec![FixedCellsValues::evaluate_for_cell( + self.pk_from_sk.acc.0, + self.domain_evals.l_first, + self.seed.0, + )], + vec![FixedCellsValues::evaluate_for_cell( + self.pk_from_sk.acc.1, + self.domain_evals.l_first, + self.seed.1, + )], + vec![FixedCellsValues::evaluate_for_cell( + self.pk_from_index.acc.0, + self.domain_evals.l_first, + self.seed.0, + )], + vec![FixedCellsValues::evaluate_for_cell( + self.pk_from_index.acc.1, + self.domain_evals.l_first, + self.seed.1, + )], + vec![FixedCellsValues::evaluate_for_cell( + self.doublings_of_in_gadget.doublings.0, + self.domain_evals.l_first, + self.vrf_in.0, + )], + vec![FixedCellsValues::evaluate_for_cell( + self.doublings_of_in_gadget.doublings.1, + self.domain_evals.l_first, + self.vrf_in.1, + )], ] .concat() } @@ -229,8 +253,8 @@ impl, Jubjub: TECurveConfig> Veri let pk_from_index_c2_lin = pk_from_index_x.mul(pk_x_coeff) + pk_from_index_y.mul(pk_y_coeff); - let pk_index_sum_lin = (&self.witness_columns_committed.pk_index_sum) - .mul(self.pk_index_sum.not_last); + let pk_index_sum_lin = + (&self.witness_columns_committed.pk_index_sum).mul(self.pk_index_sum.not_last); let per_constraint = vec![ pk_from_sk_c1_lin, From 3b716ae3d8afcd1811d9a8c362f98f3992dc30df Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Thu, 20 Mar 2025 18:32:17 +0300 Subject: [PATCH 18/23] params cleaned up --- w3f-ring-vrf-snark/src/lib.rs | 37 +++---- w3f-ring-vrf-snark/src/piop/params.rs | 114 +++++++------------- w3f-ring-vrf-snark/src/ring_vrf_prover.rs | 32 +++--- w3f-ring-vrf-snark/src/ring_vrf_verifier.rs | 12 +-- 4 files changed, 76 insertions(+), 119 deletions(-) diff --git a/w3f-ring-vrf-snark/src/lib.rs b/w3f-ring-vrf-snark/src/lib.rs index e20414d..da9d6d6 100644 --- a/w3f-ring-vrf-snark/src/lib.rs +++ b/w3f-ring-vrf-snark/src/lib.rs @@ -18,7 +18,8 @@ mod piop; pub mod ring_vrf_prover; pub mod ring_vrf_verifier; -pub type RingProof = Proof>::C>, RingEvaluations>; +pub type RingVrfProof = + Proof>::C>, RingEvaluations>; #[derive(Clone)] pub struct ArkTranscript(ark_transcript::Transcript); @@ -54,8 +55,8 @@ mod tests { use w3f_plonk_common::test_helpers::random_vec; - use crate::ring_vrf_prover::RingProver; - use crate::ring_vrf_verifier::RingVerifier; + use crate::ring_vrf_prover::RingVrfProver; + use crate::ring_vrf_verifier::RingVrfVerifier; use super::*; @@ -64,38 +65,37 @@ mod tests { let (pcs_params, piop_params) = setup::<_, CS>(rng, domain_size); - let max_keyset_size = piop_params.keyset_part_size; - let keyset_size: usize = rng.gen_range(0..max_keyset_size); + let keyset_size: usize = rng.gen_range(1..=piop_params.max_keys()); let mut pks = random_vec::(keyset_size, rng); - let k = rng.gen_range(0..keyset_size); // prover's secret index + let pk_index = rng.gen_range(0..keyset_size); // prover's secret index let sk = Fr::rand(rng); // prover's secret scalar let pk = piop_params.g.mul(sk).into_affine(); - pks[k] = pk; + pks[pk_index] = pk; let (prover_key, verifier_key) = index::<_, CS, _>(&pcs_params, &piop_params, &pks); - // PROOF generation + let vrf_in = EdwardsAffine::rand(rng); + let vrf_out = vrf_in.mul(sk).into_affine(); - let vrf_input = EdwardsAffine::rand(rng); - let vrf_output = vrf_input.mul(sk).into_affine(); - let ring_prover = RingProver::init( + let ring_prover = RingVrfProver::init( prover_key, piop_params.clone(), - k, + pk_index, + sk, ArkTranscript::new(b"w3f-ring-vrf-snark-test"), ); let t_prove = start_timer!(|| "Prove"); - let (proof, res) = ring_prover.prove(sk, vrf_input); + let (res, proof) = ring_prover.prove(vrf_in); end_timer!(t_prove); - assert_eq!(res, vrf_output); + assert_eq!(res, vrf_out); - let ring_verifier = RingVerifier::init( + let ring_verifier = RingVrfVerifier::init( verifier_key, piop_params, ArkTranscript::new(b"w3f-ring-vrf-snark-test"), ); let t_verify = start_timer!(|| "Verify"); - let res = ring_verifier.verify(proof, vrf_input, vrf_output); + let res = ring_verifier.verify(vrf_in, vrf_out, proof); end_timer!(t_verify); assert!(res); } @@ -130,13 +130,10 @@ mod tests { ) -> (CS::Params, PiopParams) { let setup_degree = 3 * domain_size; let pcs_params = CS::setup(setup_degree, rng); - let domain = Domain::new(domain_size, true); - let h = EdwardsAffine::rand(rng); let seed = EdwardsAffine::rand(rng); let padding = EdwardsAffine::rand(rng); - let piop_params = PiopParams::setup(domain, h, seed, padding); - + let piop_params = PiopParams::setup(domain, seed, padding); (pcs_params, piop_params) } diff --git a/w3f-ring-vrf-snark/src/piop/params.rs b/w3f-ring-vrf-snark/src/piop/params.rs index 77b053e..c275b65 100644 --- a/w3f-ring-vrf-snark/src/piop/params.rs +++ b/w3f-ring-vrf-snark/src/piop/params.rs @@ -1,125 +1,85 @@ use ark_ec::twisted_edwards::{Affine, TECurveConfig}; -use ark_ec::{AdditiveGroup, AffineRepr, CurveGroup}; +use ark_ec::AffineRepr; use ark_ff::{BigInteger, PrimeField}; use ark_std::{vec, vec::Vec}; +use crate::piop::FixedColumns; 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; -use crate::piop::FixedColumns; - -/// Plonk Interactive Oracle Proofs (PIOP) parameters. #[derive(Clone)] pub struct PiopParams> { - /// Domain over which the piop is represented. + /// The domain over which the piop is represented. pub(crate) domain: Domain, - /// Number of bits used to represent a jubjub scalar. + /// The number of bits required to represent a jubjub scalar (a secret key). pub(crate) scalar_bitlen: usize, - /// Length of the part of the column representing the public keys (including the padding). - pub keyset_part_size: usize, - /// The generator used to compute public keys, `pk=sk.G`. + /// The generator used to compute public keys, `pk = sk.G`. pub(crate) g: Affine, - /// Summation base point. + /// The point from which EC summations start. + /// For curves in TE form, should be an odd-order point of an unknown dlog. + /// Then we require: + /// 1. *the proofs of possession are checked for every public key in the ring*, + /// 2. the `padding` is chosen independently of the `seed`, + /// 3. the inputs `vrf_in` are independent of the `seed` + /// to avoid the doublings. pub(crate) seed: Affine, - /// The point used to pad the list of public keys. + /// The point used to pad the list of public keys up to the `domain.capacity - 1`. + /// Should be of an unknown dlog. pub(crate) padding: Affine, } impl> PiopParams { - /// Initialize PIOP parameters. - /// - /// - `domain`: polynomials evaluation domain. - /// - `h`: Blinding base point. - /// - `seed`: Accumulation base point - /// - `padding`: The point used to pad the list of public keys. - /// - /// All points should be of an unknown discrete log. - pub fn setup( - domain: Domain, - _h: Affine, //TODO: cleanup - seed: Affine, - padding: Affine, - ) -> Self { + pub fn setup(domain: Domain, seed: Affine, padding: Affine) -> Self { let scalar_bitlen = Curve::ScalarField::MODULUS_BIT_SIZE as usize; - // 1 accounts for the last cells of the points and bits columns that remain unconstrained - let keyset_part_size = domain.capacity - scalar_bitlen - 1; + assert!(scalar_bitlen + 1 <= domain.capacity); // 1 accounts for the seed cells Self { domain, scalar_bitlen, - keyset_part_size, g: Affine::::generator(), seed, padding, } } + pub fn max_keys(&self) -> usize { + self.domain.capacity - 1 + } + pub fn fixed_columns(&self, pks: Vec>) -> FixedColumns> { - let doublings_of_g = self.doublings_of_g_col(); - let mut pks = pks; - assert!(pks.len() <= self.domain.capacity - 1); - pks.resize(self.domain.capacity - 1, self.padding); - let pks = AffineColumn::public_column(pks, &self.domain); // TODO: here we assume pk.len() > 256 + // TODO: doublings_of_g.len() != pks.len() FixedColumns { - pks, - doublings_of_g, + pks: self.pks_col(pks), + doublings_of_g: self.doublings_of_g_col(), } } + fn pks_col(&self, pks: Vec>) -> AffineColumn> { + assert!(pks.len() <= self.max_keys()); + let mut pks = pks; + pks.resize(self.max_keys(), self.padding); + let pks = AffineColumn::public_column(pks, &self.domain); + pks + } + fn doublings_of_g_col(&self) -> AffineColumn> { - let mut doublings_of_g = self.doublings_of(self.g); - doublings_of_g.resize(self.domain.capacity - 1, self.g); //TODO: eh, may be zeros + let doublings_of_g = Doubling::doublings_of(self.g, &self.domain); AffineColumn::public_column(doublings_of_g, &self.domain) } + /// Represents `index` as a binary column. pub fn pk_index_col(&self, index: usize) -> BitColumn { - assert!(index <= self.domain.capacity - 1); - let mut col = vec![false; self.domain.capacity - 1]; + assert!(index < self.max_keys()); + let mut col = vec![false; self.max_keys()]; col[index] = true; BitColumn::init(col, &self.domain) } + /// Represents a scalar in binary. pub fn sk_bits(&self, sk: Curve::ScalarField) -> Vec { let bits_with_trailing_zeroes = sk.into_bigint().to_bits_le(); let significant_bits = &bits_with_trailing_zeroes[..self.scalar_bitlen]; significant_bits.to_vec() } - - pub fn doublings_of(&self, p: Affine) -> Vec> { - let mut p = p.into_group(); - let mut doublings = Vec::with_capacity(self.domain.capacity); - doublings.push(p); - for _ in 1..self.scalar_bitlen { - p.double_in_place(); - doublings.push(p); - } - CurveGroup::normalize_batch(&doublings) - } } - -// #[cfg(test)] -// mod tests { -// use ark_ed_on_bls12_381_bandersnatch::{BandersnatchConfig, EdwardsAffine, Fq, Fr}; -// use ark_std::ops::Mul; -// use ark_std::{test_rng, UniformRand}; -// -// use w3f_plonk_common::domain::Domain; -// use w3f_plonk_common::test_helpers::cond_sum; -// -// use crate::piop::params::PiopParams; -// -// #[test] -// fn test_powers_of_h() { -// let rng = &mut test_rng(); -// let h = EdwardsAffine::rand(rng); -// let seed = EdwardsAffine::rand(rng); -// let padding = EdwardsAffine::rand(rng); -// let domain = Domain::new(1024, false); -// -// let params = PiopParams::::setup(domain, h, seed, padding); -// let t = Fr::rand(rng); -// let t_bits = params.scalar_part(t); -// let th = cond_sum(&t_bits, ¶ms.power_of_2_multiples_of_h()); -// assert_eq!(th, params.h.mul(t)); -// } -// } diff --git a/w3f-ring-vrf-snark/src/ring_vrf_prover.rs b/w3f-ring-vrf-snark/src/ring_vrf_prover.rs index f2f3f12..fe14cfb 100644 --- a/w3f-ring-vrf-snark/src/ring_vrf_prover.rs +++ b/w3f-ring-vrf-snark/src/ring_vrf_prover.rs @@ -7,9 +7,9 @@ use w3f_plonk_common::transcript::PlonkTranscript; use crate::piop::params::PiopParams; use crate::piop::{FixedColumns, PiopProver, ProverKey}; -use crate::{ArkTranscript, RingProof}; +use crate::{ArkTranscript, RingVrfProof}; -pub struct RingProver +pub struct RingVrfProver where F: PrimeField, CS: PCS, @@ -18,11 +18,12 @@ where { piop_params: PiopParams, fixed_columns: FixedColumns>, - k: usize, + pk_index: usize, + sk: Curve::ScalarField, plonk_prover: PlonkProver, } -impl RingProver +impl RingVrfProver where F: PrimeField, CS: PCS, @@ -32,7 +33,8 @@ where pub fn init( prover_key: ProverKey>, piop_params: PiopParams, - k: usize, + pk_index: usize, + sk: Curve::ScalarField, empty_transcript: T, ) -> Self { let ProverKey { @@ -46,25 +48,23 @@ where Self { piop_params, fixed_columns, - k, + pk_index, + sk, plonk_prover, } } - pub fn prove( - &self, - t: Curve::ScalarField, - vrf_input: Affine, - ) -> (RingProof, Affine) { + pub fn prove(&self, vrf_in: Affine) -> (Affine, RingVrfProof) { let piop: PiopProver = PiopProver::build( &self.piop_params, self.fixed_columns.clone(), - self.k, - t, - vrf_input, + self.pk_index, + self.sk, + vrf_in, ); - let res = as ProverPiop>::result(&piop); - (self.plonk_prover.prove(piop), res) + let vrf_out = as ProverPiop>::result(&piop); + let proof = self.plonk_prover.prove(piop); + (vrf_out, proof) } pub fn piop_params(&self) -> &PiopParams { diff --git a/w3f-ring-vrf-snark/src/ring_vrf_verifier.rs b/w3f-ring-vrf-snark/src/ring_vrf_verifier.rs index 5f38f13..e245b92 100644 --- a/w3f-ring-vrf-snark/src/ring_vrf_verifier.rs +++ b/w3f-ring-vrf-snark/src/ring_vrf_verifier.rs @@ -10,9 +10,9 @@ use w3f_plonk_common::verifier::PlonkVerifier; use crate::piop::params::PiopParams; use crate::piop::{FixedColumnsCommitted, PiopVerifier, VerifierKey}; -use crate::{ArkTranscript, RingProof}; +use crate::{ArkTranscript, RingVrfProof}; -pub struct RingVerifier +pub struct RingVrfVerifier where F: PrimeField, CS: PCS, @@ -24,7 +24,7 @@ where plonk_verifier: PlonkVerifier, } -impl RingVerifier +impl RingVrfVerifier where F: PrimeField, CS: PCS, @@ -47,9 +47,9 @@ where pub fn verify( &self, - proof: RingProof, vrf_in: Affine, vrf_out: Affine, + proof: RingVrfProof, ) -> bool { let (challenges, mut rng) = self.plonk_verifier.restore_challenges( &vrf_out, @@ -60,14 +60,14 @@ where ); let seed = self.piop_params.seed; let seed_plus_out = (seed + vrf_out).into_affine(); - let domain_eval = EvaluatedDomain::new( + let domain_evals = EvaluatedDomain::new( self.piop_params.domain.domain(), challenges.zeta, self.piop_params.domain.hiding, ); let piop = PiopVerifier::<_, _, Affine>::init( - domain_eval, + domain_evals, self.fixed_columns_committed.clone(), proof.column_commitments.clone(), proof.columns_at_zeta.clone(), From 049ff2eabb86a8c1b4c3a1f68b8d8d5655bcfd47 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Thu, 20 Mar 2025 18:42:50 +0300 Subject: [PATCH 19/23] different keyset sizes tested --- w3f-ring-vrf-snark/src/lib.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/w3f-ring-vrf-snark/src/lib.rs b/w3f-ring-vrf-snark/src/lib.rs index da9d6d6..8f8e913 100644 --- a/w3f-ring-vrf-snark/src/lib.rs +++ b/w3f-ring-vrf-snark/src/lib.rs @@ -14,7 +14,6 @@ pub use w3f_pcs::pcs; pub use w3f_plonk_common::domain::Domain; mod piop; -// mod ring_vrf; pub mod ring_vrf_prover; pub mod ring_vrf_verifier; @@ -47,12 +46,13 @@ impl ArkTranscript { #[cfg(test)] mod tests { + use ark_bls12_381::Bls12_381; use ark_ec::CurveGroup; use ark_ed_on_bls12_381_bandersnatch::{BandersnatchConfig, EdwardsAffine, Fq, Fr}; use ark_std::ops::Mul; use ark_std::rand::Rng; use ark_std::{end_timer, start_timer, test_rng, UniformRand}; - + use w3f_pcs::pcs::kzg::KZG; use w3f_plonk_common::test_helpers::random_vec; use crate::ring_vrf_prover::RingVrfProver; @@ -60,12 +60,11 @@ mod tests { use super::*; - fn _test_ring_proof>(domain_size: usize) { + fn _test_ring_proof>(domain_size: usize, keyset_size: usize) { let rng = &mut test_rng(); let (pcs_params, piop_params) = setup::<_, CS>(rng, domain_size); - let keyset_size: usize = rng.gen_range(1..=piop_params.max_keys()); let mut pks = random_vec::(keyset_size, rng); let pk_index = rng.gen_range(0..keyset_size); // prover's secret index let sk = Fr::rand(rng); // prover's secret scalar @@ -137,13 +136,15 @@ mod tests { (pcs_params, piop_params) } - // #[test] - // fn test_ring_proof_kzg() { - // _test_ring_proof::>(2usize.pow(10)); - // } + #[test] + fn test_ring_proof_kzg() { + _test_ring_proof::>(2usize.pow(9), 512); + } #[test] fn test_ring_proof_id() { - _test_ring_proof::(2usize.pow(10)); + _test_ring_proof::(2usize.pow(10), 2usize.pow(10) - 4); // no padding + _test_ring_proof::(2usize.pow(9), 253); + _test_ring_proof::(2usize.pow(9), 1); } } From 4e35bc124fb0e77b9c0be75d846882c14df54cdb Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Thu, 20 Mar 2025 18:46:21 +0300 Subject: [PATCH 20/23] test fixed --- w3f-ring-vrf-snark/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/w3f-ring-vrf-snark/src/lib.rs b/w3f-ring-vrf-snark/src/lib.rs index 8f8e913..1262972 100644 --- a/w3f-ring-vrf-snark/src/lib.rs +++ b/w3f-ring-vrf-snark/src/lib.rs @@ -138,7 +138,7 @@ mod tests { #[test] fn test_ring_proof_kzg() { - _test_ring_proof::>(2usize.pow(9), 512); + _test_ring_proof::>(2usize.pow(9), 500); } #[test] From 29aa8f1f0b25d12991bf9dda2574ca5670c3ec44 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Fri, 21 Mar 2025 08:01:24 +0300 Subject: [PATCH 21/23] fixed columns also opened --- w3f-ring-vrf-snark/src/piop/prover.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/w3f-ring-vrf-snark/src/piop/prover.rs b/w3f-ring-vrf-snark/src/piop/prover.rs index fe453b3..940773b 100644 --- a/w3f-ring-vrf-snark/src/piop/prover.rs +++ b/w3f-ring-vrf-snark/src/piop/prover.rs @@ -212,6 +212,10 @@ where // Self::Evaluations::to_vec() and Self::Commitments::to_vec(). fn columns(&self) -> Vec> { vec![ + self.pks.xs.as_poly().clone(), + self.pks.ys.as_poly().clone(), + self.doublings_of_g.xs.as_poly().clone(), + self.doublings_of_g.ys.as_poly().clone(), self.sk_bits.as_poly().clone(), self.pk_index.as_poly().clone(), self.pk_from_sk.acc.xs.as_poly().clone(), From 467546285957d3aaf91729bd23b3627bda5a0455 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Mon, 7 Apr 2025 14:27:45 +0200 Subject: [PATCH 22/23] ColumnSum gadget documented --- w3f-plonk-common/src/gadgets/column_sum.rs | 28 +++++++++++++++------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/w3f-plonk-common/src/gadgets/column_sum.rs b/w3f-plonk-common/src/gadgets/column_sum.rs index d596936..2a3aec4 100644 --- a/w3f-plonk-common/src/gadgets/column_sum.rs +++ b/w3f-plonk-common/src/gadgets/column_sum.rs @@ -8,13 +8,22 @@ use crate::domain::Domain; use crate::gadgets::{ProverGadget, VerifierGadget}; use crate::{Column, FieldColumn}; +/// Computes the sum of the elements of a `col`. All but last. +/// +/// Let `c` be the domain "capacity" (`c = domain.size - ZK_ROWS`). +/// The gadget populates and constrains a witness column `acc`, +/// such that `acc[i] = acc[i-1] + col[i-1], i = 1,...,c-1`. +/// Then `acc[c-1] = acc[0] + (col[0] + ... + col[c-2]) = acc[0] + sum(col[0..c-1])`. +/// `acc[0]` and `acc[c-1]` have to be additionally constrained. pub struct ColumnSumPolys { /// Input column. - /// Should have length `n-1`, where `n` is the domain "capacity" (domain.size - ZK_ROWS): - /// `col[0], ..., col[n-2]` + /// Should have length `c-1`, + /// `col[0], ..., col[c-2]` pub col: Rc>, - /// Partial sums of `col`: `acc[0] = 0, acc[i] = col[0] + ... + col[i-1], i = 1,...,n-1` + /// Partial sums of `col`: + /// `acc[0] = 0, acc[i+1] = col[0] + ... + col[i], i = 0,...,c-2` pub acc: Rc>, + /// `p(X) = X - w^(c-1)` -- disables the constraint for `i = c-1`, i.e. between `acc[c-1]` and `acc[c]`. pub not_last: FieldColumn, } @@ -40,7 +49,7 @@ impl ColumnSumPolys { } /// Returns `col[0], col[0] + col[1], ..., col[0] + col[1] + ... + col[n-1]`. - fn partial_sums(col: &[F]) -> Vec { + pub fn partial_sums(col: &[F]) -> Vec { col.iter() .scan(F::zero(), |state, &x| { *state += x; @@ -56,6 +65,9 @@ impl ProverGadget for ColumnSumPolys { } fn constraints(&self) -> Vec> { + // A degree `n` polynomial is computed using evaluations at `4n` points. + // Still it's convenient, as we aggregate the constraints in the evaluation form + // over the `4x` domain. let col = &self.col.evals_4x; let acc = &self.acc.evals_4x; let acc_shifted = &self.acc.shifted_4x(); @@ -93,7 +105,7 @@ mod tests { use super::*; - fn _test_column_sum_gadget(hiding: bool) { + fn _column_sum_gadget(hiding: bool) { let rng = &mut test_rng(); let log_n = 10; @@ -117,8 +129,8 @@ mod tests { } #[test] - fn test_column_sum_gadget() { - _test_column_sum_gadget(false); - _test_column_sum_gadget(true); + fn column_sum_gadget() { + _column_sum_gadget(false); + _column_sum_gadget(true); } } From 16b2661c25a729c4c8f227c24cc326ff54f990ac Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Mon, 7 Apr 2025 14:48:59 +0200 Subject: [PATCH 23/23] ColumnSum gadget documented - 2 --- w3f-plonk-common/src/gadgets/column_sum.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/w3f-plonk-common/src/gadgets/column_sum.rs b/w3f-plonk-common/src/gadgets/column_sum.rs index 2a3aec4..8bda561 100644 --- a/w3f-plonk-common/src/gadgets/column_sum.rs +++ b/w3f-plonk-common/src/gadgets/column_sum.rs @@ -12,7 +12,7 @@ use crate::{Column, FieldColumn}; /// /// Let `c` be the domain "capacity" (`c = domain.size - ZK_ROWS`). /// The gadget populates and constrains a witness column `acc`, -/// such that `acc[i] = acc[i-1] + col[i-1], i = 1,...,c-1`. +/// such that `acc[i+1] = acc[i] + col[i], i = 0,...,c-2`. /// Then `acc[c-1] = acc[0] + (col[0] + ... + col[c-2]) = acc[0] + sum(col[0..c-1])`. /// `acc[0]` and `acc[c-1]` have to be additionally constrained. pub struct ColumnSumPolys {