diff --git a/algorithms/src/traits/snark.rs b/algorithms/src/traits/snark.rs index 550a4c93eb..1285d20b8d 100644 --- a/algorithms/src/traits/snark.rs +++ b/algorithms/src/traits/snark.rs @@ -60,6 +60,13 @@ pub trait SNARK { unimplemented!() } + fn trim_universal_setup_parameters( + _long: &Self::UniversalSetupParameters, + _new_config: &Self::UniversalSetupConfig, + ) -> Result { + unimplemented!() + } + fn index>( _circuit: &C, _srs: &Self::UniversalSetupParameters, diff --git a/marlin/src/parameters.rs b/marlin/src/parameters.rs index 47c0c7ffc1..55f7db4984 100644 --- a/marlin/src/parameters.rs +++ b/marlin/src/parameters.rs @@ -40,6 +40,16 @@ use std::{ }, }; +pub struct UniversalSetupConfig { + pub supported_degree : usize, + pub supported_hiding_bound : usize, +} + +pub struct UniversalSetupParameters { + pub powers_of_g : Vec, + +} + #[derive(Derivative)] #[derivative(Clone(bound = ""))] #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] diff --git a/marlin/src/snark.rs b/marlin/src/snark.rs index 41b9b87617..9795d5a272 100644 --- a/marlin/src/snark.rs +++ b/marlin/src/snark.rs @@ -116,6 +116,13 @@ where Ok(srs) } + fn trim_universal_setup_parameters( + _long: &Self::UniversalSetupParameters, + _new_config: &Self::UniversalSetupConfig, + ) -> Result { + unimplemented!() + } + fn index>( circuit: &C, srs: &Self::UniversalSetupParameters, diff --git a/polycommit/src/data_structures.rs b/polycommit/src/data_structures.rs index 880195e0e7..fe54dfda8e 100644 --- a/polycommit/src/data_structures.rs +++ b/polycommit/src/data_structures.rs @@ -17,7 +17,7 @@ use crate::{Arc, String, Vec}; pub use snarkvm_algorithms::fft::DensePolynomial as Polynomial; use snarkvm_fields::{ConstraintFieldError, Field, ToConstraintField}; -use snarkvm_utilities::{error as error_fn, errors::SerializationError, serialize::*, FromBytes, ToBytes}; +use snarkvm_utilities::{error as error_fn, ops::Range, errors::SerializationError, serialize::*, FromBytes, ToBytes}; use core::{ borrow::Borrow, @@ -29,11 +29,89 @@ use rand_core::RngCore; /// Labels a `LabeledPolynomial` or a `LabeledCommitment`. pub type PolynomialLabel = String; -/// Defines the minimal interface for public params for any polynomial -/// commitment scheme. -pub trait PCUniversalParams: CanonicalSerialize + CanonicalDeserialize + Clone + Debug + ToBytes + FromBytes { +/// An enum defining the supported degree bounds +#[derive(Clone, Debug)] +pub enum PCSupportedBounds { + Empty, // Does not support this type of bounds (e.g., no hiding) + Range(Range), // Support a..b bound (b not included) + Subset(Vec) // Support any bound in the vector +} + +impl CanonicalSerialize for PCSupportedBounds { + fn serialize(&self, writer: &mut W) -> Result<(), SerializationError> { + let which_type = match self { + PCSupportedBounds::Empty => 0u8, + PCSupportedBounds::Range(_) => 1u8, + PCSupportedBounds::Subset(_) => 2u8 + }; + which_type.serialize(writer)?; + + match self { + PCSupportedBounds::Empty => { + Ok(()) + } + PCSupportedBounds::Range(range) => { + range.serialize(writer) + } + PCSupportedBounds::Subset(set) => { + set.serialize(writer) + } + } + } + + fn serialized_size(&self) -> usize { + let which_type = match self { + PCSupportedBounds::Empty => 0u8, + PCSupportedBounds::Range(_) => 1u8, + PCSupportedBounds::Subset(_) => 2u8 + }; + + match self { + PCSupportedBounds::Empty => which_type.serialized_size(), + PCSupportedBounds::Range(range) => { + range.serialized_size() + which_type.serialized_size() + } + PCSupportedBounds::Subset(set) => { + set.serialized_size() + which_type.serialized_size() + } + } + } +} + +impl CanonicalDeserialize for PCSupportedBounds { + fn deserialize(reader: &mut R) -> Result { + let which_type = u8::deserialize(reader)?; + if which_type > 2u8 { + return Err(SerializationError::InvalidData); + } + + if which_type == 0 { + Ok(PCSupportedBounds::Empty) + } else if which_type == 1 { + let range = Range::::deserialize(reader)?; + Ok(PCSupportedBounds::Range(range)) + } else if which_type == 2 { + let set = Vec::::deserialize(reader)?; + Ok(PCSupportedBounds::Subset(set)) + } else { + unreachable!() + } + } +} + +/// Defines some common interface for the universal setup config. +pub trait PCUniversalSetupConfig: Clone + Debug { /// Outputs the maximum degree supported by the committer key. fn max_degree(&self) -> usize; + /// Outputs the supported hiding bounds. + fn supported_hiding_bounds(&self) -> PCSupportedBounds; + /// Outputs the supported degree bounds that the can be enforced. + fn supported_degree_bounds(&self) -> PCSupportedBounds; +} + +/// Defines the minimal interface for public params for any polynomial +/// commitment scheme. +pub trait PCUniversalParams: PCUniversalSetupConfig + CanonicalSerialize + CanonicalDeserialize + Clone + Debug + ToBytes + FromBytes { } /// Defines the minimal interface of committer keys for any polynomial diff --git a/polycommit/src/kzg10/data_structures.rs b/polycommit/src/kzg10/data_structures.rs index 06deca99b2..7c008dcf94 100644 --- a/polycommit/src/kzg10/data_structures.rs +++ b/polycommit/src/kzg10/data_structures.rs @@ -30,21 +30,141 @@ use snarkvm_utilities::{ use core::ops::Mul; +/// `UniversalSetupConfig` define what parameters are needed during the setup phase. +#[derive(Clone, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct UniversalSetupConfig { + pub supported_degree : usize, + pub supported_hiding_bounds : PCSupportedBounds, + pub supported_degree_bounds : PCSupportedBounds, + pub supported_g2_powers: PCSupportedBounds, +} + +impl PCUniversalSetupConfig for UniversalSetupConfig { + fn max_degree(&self) -> usize { + self.supported_degree + } + + fn supported_hiding_bounds(&self) -> PCSupportedBounds { + self.supported_hiding_bounds.clone() + } + + fn supported_degree_bounds(&self) -> PCSupportedBounds { + self.supported_degree_bounds.clone() + } +} + +#[derive(Clone, Debug)] +pub enum PowersProvider { + Empty, + Range(Vec), + Subset(BTreeMap) +} + +impl Default for PowersProvider { + fn default() -> Self { + PowersProvider::Empty + } +} + +impl CanonicalSerialize for PowersProvider{ + fn serialize(&self, writer: &mut W) -> Result<(), SerializationError> { + let which_type = match self { + PowersProvider::Empty => 0u8, + PowersProvider::Range(_) => 1u8, + PowersProvider::Subset(_) => 2u8 + }; + which_type.serialize(writer)?; + + match self { + PowersProvider::Empty => { + Ok(()) + } + PowersProvider::Range(vec) => { + vec.serialize(writer) + } + PowersProvider::Subset(map) => { + map.serialize(writer) + } + } + } + + fn serialized_size(&self) -> usize { + let which_type = match self { + PowersProvider::Empty => 0u8, + PowersProvider::Range(_) => 1u8, + PowersProvider::Subset(_) => 2u8 + }; + + match self { + PowersProvider::Empty => { + which_type.serialized_size() + } + PowersProvider::Range(vec) => { + vec.serialized_size() + which_type.serialized_size() + } + PowersProvider::Subset(map) => { + map.serialized_size() + which_type.serialized_size() + } + } + } +} + +impl CanonicalDeserialize for PowersProvider{ + fn deserialize(reader: &mut R) -> Result { + let which_type = u8::deserialize(reader)?; + if which_type > 2u8 { + return Err(SerializationError::InvalidData); + } + + if which_type == 0 { + Ok(PowersProvider::Empty) + } else if which_type == 1 { + let vec = Vec::::deserialize(reader)?; + Ok(PowersProvider::Range(vec)) + } else if which_type == 2 { + let map = BTreeMap::::deserialize(reader)?; + Ok(PowersProvider::Subset(map)) + } else { + unreachable!() + } + } +} + /// `UniversalParams` are the universal parameters for the KZG10 scheme. #[derive(Derivative)] #[derivative(Default(bound = ""), Clone(bound = ""), Debug(bound = ""))] #[derive(CanonicalSerialize, CanonicalDeserialize)] pub struct UniversalParams { - /// Group elements of the form `{ \beta^i G }`, where `i` ranges from 0 to `degree`. + pub supported_degree : usize, + pub supported_hiding_bounds : PCSupportedBounds, + pub supported_degree_bounds : PCSupportedBounds, + pub supported_g2_powers : PCSupportedBounds, + + /// An array of group elements of the form `{ \beta^i G }`, + /// where `i` ranges from 0 to `supported_degree`. pub powers_of_g: Vec, - /// Group elements of the form `{ \beta^i \gamma G }`, where `i` ranges from 0 to `degree`. - pub powers_of_gamma_g: BTreeMap, + + /// An array of group elements of the form `{ \beta^i \gamma G }`, + /// where `i` is in the set of `supported_hiding_bounds`. + pub powers_of_gamma_g: PowersProvider, + + /// An array of group elements of the form `{ \beta^{max_degree - i} G }`, + /// where `i` is in the set of `supported_degree_bounds`. + /// + /// Initially, it supports the entire range of `powers_of_g`, + /// in which case it will be stored as `None`, and the KZG10 + /// implementation will use `powers_of_g`. + /// + pub reverse_powers_of_g: Option>, + /// The generator of G2. pub h: E::G2Affine, /// \beta times the above generator of G2. pub beta_h: E::G2Affine, - /// Group elements of the form `{ \beta^i G2 }`, where `i` ranges from `0` to `-degree`. - pub prepared_neg_powers_of_h: BTreeMap::Prepared>, + + /// Group elements of the form `{ \beta^i G2 }`, where `i` ranges from `0` to `-supported_degree`. + pub prepared_neg_powers_of_h: PowersProvider<::Prepared>, + /// The generator of G2, prepared for use in pairings. #[derivative(Debug = "ignore")] pub prepared_h: ::Prepared, @@ -53,12 +173,24 @@ pub struct UniversalParams { pub prepared_beta_h: ::Prepared, } +impl PCUniversalSetupConfig for UniversalParams { + fn max_degree(&self) -> usize { + self.supported_degree + // which equals the len of `powers_of_g` - 1 + } + + fn supported_hiding_bounds(&self) -> PCSupportedBounds { + self.supported_hiding_bounds.clone() + } + + fn supported_degree_bounds(&self) -> PCSupportedBounds { + self.supported_degree_bounds.clone() + } +} + impl_bytes!(UniversalParams); impl PCUniversalParams for UniversalParams { - fn max_degree(&self) -> usize { - self.powers_of_g.len() - 1 - } } /// `Powers` is used to commit to and create evaluation proofs for a given diff --git a/polycommit/src/kzg10/mod.rs b/polycommit/src/kzg10/mod.rs index ccf95bf8fe..de76e8787c 100644 --- a/polycommit/src/kzg10/mod.rs +++ b/polycommit/src/kzg10/mod.rs @@ -21,7 +21,7 @@ //! proposed by Kate, Zaverucha, and Goldberg ([KZG11](http://cacr.uwaterloo.ca/techreports/2010/cacr2010-10.pdf)). //! This construction achieves extractability in the algebraic group model (AGM). -use crate::{BTreeMap, Error, LabeledPolynomial, PCRandomness, Polynomial, ToString, Vec}; +use crate::{BTreeMap, Error, LabeledPolynomial, PCRandomness, Polynomial, ToString, Vec, PCSupportedBounds}; use snarkvm_algorithms::{ cfg_iter, msm::{FixedBaseMSM, VariableBaseMSM}, @@ -50,62 +50,132 @@ impl KZG10 { /// Constructs public parameters when given as input the maximum degree `degree` /// for the polynomial commitment scheme. pub fn setup( - max_degree: usize, - produce_g2_powers: bool, + config: &UniversalSetupConfig, rng: &mut R, ) -> Result, Error> { + let max_degree = config.supported_degree; if max_degree < 1 { return Err(Error::DegreeIsZero); } let setup_time = start_timer!(|| format!("KZG10::Setup with degree {}", max_degree)); + + // Sample secret parameters (aka toxic waste) let beta = E::Fr::rand(rng); let g = E::G1Projective::rand(rng); let gamma_g = E::G1Projective::rand(rng); let h = E::G2Projective::rand(rng); + // Precompute powers of beta in the scalar field let mut powers_of_beta = vec![E::Fr::one()]; - let mut cur = beta; for _ in 0..max_degree { powers_of_beta.push(cur); cur *= β } + // Compute `powers_of_g` up to the `max_degree` let window_size = FixedBaseMSM::get_mul_window_size(max_degree + 1); - let scalar_bits = E::Fr::size_in_bits(); let g_time = start_timer!(|| "Generating powers of G"); let g_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, g); let powers_of_g = FixedBaseMSM::multi_scalar_mul::(scalar_bits, window_size, &g_table, &powers_of_beta); end_timer!(g_time); + // Normalize them into affine representations + let powers_of_g = E::G1Projective::batch_normalization_into_affine(powers_of_g); + + // Compute `powers_of_gamma_g` depending on the configuration let gamma_g_time = start_timer!(|| "Generating powers of gamma * G"); - let gamma_g_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, gamma_g); - let mut powers_of_gamma_g = FixedBaseMSM::multi_scalar_mul::( - scalar_bits, - window_size, - &gamma_g_table, - &powers_of_beta, - ); - // Add an additional power of gamma_g, because we want to be able to support - // up to D queries. - powers_of_gamma_g.push(powers_of_gamma_g.last().unwrap().mul(beta)); + let powers_of_gamma_g = match &config.supported_hiding_bounds { + PCSupportedBounds::Empty => { + PowersProvider::Empty + } + PCSupportedBounds::Range(range) => { + if range.start == 0 && (range.end == max_degree || range.end == max_degree + 1) { + // reuse the `powers_of_beta` + let gamma_g_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, gamma_g); + let mut powers_of_gamma_g = FixedBaseMSM::multi_scalar_mul::( + scalar_bits, + window_size, + &gamma_g_table, + &powers_of_beta, + ); + if end == max_degree + 1 { + // Add an additional power of gamma_g, because we want to be able to support + // up to D queries. + powers_of_gamma_g.push(powers_of_gamma_g.last().unwrap().mul(beta)); + } + + let powers_of_gamma_g = E::G1Projective::batch_normalization_into_affine(powers_of_gamma_g) + .into_iter() + .enumerate() + .collect(); + + PowersProvider::Range(powers_of_gamma_g) + } else { + let mut powers_of_beta_for_hiding = powers_of_beta[range].into_vec(); + + let window_size = FixedBaseMSM::get_mul_window_size(powers_of_beta_for_hiding.len()); + let gamma_g_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, gamma_g); + let powers_of_gamma_g = FixedBaseMSM::multi_scalar_mul::( + scalar_bits, + window_size, + &gamma_g_table, + &powers_of_beta, + ); + let powers_of_gamma_g = E::G1Projective::batch_normalization_into_affine(powers_of_gamma_g) + .into_iter() + .enumerate() + .collect(); + + PowersProvider::Range(powers_of_gamma_g) + } + } + PCSupportedBounds::Subset(set) => { + let mut powers_of_beta_for_hiding = vec![]; + for i in set.iter() { + powers_of_beta_for_hiding.push(powers_of_beta[i]); + } + + let window_size = FixedBaseMSM::get_mul_window_size(powers_of_beta_for_hiding.len()); + let gamma_g_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, gamma_g); + let powers_of_gamma_g = FixedBaseMSM::multi_scalar_mul::( + scalar_bits, + window_size, + &gamma_g_table, + &powers_of_beta, + ); + let powers_of_gamma_g = set.iter().zip( + E::G1Projective::batch_normalization_into_affine(powers_of_gamma_g).iter() + ).collect(); + + PowersProvider::Subset(powers_of_gamma_g) + } + }; end_timer!(gamma_g_time); - let powers_of_g = E::G1Projective::batch_normalization_into_affine(powers_of_g); - let powers_of_gamma_g = E::G1Projective::batch_normalization_into_affine(powers_of_gamma_g) - .into_iter() - .enumerate() - .collect(); - let prepared_neg_powers_of_h_time = start_timer!(|| "Generating negative powers of h in G2"); - let prepared_neg_powers_of_h = if produce_g2_powers { - let mut neg_powers_of_beta = vec![E::Fr::one()]; - let mut cur = E::Fr::one() / β - for _ in 0..max_degree { - neg_powers_of_beta.push(cur); - cur /= β + let prepared_neg_powers_of_h = match &config.supported_g2_powers.clone() { + PCSupportedBounds::Empty => { + PowersProvider::Empty + } + PCSupportedBounds::Range(range) => { + let mut multiply = E::Fr::one() / β + let mut cur = multiply.pow(&[range.start as u64]); + let mut neg_powers_of_beta = vec![cur]; + cur *= &multiply; + for _ in start..end { + neg_powers_of_beta.push(cur); + cur *= &multiply; + } + + // TO FINISH } + PCSupportedBounds::Subset(_) => {} + }; + + let prepared_neg_powers_of_h = if produce_g2_powers { + let neg_h_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, h); let neg_powers_of_h = FixedBaseMSM::multi_scalar_mul::( @@ -137,8 +207,12 @@ impl KZG10 { let prepared_beta_h = beta_h.prepare(); let pp = UniversalParams { + supported_degree: config.supported_degree, + supported_hiding_bounds: config.supported_hiding_bounds.clone(), + supported_degree_bounds: config.supported_degree_bounds.clone(), powers_of_g, powers_of_gamma_g, + reverse_powers_of_g: None, h, beta_h, prepared_neg_powers_of_h, diff --git a/polycommit/src/lib.rs b/polycommit/src/lib.rs index c32eda615c..2de7519736 100644 --- a/polycommit/src/lib.rs +++ b/polycommit/src/lib.rs @@ -149,6 +149,10 @@ impl> ToBytes for BatchLCProof { /// of evaluation for the corresponding commitments at a query set `Q`, while /// enforcing per-polynomial degree bounds. pub trait PolynomialCommitment: Sized + Clone + Debug { + /// The config for universal parameters setup. For example, it can specify + /// the degree to support and hiding bounds, such that we are not overly + /// generating the parameters + type UniversalSetupConfig: PCUniversalSetupConfig + Clone; /// The universal parameters for the commitment scheme. These are "trimmed" /// down to `Self::CommitterKey` and `Self::VerifierKey` by `Self::trim`. type UniversalParams: PCUniversalParams + Clone; @@ -179,7 +183,13 @@ pub trait PolynomialCommitment: Sized + Clone + Debug { /// Constructs public parameters when given as input the maximum degree `degree` /// for the polynomial commitment scheme. - fn setup(max_degree: usize, rng: &mut R) -> Result; + fn setup(config: &Self::UniversalSetupConfig, rng: &mut R) -> Result; + + /// Resizes the universal parameters for a different config (that needs less information). + fn trim_srs( + parameters: &Self::UniversalParams, + new_config: &Self::UniversalSetupConfig, + ) -> Result; /// Specializes the public parameters for polynomials up to the given `supported_degree` /// and for enforcing degree bounds in the range `1..=supported_degree`. diff --git a/polycommit/src/marlin_pc/mod.rs b/polycommit/src/marlin_pc/mod.rs index c4ee92a4e0..869eef4441 100644 --- a/polycommit/src/marlin_pc/mod.rs +++ b/polycommit/src/marlin_pc/mod.rs @@ -88,6 +88,8 @@ pub struct MarlinKZG10 { _engine: PhantomData, } +pub type UniversalSetupConfig = kzg10::UniversalSetupConfig; + impl PolynomialCommitment for MarlinKZG10 { type BatchProof = Vec; type Commitment = Commitment; @@ -98,6 +100,7 @@ impl PolynomialCommitment for MarlinKZG10 { type Proof = kzg10::Proof; type Randomness = Randomness; type UniversalParams = UniversalParams; + type UniversalSetupConfig = UniversalSetupConfig; type VerifierKey = VerifierKey; /// Constructs public parameters when given as input the maximum degree `max_degree` @@ -761,6 +764,10 @@ impl PolynomialCommitment for MarlinKZG10 { rng, ) } + + fn trim_srs(parameters: &Self::UniversalParams, new_config: &Self::UniversalSetupConfig) -> Result { + todo!() + } } impl MarlinKZG10 { diff --git a/utilities/src/serialize/impls.rs b/utilities/src/serialize/impls.rs index 846fc2d9e6..639a54a9e7 100644 --- a/utilities/src/serialize/impls.rs +++ b/utilities/src/serialize/impls.rs @@ -20,6 +20,7 @@ pub use crate::{ Write, {self}, }, + ops::Range, FromBytes, ToBytes, Vec, @@ -481,6 +482,35 @@ where } } +impl CanonicalSerialize for Range +where + T: CanonicalSerialize, +{ + fn serialize(&self, writer: &mut W) -> Result<(), SerializationError> { + self.start.serialize(writer)?; + self.end.serialize(writer) + } + + fn serialized_size(&self) -> usize { + self.start.serialized_size() + self.end.serialized_size() + } +} + +impl CanonicalDeserialize for Range +where + T: CanonicalDeserialize, +{ + fn deserialize(reader: &mut R) -> Result { + let start = T::deserialize(reader)?; + let end = T::deserialize(reader)?; + + Ok(Self { + start, + end + }) + } +} + #[cfg(test)] mod test { use super::*;