From c6934fce2dd1c3fb18e1f6b8f35ce7e77645f19a Mon Sep 17 00:00:00 2001 From: swasilyev Date: Tue, 11 Jul 2023 14:57:11 +0200 Subject: [PATCH 1/4] Blinding base made external param --- ring/src/lib.rs | 5 +++-- ring/src/piop/params.rs | 14 ++++---------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/ring/src/lib.rs b/ring/src/lib.rs index ec360a9..c974bad 100644 --- a/ring/src/lib.rs +++ b/ring/src/lib.rs @@ -36,8 +36,9 @@ mod tests { let rng = &mut test_rng(); // SETUP per curve and domain + let h = SWAffine::rand(rng); let domain = Domain::new(domain_size, true); - let piop_params = PiopParams::setup(domain.clone(), &mut test_rng()); + let piop_params = PiopParams::setup(domain.clone(), h); let setup_degree = 3 * domain_size; let pcs_params = CS::setup(setup_degree, rng); @@ -52,7 +53,7 @@ mod tests { // PROOF generation let secret = Fr::rand(rng); // prover's secret scalar - let result = piop_params.h.mul(secret) + pk; + let result = h.mul(secret) + pk; let ring_prover = RingProver::init(prover_key, piop_params.clone(), k, Transcript::new(b"ring-vrf-test")); let t_prove = start_timer!(|| "Prove"); let proof = ring_prover.prove(secret); diff --git a/ring/src/piop/params.rs b/ring/src/piop/params.rs index d2b7506..c2c5686 100644 --- a/ring/src/piop/params.rs +++ b/ring/src/piop/params.rs @@ -1,8 +1,6 @@ use ark_ec::{AffineRepr, CurveGroup, Group}; use ark_ec::short_weierstrass::{Affine, SWCurveConfig}; use ark_ff::{BigInteger, PrimeField}; -use ark_std::rand::Rng; -use ark_std::UniformRand; use ark_std::{vec, vec::Vec}; use common::domain::Domain; @@ -26,15 +24,10 @@ pub struct PiopParams> { } impl> PiopParams { - pub fn setup(domain: Domain, rng: &mut R) -> Self { + pub fn setup(domain: Domain, h: 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; - - let h = Affine::::rand(rng); - // let powers_of_h = Self::power_of_2_multiples(scalar_bitlen, h.into_projective()); - // let powers_of_h = CurveGroup::batch_normalization_into_affine(&powers_of_h); - Self { domain, scalar_bitlen, @@ -91,7 +84,7 @@ impl> PiopParams { #[cfg(test)] mod tests { - use ark_ed_on_bls12_381_bandersnatch::{BandersnatchConfig, Fq, Fr}; + use ark_ed_on_bls12_381_bandersnatch::{BandersnatchConfig, Fq, Fr, SWAffine}; use ark_std::{test_rng, UniformRand}; use ark_std::ops::Mul; @@ -103,8 +96,9 @@ mod tests { #[test] fn test_powers_of_h() { let rng = &mut test_rng(); + let h = SWAffine::rand(rng); let domain = Domain::new(1024, false); - let params = PiopParams::::setup(domain, rng); + let params = PiopParams::::setup(domain, h); 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()); From f5fa1fff6d248aad27ed8cde82e65b2f3eb39dbf Mon Sep 17 00:00:00 2001 From: swasilyev Date: Tue, 11 Jul 2023 15:46:33 +0200 Subject: [PATCH 2/4] padding point specified --- ring/Cargo.toml | 1 + ring/src/piop/params.rs | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/ring/Cargo.toml b/ring/Cargo.toml index 8076de6..3d9f384 100644 --- a/ring/Cargo.toml +++ b/ring/Cargo.toml @@ -17,6 +17,7 @@ fflonk.workspace = true merlin.workspace = true rayon = { workspace = true, optional = true } common = { path = "../common" } +blake2 = "0.10" [dev-dependencies] ark-bls12-381 = { version = "0.4", default-features = false, features = ["curve"] } diff --git a/ring/src/piop/params.rs b/ring/src/piop/params.rs index c2c5686..6f0383f 100644 --- a/ring/src/piop/params.rs +++ b/ring/src/piop/params.rs @@ -1,7 +1,7 @@ use ark_ec::{AffineRepr, CurveGroup, Group}; use ark_ec::short_weierstrass::{Affine, SWCurveConfig}; use ark_ff::{BigInteger, PrimeField}; -use ark_std::{vec, vec::Vec}; +use ark_std::{rand, vec, vec::Vec}; use common::domain::Domain; use common::gadgets::sw_cond_add::AffineColumn; @@ -46,7 +46,7 @@ impl> PiopParams { 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_point = Affine::::generator(); //TODO!!! + let padding_point = hash_to_curve::>(b"w3f/ring-proof/common/padding"); let padding = vec![padding_point; padding_len]; let points = [ keys, @@ -82,6 +82,16 @@ impl> PiopParams { } } +// TODO: switch to better hash to curve when available +fn hash_to_curve(message: &[u8]) -> A { + use blake2::Digest; + use ark_std::rand::SeedableRng; + + let seed = blake2::Blake2s::digest(message); + let rng = &mut rand::rngs::StdRng::from_seed(seed.into()); + A::rand(rng) +} + #[cfg(test)] mod tests { use ark_ed_on_bls12_381_bandersnatch::{BandersnatchConfig, Fq, Fr, SWAffine}; From cb4e38b207f582840197deed545507e1d48d66d4 Mon Sep 17 00:00:00 2001 From: swasilyev Date: Thu, 13 Jul 2023 16:04:40 +0200 Subject: [PATCH 3/4] test_rng removed from kzg verification --- common/Cargo.toml | 1 + common/src/transcript.rs | 10 ++++++++++ common/src/verifier.rs | 12 +++++++----- ring/src/ring_verifier.rs | 4 ++-- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/common/Cargo.toml b/common/Cargo.toml index 1b8e914..ca60120 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -16,6 +16,7 @@ ark-serialize.workspace = true fflonk.workspace = true merlin.workspace = true rayon = { workspace = true, optional = true } +rand_chacha = "0.3.1" [dev-dependencies] ark-ed-on-bls12-381-bandersnatch = { version = "0.4", default-features = false } diff --git a/common/src/transcript.rs b/common/src/transcript.rs index bb86219..04851d2 100644 --- a/common/src/transcript.rs +++ b/common/src/transcript.rs @@ -2,7 +2,9 @@ use ark_ff::PrimeField; use ark_poly::GeneralEvaluationDomain; use ark_serialize::CanonicalSerialize; use ark_std::{vec, vec::Vec}; +use ark_std::rand::SeedableRng; use fflonk::pcs::{PCS, PcsParams}; +use rand_chacha::ChaCha20Rng; use crate::{ColumnsCommited, ColumnsEvaluated}; @@ -60,6 +62,8 @@ pub trait Transcript>: Clone { } fn _add_serializable(&mut self, label: &'static [u8], message: &impl CanonicalSerialize); + + fn to_rng(self) -> ChaCha20Rng; } impl> Transcript for merlin::Transcript { @@ -74,4 +78,10 @@ impl> Transcript for merlin::Transcript { message.serialize_uncompressed(&mut buf).unwrap(); self.append_message(label, &buf); } + + fn to_rng(mut self) -> ChaCha20Rng { + let mut buf = [0u8; 32]; + self.challenge_bytes(b"transcript_rng", &mut buf); + ChaCha20Rng::from_seed(buf) + } } \ No newline at end of file diff --git a/common/src/verifier.rs b/common/src/verifier.rs index fe0554b..b0b8caa 100644 --- a/common/src/verifier.rs +++ b/common/src/verifier.rs @@ -1,8 +1,9 @@ use ark_ff::{Field, PrimeField}; use ark_serialize::CanonicalSerialize; -use ark_std::test_rng; use ark_std::{vec, vec::Vec}; +use ark_std::rand::Rng; use fflonk::pcs::{Commitment, PCS, PcsParams}; +use rand_chacha::ChaCha20Rng; use crate::{ColumnsCommited, ColumnsEvaluated, Proof}; use crate::piop::VerifierPiop; @@ -29,11 +30,12 @@ impl, T: Transcript> PlonkVerifier { } } - pub fn verify( + pub fn verify( &self, piop: Piop, proof: Proof, challenges: Challenges, + rng: &mut R, ) -> bool where Piop: VerifierPiop, @@ -63,7 +65,7 @@ impl, T: Transcript> PlonkVerifier { let zeta_omega = zeta * domain_evaluated.omega(); - CS::batch_verify(&self.pcs_vk, vec![cl, lin_comm], vec![challenges.zeta, zeta_omega], vec![agg_y, proof.lin_at_zeta_omega], vec![proof.agg_at_zeta_proof, proof.lin_at_zeta_omega_proof], &mut test_rng()) + CS::batch_verify(&self.pcs_vk, vec![cl, lin_comm], vec![challenges.zeta, zeta_omega], vec![agg_y, proof.lin_at_zeta_omega], vec![proof.agg_at_zeta_proof, proof.lin_at_zeta_omega_proof], rng) } pub fn restore_challenges( @@ -72,7 +74,7 @@ impl, T: Transcript> PlonkVerifier { proof: &Proof, n_polys: usize, n_constraints: usize, - ) -> Challenges//, TranscriptRng) + ) -> (Challenges, ChaCha20Rng) where Commitments: ColumnsCommited, Evaluations: ColumnsEvaluated, @@ -92,7 +94,7 @@ impl, T: Transcript> PlonkVerifier { zeta, nus, }; - challenges //, fiat_shamir_rng(&mut transcript)) + (challenges, transcript.to_rng()) } } diff --git a/ring/src/ring_verifier.rs b/ring/src/ring_verifier.rs index 1deba16..1c0de79 100644 --- a/ring/src/ring_verifier.rs +++ b/ring/src/ring_verifier.rs @@ -33,7 +33,7 @@ impl, Curve: SWCurveConfig> RingVerifier< } pub fn verify_ring_proof(&self, proof: RingProof, result: Affine) -> bool { - let challenges = self.plonk_verifier.restore_challenges( + let (challenges, mut rng) = self.plonk_verifier.restore_challenges( &result, &proof, // '1' accounts for the quotient polynomial that is aggregated together with the columns @@ -53,7 +53,7 @@ impl, Curve: SWCurveConfig> RingVerifier< (init_plus_result.x, init_plus_result.y), ); - self.plonk_verifier.verify(piop, proof, challenges) + self.plonk_verifier.verify(piop, proof, challenges, &mut rng) } pub fn piop_params(&self) -> &PiopParams { From d0d94d8f82cb2165151dedeb193ec3b8ec56b918 Mon Sep 17 00:00:00 2001 From: swasilyev Date: Mon, 17 Jul 2023 17:46:31 +0200 Subject: [PATCH 4/4] seed point added to piop params --- common/src/gadgets/sw_cond_add.rs | 27 ++++++++++++--------------- ring/src/lib.rs | 29 +++++++++++++++++++++++++++-- ring/src/piop/params.rs | 10 ++++++++-- ring/src/piop/prover.rs | 2 +- ring/src/ring_verifier.rs | 9 ++++----- 5 files changed, 52 insertions(+), 25 deletions(-) diff --git a/common/src/gadgets/sw_cond_add.rs b/common/src/gadgets/sw_cond_add.rs index c6242a2..5100807 100644 --- a/common/src/gadgets/sw_cond_add.rs +++ b/common/src/gadgets/sw_cond_add.rs @@ -44,7 +44,8 @@ pub struct CondAdd> { points: AffineColumn, // The polynomial `X - w^{n-1}` in the Lagrange basis not_last: FieldColumn, - pub acc: AffineColumn, // accumulates the (conditional) rolling sum of the points + // Accumulates the (conditional) rolling sum of the points + pub acc: AffineColumn, pub result: P, } @@ -60,29 +61,30 @@ impl CondAdd> where F: FftField, Curve: SWCurveConfig, { - // Populates the acc column starting from the supplied initial point (as 0 doesn't have an affine representation). + // Populates the acc column starting from the supplied seed (as 0 doesn't have an affine SW representation). + // As the SW addition formula used is not complete, the seed must be selected in a way that would prevent + // exceptional cases (doublings or adding the opposite point). // The last point of the input column is ignored, as adding it would made the acc column overflow due the initial point. pub fn init(bitmask: BitColumn, points: AffineColumn>, + seed: Affine, domain: &Domain) -> Self { assert_eq!(bitmask.bits.len(), domain.capacity - 1); assert_eq!(points.points.len(), domain.capacity - 1); let not_last = domain.not_last_row.clone(); - let init = Self::point_in_g1_complement(); - assert!(!init.is_zero()); let acc = bitmask.bits.iter() .zip(points.points.iter()) - .scan(init.clone(), |acc, (&b, point)| { + .scan(seed, |acc, (&b, point)| { if b { *acc = (*acc + point).into_affine(); } Some(*acc) }); - let acc: Vec<_> = ark_std::iter::once(init) + let acc: Vec<_> = ark_std::iter::once(seed) .chain(acc) .collect(); let init_plus_result = acc.last().unwrap(); - let result = init_plus_result.into_group() - init.into_group(); + let result = init_plus_result.into_group() - seed.into_group(); let result = result.into_affine(); let acc = AffineColumn::init(acc, domain); @@ -97,11 +99,6 @@ impl CondAdd> where acc: self.acc.evaluate(z), } } - - //TODO: find - pub fn point_in_g1_complement() -> Affine { - Affine::::generator() - } } @@ -261,15 +258,15 @@ mod tests { let log_n = 10; let n = 2usize.pow(log_n); let domain = Domain::new(n, hiding); + let seed = SWAffine::generator(); let bitmask = random_bitvec(domain.capacity - 1, 0.5, rng); let points = random_vec::(domain.capacity - 1, rng); - let init = CondAdd::point_in_g1_complement(); - let expected_res = init + cond_sum(&bitmask, &points); + let expected_res = seed + cond_sum(&bitmask, &points); let bitmask_col = BitColumn::init(bitmask, &domain); let points_col = AffineColumn::init(points, &domain); - let gadget = CondAdd::init(bitmask_col, points_col, &domain); + let gadget = CondAdd::init(bitmask_col, points_col, seed, &domain); let res = gadget.acc.points.last().unwrap(); assert_eq!(res, &expected_res); diff --git a/ring/src/lib.rs b/ring/src/lib.rs index c974bad..fa6ab92 100644 --- a/ring/src/lib.rs +++ b/ring/src/lib.rs @@ -1,5 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std)] +use ark_ec::short_weierstrass::{Affine, SWCurveConfig}; +use ark_ff::{One, Zero}; use fflonk::pcs::PCS; use common::Proof; @@ -15,10 +17,23 @@ pub mod ring_verifier; pub type RingProof = Proof>::C>, RingEvaluations>; +// Calling the method for a prime-order curve results in an infinite loop. +pub fn find_complement_point() -> Affine { + let mut x = Curve::BaseField::zero(); + loop { + let p = Affine::::get_point_from_x_unchecked(x, false); + if p.is_some() && !p.unwrap().is_in_correct_subgroup_assuming_on_curve() { + return p.unwrap() + } + x = x + Curve::BaseField::one() + } +} + #[cfg(test)] mod tests { use ark_ec::CurveGroup; - use ark_ed_on_bls12_381_bandersnatch::{Fq, Fr, SWAffine}; + use ark_ed_on_bls12_381_bandersnatch::{BandersnatchConfig, Fq, Fr, SWAffine}; + use ark_ff::MontFp; use ark_std::{end_timer, start_timer, test_rng, UniformRand}; use ark_std::rand::Rng; use ark_std::ops::Mul; @@ -27,6 +42,7 @@ mod tests { use common::domain::Domain; use common::test_helpers::*; + use crate::find_complement_point; use crate::piop::params::PiopParams; use crate::ring_prover::RingProver; @@ -37,8 +53,9 @@ mod tests { // SETUP per curve and domain let h = SWAffine::rand(rng); + let seed = find_complement_point::(); let domain = Domain::new(domain_size, true); - let piop_params = PiopParams::setup(domain.clone(), h); + let piop_params = PiopParams::setup(domain.clone(), h, seed); let setup_degree = 3 * domain_size; let pcs_params = CS::setup(setup_degree, rng); @@ -66,6 +83,14 @@ mod tests { assert!(res); } + #[test] + fn test_complement_point() { + let p = find_complement_point::(); + assert!(p.is_on_curve()); + assert!(!p.is_in_correct_subgroup_assuming_on_curve()); + assert_eq!(p, SWAffine::new_unchecked(MontFp!("0"), MontFp!("11982629110561008531870698410380659621661946968466267969586599013782997959645"))) + } + #[test] fn test_ring_proof_kzg() { _test_ring_proof::>(2usize.pow(10)); diff --git a/ring/src/piop/params.rs b/ring/src/piop/params.rs index 6f0383f..f6509f1 100644 --- a/ring/src/piop/params.rs +++ b/ring/src/piop/params.rs @@ -21,10 +21,14 @@ pub struct PiopParams> { // The blinding base, a point from jubjub. pub h: Affine, + + // The point to start the summation from (as zero doesn't have a SW affine representation), + // should be from the jubjub prime-order subgroup complement. + pub seed: Affine, } impl> PiopParams { - pub fn setup(domain: Domain, h: Affine) -> Self { + pub fn setup(domain: Domain, h: Affine, seed: 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; @@ -33,6 +37,7 @@ impl> PiopParams { scalar_bitlen, keyset_part_size, h, + seed, } } @@ -107,8 +112,9 @@ mod tests { fn test_powers_of_h() { let rng = &mut test_rng(); let h = SWAffine::rand(rng); + let seed = SWAffine::rand(rng); let domain = Domain::new(1024, false); - let params = PiopParams::::setup(domain, h); + let params = PiopParams::::setup(domain, h, seed); 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()); diff --git a/ring/src/piop/prover.rs b/ring/src/piop/prover.rs index 2509fce..2f94616 100644 --- a/ring/src/piop/prover.rs +++ b/ring/src/piop/prover.rs @@ -47,7 +47,7 @@ impl> PiopProver let FixedColumns { points, ring_selector } = fixed_columns; let bits = Self::bits_column(¶ms, prover_index_in_keys, secret); let inner_prod = InnerProd::init(ring_selector.clone(), bits.col.clone(), &domain); - let cond_add = CondAdd::init(bits.clone(), points.clone(), &domain); + let cond_add = CondAdd::init(bits.clone(), points.clone(), params.seed, &domain); let booleanity = Booleanity::init(bits.clone()); let cond_add_acc_x = FixedCells::init(cond_add.acc.xs.clone(), &domain); let cond_add_acc_y = FixedCells::init(cond_add.acc.ys.clone(), &domain); diff --git a/ring/src/ring_verifier.rs b/ring/src/ring_verifier.rs index 1c0de79..2268580 100644 --- a/ring/src/ring_verifier.rs +++ b/ring/src/ring_verifier.rs @@ -4,7 +4,6 @@ use ark_ff::PrimeField; use fflonk::pcs::{PCS, RawVerifierKey}; use common::domain::EvaluatedDomain; -use common::gadgets::sw_cond_add::CondAdd; use common::piop::VerifierPiop; use common::verifier::PlonkVerifier; @@ -40,8 +39,8 @@ impl, Curve: SWCurveConfig> RingVerifier< PiopVerifier::::N_COLUMNS + 1, PiopVerifier::::N_CONSTRAINTS, ); - let init = CondAdd::>::point_in_g1_complement(); - let init_plus_result = (init + result).into_affine(); + let seed = self.piop_params.seed; + let seed_plus_result = (seed + result).into_affine(); let domain_eval = EvaluatedDomain::new(self.piop_params.domain.domain(), challenges.zeta, self.piop_params.domain.hiding); let piop = PiopVerifier::init( @@ -49,8 +48,8 @@ impl, Curve: SWCurveConfig> RingVerifier< self.fixed_columns_committed.clone(), proof.column_commitments.clone(), proof.columns_at_zeta.clone(), - (init.x, init.y), - (init_plus_result.x, init_plus_result.y), + (seed.x, seed.y), + (seed_plus_result.x, seed_plus_result.y), ); self.plonk_verifier.verify(piop, proof, challenges, &mut rng)