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::proof::ProofMode;
use crate::reduce_and_fold::{DoryProverState, DoryVerifierState, FinalCheck};
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");
prover_state.apply_fold_scalars(&gamma);
#[cfg(feature = "zk")]
let scalar_product_proof = if Mo::BLINDING {
Some(prover_state.scalar_product_proof(transcript))
} else {
None
};
let final_message = if Mo::BLINDING {
None
} else {
let msg = prover_state.compute_final_message();
transcript.append_serde(b"final_e1", &msg.e1);
transcript.append_serde(b"final_e2", &msg.e2);
Some(msg)
};
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,
};
assert!(
proof.mode().is_ok(),
"prover constructed a malformed proof shape"
);
#[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(),
});
}
if nu > sigma {
return Err(DoryError::InvalidSize {
expected: sigma,
actual: nu,
});
}
let mode = proof.mode()?;
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, zk_final) = match mode {
ProofMode::Zk {
e2,
y_com,
sigma1,
sigma2,
scalar_product,
} => {
use crate::reduce_and_fold::{absorb_sigma2_proof, verify_sigma1_proof};
transcript.append_serde(b"vmv_e2", e2);
transcript.append_serde(b"vmv_y_com", y_com);
verify_sigma1_proof::<E, T>(e2, y_com, sigma1, &setup, transcript)?;
let sigma2_c = absorb_sigma2_proof::<E, T>(sigma2, transcript);
(*e2, Some((scalar_product, sigma2, sigma2_c)))
}
ProofMode::Transparent(..) => (setup.g2_0.scale(&evaluation), None),
};
#[cfg(not(feature = "zk"))]
let e2 = setup.g2_0.scale(&evaluation);
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")]
if let Some((sp, sigma2, sigma2_c)) = zk_final {
use crate::reduce_and_fold::absorb_scalar_product_proof;
let sigma_c = absorb_scalar_product_proof::<E, T>(sp, transcript);
let d = transcript.challenge_scalar(b"d");
return verifier_state.verify_final(
FinalCheck::Zk {
scalar_product: sp,
sigma_c,
sigma2,
sigma2_c,
},
&gamma,
&d,
);
}
let msg = match mode {
ProofMode::Transparent(msg, _) => msg,
#[cfg(feature = "zk")]
ProofMode::Zk { .. } => return Err(DoryError::InvalidProof),
};
transcript.append_serde(b"final_e1", &msg.e1);
transcript.append_serde(b"final_e2", &msg.e2);
let d = transcript.challenge_scalar(b"d");
verifier_state.verify_final(FinalCheck::Transparent(msg), &gamma, &d)
}