use ark_ec::pairing::{Pairing, PairingOutput};
use ark_ec::{AffineRepr, CurveGroup, Group, VariableBaseMSM};
use ark_ff::{Field, One, PrimeField, Zero};
use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::ops::{MulAssign, Neg};
use ark_std::{cfg_iter, format, string::ToString, vec, vec::Vec};
use dock_crypto_utils::randomized_pairing_check::RandomizedPairingChecker;
#[cfg(feature = "parallel")]
use rayon::prelude::*;
use crate::aggregation::error::AggregationError;
use crate::aggregation::srs::VerifierSRSProjective;
#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
pub struct KZGOpening<G: AffineRepr>(pub G, pub G);
impl<G: AffineRepr> KZGOpening<G> {
pub fn new_from_proj(a: G::Group, b: G::Group) -> Self {
KZGOpening(a.into_affine(), b.into_affine())
}
}
pub(crate) fn verify_kzg_v<E: Pairing>(
v_srs: &VerifierSRSProjective<E>,
final_vkey: &(E::G2Affine, E::G2Affine),
vkey_opening: &KZGOpening<E::G2Affine>,
challenges: &[E::ScalarField],
kzg_challenge: &E::ScalarField,
pairing_checker: &mut RandomizedPairingChecker<E>,
) {
let vpoly_eval_z = polynomial_evaluation_product_form_from_transcript(
challenges,
kzg_challenge,
&E::ScalarField::one(),
);
let ng = v_srs.g.neg().into_affine();
kzg_check_v::<E>(
v_srs,
ng,
*kzg_challenge,
vpoly_eval_z,
final_vkey.0.into_group(),
v_srs.g_alpha,
vkey_opening.0,
pairing_checker,
);
kzg_check_v::<E>(
v_srs,
ng,
*kzg_challenge,
vpoly_eval_z,
final_vkey.1.into_group(),
v_srs.g_beta,
vkey_opening.1,
pairing_checker,
);
}
pub(crate) fn verify_kzg_w<E: Pairing>(
v_srs: &VerifierSRSProjective<E>,
final_wkey: &(E::G1Affine, E::G1Affine),
wkey_opening: &KZGOpening<E::G1Affine>,
challenges: &[E::ScalarField],
r_shift: &E::ScalarField,
kzg_challenge: &E::ScalarField,
pairing_checker: &mut RandomizedPairingChecker<E>,
) {
let fz = polynomial_evaluation_product_form_from_transcript(challenges, kzg_challenge, r_shift);
let zn = kzg_challenge.pow(&[v_srs.n as u64]);
let mut fwz = fz;
fwz.mul_assign(&zn);
let nh = v_srs.h.neg().into_affine();
kzg_check_w::<E>(
v_srs,
nh,
*kzg_challenge,
fwz,
final_wkey.0.into_group(),
v_srs.h_alpha,
wkey_opening.0,
pairing_checker,
);
kzg_check_w::<E>(
v_srs,
nh,
*kzg_challenge,
fwz,
final_wkey.1.into_group(),
v_srs.h_beta,
wkey_opening.1,
pairing_checker,
);
}
fn kzg_check_v<E: Pairing>(
v_srs: &VerifierSRSProjective<E>,
ng: E::G1Affine,
x: E::ScalarField,
y: E::ScalarField,
cf: E::G2,
vk: E::G1,
pi: E::G2Affine,
pairing_checker: &mut RandomizedPairingChecker<E>,
) {
let b = (cf - v_srs.h.mul_bigint(y.into_bigint())).into();
let c = (vk - (v_srs.g.mul_bigint(x.into_bigint()))).into();
pairing_checker.add_multiple_sources_and_target(
&[ng, c],
&[b, pi],
&PairingOutput::<E>::zero(),
);
}
fn kzg_check_w<E: Pairing>(
v_srs: &VerifierSRSProjective<E>,
nh: E::G2Affine,
x: E::ScalarField,
y: E::ScalarField,
cf: E::G1,
wk: E::G2,
pi: E::G1Affine,
pairing_checker: &mut RandomizedPairingChecker<E>,
) {
let a = (cf - (v_srs.g.mul_bigint(y.into_bigint()))).into();
let d = (wk - (v_srs.h.mul_bigint(x.into_bigint()))).into();
pairing_checker.add_multiple_sources_and_target(
&[a, pi],
&[nh, d],
&PairingOutput::<E>::zero(),
);
}
fn create_kzg_opening<G: AffineRepr>(
srs_powers_alpha_table: &[G], srs_powers_beta_table: &[G], poly: DensePolynomial<G::ScalarField>,
eval_poly: G::ScalarField,
kzg_challenge: &G::ScalarField,
) -> Result<KZGOpening<G>, AggregationError> {
let mut neg_kzg_challenge = *kzg_challenge;
neg_kzg_challenge = neg_kzg_challenge.neg();
if poly.coeffs().len() != srs_powers_alpha_table.len() {
return Err(AggregationError::InvalidSRS(
format!(
"SRS len {} != coefficients len {}",
srs_powers_alpha_table.len(),
poly.coeffs().len(),
)
.to_string(),
));
}
let quotient_polynomial = &(&poly - &DensePolynomial::from_coefficients_vec(vec![eval_poly]))
/ &(DensePolynomial::from_coefficients_vec(vec![neg_kzg_challenge, G::ScalarField::one()]));
let mut quotient_polynomial_coeffs = quotient_polynomial.coeffs;
quotient_polynomial_coeffs.resize(srs_powers_alpha_table.len(), <G::ScalarField>::zero());
let quotient_repr = cfg_iter!(quotient_polynomial_coeffs)
.map(|s| s.into_bigint())
.collect::<Vec<_>>();
assert_eq!(
quotient_polynomial_coeffs.len(),
srs_powers_alpha_table.len()
);
assert_eq!(
quotient_polynomial_coeffs.len(),
srs_powers_beta_table.len()
);
let (a, b) = (
G::Group::msm_bigint(&srs_powers_alpha_table, "ient_repr),
G::Group::msm_bigint(&srs_powers_beta_table, "ient_repr),
);
Ok(KZGOpening::new_from_proj(a, b))
}
pub(crate) fn polynomial_evaluation_product_form_from_transcript<F: Field>(
transcript: &[F],
z: &F,
r_shift: &F,
) -> F {
let mut power_zr = *z;
power_zr.mul_assign(r_shift);
let one = F::one();
let mut res = one + transcript[0] * &power_zr;
for x in &transcript[1..] {
power_zr = power_zr.square();
res.mul_assign(one + *x * &power_zr);
}
res
}
fn polynomial_coefficients_from_transcript<F: Field>(transcript: &[F], r_shift: &F) -> Vec<F> {
let mut coefficients = vec![F::one()];
let mut power_2_r = *r_shift;
for (i, x) in transcript.iter().enumerate() {
let n = coefficients.len();
if i > 0 {
power_2_r = power_2_r.square();
}
for j in 0..n {
let coeff = coefficients[j] * &(*x * &power_2_r);
coefficients.push(coeff);
}
}
coefficients
}
pub fn prove_commitment_v<G: AffineRepr>(
srs_powers_alpha_table: &[G],
srs_powers_beta_table: &[G],
transcript: &[G::ScalarField],
kzg_challenge: &G::ScalarField,
) -> Result<KZGOpening<G>, AggregationError> {
let vkey_poly = DensePolynomial::from_coefficients_vec(
polynomial_coefficients_from_transcript(transcript, &G::ScalarField::one()),
);
let vkey_poly_z = polynomial_evaluation_product_form_from_transcript(
&transcript,
kzg_challenge,
&G::ScalarField::one(),
);
create_kzg_opening(
srs_powers_alpha_table,
srs_powers_beta_table,
vkey_poly,
vkey_poly_z,
kzg_challenge,
)
}
pub fn prove_commitment_w<G: AffineRepr>(
srs_powers_alpha_table: &[G],
srs_powers_beta_table: &[G],
transcript: &[G::ScalarField],
r_shift: &G::ScalarField,
kzg_challenge: &G::ScalarField,
) -> Result<KZGOpening<G>, AggregationError> {
let n = srs_powers_alpha_table.len();
let mut fcoeffs = polynomial_coefficients_from_transcript(transcript, r_shift);
let mut fwcoeffs = vec![G::ScalarField::zero(); fcoeffs.len()];
fwcoeffs.append(&mut fcoeffs);
let fw = DensePolynomial::from_coefficients_vec(fwcoeffs);
let fz =
polynomial_evaluation_product_form_from_transcript(&transcript, kzg_challenge, &r_shift);
let zn = kzg_challenge.pow(&[n as u64]);
let mut fwz = fz;
fwz.mul_assign(&zn);
create_kzg_opening(
srs_powers_alpha_table,
srs_powers_beta_table,
fw,
fwz,
kzg_challenge,
)
}