Skip to content

Commit 002f598

Browse files
committed
Proper computation of PCS domain size requirements
1 parent 95e271d commit 002f598

File tree

1 file changed

+66
-49
lines changed

1 file changed

+66
-49
lines changed

src/ring.rs

Lines changed: 66 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Ring VRF.
22
//!
33
//! This module is gated by the `ring` feature.
4+
45
use crate::*;
56
use ark_ec::{
67
pairing::Pairing,
@@ -22,6 +23,35 @@ pub const ACCUMULATOR_BASE_SEED: &[u8] =
2223
pub const PADDING_SEED: &[u8] =
2324
b"umbra quae vacuum implet ab animabus perditis relictum inter tenebras resonans";
2425

26+
/// Max ring size that can be managed with the given PCS domain size.
27+
pub const fn max_ring_size_from_pcs_domain_size<S: Suite>(pcs_domain_size: usize) -> usize {
28+
let piop_domain_size = piop_domain_size_from_pcs_domain_size(pcs_domain_size);
29+
max_ring_size_from_piop_domain_size::<S>(piop_domain_size)
30+
}
31+
32+
/// PCS domain size required to manage the given ring size.
33+
pub const fn pcs_domain_size<S: Suite>(ring_size: usize) -> usize {
34+
3 * piop_domain_size::<S>(ring_size) + 1
35+
}
36+
37+
/// Max ring size that can be managed with the given PIOP domain size.
38+
const fn max_ring_size_from_piop_domain_size<S: Suite>(piop_domain_size: usize) -> usize {
39+
piop_domain_size - (4 + ScalarField::<S>::MODULUS_BIT_SIZE as usize)
40+
}
41+
42+
/// PIOP domain size required to manage the given ring size.
43+
///
44+
/// Next power of two after accouting for 3 ZK + 1 extra point used internally.
45+
const fn piop_domain_size<S: Suite>(ring_size: usize) -> usize {
46+
(ring_size + 4 + ScalarField::<S>::MODULUS_BIT_SIZE as usize).next_power_of_two()
47+
}
48+
49+
/// A properly constructed PCS domain has size equal to 3*piop_domain_size+1,
50+
/// with piop_domain_size a power of 2.
51+
const fn piop_domain_size_from_pcs_domain_size(pcs_domain_size: usize) -> usize {
52+
1 << ((pcs_domain_size - 1) / 3).ilog2()
53+
}
54+
2555
/// Ring suite.
2656
pub trait RingSuite: PedersenSuite
2757
where
@@ -191,33 +221,7 @@ where
191221
pub piop: PiopParams<S>,
192222
}
193223

