use crate::error::DoryError;
use crate::messages::VMVMessage;
use crate::mode::Mode;
use crate::primitives::arithmetic::{DoryRoutines, Field, Group, PairingCurve};
use crate::primitives::poly::MultilinearLagrange;
use crate::primitives::transcript::Transcript;
use crate::proof::DoryProof;
use crate::reduce_and_fold::{DoryProverState, DoryVerifierState};
use crate::setup::{ProverSetup, VerifierSetup};
#[allow(clippy::type_complexity)]
#[allow(clippy::too_many_arguments)]
#[tracing::instrument(skip_all, name = "create_evaluation_proof")]
pub fn create_evaluation_proof<F, E, M1, M2, T, P, Mo>(
polynomial: &P,
point: &[F],
row_commitments: Option<Vec<E::G1>>,
commit_blind: F,
nu: usize,
sigma: usize,
setup: &ProverSetup<E>,
transcript: &mut T,
) -> Result<(DoryProof<E::G1, E::G2, E::GT>, Option<F>), DoryError>
where
F: Field,
E: PairingCurve,
E::G1: Group<Scalar = F>,
E::G2: Group<Scalar = F>,
E::GT: Group<Scalar = F>,
M1: DoryRoutines<E::G1>,
M2: DoryRoutines<E::G2>,
T: Transcript<Curve = E>,
P: MultilinearLagrange<F>,
Mo: Mode,
{
if point.len() != nu + sigma {
return Err(DoryError::InvalidPointDimension {
expected: nu + sigma,
actual: point.len(),
});
}
if nu > sigma {
return Err(DoryError::InvalidSize {
expected: sigma,
actual: nu,
});
}
let (row_commitments, commit_blind) = match row_commitments {
Some(rc) => (rc, commit_blind),
None => {
let (_, rc, blind) = polynomial.commit::<E, Mo, M1>(nu, sigma, setup)?;
(rc, blind)
}
};
let (left_vec, right_vec) = polynomial.compute_evaluation_vectors(point, nu, sigma);
let v_vec = polynomial.vector_matrix_product(&left_vec, nu, sigma);
let mut padded_row_commitments = row_commitments.clone();
if nu < sigma {
padded_row_commitments.resize(1 << sigma, E::G1::identity());
}
let (r_c, r_d2, r_e1, r_e2): (F, F, F, F) =
(Mo::sample(), Mo::sample(), Mo::sample(), Mo::sample());
let g2_fin = &setup.g2_vec[0];
let t_vec_v = M1::msm(&padded_row_commitments, &v_vec);
let c = Mo::mask(E::pair(&t_vec_v, g2_fin), &setup.ht, &r_c);
let d2 = Mo::mask(
E::pair(&M1::msm(&setup.g1_vec[..1 << sigma], &v_vec), g2_fin),
&setup.ht,
&r_d2,
);
let e1 = Mo::mask(M1::msm(&row_commitments, &left_vec), &setup.h1, &r_e1);
let vmv_message = VMVMessage { c, d2, e1 };
transcript.append_serde(b"vmv_c", &vmv_message.c);
transcript.append_serde(b"vmv_d2", &vmv_message.d2);
transcript.append_serde(b"vmv_e1", &vmv_message.e1);
#[cfg(feature = "zk")]
let (zk_e2, zk_y_com, zk_sigma1, zk_sigma2, zk_r_y) = if Mo::BLINDING {
use crate::reduce_and_fold::{generate_sigma1_proof, generate_sigma2_proof};
let y = polynomial.evaluate(point);
let r_y: F = Mo::sample();
let e2 = Mo::mask(g2_fin.scale(&y), &setup.h2, &r_e2);
let y_com = setup.g1_vec[0].scale(&y) + setup.h1.scale(&r_y);
transcript.append_serde(b"vmv_e2", &e2);
transcript.append_serde(b"vmv_y_com", &y_com);
let s1 = generate_sigma1_proof::<E, T>(&y, &r_e2, &r_y, setup, transcript);
let s2 = generate_sigma2_proof::<E, T>(&r_e1, &-r_d2, setup, transcript);
(Some(e2), Some(y_com), Some(s1), Some(s2), Some(r_y))
} else {
(None, None, None, None, None)
};
let v2 = M2::fixed_base_vector_scalar_mul(g2_fin, &v_vec);
let mut padded_right_vec = right_vec.clone();
let mut padded_left_vec = left_vec.clone();
if nu < sigma {
padded_right_vec.resize(1 << sigma, F::zero());
padded_left_vec.resize(1 << sigma, F::zero());
}
let mut prover_state: DoryProverState<'_, E, Mo> = DoryProverState::new(
padded_row_commitments, v2, Some(v_vec), padded_right_vec, padded_left_vec, setup,
);
prover_state.set_initial_blinds(commit_blind, r_c, r_d2, r_e1, r_e2);
let num_rounds = nu.max(sigma);
let mut first_messages = Vec::with_capacity(num_rounds);
let mut second_messages = Vec::with_capacity(num_rounds);
for _round in 0..num_rounds {
let first_msg = prover_state.compute_first_message::<M1, M2>();
transcript.append_serde(b"d1_left", &first_msg.d1_left);
transcript.append_serde(b"d1_right", &first_msg.d1_right);
transcript.append_serde(b"d2_left", &first_msg.d2_left);
transcript.append_serde(b"d2_right", &first_msg.d2_right);
transcript.append_serde(b"e1_beta", &first_msg.e1_beta);
transcript.append_serde(b"e2_beta", &first_msg.e2_beta);
let beta = transcript.challenge_scalar(b"beta");
prover_state.apply_first_challenge::<M1, M2>(&beta);
first_messages.push(first_msg);
let second_msg = prover_state.compute_second_message::<M1, M2>();
transcript.append_serde(b"c_plus", &second_msg.c_plus);
transcript.append_serde(b"c_minus", &second_msg.c_minus);
transcript.append_serde(b"e1_plus", &second_msg.e1_plus);
transcript.append_serde(b"e1_minus", &second_msg.e1_minus);
transcript.append_serde(b"e2_plus", &second_msg.e2_plus);
transcript.append_serde(b"e2_minus", &second_msg.e2_minus);
let alpha = transcript.challenge_scalar(b"alpha");
prover_state.apply_second_challenge::<M1, M2>(&alpha);
second_messages.push(second_msg);
}
let gamma = transcript.challenge_scalar(b"gamma");
#[cfg(feature = "zk")]
let scalar_product_proof = if Mo::BLINDING {
Some(prover_state.scalar_product_proof(transcript))
} else {
None
};
let final_message = prover_state.compute_final_message::<M1, M2>(&gamma);
transcript.append_serde(b"final_e1", &final_message.e1);
transcript.append_serde(b"final_e2", &final_message.e2);
let _d = transcript.challenge_scalar(b"d");
let proof = DoryProof {
vmv_message,
first_messages,
second_messages,
final_message,
nu,
sigma,
#[cfg(feature = "zk")]
e2: zk_e2,
#[cfg(feature = "zk")]
y_com: zk_y_com,
#[cfg(feature = "zk")]
sigma1_proof: zk_sigma1,
#[cfg(feature = "zk")]
sigma2_proof: zk_sigma2,
#[cfg(feature = "zk")]
scalar_product_proof,
};
#[cfg(feature = "zk")]
return Ok((proof, zk_r_y));
#[cfg(not(feature = "zk"))]
Ok((proof, None))
}
#[tracing::instrument(skip_all, name = "verify_evaluation_proof")]
pub fn verify_evaluation_proof<F, E, M1, M2, T>(
commitment: E::GT,
evaluation: F,
point: &[F],
proof: &DoryProof<E::G1, E::G2, E::GT>,
setup: VerifierSetup<E>,
transcript: &mut T,
) -> Result<(), DoryError>
where
F: Field,
E: PairingCurve,
E::G1: Group<Scalar = F>,
E::G2: Group<Scalar = F>,
E::GT: Group<Scalar = F>,
M1: DoryRoutines<E::G1>,
M2: DoryRoutines<E::G2>,
T: Transcript<Curve = E>,
{
let nu = proof.nu;
let sigma = proof.sigma;
if point.len() != nu + sigma {
return Err(DoryError::InvalidPointDimension {
expected: nu + sigma,
actual: point.len(),
});
}
let vmv_message = &proof.vmv_message;
transcript.append_serde(b"vmv_c", &vmv_message.c);
transcript.append_serde(b"vmv_d2", &vmv_message.d2);
transcript.append_serde(b"vmv_e1", &vmv_message.e1);
#[cfg(feature = "zk")]
let (e2, is_zk) = match (&proof.e2, &proof.y_com) {
(Some(pe2), Some(yc)) => {
use crate::reduce_and_fold::{verify_sigma1_proof, verify_sigma2_proof};
transcript.append_serde(b"vmv_e2", pe2);
transcript.append_serde(b"vmv_y_com", yc);
match (&proof.sigma1_proof, &proof.sigma2_proof) {
(Some(s1), Some(s2)) => {
verify_sigma1_proof::<E, T>(pe2, yc, s1, &setup, transcript)?;
verify_sigma2_proof::<E, T>(
&vmv_message.e1,
&vmv_message.d2,
s2,
&setup,
transcript,
)?;
}
_ => return Err(DoryError::InvalidProof),
}
(*pe2, true)
}
(None, None) => (setup.g2_0.scale(&evaluation), false),
_ => return Err(DoryError::InvalidProof),
};
#[cfg(not(feature = "zk"))]
let (e2, _is_zk) = (setup.g2_0.scale(&evaluation), false);
let num_rounds = sigma;
let max_rounds = setup.max_log_n / 2;
if num_rounds > max_rounds
|| proof.first_messages.len() != num_rounds
|| proof.second_messages.len() != num_rounds
{
return Err(DoryError::InvalidProof);
}
let s1_coords: Vec<F> = point[..sigma].to_vec();
let mut s2_coords: Vec<F> = vec![F::zero(); sigma];
s2_coords[..nu].copy_from_slice(&point[sigma..sigma + nu]);
let mut verifier_state = DoryVerifierState::new(
vmv_message.c, commitment, vmv_message.d2, vmv_message.e1, e2, s1_coords, s2_coords, num_rounds,
setup.clone(),
);
for round in 0..num_rounds {
let first_msg = &proof.first_messages[round];
let second_msg = &proof.second_messages[round];
transcript.append_serde(b"d1_left", &first_msg.d1_left);
transcript.append_serde(b"d1_right", &first_msg.d1_right);
transcript.append_serde(b"d2_left", &first_msg.d2_left);
transcript.append_serde(b"d2_right", &first_msg.d2_right);
transcript.append_serde(b"e1_beta", &first_msg.e1_beta);
transcript.append_serde(b"e2_beta", &first_msg.e2_beta);
let beta = transcript.challenge_scalar(b"beta");
transcript.append_serde(b"c_plus", &second_msg.c_plus);
transcript.append_serde(b"c_minus", &second_msg.c_minus);
transcript.append_serde(b"e1_plus", &second_msg.e1_plus);
transcript.append_serde(b"e1_minus", &second_msg.e1_minus);
transcript.append_serde(b"e2_plus", &second_msg.e2_plus);
transcript.append_serde(b"e2_minus", &second_msg.e2_minus);
let alpha = transcript.challenge_scalar(b"alpha");
verifier_state.process_round(first_msg, second_msg, &alpha, &beta)?;
}
let gamma = transcript.challenge_scalar(b"gamma");
#[cfg(feature = "zk")]
let zk_data = if is_zk {
if let Some(ref sp) = proof.scalar_product_proof {
for (l, v) in [
(b"sigma_p1" as &[u8], &sp.p1),
(b"sigma_p2", &sp.p2),
(b"sigma_q", &sp.q),
(b"sigma_r", &sp.r),
] {
transcript.append_serde(l, v);
}
let c = transcript.challenge_scalar(b"sigma_c");
Some((sp, c))
} else {
return Err(DoryError::InvalidProof);
}
} else {
None
};
transcript.append_serde(b"final_e1", &proof.final_message.e1);
transcript.append_serde(b"final_e2", &proof.final_message.e2);
let d = transcript.challenge_scalar(b"d");
#[cfg(feature = "zk")]
let zk = zk_data.as_ref().map(|(sp, c)| (*sp, c));
#[cfg(not(feature = "zk"))]
let zk: Option<(&crate::messages::ScalarProductProof<_, _, _, _>, _)> = None;
verifier_state.verify_final(&proof.final_message, &gamma, &d, zk)
}