use super::linearisation_poly::ProofEvaluations;
use super::proof_system_errors::{ProofError, ProofErrors};
use crate::commitment_scheme::kzg10::{AggregateProof, Commitment, OpeningKey};
use crate::fft::EvaluationDomain;
use crate::proof_system::widget::VerifierKey;
use crate::transcript::TranscriptProtocol;
use dusk_bls12_381::{multiscalar_mul::msm_variable_base, G1Affine, Scalar};
use failure::Error;
use merlin::Transcript;
#[derive(Debug, Eq, PartialEq)]
pub struct Proof {
pub a_comm: Commitment,
pub b_comm: Commitment,
pub c_comm: Commitment,
pub d_comm: Commitment,
pub z_comm: Commitment,
pub t_1_comm: Commitment,
pub t_2_comm: Commitment,
pub t_3_comm: Commitment,
pub t_4_comm: Commitment,
pub w_z_comm: Commitment,
pub w_zw_comm: Commitment,
pub evaluations: ProofEvaluations,
}
impl Proof {
pub(crate) fn verify(
&self,
verifier_key: &VerifierKey,
transcript: &mut Transcript,
opening_key: &OpeningKey,
pub_inputs: &[Scalar],
) -> Result<(), Error> {
let domain = EvaluationDomain::new(verifier_key.n)?;
transcript.append_commitment(b"w_l", &self.a_comm);
transcript.append_commitment(b"w_r", &self.b_comm);
transcript.append_commitment(b"w_o", &self.c_comm);
transcript.append_commitment(b"w_4", &self.d_comm);
let beta = transcript.challenge_scalar(b"beta");
transcript.append_scalar(b"beta", &beta);
let gamma = transcript.challenge_scalar(b"gamma");
transcript.append_commitment(b"z", &self.z_comm);
let alpha = transcript.challenge_scalar(b"alpha");
let range_sep_challenge = transcript.challenge_scalar(b"range separation challenge");
let logic_sep_challenge = transcript.challenge_scalar(b"logic separation challenge");
let ecc_sep_challenge = transcript.challenge_scalar(b"ecc separation challenge");
transcript.append_commitment(b"t_1", &self.t_1_comm);
transcript.append_commitment(b"t_2", &self.t_2_comm);
transcript.append_commitment(b"t_3", &self.t_3_comm);
transcript.append_commitment(b"t_4", &self.t_4_comm);
let z_challenge = transcript.challenge_scalar(b"z");
let z_h_eval = domain.evaluate_vanishing_polynomial(&z_challenge);
let l1_eval = compute_first_lagrange_evaluation(&domain, &z_h_eval, &z_challenge);
let t_eval = self.compute_quotient_evaluation(
&domain,
pub_inputs,
&alpha,
&beta,
&gamma,
&z_challenge,
&z_h_eval,
&l1_eval,
&self.evaluations.perm_eval,
);
let t_comm = self.compute_quotient_commitment(&z_challenge, domain.size());
transcript.append_scalar(b"a_eval", &self.evaluations.a_eval);
transcript.append_scalar(b"b_eval", &self.evaluations.b_eval);
transcript.append_scalar(b"c_eval", &self.evaluations.c_eval);
transcript.append_scalar(b"d_eval", &self.evaluations.d_eval);
transcript.append_scalar(b"a_next_eval", &self.evaluations.a_next_eval);
transcript.append_scalar(b"b_next_eval", &self.evaluations.b_next_eval);
transcript.append_scalar(b"d_next_eval", &self.evaluations.d_next_eval);
transcript.append_scalar(b"left_sig_eval", &self.evaluations.left_sigma_eval);
transcript.append_scalar(b"right_sig_eval", &self.evaluations.right_sigma_eval);
transcript.append_scalar(b"out_sig_eval", &self.evaluations.out_sigma_eval);
transcript.append_scalar(b"q_arith_eval", &self.evaluations.q_arith_eval);
transcript.append_scalar(b"q_c_eval", &self.evaluations.q_c_eval);
transcript.append_scalar(b"q_l_eval", &self.evaluations.q_l_eval);
transcript.append_scalar(b"q_r_eval", &self.evaluations.q_r_eval);
transcript.append_scalar(b"perm_eval", &self.evaluations.perm_eval);
transcript.append_scalar(b"t_eval", &t_eval);
transcript.append_scalar(b"r_eval", &self.evaluations.lin_poly_eval);
let r_comm = self.compute_linearisation_commitment(
&alpha,
&beta,
&gamma,
(
&range_sep_challenge,
&logic_sep_challenge,
&ecc_sep_challenge,
),
&z_challenge,
l1_eval,
&verifier_key,
);
let mut aggregate_proof = AggregateProof::with_witness(self.w_z_comm);
aggregate_proof.add_part((t_eval, t_comm));
aggregate_proof.add_part((self.evaluations.lin_poly_eval, r_comm));
aggregate_proof.add_part((self.evaluations.a_eval, self.a_comm));
aggregate_proof.add_part((self.evaluations.b_eval, self.b_comm));
aggregate_proof.add_part((self.evaluations.c_eval, self.c_comm));
aggregate_proof.add_part((self.evaluations.d_eval, self.d_comm));
aggregate_proof.add_part((
self.evaluations.left_sigma_eval,
verifier_key.permutation.left_sigma,
));
aggregate_proof.add_part((
self.evaluations.right_sigma_eval,
verifier_key.permutation.right_sigma,
));
aggregate_proof.add_part((
self.evaluations.out_sigma_eval,
verifier_key.permutation.out_sigma,
));
let flattened_proof_a = aggregate_proof.flatten(transcript);
let mut shifted_aggregate_proof = AggregateProof::with_witness(self.w_zw_comm);
shifted_aggregate_proof.add_part((self.evaluations.perm_eval, self.z_comm));
shifted_aggregate_proof.add_part((self.evaluations.a_next_eval, self.a_comm));
shifted_aggregate_proof.add_part((self.evaluations.b_next_eval, self.b_comm));
shifted_aggregate_proof.add_part((self.evaluations.d_next_eval, self.d_comm));
let flattened_proof_b = shifted_aggregate_proof.flatten(transcript);
transcript.append_commitment(b"w_z", &self.w_z_comm);
transcript.append_commitment(b"w_z_w", &self.w_zw_comm);
if opening_key
.batch_check(
&[z_challenge, (z_challenge * domain.group_gen)],
&[flattened_proof_a, flattened_proof_b],
transcript,
)
.is_err()
{
return Err(ProofError(ProofErrors::ProofVerificationError.into()).into());
}
Ok(())
}
#[allow(clippy::too_many_arguments)]
fn compute_quotient_evaluation(
&self,
domain: &EvaluationDomain,
pub_inputs: &[Scalar],
alpha: &Scalar,
beta: &Scalar,
gamma: &Scalar,
z_challenge: &Scalar,
z_h_eval: &Scalar,
l1_eval: &Scalar,
z_hat_eval: &Scalar,
) -> Scalar {
let pi_eval = compute_barycentric_eval(pub_inputs, z_challenge, domain);
let alpha_sq = alpha.square();
let a = self.evaluations.lin_poly_eval + pi_eval;
let beta_sig1 = beta * self.evaluations.left_sigma_eval;
let b_0 = self.evaluations.a_eval + beta_sig1 + gamma;
let beta_sig2 = beta * self.evaluations.right_sigma_eval;
let b_1 = self.evaluations.b_eval + beta_sig2 + gamma;
let beta_sig3 = beta * self.evaluations.out_sigma_eval;
let b_2 = self.evaluations.c_eval + beta_sig3 + gamma;
let b_3 = (self.evaluations.d_eval + gamma) * z_hat_eval * alpha;
let b = b_0 * b_1 * b_2 * b_3;
let c = l1_eval * alpha_sq;
(a - b - c) * z_h_eval.invert().unwrap()
}
fn compute_quotient_commitment(&self, z_challenge: &Scalar, n: usize) -> Commitment {
let z_n = z_challenge.pow(&[n as u64, 0, 0, 0]);
let z_two_n = z_challenge.pow(&[2 * n as u64, 0, 0, 0]);
let z_three_n = z_challenge.pow(&[3 * n as u64, 0, 0, 0]);
let t_comm = self.t_1_comm.0
+ self.t_2_comm.0 * z_n
+ self.t_3_comm.0 * z_two_n
+ self.t_4_comm.0 * z_three_n;
Commitment::from_projective(t_comm)
}
#[allow(clippy::too_many_arguments)]
fn compute_linearisation_commitment(
&self,
alpha: &Scalar,
beta: &Scalar,
gamma: &Scalar,
(range_sep_challenge, logic_sep_challenge, ecc_sep_challenge): (&Scalar, &Scalar, &Scalar),
z_challenge: &Scalar,
l1_eval: Scalar,
verifier_key: &VerifierKey,
) -> Commitment {
let mut scalars: Vec<_> = Vec::with_capacity(6);
let mut points: Vec<G1Affine> = Vec::with_capacity(6);
verifier_key.arithmetic.compute_linearisation_commitment(
&mut scalars,
&mut points,
&self.evaluations,
);
verifier_key.range.compute_linearisation_commitment(
&range_sep_challenge,
&mut scalars,
&mut points,
&self.evaluations,
);
verifier_key.logic.compute_linearisation_commitment(
&logic_sep_challenge,
&mut scalars,
&mut points,
&self.evaluations,
);
verifier_key.ecc.compute_linearisation_commitment(
&ecc_sep_challenge,
&mut scalars,
&mut points,
&self.evaluations,
);
verifier_key.permutation.compute_linearisation_commitment(
&mut scalars,
&mut points,
&self.evaluations,
z_challenge,
(alpha, beta, gamma),
&l1_eval,
self.z_comm.0,
);
Commitment::from_projective(msm_variable_base(&points, &scalars))
}
}
fn compute_first_lagrange_evaluation(
domain: &EvaluationDomain,
z_h_eval: &Scalar,
z_challenge: &Scalar,
) -> Scalar {
let n_fr = Scalar::from(domain.size() as u64);
let denom = n_fr * (z_challenge - Scalar::one());
z_h_eval * denom.invert().unwrap()
}
#[warn(clippy::needless_range_loop)]
fn compute_barycentric_eval(
evaluations: &[Scalar],
point: &Scalar,
domain: &EvaluationDomain,
) -> Scalar {
use crate::util::batch_inversion;
use rayon::iter::IntoParallelIterator;
use rayon::prelude::*;
let numerator = (point.pow(&[domain.size() as u64, 0, 0, 0]) - Scalar::one()) * domain.size_inv;
let non_zero_evaluations: Vec<usize> = (0..evaluations.len())
.into_par_iter()
.filter(|&i| {
let evaluation = &evaluations[i];
evaluation != &Scalar::zero()
})
.collect();
let mut denominators: Vec<Scalar> = (0..non_zero_evaluations.len())
.into_par_iter()
.map(|i| {
let index = non_zero_evaluations[i];
(domain.group_gen_inv.pow(&[index as u64, 0, 0, 0]) * point) - Scalar::one()
})
.collect();
batch_inversion(&mut denominators);
let result: Scalar = (0..non_zero_evaluations.len())
.into_par_iter()
.map(|i| {
let eval_index = non_zero_evaluations[i];
let eval = evaluations[eval_index];
denominators[i] * eval
})
.sum();
result * numerator
}