194-
/// Evaluation domain size required for the given ring size.
195-
///
196-
/// This determines the size of the [`PcsParams`] multiples of g1.
197-
#[inline(always)]
198-
pub fn piop_domain_size<S: RingSuite>(ring_size: usize) -> usize
199-
where
200-
BaseField<S>: ark_ff::PrimeField,
201-
CurveConfig<S>: TECurveConfig + Clone,
202-
AffinePoint<S>: TEMapping<CurveConfig<S>>,
203-
{
204-
1 << ark_std::log2(ring_size + ScalarField::<S>::MODULUS_BIT_SIZE as usize + 4)
205-
}
206-
207-
/// PCS params `powers_in_g1` is expected to have length equal to 3*piop_domain_size +1.
208-
/// This is a strong assumption, guaranteed only for internal usage (e.g.
209-
/// `PcsParams` constructed as part of `RingProofParams`)
210-
#[inline(always)]
211-
fn piop_domain_size_from_pcs_params<S: RingSuite>(pcs_params: &PcsParams<S>) -> usize
212-
where
213-
BaseField<S>: ark_ff::PrimeField,
214-
CurveConfig<S>: TECurveConfig + Clone,
215-
AffinePoint<S>: TEMapping<CurveConfig<S>>,
216-
{
217-
pcs_params.powers_in_g1.len() / 3
218-
}
219-
220-
fn piop_params<S: RingSuite>(domain_size: usize) -> PiopParams<S>
224+
pub(crate) fn piop_params<S: RingSuite>(domain_size: usize) -> PiopParams<S>
221225
where
222226
BaseField<S>: ark_ff::PrimeField,
223227
CurveConfig<S>: TECurveConfig + Clone,
@@ -237,7 +241,7 @@ where
237241
CurveConfig<S>: TECurveConfig + Clone,
238242
AffinePoint<S>: TEMapping<CurveConfig<S>>,
239243
{
240-
/// Construct a new ring context suitable to manage the given ring size.
244+
/// Construct new ring proof params suitable for the given ring size.
241245
///
242246
/// Calls into [`RingProofParams::from_rand`] with a `ChaCha20Rng` seeded with `seed`.
243247
pub fn from_seed(ring_size: usize, seed: [u8; 32]) -> Self {
@@ -248,33 +252,37 @@ where
248252

249253
/// Construct a new random ring context suitable for the given ring size.
250254
///
251-
/// Calls into [`RingProofParams::from_srs`] with a randomly generated [`PcsParams`]
252-
/// large enough to be used with the given `ring_size`.
255+
/// Calls into [`RingProofParams::from_srs`] with randomly generated [`PcsParams`]
256+
/// large enough to be used for the given `ring_size`.
253257
pub fn from_rand(ring_size: usize, rng: &mut impl ark_std::rand::RngCore) -> Self {
254258
use ring_proof::pcs::PCS;
255-
let domain_size = piop_domain_size::<S>(ring_size);
256-
let pcs_params = Pcs::<S>::setup(3 * domain_size, rng);
257-
Self::from_srs(ring_size, pcs_params).expect("PCS params is correct")
259+
let max_degree = pcs_domain_size::<S>(ring_size) - 1;
260+
let pcs_params = Pcs::<S>::setup(max_degree, rng);
261+
Self::from_pcs_params(ring_size, pcs_params).expect("PCS params is correct")
258262
}
259263

260264
/// Construct a new random ring context suitable for the given [`PcsParams`].
261265
///
262-
/// Fails if `PcsParams` are not
263-
pub fn from_srs(ring_size: usize, mut pcs_params: PcsParams<S>) -> Result<Self, Error> {
264-
let domain_size = piop_domain_size::<S>(ring_size);
265-
if pcs_params.powers_in_g1.len() <= 3 * domain_size || pcs_params.powers_in_g2.len() < 2 {
266+
/// Fails if the domain representable via the supplied `PcsParams` is not sufficiently
267+
/// large for the given `ring_size`.
268+
///
269+
/// If the domain size of `PcsParams` exceeds the required limit, the extra items are truncated.
270+
pub fn from_pcs_params(ring_size: usize, mut pcs_params: PcsParams<S>) -> Result<Self, Error> {
271+
let pcs_domain_size = pcs_domain_size::<S>(ring_size);
272+
if pcs_params.powers_in_g1.len() < pcs_domain_size || pcs_params.powers_in_g2.len() < 2 {
266273
return Err(Error::InvalidData);
267274
}
268275
// Keep only the required powers of tau.
269-
pcs_params.powers_in_g1.truncate(3 * domain_size + 1);
276+
pcs_params.powers_in_g1.truncate(pcs_domain_size);
270277
pcs_params.powers_in_g2.truncate(2);
278+
let piop_domain_size = piop_domain_size::<S>(ring_size);
271279
Ok(Self {
272280
pcs: pcs_params,
273-
piop: piop_params::<S>(domain_size),
281+
piop: piop_params::<S>(piop_domain_size),
274282
})
275283
}
276284

277-
/// The max ring size this context is able to manage.
285+
/// The max ring size these parameters are able to handle.
278286
#[inline(always)]
279287
pub fn max_ring_size(&self) -> usize {
280288
self.piop.keyset_part_size
@@ -323,15 +331,15 @@ where
323331
RingVerifierKey::<S>::from_commitment_and_kzg_vk(commitment, self.pcs.raw_vk())
324332
}
325333

326-
/// Builder for incremental construction of verifier key.
334+
/// Builder for incremental construction of the verifier key.
327335
///
328-
/// This returns both the builder and `RingBuilderKey`, which may be used to append
329-
/// new key items to the ring builder (as it implements `SrsLookup`).
336+
/// This also returns a `RingBuilderPcsParams` which may be used to append new key items
337+
/// to the `RingVerifierKeyBuilder` instance its tne `SrsLookup` implementation.
330338
pub fn verifier_key_builder(&self) -> (RingVerifierKeyBuilder<S>, RingBuilderPcsParams<S>) {
331339
type RingBuilderKey<S> =
332340
ring_proof::ring::RingBuilderKey<BaseField<S>, <S as RingSuite>::Pairing>;
333-
let domain_size = piop_domain_size_from_pcs_params::<S>(&self.pcs);
334-
let builder_key = RingBuilderKey::<S>::from_srs(&self.pcs, domain_size);
341+
let piop_domain_size = piop_domain_size_from_pcs_domain_size(self.pcs.powers_in_g1.len());
342+
let builder_key = RingBuilderKey::<S>::from_srs(&self.pcs, piop_domain_size);
335343
let builder_pcs_params = RingBuilderPcsParams(builder_key.lis_in_g1);
336344
let builder = RingVerifierKeyBuilder::new(self, &builder_pcs_params);
337345
(builder, builder_pcs_params)
@@ -348,7 +356,7 @@ where
348356

349357
/// Constructs a `RingVerifier` from `RingVerifierKey` without no `RingProofParams`.
350358
///
351-
/// While this approach is slightly less efficient than using a pre-constructed `RingProofParams`,
359+
/// While this approach is slightly less efficient than using pre-constructed `RingProofParams`,
352360
/// as some parameters need to be computed on-the-fly, it is beneficial in memory or
353361
/// storage constrained environments. This avoids the need to retain the full `RingProofParams` for
354362
/// ring signature verification. Instead, the `VerifierKey` contains only the essential information
@@ -366,7 +374,7 @@ where
366374

367375
/// Get the padding point.
368376
///
369-
/// This is a point of unknown dlog that can be used to replace of any key during
377+
/// This is a point of unknown dlog that can be used in place of any key during
370378
/// ring construciton.
371379
#[inline(always)]
372380
pub const fn padding_point() -> AffinePoint<S> {
@@ -409,7 +417,7 @@ where
409417
compress,
410418
validate,
411419
)?;
412-
let piop_domain_size = piop_domain_size_from_pcs_params::<S>(&pcs_params);
420+
let piop_domain_size = piop_domain_size_from_pcs_domain_size(pcs_params.powers_in_g1.len());
413421
Ok(Self {
414422
pcs: pcs_params,
415423
piop: piop_params::<S>(piop_domain_size),
@@ -659,6 +667,14 @@ pub(crate) mod testing {
659667
let output = secret.output(input);
660668

661669
let ring_size = params.max_ring_size();
670+
let pcs_dom_size = pcs_domain_size::<S>(ring_size);
671+
assert_eq!(pcs_dom_size, params.pcs.powers_in_g1.len());
672+
assert_eq!(pcs_dom_size / 3, piop_domain_size::<S>(ring_size));
673+
674+
assert_eq!(
675+
max_ring_size_from_pcs_domain_size::<S>(pcs_dom_size),
676+
ring_size
677+
);
662678

663679
let prover_idx = 3;
664680
let mut pks = common::random_vec::<AffinePoint<S>>(ring_size, Some(rng));
@@ -808,7 +824,8 @@ pub(crate) mod testing {
808824
file.read_to_end(&mut buf).unwrap();
809825
let pcs_params =
810826
PcsParams::<Self>::deserialize_uncompressed_unchecked(&mut &buf[..]).unwrap();
811-
RingProofParams::from_srs(crate::ring::testing::TEST_RING_SIZE, pcs_params).unwrap()
827+
RingProofParams::from_pcs_params(crate::ring::testing::TEST_RING_SIZE, pcs_params)
828+
.unwrap()
812829
}
813830

814831
#[allow(unused)]

0 commit comments

Comments
 (0)