use ark_ec::pairing::Pairing;
use ark_ec::scalar_mul::fixed_base::FixedBase;
use ark_ec::CurveGroup;
use ark_ff::{PrimeField, Zero};
use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial};
use ark_std::{borrow::Borrow, ops::Div, ops::Mul, rand::RngCore, vec::Vec, UniformRand};
use crate::streaming_kzg::{
linear_combination, msm, powers, Commitment, EvaluationProof, VerifierKey,
};
use super::vanishing_polynomial;
pub struct CommitterKey<E: Pairing> {
pub(crate) powers_of_g: Vec<E::G1Affine>,
pub(crate) powers_of_g2: Vec<E::G2Affine>,
}
impl<E: Pairing> From<&CommitterKey<E>> for VerifierKey<E> {
fn from(ck: &CommitterKey<E>) -> VerifierKey<E> {
let max_eval_points = ck.max_eval_points();
let powers_of_g2 = ck.powers_of_g2[..max_eval_points + 1].to_vec();
let powers_of_g = ck.powers_of_g[..max_eval_points].to_vec();
VerifierKey {
powers_of_g,
powers_of_g2,
}
}
}
impl<E: Pairing> CommitterKey<E> {
pub fn new(max_degree: usize, max_eval_points: usize, rng: &mut impl RngCore) -> Self {
let tau = E::ScalarField::rand(rng);
let powers_of_tau = powers(tau, max_degree + 1);
let g = E::G1::rand(rng);
let window_size = FixedBase::get_mul_window_size(max_degree + 1);
let scalar_bits = E::ScalarField::MODULUS_BIT_SIZE as usize;
let g_table = FixedBase::get_window_table(scalar_bits, window_size, g);
let powers_of_g_proj = FixedBase::msm(scalar_bits, window_size, &g_table, &powers_of_tau);
let powers_of_g = E::G1::normalize_batch(&powers_of_g_proj);
let g2 = E::G2::rand(rng).into_affine();
let powers_of_g2 = powers_of_tau
.iter()
.take(max_eval_points + 1)
.map(|t| g2.mul(t).into_affine())
.collect::<Vec<_>>();
CommitterKey {
powers_of_g,
powers_of_g2,
}
}
#[inline]
pub fn max_eval_points(&self) -> usize {
self.powers_of_g2.len() - 1
}
pub fn commit(&self, polynomial: &[E::ScalarField]) -> Commitment<E> {
Commitment(msm::<E>(&self.powers_of_g, polynomial))
}
pub fn index_by(&self, indices: &[usize]) -> Self {
let mut indexed_powers_of_g = vec![E::G1::zero(); self.powers_of_g.len()];
indices
.iter()
.zip(self.powers_of_g.iter())
.for_each(|(&i, &g)| indexed_powers_of_g[i] = indexed_powers_of_g[i] + g);
Self {
powers_of_g2: self.powers_of_g2.clone(),
powers_of_g: E::G1::normalize_batch(indexed_powers_of_g.as_slice()),
}
}
pub fn batch_commit<J>(&self, polynomials: J) -> Vec<Commitment<E>>
where
J: IntoIterator,
J::Item: Borrow<Vec<E::ScalarField>>,
{
polynomials
.into_iter()
.map(|p| self.commit(p.borrow()))
.collect::<Vec<_>>()
}
pub fn open(
&self,
polynomial: &[E::ScalarField],
evalualtion_point: &E::ScalarField,
) -> (E::ScalarField, EvaluationProof<E>) {
let mut quotient = Vec::new();
let mut previous = E::ScalarField::zero();
for &c in polynomial.iter().rev() {
let coefficient = c + previous * evalualtion_point;
quotient.insert(0, coefficient);
previous = coefficient;
}
let (&evaluation, quotient) = quotient
.split_first()
.unwrap_or((&E::ScalarField::zero(), &[]));
let evaluation_proof = msm::<E>(&self.powers_of_g, quotient);
(evaluation, EvaluationProof(evaluation_proof))
}
pub fn open_multi_points(
&self,
polynomial: &[E::ScalarField],
eval_points: &[E::ScalarField],
) -> EvaluationProof<E> {
let z_poly = vanishing_polynomial(eval_points);
let f_poly = DensePolynomial::from_coefficients_slice(polynomial);
let q_poly = f_poly.div(&z_poly);
EvaluationProof(self.commit(&q_poly.coeffs).0)
}
pub fn batch_open_multi_points(
&self,
polynomials: &[&Vec<E::ScalarField>],
eval_points: &[E::ScalarField],
eval_chal: &E::ScalarField,
) -> EvaluationProof<E> {
assert!(eval_points.len() < self.powers_of_g2.len());
let etas = powers(*eval_chal, polynomials.len());
let batched_polynomial =
linear_combination(polynomials, &etas).unwrap_or_else(|| vec![E::ScalarField::zero()]);
self.open_multi_points(&batched_polynomial, eval_points)
}
}