use alloc::{string::ToString, vec, vec::Vec};
use bn::{pairing_batch, AffineG1, Fr, G1, G2};
use crate::{error::Error, plonk::transcript::Transcript};
use super::{converter::g1_to_bytes, error::PlonkError, GAMMA, U};
pub(crate) type Digest = AffineG1;
#[derive(Clone, Copy, Debug)]
#[allow(dead_code)]
pub(crate) struct E2 {
pub(crate) a0: Fr,
pub(crate) a1: Fr,
}
#[derive(Clone, Copy, Debug)]
#[allow(dead_code)]
pub(crate) struct LineEvaluationAff {
pub(crate) r0: E2,
pub(crate) r1: E2,
}
#[derive(Clone, Copy, Debug)]
#[allow(dead_code)]
pub(crate) struct KZGVerifyingKey {
pub(crate) g2: [G2; 2], pub(crate) g1: G1,
pub(crate) lines: [[[LineEvaluationAff; 66]; 2]; 2],
}
#[derive(Clone, Debug)]
pub(crate) struct BatchOpeningProof {
pub(crate) h: AffineG1,
pub(crate) claimed_values: Vec<Fr>,
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct OpeningProof {
pub(crate) h: AffineG1,
pub(crate) claimed_value: Fr,
}
fn derive_gamma(
point: &Fr,
digests: Vec<Digest>,
claimed_values: Vec<Fr>,
data_transcript: Option<Vec<u8>>,
) -> Result<Fr, PlonkError> {
let mut transcript = Transcript::new(Some([GAMMA.to_string()].to_vec()));
transcript.bind(GAMMA, &point.into_u256().to_bytes_be())?;
for digest in digests.iter() {
transcript.bind(GAMMA, &g1_to_bytes(digest)?)?;
}
for claimed_value in claimed_values.iter() {
transcript.bind(GAMMA, &claimed_value.into_u256().to_bytes_be())?;
}
if let Some(data_transcript) = data_transcript {
transcript.bind(GAMMA, &data_transcript)?;
}
let gamma_byte = transcript.compute_challenge(GAMMA)?;
let x = Fr::from_bytes_be_mod_order(gamma_byte.as_slice())
.map_err(|e| PlonkError::GeneralError(Error::Field(e)))?;
Ok(x)
}
fn fold(di: Vec<Digest>, fai: Vec<Fr>, ci: Vec<Fr>) -> (AffineG1, Fr) {
let nb_digests = di.len();
let mut folded_evaluations = Fr::zero();
for i in 0..nb_digests {
folded_evaluations += fai[i] * ci[i];
}
let folded_digests = AffineG1::msm(&di, &ci);
(folded_digests, folded_evaluations)
}
pub(crate) fn fold_proof(
digests: Vec<Digest>,
batch_opening_proof: &BatchOpeningProof,
point: &Fr,
data_transcript: Option<Vec<u8>>,
global_transcript: &mut Transcript,
) -> Result<(OpeningProof, AffineG1), PlonkError> {
let nb_digests = digests.len();
if nb_digests != batch_opening_proof.claimed_values.len() {
return Err(PlonkError::InvalidNumberOfDigests);
}
let gamma = derive_gamma(
point,
digests.clone(),
batch_opening_proof.claimed_values.clone(),
data_transcript,
)?;
global_transcript.bind(U, &gamma.into_u256().to_bytes_be())?;
let mut gammai = vec![Fr::zero(); nb_digests];
gammai[0] = Fr::one();
if nb_digests > 1 {
gammai[1] = gamma;
}
for i in 2..nb_digests {
gammai[i] = gammai[i - 1] * gamma;
}
let (folded_digests, folded_evaluations) =
fold(digests, batch_opening_proof.claimed_values.clone(), gammai);
let open_proof = OpeningProof { h: batch_opening_proof.h, claimed_value: folded_evaluations };
Ok((open_proof, folded_digests))
}
pub(crate) fn batch_verify_multi_points(
digests: Vec<Digest>,
proofs: Vec<OpeningProof>,
points: Vec<Fr>,
u: Fr,
vk: &KZGVerifyingKey,
) -> Result<(), PlonkError> {
let nb_digests = digests.len();
let nb_proofs = proofs.len();
let nb_points = points.len();
if nb_digests != nb_proofs || nb_digests != nb_points || nb_digests == 1 {
return Err(PlonkError::InvalidNumberOfDigests);
}
let mut random_numbers = Vec::with_capacity(nb_digests);
random_numbers.push(Fr::one());
for i in 1..nb_digests {
random_numbers.push(u * random_numbers[i - 1]);
}
let mut quotients = Vec::with_capacity(nb_proofs);
for item in proofs.iter() {
quotients.push(item.h);
}
let mut folded_quotients = AffineG1::msm("ients, &random_numbers);
let mut evals = Vec::with_capacity(nb_digests);
for item in proofs.iter() {
evals.push(item.claimed_value);
}
let (mut folded_digests, folded_evals) = fold(digests, evals, random_numbers.clone());
let folded_evals_commit = vk.g1 * folded_evals;
folded_digests = folded_digests - folded_evals_commit.into();
for i in 0..random_numbers.len() {
random_numbers[i] *= points[i];
}
let folded_points_quotients = AffineG1::msm("ients, &random_numbers);
folded_digests = folded_digests + folded_points_quotients;
folded_quotients = -folded_quotients;
let pairing_result =
pairing_batch(&[(folded_digests.into(), vk.g2[0]), (folded_quotients.into(), vk.g2[1])]);
if !pairing_result.is_one() {
return Err(PlonkError::PairingCheckFailed);
}
Ok(())
}