* spark-based commitments to R1CS matrices * small fixesmain
@ -0,0 +1,30 @@ | 
				
			|||
pub trait Math {
 | 
				
			|||
  fn pow2(self) -> usize;
 | 
				
			|||
  fn get_bits(self, num_bits: usize) -> Vec<bool>;
 | 
				
			|||
  fn log_2(self) -> usize;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl Math for usize {
 | 
				
			|||
  #[inline]
 | 
				
			|||
  fn pow2(self) -> usize {
 | 
				
			|||
    let base: usize = 2;
 | 
				
			|||
    base.pow(self as u32)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  /// Returns the num_bits from n in a canonical order
 | 
				
			|||
  fn get_bits(self, num_bits: usize) -> Vec<bool> {
 | 
				
			|||
    (0..num_bits)
 | 
				
			|||
      .map(|shift_amount| ((self & (1 << (num_bits - shift_amount - 1))) > 0))
 | 
				
			|||
      .collect::<Vec<bool>>()
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn log_2(self) -> usize {
 | 
				
			|||
    assert_ne!(self, 0);
 | 
				
			|||
 | 
				
			|||
    if self.is_power_of_two() {
 | 
				
			|||
      (1usize.leading_zeros() - self.leading_zeros()) as usize
 | 
				
			|||
    } else {
 | 
				
			|||
      (0usize.leading_zeros() - self.leading_zeros()) as usize
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
@ -0,0 +1,220 @@ | 
				
			|||
//! This module implements `CompCommitmentEngineTrait` using Spartan's SPARK compiler
 | 
				
			|||
//! We also provide a trivial implementation that has the verifier evaluate the sparse polynomials
 | 
				
			|||
use crate::{
 | 
				
			|||
  errors::NovaError,
 | 
				
			|||
  r1cs::R1CSShape,
 | 
				
			|||
  spartan::{math::Math, CompCommitmentEngineTrait},
 | 
				
			|||
  traits::{evaluation::EvaluationEngineTrait, Group, TranscriptReprTrait},
 | 
				
			|||
  CommitmentKey,
 | 
				
			|||
};
 | 
				
			|||
use core::marker::PhantomData;
 | 
				
			|||
use serde::{Deserialize, Serialize};
 | 
				
			|||
 | 
				
			|||
/// A trivial implementation of `ComputationCommitmentEngineTrait`
 | 
				
			|||
pub struct TrivialCompComputationEngine<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> {
 | 
				
			|||
  _p: PhantomData<G>,
 | 
				
			|||
  _p2: PhantomData<EE>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
/// Provides an implementation of a trivial commitment
 | 
				
			|||
#[derive(Clone, Debug, Serialize, Deserialize)]
 | 
				
			|||
#[serde(bound = "")]
 | 
				
			|||
pub struct TrivialCommitment<G: Group> {
 | 
				
			|||
  S: R1CSShape<G>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
/// Provides an implementation of a trivial decommitment
 | 
				
			|||
#[derive(Clone, Debug, Serialize, Deserialize)]
 | 
				
			|||
#[serde(bound = "")]
 | 
				
			|||
pub struct TrivialDecommitment<G: Group> {
 | 
				
			|||
  _p: PhantomData<G>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
/// Provides an implementation of a trivial evaluation argument
 | 
				
			|||
#[derive(Clone, Debug, Serialize, Deserialize)]
 | 
				
			|||
#[serde(bound = "")]
 | 
				
			|||
pub struct TrivialEvaluationArgument<G: Group> {
 | 
				
			|||
  _p: PhantomData<G>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<G: Group> TranscriptReprTrait<G> for TrivialCommitment<G> {
 | 
				
			|||
  fn to_transcript_bytes(&self) -> Vec<u8> {
 | 
				
			|||
    self.S.to_transcript_bytes()
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> CompCommitmentEngineTrait<G, EE>
 | 
				
			|||
  for TrivialCompComputationEngine<G, EE>
 | 
				
			|||
{
 | 
				
			|||
  type Decommitment = TrivialDecommitment<G>;
 | 
				
			|||
  type Commitment = TrivialCommitment<G>;
 | 
				
			|||
  type EvaluationArgument = TrivialEvaluationArgument<G>;
 | 
				
			|||
 | 
				
			|||
  /// commits to R1CS matrices
 | 
				
			|||
  fn commit(
 | 
				
			|||
    _ck: &CommitmentKey<G>,
 | 
				
			|||
    S: &R1CSShape<G>,
 | 
				
			|||
  ) -> Result<(Self::Commitment, Self::Decommitment), NovaError> {
 | 
				
			|||
    Ok((
 | 
				
			|||
      TrivialCommitment { S: S.clone() },
 | 
				
			|||
      TrivialDecommitment {
 | 
				
			|||
        _p: Default::default(),
 | 
				
			|||
      },
 | 
				
			|||
    ))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  /// proves an evaluation of R1CS matrices viewed as polynomials
 | 
				
			|||
  fn prove(
 | 
				
			|||
    _ck: &CommitmentKey<G>,
 | 
				
			|||
    _ek: &EE::ProverKey,
 | 
				
			|||
    _S: &R1CSShape<G>,
 | 
				
			|||
    _decomm: &Self::Decommitment,
 | 
				
			|||
    _comm: &Self::Commitment,
 | 
				
			|||
    _r: &(&[G::Scalar], &[G::Scalar]),
 | 
				
			|||
    _transcript: &mut G::TE,
 | 
				
			|||
  ) -> Result<Self::EvaluationArgument, NovaError> {
 | 
				
			|||
    Ok(TrivialEvaluationArgument {
 | 
				
			|||
      _p: Default::default(),
 | 
				
			|||
    })
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  /// verifies an evaluation of R1CS matrices viewed as polynomials
 | 
				
			|||
  fn verify(
 | 
				
			|||
    _vk: &EE::VerifierKey,
 | 
				
			|||
    comm: &Self::Commitment,
 | 
				
			|||
    r: &(&[G::Scalar], &[G::Scalar]),
 | 
				
			|||
    _arg: &Self::EvaluationArgument,
 | 
				
			|||
    _transcript: &mut G::TE,
 | 
				
			|||
  ) -> Result<(G::Scalar, G::Scalar, G::Scalar), NovaError> {
 | 
				
			|||
    let (r_x, r_y) = r;
 | 
				
			|||
    let evals = SparsePolynomial::<G>::multi_evaluate(&[&comm.S.A, &comm.S.B, &comm.S.C], r_x, r_y);
 | 
				
			|||
    Ok((evals[0], evals[1], evals[2]))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
mod product;
 | 
				
			|||
mod sparse;
 | 
				
			|||
 | 
				
			|||
use sparse::{SparseEvaluationArgument, SparsePolynomial, SparsePolynomialCommitment};
 | 
				
			|||
 | 
				
			|||
/// A non-trivial implementation of `CompCommitmentEngineTrait` using Spartan's SPARK compiler
 | 
				
			|||
pub struct SparkEngine<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> {
 | 
				
			|||
  _p: PhantomData<G>,
 | 
				
			|||
  _p2: PhantomData<EE>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
/// An implementation of Spark decommitment
 | 
				
			|||
#[derive(Clone, Serialize, Deserialize)]
 | 
				
			|||
#[serde(bound = "")]
 | 
				
			|||
pub struct SparkDecommitment<G: Group> {
 | 
				
			|||
  A: SparsePolynomial<G>,
 | 
				
			|||
  B: SparsePolynomial<G>,
 | 
				
			|||
  C: SparsePolynomial<G>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<G: Group> SparkDecommitment<G> {
 | 
				
			|||
  fn new(S: &R1CSShape<G>) -> Self {
 | 
				
			|||
    let ell = (S.num_cons.log_2(), S.num_vars.log_2() + 1);
 | 
				
			|||
    let A = SparsePolynomial::new(ell, &S.A);
 | 
				
			|||
    let B = SparsePolynomial::new(ell, &S.B);
 | 
				
			|||
    let C = SparsePolynomial::new(ell, &S.C);
 | 
				
			|||
 | 
				
			|||
    Self { A, B, C }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn commit(&self, ck: &CommitmentKey<G>) -> SparkCommitment<G> {
 | 
				
			|||
    let comm_A = self.A.commit(ck);
 | 
				
			|||
    let comm_B = self.B.commit(ck);
 | 
				
			|||
    let comm_C = self.C.commit(ck);
 | 
				
			|||
 | 
				
			|||
    SparkCommitment {
 | 
				
			|||
      comm_A,
 | 
				
			|||
      comm_B,
 | 
				
			|||
      comm_C,
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
/// An implementation of Spark commitment
 | 
				
			|||
#[derive(Clone, Serialize, Deserialize)]
 | 
				
			|||
#[serde(bound = "")]
 | 
				
			|||
pub struct SparkCommitment<G: Group> {
 | 
				
			|||
  comm_A: SparsePolynomialCommitment<G>,
 | 
				
			|||
  comm_B: SparsePolynomialCommitment<G>,
 | 
				
			|||
  comm_C: SparsePolynomialCommitment<G>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<G: Group> TranscriptReprTrait<G> for SparkCommitment<G> {
 | 
				
			|||
  fn to_transcript_bytes(&self) -> Vec<u8> {
 | 
				
			|||
    let mut bytes = self.comm_A.to_transcript_bytes();
 | 
				
			|||
    bytes.extend(self.comm_B.to_transcript_bytes());
 | 
				
			|||
    bytes.extend(self.comm_C.to_transcript_bytes());
 | 
				
			|||
    bytes
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
/// Provides an implementation of a trivial evaluation argument
 | 
				
			|||
#[derive(Clone, Serialize, Deserialize)]
 | 
				
			|||
#[serde(bound = "")]
 | 
				
			|||
pub struct SparkEvaluationArgument<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> {
 | 
				
			|||
  arg_A: SparseEvaluationArgument<G, EE>,
 | 
				
			|||
  arg_B: SparseEvaluationArgument<G, EE>,
 | 
				
			|||
  arg_C: SparseEvaluationArgument<G, EE>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> CompCommitmentEngineTrait<G, EE>
 | 
				
			|||
  for SparkEngine<G, EE>
 | 
				
			|||
{
 | 
				
			|||
  type Decommitment = SparkDecommitment<G>;
 | 
				
			|||
  type Commitment = SparkCommitment<G>;
 | 
				
			|||
  type EvaluationArgument = SparkEvaluationArgument<G, EE>;
 | 
				
			|||
 | 
				
			|||
  /// commits to R1CS matrices
 | 
				
			|||
  fn commit(
 | 
				
			|||
    ck: &CommitmentKey<G>,
 | 
				
			|||
    S: &R1CSShape<G>,
 | 
				
			|||
  ) -> Result<(Self::Commitment, Self::Decommitment), NovaError> {
 | 
				
			|||
    let sparse = SparkDecommitment::new(S);
 | 
				
			|||
    let comm = sparse.commit(ck);
 | 
				
			|||
    Ok((comm, sparse))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  /// proves an evaluation of R1CS matrices viewed as polynomials
 | 
				
			|||
  fn prove(
 | 
				
			|||
    ck: &CommitmentKey<G>,
 | 
				
			|||
    pk_ee: &EE::ProverKey,
 | 
				
			|||
    S: &R1CSShape<G>,
 | 
				
			|||
    decomm: &Self::Decommitment,
 | 
				
			|||
    comm: &Self::Commitment,
 | 
				
			|||
    r: &(&[G::Scalar], &[G::Scalar]),
 | 
				
			|||
    transcript: &mut G::TE,
 | 
				
			|||
  ) -> Result<Self::EvaluationArgument, NovaError> {
 | 
				
			|||
    let arg_A =
 | 
				
			|||
      SparseEvaluationArgument::prove(ck, pk_ee, &decomm.A, &S.A, &comm.comm_A, r, transcript)?;
 | 
				
			|||
    let arg_B =
 | 
				
			|||
      SparseEvaluationArgument::prove(ck, pk_ee, &decomm.B, &S.B, &comm.comm_B, r, transcript)?;
 | 
				
			|||
    let arg_C =
 | 
				
			|||
      SparseEvaluationArgument::prove(ck, pk_ee, &decomm.C, &S.C, &comm.comm_C, r, transcript)?;
 | 
				
			|||
 | 
				
			|||
    Ok(SparkEvaluationArgument {
 | 
				
			|||
      arg_A,
 | 
				
			|||
      arg_B,
 | 
				
			|||
      arg_C,
 | 
				
			|||
    })
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  /// verifies an evaluation of R1CS matrices viewed as polynomials
 | 
				
			|||
  fn verify(
 | 
				
			|||
    vk_ee: &EE::VerifierKey,
 | 
				
			|||
    comm: &Self::Commitment,
 | 
				
			|||
    r: &(&[G::Scalar], &[G::Scalar]),
 | 
				
			|||
    arg: &Self::EvaluationArgument,
 | 
				
			|||
    transcript: &mut G::TE,
 | 
				
			|||
  ) -> Result<(G::Scalar, G::Scalar, G::Scalar), NovaError> {
 | 
				
			|||
    let eval_A = arg.arg_A.verify(vk_ee, &comm.comm_A, r, transcript)?;
 | 
				
			|||
    let eval_B = arg.arg_B.verify(vk_ee, &comm.comm_B, r, transcript)?;
 | 
				
			|||
    let eval_C = arg.arg_C.verify(vk_ee, &comm.comm_C, r, transcript)?;
 | 
				
			|||
 | 
				
			|||
    Ok((eval_A, eval_B, eval_C))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
@ -0,0 +1,477 @@ | 
				
			|||
use crate::{
 | 
				
			|||
  errors::NovaError,
 | 
				
			|||
  spartan::{
 | 
				
			|||
    math::Math,
 | 
				
			|||
    polynomial::{EqPolynomial, MultilinearPolynomial},
 | 
				
			|||
    sumcheck::{CompressedUniPoly, SumcheckProof, UniPoly},
 | 
				
			|||
  },
 | 
				
			|||
  traits::{Group, TranscriptEngineTrait},
 | 
				
			|||
};
 | 
				
			|||
use core::marker::PhantomData;
 | 
				
			|||
use ff::{Field, PrimeField};
 | 
				
			|||
use serde::{Deserialize, Serialize};
 | 
				
			|||
 | 
				
			|||
pub(crate) struct IdentityPolynomial<Scalar: PrimeField> {
 | 
				
			|||
  ell: usize,
 | 
				
			|||
  _p: PhantomData<Scalar>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<Scalar: PrimeField> IdentityPolynomial<Scalar> {
 | 
				
			|||
  pub fn new(ell: usize) -> Self {
 | 
				
			|||
    IdentityPolynomial {
 | 
				
			|||
      ell,
 | 
				
			|||
      _p: Default::default(),
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn evaluate(&self, r: &[Scalar]) -> Scalar {
 | 
				
			|||
    assert_eq!(self.ell, r.len());
 | 
				
			|||
    (0..self.ell)
 | 
				
			|||
      .map(|i| Scalar::from(2_usize.pow((self.ell - i - 1) as u32) as u64) * r[i])
 | 
				
			|||
      .fold(Scalar::zero(), |acc, item| acc + item)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<G: Group> SumcheckProof<G> {
 | 
				
			|||
  pub fn prove_cubic<F>(
 | 
				
			|||
    claim: &G::Scalar,
 | 
				
			|||
    num_rounds: usize,
 | 
				
			|||
    poly_A: &mut MultilinearPolynomial<G::Scalar>,
 | 
				
			|||
    poly_B: &mut MultilinearPolynomial<G::Scalar>,
 | 
				
			|||
    poly_C: &mut MultilinearPolynomial<G::Scalar>,
 | 
				
			|||
    comb_func: F,
 | 
				
			|||
    transcript: &mut G::TE,
 | 
				
			|||
  ) -> Result<(Self, Vec<G::Scalar>, Vec<G::Scalar>), NovaError>
 | 
				
			|||
  where
 | 
				
			|||
    F: Fn(&G::Scalar, &G::Scalar, &G::Scalar) -> G::Scalar,
 | 
				
			|||
  {
 | 
				
			|||
    let mut e = *claim;
 | 
				
			|||
    let mut r: Vec<G::Scalar> = Vec::new();
 | 
				
			|||
    let mut cubic_polys: Vec<CompressedUniPoly<G>> = Vec::new();
 | 
				
			|||
    for _j in 0..num_rounds {
 | 
				
			|||
      let mut eval_point_0 = G::Scalar::zero();
 | 
				
			|||
      let mut eval_point_2 = G::Scalar::zero();
 | 
				
			|||
      let mut eval_point_3 = G::Scalar::zero();
 | 
				
			|||
 | 
				
			|||
      let len = poly_A.len() / 2;
 | 
				
			|||
      for i in 0..len {
 | 
				
			|||
        // eval 0: bound_func is A(low)
 | 
				
			|||
        eval_point_0 += comb_func(&poly_A[i], &poly_B[i], &poly_C[i]);
 | 
				
			|||
 | 
				
			|||
        // eval 2: bound_func is -A(low) + 2*A(high)
 | 
				
			|||
        let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i];
 | 
				
			|||
        let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i];
 | 
				
			|||
        let poly_C_bound_point = poly_C[len + i] + poly_C[len + i] - poly_C[i];
 | 
				
			|||
        eval_point_2 += comb_func(
 | 
				
			|||
          &poly_A_bound_point,
 | 
				
			|||
          &poly_B_bound_point,
 | 
				
			|||
          &poly_C_bound_point,
 | 
				
			|||
        );
 | 
				
			|||
 | 
				
			|||
        // eval 3: bound_func is -2A(low) + 3A(high); computed incrementally with bound_func applied to eval(2)
 | 
				
			|||
        let poly_A_bound_point = poly_A_bound_point + poly_A[len + i] - poly_A[i];
 | 
				
			|||
        let poly_B_bound_point = poly_B_bound_point + poly_B[len + i] - poly_B[i];
 | 
				
			|||
        let poly_C_bound_point = poly_C_bound_point + poly_C[len + i] - poly_C[i];
 | 
				
			|||
 | 
				
			|||
        eval_point_3 += comb_func(
 | 
				
			|||
          &poly_A_bound_point,
 | 
				
			|||
          &poly_B_bound_point,
 | 
				
			|||
          &poly_C_bound_point,
 | 
				
			|||
        );
 | 
				
			|||
      }
 | 
				
			|||
 | 
				
			|||
      let evals = vec![eval_point_0, e - eval_point_0, eval_point_2, eval_point_3];
 | 
				
			|||
      let poly = UniPoly::from_evals(&evals);
 | 
				
			|||
 | 
				
			|||
      // append the prover's message to the transcript
 | 
				
			|||
      transcript.absorb(b"p", &poly);
 | 
				
			|||
 | 
				
			|||
      //derive the verifier's challenge for the next round
 | 
				
			|||
      let r_i = transcript.squeeze(b"c")?;
 | 
				
			|||
      r.push(r_i);
 | 
				
			|||
 | 
				
			|||
      // bound all tables to the verifier's challenege
 | 
				
			|||
      poly_A.bound_poly_var_top(&r_i);
 | 
				
			|||
      poly_B.bound_poly_var_top(&r_i);
 | 
				
			|||
      poly_C.bound_poly_var_top(&r_i);
 | 
				
			|||
      e = poly.evaluate(&r_i);
 | 
				
			|||
      cubic_polys.push(poly.compress());
 | 
				
			|||
    }
 | 
				
			|||
 | 
				
			|||
    Ok((
 | 
				
			|||
      Self::new(cubic_polys),
 | 
				
			|||
      r,
 | 
				
			|||
      vec![poly_A[0], poly_B[0], poly_C[0]],
 | 
				
			|||
    ))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn prove_cubic_batched<F>(
 | 
				
			|||
    claim: &G::Scalar,
 | 
				
			|||
    num_rounds: usize,
 | 
				
			|||
    poly_vec: (
 | 
				
			|||
      &mut Vec<&mut MultilinearPolynomial<G::Scalar>>,
 | 
				
			|||
      &mut Vec<&mut MultilinearPolynomial<G::Scalar>>,
 | 
				
			|||
      &mut MultilinearPolynomial<G::Scalar>,
 | 
				
			|||
    ),
 | 
				
			|||
    coeffs: &[G::Scalar],
 | 
				
			|||
    comb_func: F,
 | 
				
			|||
    transcript: &mut G::TE,
 | 
				
			|||
  ) -> Result<
 | 
				
			|||
    (
 | 
				
			|||
      Self,
 | 
				
			|||
      Vec<G::Scalar>,
 | 
				
			|||
      (Vec<G::Scalar>, Vec<G::Scalar>, G::Scalar),
 | 
				
			|||
    ),
 | 
				
			|||
    NovaError,
 | 
				
			|||
  >
 | 
				
			|||
  where
 | 
				
			|||
    F: Fn(&G::Scalar, &G::Scalar, &G::Scalar) -> G::Scalar,
 | 
				
			|||
  {
 | 
				
			|||
    let (poly_A_vec, poly_B_vec, poly_C) = poly_vec;
 | 
				
			|||
 | 
				
			|||
    let mut e = *claim;
 | 
				
			|||
    let mut r: Vec<G::Scalar> = Vec::new();
 | 
				
			|||
    let mut cubic_polys: Vec<CompressedUniPoly<G>> = Vec::new();
 | 
				
			|||
 | 
				
			|||
    for _j in 0..num_rounds {
 | 
				
			|||
      let mut evals: Vec<(G::Scalar, G::Scalar, G::Scalar)> = Vec::new();
 | 
				
			|||
 | 
				
			|||
      for (poly_A, poly_B) in poly_A_vec.iter().zip(poly_B_vec.iter()) {
 | 
				
			|||
        let mut eval_point_0 = G::Scalar::zero();
 | 
				
			|||
        let mut eval_point_2 = G::Scalar::zero();
 | 
				
			|||
        let mut eval_point_3 = G::Scalar::zero();
 | 
				
			|||
 | 
				
			|||
        let len = poly_A.len() / 2;
 | 
				
			|||
        for i in 0..len {
 | 
				
			|||
          // eval 0: bound_func is A(low)
 | 
				
			|||
          eval_point_0 += comb_func(&poly_A[i], &poly_B[i], &poly_C[i]);
 | 
				
			|||
 | 
				
			|||
          // eval 2: bound_func is -A(low) + 2*A(high)
 | 
				
			|||
          let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i];
 | 
				
			|||
          let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i];
 | 
				
			|||
          let poly_C_bound_point = poly_C[len + i] + poly_C[len + i] - poly_C[i];
 | 
				
			|||
          eval_point_2 += comb_func(
 | 
				
			|||
            &poly_A_bound_point,
 | 
				
			|||
            &poly_B_bound_point,
 | 
				
			|||
            &poly_C_bound_point,
 | 
				
			|||
          );
 | 
				
			|||
 | 
				
			|||
          // eval 3: bound_func is -2A(low) + 3A(high); computed incrementally with bound_func applied to eval(2)
 | 
				
			|||
          let poly_A_bound_point = poly_A_bound_point + poly_A[len + i] - poly_A[i];
 | 
				
			|||
          let poly_B_bound_point = poly_B_bound_point + poly_B[len + i] - poly_B[i];
 | 
				
			|||
          let poly_C_bound_point = poly_C_bound_point + poly_C[len + i] - poly_C[i];
 | 
				
			|||
 | 
				
			|||
          eval_point_3 += comb_func(
 | 
				
			|||
            &poly_A_bound_point,
 | 
				
			|||
            &poly_B_bound_point,
 | 
				
			|||
            &poly_C_bound_point,
 | 
				
			|||
          );
 | 
				
			|||
        }
 | 
				
			|||
 | 
				
			|||
        evals.push((eval_point_0, eval_point_2, eval_point_3));
 | 
				
			|||
      }
 | 
				
			|||
 | 
				
			|||
      let evals_combined_0 = (0..evals.len())
 | 
				
			|||
        .map(|i| evals[i].0 * coeffs[i])
 | 
				
			|||
        .fold(G::Scalar::zero(), |acc, item| acc + item);
 | 
				
			|||
      let evals_combined_2 = (0..evals.len())
 | 
				
			|||
        .map(|i| evals[i].1 * coeffs[i])
 | 
				
			|||
        .fold(G::Scalar::zero(), |acc, item| acc + item);
 | 
				
			|||
      let evals_combined_3 = (0..evals.len())
 | 
				
			|||
        .map(|i| evals[i].2 * coeffs[i])
 | 
				
			|||
        .fold(G::Scalar::zero(), |acc, item| acc + item);
 | 
				
			|||
 | 
				
			|||
      let evals = vec![
 | 
				
			|||
        evals_combined_0,
 | 
				
			|||
        e - evals_combined_0,
 | 
				
			|||
        evals_combined_2,
 | 
				
			|||
        evals_combined_3,
 | 
				
			|||
      ];
 | 
				
			|||
      let poly = UniPoly::from_evals(&evals);
 | 
				
			|||
 | 
				
			|||
      // append the prover's message to the transcript
 | 
				
			|||
      transcript.absorb(b"p", &poly);
 | 
				
			|||
 | 
				
			|||
      // derive the verifier's challenge for the next round
 | 
				
			|||
      let r_i = transcript.squeeze(b"c")?;
 | 
				
			|||
      r.push(r_i);
 | 
				
			|||
 | 
				
			|||
      // bound all tables to the verifier's challenege
 | 
				
			|||
      for (poly_A, poly_B) in poly_A_vec.iter_mut().zip(poly_B_vec.iter_mut()) {
 | 
				
			|||
        poly_A.bound_poly_var_top(&r_i);
 | 
				
			|||
        poly_B.bound_poly_var_top(&r_i);
 | 
				
			|||
      }
 | 
				
			|||
      poly_C.bound_poly_var_top(&r_i);
 | 
				
			|||
 | 
				
			|||
      e = poly.evaluate(&r_i);
 | 
				
			|||
      cubic_polys.push(poly.compress());
 | 
				
			|||
    }
 | 
				
			|||
 | 
				
			|||
    let poly_A_final = (0..poly_A_vec.len()).map(|i| poly_A_vec[i][0]).collect();
 | 
				
			|||
    let poly_B_final = (0..poly_B_vec.len()).map(|i| poly_B_vec[i][0]).collect();
 | 
				
			|||
    let claims_prod = (poly_A_final, poly_B_final, poly_C[0]);
 | 
				
			|||
 | 
				
			|||
    Ok((SumcheckProof::new(cubic_polys), r, claims_prod))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug)]
 | 
				
			|||
pub struct ProductArgumentInputs<G: Group> {
 | 
				
			|||
  left_vec: Vec<MultilinearPolynomial<G::Scalar>>,
 | 
				
			|||
  right_vec: Vec<MultilinearPolynomial<G::Scalar>>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<G: Group> ProductArgumentInputs<G> {
 | 
				
			|||
  fn compute_layer(
 | 
				
			|||
    inp_left: &MultilinearPolynomial<G::Scalar>,
 | 
				
			|||
    inp_right: &MultilinearPolynomial<G::Scalar>,
 | 
				
			|||
  ) -> (
 | 
				
			|||
    MultilinearPolynomial<G::Scalar>,
 | 
				
			|||
    MultilinearPolynomial<G::Scalar>,
 | 
				
			|||
  ) {
 | 
				
			|||
    let len = inp_left.len() + inp_right.len();
 | 
				
			|||
    let outp_left = (0..len / 4)
 | 
				
			|||
      .map(|i| inp_left[i] * inp_right[i])
 | 
				
			|||
      .collect::<Vec<G::Scalar>>();
 | 
				
			|||
    let outp_right = (len / 4..len / 2)
 | 
				
			|||
      .map(|i| inp_left[i] * inp_right[i])
 | 
				
			|||
      .collect::<Vec<G::Scalar>>();
 | 
				
			|||
 | 
				
			|||
    (
 | 
				
			|||
      MultilinearPolynomial::new(outp_left),
 | 
				
			|||
      MultilinearPolynomial::new(outp_right),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn new(poly: &MultilinearPolynomial<G::Scalar>) -> Self {
 | 
				
			|||
    let mut left_vec: Vec<MultilinearPolynomial<G::Scalar>> = Vec::new();
 | 
				
			|||
    let mut right_vec: Vec<MultilinearPolynomial<G::Scalar>> = Vec::new();
 | 
				
			|||
    let num_layers = poly.len().log_2();
 | 
				
			|||
    let (outp_left, outp_right) = poly.split(poly.len() / 2);
 | 
				
			|||
 | 
				
			|||
    left_vec.push(outp_left);
 | 
				
			|||
    right_vec.push(outp_right);
 | 
				
			|||
 | 
				
			|||
    for i in 0..num_layers - 1 {
 | 
				
			|||
      let (outp_left, outp_right) =
 | 
				
			|||
        ProductArgumentInputs::<G>::compute_layer(&left_vec[i], &right_vec[i]);
 | 
				
			|||
      left_vec.push(outp_left);
 | 
				
			|||
      right_vec.push(outp_right);
 | 
				
			|||
    }
 | 
				
			|||
 | 
				
			|||
    Self {
 | 
				
			|||
      left_vec,
 | 
				
			|||
      right_vec,
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn evaluate(&self) -> G::Scalar {
 | 
				
			|||
    let len = self.left_vec.len();
 | 
				
			|||
    assert_eq!(self.left_vec[len - 1].get_num_vars(), 0);
 | 
				
			|||
    assert_eq!(self.right_vec[len - 1].get_num_vars(), 0);
 | 
				
			|||
    self.left_vec[len - 1][0] * self.right_vec[len - 1][0]
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
#[derive(Clone, Debug, Serialize, Deserialize)]
 | 
				
			|||
#[serde(bound = "")]
 | 
				
			|||
pub struct LayerProofBatched<G: Group> {
 | 
				
			|||
  proof: SumcheckProof<G>,
 | 
				
			|||
  claims_prod_left: Vec<G::Scalar>,
 | 
				
			|||
  claims_prod_right: Vec<G::Scalar>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<G: Group> LayerProofBatched<G> {
 | 
				
			|||
  pub fn verify(
 | 
				
			|||
    &self,
 | 
				
			|||
    claim: G::Scalar,
 | 
				
			|||
    num_rounds: usize,
 | 
				
			|||
    degree_bound: usize,
 | 
				
			|||
    transcript: &mut G::TE,
 | 
				
			|||
  ) -> Result<(G::Scalar, Vec<G::Scalar>), NovaError> {
 | 
				
			|||
    self
 | 
				
			|||
      .proof
 | 
				
			|||
      .verify(claim, num_rounds, degree_bound, transcript)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Clone, Debug, Serialize, Deserialize)]
 | 
				
			|||
#[serde(bound = "")]
 | 
				
			|||
pub(crate) struct ProductArgumentBatched<G: Group> {
 | 
				
			|||
  proof: Vec<LayerProofBatched<G>>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<G: Group> ProductArgumentBatched<G> {
 | 
				
			|||
  pub fn prove(
 | 
				
			|||
    poly_vec: &[&MultilinearPolynomial<G::Scalar>],
 | 
				
			|||
    transcript: &mut G::TE,
 | 
				
			|||
  ) -> Result<(Self, Vec<G::Scalar>, Vec<G::Scalar>), NovaError> {
 | 
				
			|||
    let mut prod_circuit_vec: Vec<_> = (0..poly_vec.len())
 | 
				
			|||
      .map(|i| ProductArgumentInputs::<G>::new(poly_vec[i]))
 | 
				
			|||
      .collect();
 | 
				
			|||
 | 
				
			|||
    let mut proof_layers: Vec<LayerProofBatched<G>> = Vec::new();
 | 
				
			|||
    let num_layers = prod_circuit_vec[0].left_vec.len();
 | 
				
			|||
    let evals = (0..prod_circuit_vec.len())
 | 
				
			|||
      .map(|i| prod_circuit_vec[i].evaluate())
 | 
				
			|||
      .collect::<Vec<G::Scalar>>();
 | 
				
			|||
 | 
				
			|||
    let mut claims_to_verify = evals.clone();
 | 
				
			|||
    let mut rand = Vec::new();
 | 
				
			|||
    for layer_id in (0..num_layers).rev() {
 | 
				
			|||
      let len = prod_circuit_vec[0].left_vec[layer_id].len()
 | 
				
			|||
        + prod_circuit_vec[0].right_vec[layer_id].len();
 | 
				
			|||
 | 
				
			|||
      let mut poly_C = MultilinearPolynomial::new(EqPolynomial::new(rand.clone()).evals());
 | 
				
			|||
      assert_eq!(poly_C.len(), len / 2);
 | 
				
			|||
 | 
				
			|||
      let num_rounds_prod = poly_C.len().log_2();
 | 
				
			|||
      let comb_func_prod = |poly_A_comp: &G::Scalar,
 | 
				
			|||
                            poly_B_comp: &G::Scalar,
 | 
				
			|||
                            poly_C_comp: &G::Scalar|
 | 
				
			|||
       -> G::Scalar { *poly_A_comp * *poly_B_comp * *poly_C_comp };
 | 
				
			|||
 | 
				
			|||
      let mut poly_A_batched: Vec<&mut MultilinearPolynomial<G::Scalar>> = Vec::new();
 | 
				
			|||
      let mut poly_B_batched: Vec<&mut MultilinearPolynomial<G::Scalar>> = Vec::new();
 | 
				
			|||
      for prod_circuit in prod_circuit_vec.iter_mut() {
 | 
				
			|||
        poly_A_batched.push(&mut prod_circuit.left_vec[layer_id]);
 | 
				
			|||
        poly_B_batched.push(&mut prod_circuit.right_vec[layer_id])
 | 
				
			|||
      }
 | 
				
			|||
      let poly_vec = (&mut poly_A_batched, &mut poly_B_batched, &mut poly_C);
 | 
				
			|||
 | 
				
			|||
      // produce a fresh set of coeffs and a joint claim
 | 
				
			|||
      let coeff_vec = {
 | 
				
			|||
        let s = transcript.squeeze(b"r")?;
 | 
				
			|||
        let mut s_vec = vec![s];
 | 
				
			|||
        for i in 1..claims_to_verify.len() {
 | 
				
			|||
          s_vec.push(s_vec[i - 1] * s);
 | 
				
			|||
        }
 | 
				
			|||
        s_vec
 | 
				
			|||
      };
 | 
				
			|||
 | 
				
			|||
      let claim = (0..claims_to_verify.len())
 | 
				
			|||
        .map(|i| claims_to_verify[i] * coeff_vec[i])
 | 
				
			|||
        .fold(G::Scalar::zero(), |acc, item| acc + item);
 | 
				
			|||
 | 
				
			|||
      let (proof, rand_prod, claims_prod) = SumcheckProof::prove_cubic_batched(
 | 
				
			|||
        &claim,
 | 
				
			|||
        num_rounds_prod,
 | 
				
			|||
        poly_vec,
 | 
				
			|||
        &coeff_vec,
 | 
				
			|||
        comb_func_prod,
 | 
				
			|||
        transcript,
 | 
				
			|||
      )?;
 | 
				
			|||
 | 
				
			|||
      let (claims_prod_left, claims_prod_right, _claims_eq) = claims_prod;
 | 
				
			|||
 | 
				
			|||
      let v = {
 | 
				
			|||
        let mut v = claims_prod_left.clone();
 | 
				
			|||
        v.extend(&claims_prod_right);
 | 
				
			|||
        v
 | 
				
			|||
      };
 | 
				
			|||
      transcript.absorb(b"p", &v.as_slice());
 | 
				
			|||
 | 
				
			|||
      // produce a random challenge to condense two claims into a single claim
 | 
				
			|||
      let r_layer = transcript.squeeze(b"c")?;
 | 
				
			|||
 | 
				
			|||
      claims_to_verify = (0..prod_circuit_vec.len())
 | 
				
			|||
        .map(|i| claims_prod_left[i] + r_layer * (claims_prod_right[i] - claims_prod_left[i]))
 | 
				
			|||
        .collect::<Vec<G::Scalar>>();
 | 
				
			|||
 | 
				
			|||
      let mut ext = vec![r_layer];
 | 
				
			|||
      ext.extend(rand_prod);
 | 
				
			|||
      rand = ext;
 | 
				
			|||
 | 
				
			|||
      proof_layers.push(LayerProofBatched {
 | 
				
			|||
        proof,
 | 
				
			|||
        claims_prod_left,
 | 
				
			|||
        claims_prod_right,
 | 
				
			|||
      });
 | 
				
			|||
    }
 | 
				
			|||
 | 
				
			|||
    Ok((
 | 
				
			|||
      ProductArgumentBatched {
 | 
				
			|||
        proof: proof_layers,
 | 
				
			|||
      },
 | 
				
			|||
      evals,
 | 
				
			|||
      rand,
 | 
				
			|||
    ))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn verify(
 | 
				
			|||
    &self,
 | 
				
			|||
    claims_prod_vec: &[G::Scalar],
 | 
				
			|||
    len: usize,
 | 
				
			|||
    transcript: &mut G::TE,
 | 
				
			|||
  ) -> Result<(Vec<G::Scalar>, Vec<G::Scalar>), NovaError> {
 | 
				
			|||
    let num_layers = len.log_2();
 | 
				
			|||
 | 
				
			|||
    let mut rand: Vec<G::Scalar> = Vec::new();
 | 
				
			|||
    if self.proof.len() != num_layers {
 | 
				
			|||
      return Err(NovaError::InvalidProductProof);
 | 
				
			|||
    }
 | 
				
			|||
 | 
				
			|||
    let mut claims_to_verify = claims_prod_vec.to_owned();
 | 
				
			|||
    for (num_rounds, i) in (0..num_layers).enumerate() {
 | 
				
			|||
      // produce random coefficients, one for each instance
 | 
				
			|||
      let coeff_vec = {
 | 
				
			|||
        let s = transcript.squeeze(b"r")?;
 | 
				
			|||
        let mut s_vec = vec![s];
 | 
				
			|||
        for i in 1..claims_to_verify.len() {
 | 
				
			|||
          s_vec.push(s_vec[i - 1] * s);
 | 
				
			|||
        }
 | 
				
			|||
        s_vec
 | 
				
			|||
      };
 | 
				
			|||
 | 
				
			|||
      // produce a joint claim
 | 
				
			|||
      let claim = (0..claims_to_verify.len())
 | 
				
			|||
        .map(|i| claims_to_verify[i] * coeff_vec[i])
 | 
				
			|||
        .fold(G::Scalar::zero(), |acc, item| acc + item);
 | 
				
			|||
 | 
				
			|||
      let (claim_last, rand_prod) = self.proof[i].verify(claim, num_rounds, 3, transcript)?;
 | 
				
			|||
 | 
				
			|||
      let claims_prod_left = &self.proof[i].claims_prod_left;
 | 
				
			|||
      let claims_prod_right = &self.proof[i].claims_prod_right;
 | 
				
			|||
      if claims_prod_left.len() != claims_prod_vec.len()
 | 
				
			|||
        || claims_prod_right.len() != claims_prod_vec.len()
 | 
				
			|||
      {
 | 
				
			|||
        return Err(NovaError::InvalidProductProof);
 | 
				
			|||
      }
 | 
				
			|||
 | 
				
			|||
      let v = {
 | 
				
			|||
        let mut v = claims_prod_left.clone();
 | 
				
			|||
        v.extend(claims_prod_right);
 | 
				
			|||
        v
 | 
				
			|||
      };
 | 
				
			|||
      transcript.absorb(b"p", &v.as_slice());
 | 
				
			|||
 | 
				
			|||
      if rand.len() != rand_prod.len() {
 | 
				
			|||
        return Err(NovaError::InvalidProductProof);
 | 
				
			|||
      }
 | 
				
			|||
 | 
				
			|||
      let eq: G::Scalar = (0..rand.len())
 | 
				
			|||
        .map(|i| {
 | 
				
			|||
          rand[i] * rand_prod[i] + (G::Scalar::one() - rand[i]) * (G::Scalar::one() - rand_prod[i])
 | 
				
			|||
        })
 | 
				
			|||
        .fold(G::Scalar::one(), |acc, item| acc * item);
 | 
				
			|||
      let claim_expected: G::Scalar = (0..claims_prod_vec.len())
 | 
				
			|||
        .map(|i| coeff_vec[i] * (claims_prod_left[i] * claims_prod_right[i] * eq))
 | 
				
			|||
        .fold(G::Scalar::zero(), |acc, item| acc + item);
 | 
				
			|||
 | 
				
			|||
      if claim_expected != claim_last {
 | 
				
			|||
        return Err(NovaError::InvalidProductProof);
 | 
				
			|||
      }
 | 
				
			|||
 | 
				
			|||
      // produce a random challenge
 | 
				
			|||
      let r_layer = transcript.squeeze(b"c")?;
 | 
				
			|||
 | 
				
			|||
      claims_to_verify = (0..claims_prod_left.len())
 | 
				
			|||
        .map(|i| claims_prod_left[i] + r_layer * (claims_prod_right[i] - claims_prod_left[i]))
 | 
				
			|||
        .collect::<Vec<G::Scalar>>();
 | 
				
			|||
 | 
				
			|||
      let mut ext = vec![r_layer];
 | 
				
			|||
      ext.extend(rand_prod);
 | 
				
			|||
      rand = ext;
 | 
				
			|||
    }
 | 
				
			|||
    Ok((claims_to_verify, rand))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
@ -0,0 +1,732 @@ | 
				
			|||
#![allow(clippy::type_complexity)]
 | 
				
			|||
#![allow(clippy::too_many_arguments)]
 | 
				
			|||
#![allow(clippy::needless_range_loop)]
 | 
				
			|||
use crate::{
 | 
				
			|||
  errors::NovaError,
 | 
				
			|||
  spartan::{
 | 
				
			|||
    math::Math,
 | 
				
			|||
    polynomial::{EqPolynomial, MultilinearPolynomial},
 | 
				
			|||
    spark::product::{IdentityPolynomial, ProductArgumentBatched},
 | 
				
			|||
    SumcheckProof,
 | 
				
			|||
  },
 | 
				
			|||
  traits::{
 | 
				
			|||
    commitment::CommitmentEngineTrait, evaluation::EvaluationEngineTrait, Group,
 | 
				
			|||
    TranscriptEngineTrait, TranscriptReprTrait,
 | 
				
			|||
  },
 | 
				
			|||
  Commitment, CommitmentKey,
 | 
				
			|||
};
 | 
				
			|||
use ff::Field;
 | 
				
			|||
use rayon::prelude::*;
 | 
				
			|||
use serde::{Deserialize, Serialize};
 | 
				
			|||
 | 
				
			|||
/// A type that holds a sparse polynomial in dense representation
 | 
				
			|||
#[derive(Clone, Serialize, Deserialize)]
 | 
				
			|||
#[serde(bound = "")]
 | 
				
			|||
pub struct SparsePolynomial<G: Group> {
 | 
				
			|||
  ell: (usize, usize), // number of variables in each dimension
 | 
				
			|||
 | 
				
			|||
  // dense representation
 | 
				
			|||
  row: Vec<G::Scalar>,
 | 
				
			|||
  col: Vec<G::Scalar>,
 | 
				
			|||
  val: Vec<G::Scalar>,
 | 
				
			|||
 | 
				
			|||
  // timestamp polynomials
 | 
				
			|||
  row_read_ts: Vec<G::Scalar>,
 | 
				
			|||
  row_audit_ts: Vec<G::Scalar>,
 | 
				
			|||
  col_read_ts: Vec<G::Scalar>,
 | 
				
			|||
  col_audit_ts: Vec<G::Scalar>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
/// A type that holds a commitment to a sparse polynomial
 | 
				
			|||
#[derive(Clone, Serialize, Deserialize)]
 | 
				
			|||
#[serde(bound = "")]
 | 
				
			|||
pub struct SparsePolynomialCommitment<G: Group> {
 | 
				
			|||
  ell: (usize, usize), // number of variables
 | 
				
			|||
  size: usize,         // size of the dense representation
 | 
				
			|||
 | 
				
			|||
  // commitments to the dense representation
 | 
				
			|||
  comm_row: Commitment<G>,
 | 
				
			|||
  comm_col: Commitment<G>,
 | 
				
			|||
  comm_val: Commitment<G>,
 | 
				
			|||
 | 
				
			|||
  // commitments to the timestamp polynomials
 | 
				
			|||
  comm_row_read_ts: Commitment<G>,
 | 
				
			|||
  comm_row_audit_ts: Commitment<G>,
 | 
				
			|||
  comm_col_read_ts: Commitment<G>,
 | 
				
			|||
  comm_col_audit_ts: Commitment<G>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<G: Group> TranscriptReprTrait<G> for SparsePolynomialCommitment<G> {
 | 
				
			|||
  fn to_transcript_bytes(&self) -> Vec<u8> {
 | 
				
			|||
    [
 | 
				
			|||
      self.comm_row,
 | 
				
			|||
      self.comm_col,
 | 
				
			|||
      self.comm_val,
 | 
				
			|||
      self.comm_row_read_ts,
 | 
				
			|||
      self.comm_row_audit_ts,
 | 
				
			|||
      self.comm_col_read_ts,
 | 
				
			|||
      self.comm_col_audit_ts,
 | 
				
			|||
    ]
 | 
				
			|||
    .as_slice()
 | 
				
			|||
    .to_transcript_bytes()
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<G: Group> SparsePolynomial<G> {
 | 
				
			|||
  pub fn new(ell: (usize, usize), M: &[(usize, usize, G::Scalar)]) -> Self {
 | 
				
			|||
    let mut row = M.iter().map(|(r, _, _)| *r).collect::<Vec<usize>>();
 | 
				
			|||
    let mut col = M.iter().map(|(_, c, _)| *c).collect::<Vec<usize>>();
 | 
				
			|||
    let mut val = M.iter().map(|(_, _, v)| *v).collect::<Vec<G::Scalar>>();
 | 
				
			|||
 | 
				
			|||
    let num_ops = M.len().next_power_of_two();
 | 
				
			|||
    let num_cells_row = ell.0.pow2();
 | 
				
			|||
    let num_cells_col = ell.1.pow2();
 | 
				
			|||
    row.resize(num_ops, 0usize);
 | 
				
			|||
    col.resize(num_ops, 0usize);
 | 
				
			|||
    val.resize(num_ops, G::Scalar::zero());
 | 
				
			|||
 | 
				
			|||
    // timestamp calculation routine
 | 
				
			|||
    let timestamp_calc =
 | 
				
			|||
      |num_ops: usize, num_cells: usize, addr_trace: &[usize]| -> (Vec<usize>, Vec<usize>) {
 | 
				
			|||
        let mut read_ts = vec![0usize; num_ops];
 | 
				
			|||
        let mut audit_ts = vec![0usize; num_cells];
 | 
				
			|||
 | 
				
			|||
        assert!(num_ops >= addr_trace.len());
 | 
				
			|||
        for i in 0..addr_trace.len() {
 | 
				
			|||
          let addr = addr_trace[i];
 | 
				
			|||
          assert!(addr < num_cells);
 | 
				
			|||
          let r_ts = audit_ts[addr];
 | 
				
			|||
          read_ts[i] = r_ts;
 | 
				
			|||
 | 
				
			|||
          let w_ts = r_ts + 1;
 | 
				
			|||
          audit_ts[addr] = w_ts;
 | 
				
			|||
        }
 | 
				
			|||
        (read_ts, audit_ts)
 | 
				
			|||
      };
 | 
				
			|||
 | 
				
			|||
    // timestamp polynomials for row
 | 
				
			|||
    let (row_read_ts, row_audit_ts) = timestamp_calc(num_ops, num_cells_row, &row);
 | 
				
			|||
    let (col_read_ts, col_audit_ts) = timestamp_calc(num_ops, num_cells_col, &col);
 | 
				
			|||
 | 
				
			|||
    let to_vec_scalar = |v: &[usize]| -> Vec<G::Scalar> {
 | 
				
			|||
      (0..v.len())
 | 
				
			|||
        .map(|i| G::Scalar::from(v[i] as u64))
 | 
				
			|||
        .collect::<Vec<G::Scalar>>()
 | 
				
			|||
    };
 | 
				
			|||
 | 
				
			|||
    Self {
 | 
				
			|||
      ell,
 | 
				
			|||
      // dense representation
 | 
				
			|||
      row: to_vec_scalar(&row),
 | 
				
			|||
      col: to_vec_scalar(&col),
 | 
				
			|||
      val,
 | 
				
			|||
 | 
				
			|||
      // timestamp polynomials
 | 
				
			|||
      row_read_ts: to_vec_scalar(&row_read_ts),
 | 
				
			|||
      row_audit_ts: to_vec_scalar(&row_audit_ts),
 | 
				
			|||
      col_read_ts: to_vec_scalar(&col_read_ts),
 | 
				
			|||
      col_audit_ts: to_vec_scalar(&col_audit_ts),
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn commit(&self, ck: &CommitmentKey<G>) -> SparsePolynomialCommitment<G> {
 | 
				
			|||
    let comm_vec: Vec<Commitment<G>> = [
 | 
				
			|||
      &self.row,
 | 
				
			|||
      &self.col,
 | 
				
			|||
      &self.val,
 | 
				
			|||
      &self.row_read_ts,
 | 
				
			|||
      &self.row_audit_ts,
 | 
				
			|||
      &self.col_read_ts,
 | 
				
			|||
      &self.col_audit_ts,
 | 
				
			|||
    ]
 | 
				
			|||
    .par_iter()
 | 
				
			|||
    .map(|v| G::CE::commit(ck, v))
 | 
				
			|||
    .collect();
 | 
				
			|||
 | 
				
			|||
    SparsePolynomialCommitment {
 | 
				
			|||
      ell: self.ell,
 | 
				
			|||
      size: self.row.len(),
 | 
				
			|||
      comm_row: comm_vec[0],
 | 
				
			|||
      comm_col: comm_vec[1],
 | 
				
			|||
      comm_val: comm_vec[2],
 | 
				
			|||
      comm_row_read_ts: comm_vec[3],
 | 
				
			|||
      comm_row_audit_ts: comm_vec[4],
 | 
				
			|||
      comm_col_read_ts: comm_vec[5],
 | 
				
			|||
      comm_col_audit_ts: comm_vec[6],
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn multi_evaluate(
 | 
				
			|||
    M_vec: &[&[(usize, usize, G::Scalar)]],
 | 
				
			|||
    r_x: &[G::Scalar],
 | 
				
			|||
    r_y: &[G::Scalar],
 | 
				
			|||
  ) -> Vec<G::Scalar> {
 | 
				
			|||
    let evaluate_with_table =
 | 
				
			|||
      |M: &[(usize, usize, G::Scalar)], T_x: &[G::Scalar], T_y: &[G::Scalar]| -> G::Scalar {
 | 
				
			|||
        (0..M.len())
 | 
				
			|||
          .collect::<Vec<usize>>()
 | 
				
			|||
          .par_iter()
 | 
				
			|||
          .map(|&i| {
 | 
				
			|||
            let (row, col, val) = M[i];
 | 
				
			|||
            T_x[row] * T_y[col] * val
 | 
				
			|||
          })
 | 
				
			|||
          .reduce(G::Scalar::zero, |acc, x| acc + x)
 | 
				
			|||
      };
 | 
				
			|||
 | 
				
			|||
    let (T_x, T_y) = rayon::join(
 | 
				
			|||
      || EqPolynomial::new(r_x.to_vec()).evals(),
 | 
				
			|||
      || EqPolynomial::new(r_y.to_vec()).evals(),
 | 
				
			|||
    );
 | 
				
			|||
 | 
				
			|||
    (0..M_vec.len())
 | 
				
			|||
      .collect::<Vec<usize>>()
 | 
				
			|||
      .par_iter()
 | 
				
			|||
      .map(|&i| evaluate_with_table(M_vec[i], &T_x, &T_y))
 | 
				
			|||
      .collect()
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn evaluation_oracles(
 | 
				
			|||
    M: &[(usize, usize, G::Scalar)],
 | 
				
			|||
    r_x: &[G::Scalar],
 | 
				
			|||
    r_y: &[G::Scalar],
 | 
				
			|||
  ) -> (
 | 
				
			|||
    Vec<G::Scalar>,
 | 
				
			|||
    Vec<G::Scalar>,
 | 
				
			|||
    Vec<G::Scalar>,
 | 
				
			|||
    Vec<G::Scalar>,
 | 
				
			|||
  ) {
 | 
				
			|||
    let evaluation_oracles_with_table = |M: &[(usize, usize, G::Scalar)],
 | 
				
			|||
                                         T_x: &[G::Scalar],
 | 
				
			|||
                                         T_y: &[G::Scalar]|
 | 
				
			|||
     -> (Vec<G::Scalar>, Vec<G::Scalar>) {
 | 
				
			|||
      (0..M.len())
 | 
				
			|||
        .collect::<Vec<usize>>()
 | 
				
			|||
        .par_iter()
 | 
				
			|||
        .map(|&i| {
 | 
				
			|||
          let (row, col, _val) = M[i];
 | 
				
			|||
          (T_x[row], T_y[col])
 | 
				
			|||
        })
 | 
				
			|||
        .collect::<Vec<(G::Scalar, G::Scalar)>>()
 | 
				
			|||
        .into_par_iter()
 | 
				
			|||
        .unzip()
 | 
				
			|||
    };
 | 
				
			|||
 | 
				
			|||
    let (T_x, T_y) = rayon::join(
 | 
				
			|||
      || EqPolynomial::new(r_x.to_vec()).evals(),
 | 
				
			|||
      || EqPolynomial::new(r_y.to_vec()).evals(),
 | 
				
			|||
    );
 | 
				
			|||
 | 
				
			|||
    let (mut E_row, mut E_col) = evaluation_oracles_with_table(M, &T_x, &T_y);
 | 
				
			|||
 | 
				
			|||
    // resize the returned vectors
 | 
				
			|||
    E_row.resize(M.len().next_power_of_two(), T_x[0]); // we place T_x[0] since resized row is appended with 0s
 | 
				
			|||
    E_col.resize(M.len().next_power_of_two(), T_y[0]);
 | 
				
			|||
    (E_row, E_col, T_x, T_y)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Clone, Debug, Serialize, Deserialize)]
 | 
				
			|||
#[serde(bound = "")]
 | 
				
			|||
pub struct SparseEvaluationArgument<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> {
 | 
				
			|||
  // claimed evaluation
 | 
				
			|||
  eval: G::Scalar,
 | 
				
			|||
 | 
				
			|||
  // oracles
 | 
				
			|||
  comm_E_row: Commitment<G>,
 | 
				
			|||
  comm_E_col: Commitment<G>,
 | 
				
			|||
 | 
				
			|||
  // proof of correct evaluation wrt oracles
 | 
				
			|||
  sc_proof_eval: SumcheckProof<G>,
 | 
				
			|||
  eval_E_row: G::Scalar,
 | 
				
			|||
  eval_E_col: G::Scalar,
 | 
				
			|||
  eval_val: G::Scalar,
 | 
				
			|||
  arg_eval: EE::EvaluationArgument,
 | 
				
			|||
 | 
				
			|||
  // proof that E_row is well-formed
 | 
				
			|||
  eval_init_row: G::Scalar,
 | 
				
			|||
  eval_read_row: G::Scalar,
 | 
				
			|||
  eval_write_row: G::Scalar,
 | 
				
			|||
  eval_audit_row: G::Scalar,
 | 
				
			|||
  eval_init_col: G::Scalar,
 | 
				
			|||
  eval_read_col: G::Scalar,
 | 
				
			|||
  eval_write_col: G::Scalar,
 | 
				
			|||
  eval_audit_col: G::Scalar,
 | 
				
			|||
  sc_prod_init_audit_row: ProductArgumentBatched<G>,
 | 
				
			|||
  sc_prod_read_write_row_col: ProductArgumentBatched<G>,
 | 
				
			|||
  sc_prod_init_audit_col: ProductArgumentBatched<G>,
 | 
				
			|||
  eval_row: G::Scalar,
 | 
				
			|||
  eval_row_read_ts: G::Scalar,
 | 
				
			|||
  eval_E_row2: G::Scalar,
 | 
				
			|||
  eval_row_audit_ts: G::Scalar,
 | 
				
			|||
  eval_col: G::Scalar,
 | 
				
			|||
  eval_col_read_ts: G::Scalar,
 | 
				
			|||
  eval_E_col2: G::Scalar,
 | 
				
			|||
  eval_col_audit_ts: G::Scalar,
 | 
				
			|||
  arg_row_col_joint: EE::EvaluationArgument,
 | 
				
			|||
  arg_row_audit_ts: EE::EvaluationArgument,
 | 
				
			|||
  arg_col_audit_ts: EE::EvaluationArgument,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> SparseEvaluationArgument<G, EE> {
 | 
				
			|||
  pub fn prove(
 | 
				
			|||
    ck: &CommitmentKey<G>,
 | 
				
			|||
    pk_ee: &EE::ProverKey,
 | 
				
			|||
    poly: &SparsePolynomial<G>,
 | 
				
			|||
    sparse: &[(usize, usize, G::Scalar)],
 | 
				
			|||
    comm: &SparsePolynomialCommitment<G>,
 | 
				
			|||
    r: &(&[G::Scalar], &[G::Scalar]),
 | 
				
			|||
    transcript: &mut G::TE,
 | 
				
			|||
  ) -> Result<Self, NovaError> {
 | 
				
			|||
    let (r_x, r_y) = r;
 | 
				
			|||
    let eval = SparsePolynomial::<G>::multi_evaluate(&[sparse], r_x, r_y)[0];
 | 
				
			|||
 | 
				
			|||
    // compute oracles to prove the correctness of `eval`
 | 
				
			|||
    let (E_row, E_col, T_x, T_y) = SparsePolynomial::<G>::evaluation_oracles(sparse, r_x, r_y);
 | 
				
			|||
    let val = poly.val.clone();
 | 
				
			|||
 | 
				
			|||
    // commit to the two oracles
 | 
				
			|||
    let comm_E_row = G::CE::commit(ck, &E_row);
 | 
				
			|||
    let comm_E_col = G::CE::commit(ck, &E_col);
 | 
				
			|||
 | 
				
			|||
    // absorb the commitments and the claimed evaluation
 | 
				
			|||
    transcript.absorb(b"E", &vec![comm_E_row, comm_E_col].as_slice());
 | 
				
			|||
    transcript.absorb(b"e", &eval);
 | 
				
			|||
 | 
				
			|||
    let comb_func_eval = |poly_A_comp: &G::Scalar,
 | 
				
			|||
                          poly_B_comp: &G::Scalar,
 | 
				
			|||
                          poly_C_comp: &G::Scalar|
 | 
				
			|||
     -> G::Scalar { *poly_A_comp * *poly_B_comp * *poly_C_comp };
 | 
				
			|||
    let (sc_proof_eval, r_eval, claims_eval) = SumcheckProof::<G>::prove_cubic(
 | 
				
			|||
      &eval,
 | 
				
			|||
      E_row.len().log_2(), // number of rounds
 | 
				
			|||
      &mut MultilinearPolynomial::new(E_row.clone()),
 | 
				
			|||
      &mut MultilinearPolynomial::new(E_col.clone()),
 | 
				
			|||
      &mut MultilinearPolynomial::new(val.clone()),
 | 
				
			|||
      comb_func_eval,
 | 
				
			|||
      transcript,
 | 
				
			|||
    )?;
 | 
				
			|||
 | 
				
			|||
    // prove evaluations of E_row, E_col and val at r_eval
 | 
				
			|||
    let rho = transcript.squeeze(b"r")?;
 | 
				
			|||
    let comm_joint = comm_E_row + comm_E_col * rho + comm.comm_val * rho * rho;
 | 
				
			|||
    let eval_joint = claims_eval[0] + rho * claims_eval[1] + rho * rho * claims_eval[2];
 | 
				
			|||
    let poly_eval = E_row
 | 
				
			|||
      .iter()
 | 
				
			|||
      .zip(E_col.iter())
 | 
				
			|||
      .zip(val.iter())
 | 
				
			|||
      .map(|((a, b), c)| *a + rho * *b + rho * rho * *c)
 | 
				
			|||
      .collect::<Vec<G::Scalar>>();
 | 
				
			|||
    let arg_eval = EE::prove(
 | 
				
			|||
      ck,
 | 
				
			|||
      pk_ee,
 | 
				
			|||
      transcript,
 | 
				
			|||
      &comm_joint,
 | 
				
			|||
      &poly_eval,
 | 
				
			|||
      &r_eval,
 | 
				
			|||
      &eval_joint,
 | 
				
			|||
    )?;
 | 
				
			|||
 | 
				
			|||
    // we now need to prove that E_row and E_col are well-formed
 | 
				
			|||
    // we use memory checking: H(INIT) * H(WS) =? H(RS) * H(FINAL)
 | 
				
			|||
    let gamma_1 = transcript.squeeze(b"g1")?;
 | 
				
			|||
    let gamma_2 = transcript.squeeze(b"g2")?;
 | 
				
			|||
 | 
				
			|||
    let gamma_1_sqr = gamma_1 * gamma_1;
 | 
				
			|||
    let hash_func = |addr: &G::Scalar, val: &G::Scalar, ts: &G::Scalar| -> G::Scalar {
 | 
				
			|||
      (*ts * gamma_1_sqr + *val * gamma_1 + *addr) - gamma_2
 | 
				
			|||
    };
 | 
				
			|||
 | 
				
			|||
    let init_row = (0..T_x.len())
 | 
				
			|||
      .map(|i| hash_func(&G::Scalar::from(i as u64), &T_x[i], &G::Scalar::zero()))
 | 
				
			|||
      .collect::<Vec<G::Scalar>>();
 | 
				
			|||
    let read_row = (0..E_row.len())
 | 
				
			|||
      .map(|i| hash_func(&poly.row[i], &E_row[i], &poly.row_read_ts[i]))
 | 
				
			|||
      .collect::<Vec<G::Scalar>>();
 | 
				
			|||
    let write_row = (0..E_row.len())
 | 
				
			|||
      .map(|i| {
 | 
				
			|||
        hash_func(
 | 
				
			|||
          &poly.row[i],
 | 
				
			|||
          &E_row[i],
 | 
				
			|||
          &(poly.row_read_ts[i] + G::Scalar::one()),
 | 
				
			|||
        )
 | 
				
			|||
      })
 | 
				
			|||
      .collect::<Vec<G::Scalar>>();
 | 
				
			|||
    let audit_row = (0..T_x.len())
 | 
				
			|||
      .map(|i| hash_func(&G::Scalar::from(i as u64), &T_x[i], &poly.row_audit_ts[i]))
 | 
				
			|||
      .collect::<Vec<G::Scalar>>();
 | 
				
			|||
    let init_col = (0..T_y.len())
 | 
				
			|||
      .map(|i| hash_func(&G::Scalar::from(i as u64), &T_y[i], &G::Scalar::zero()))
 | 
				
			|||
      .collect::<Vec<G::Scalar>>();
 | 
				
			|||
    let read_col = (0..E_col.len())
 | 
				
			|||
      .map(|i| hash_func(&poly.col[i], &E_col[i], &poly.col_read_ts[i]))
 | 
				
			|||
      .collect::<Vec<G::Scalar>>();
 | 
				
			|||
    let write_col = (0..E_col.len())
 | 
				
			|||
      .map(|i| {
 | 
				
			|||
        hash_func(
 | 
				
			|||
          &poly.col[i],
 | 
				
			|||
          &E_col[i],
 | 
				
			|||
          &(poly.col_read_ts[i] + G::Scalar::one()),
 | 
				
			|||
        )
 | 
				
			|||
      })
 | 
				
			|||
      .collect::<Vec<G::Scalar>>();
 | 
				
			|||
    let audit_col = (0..T_y.len())
 | 
				
			|||
      .map(|i| hash_func(&G::Scalar::from(i as u64), &T_y[i], &poly.col_audit_ts[i]))
 | 
				
			|||
      .collect::<Vec<G::Scalar>>();
 | 
				
			|||
 | 
				
			|||
    let (sc_prod_init_audit_row, eval_init_audit_row, r_init_audit_row) =
 | 
				
			|||
      ProductArgumentBatched::prove(
 | 
				
			|||
        &[
 | 
				
			|||
          &MultilinearPolynomial::new(init_row),
 | 
				
			|||
          &MultilinearPolynomial::new(audit_row),
 | 
				
			|||
        ],
 | 
				
			|||
        transcript,
 | 
				
			|||
      )?;
 | 
				
			|||
 | 
				
			|||
    assert_eq!(init_col.len(), audit_col.len());
 | 
				
			|||
    let (sc_prod_init_audit_col, eval_init_audit_col, r_init_audit_col) =
 | 
				
			|||
      ProductArgumentBatched::prove(
 | 
				
			|||
        &[
 | 
				
			|||
          &MultilinearPolynomial::new(init_col),
 | 
				
			|||
          &MultilinearPolynomial::new(audit_col),
 | 
				
			|||
        ],
 | 
				
			|||
        transcript,
 | 
				
			|||
      )?;
 | 
				
			|||
 | 
				
			|||
    assert_eq!(read_row.len(), write_row.len());
 | 
				
			|||
    assert_eq!(read_row.len(), read_col.len());
 | 
				
			|||
    assert_eq!(read_row.len(), write_col.len());
 | 
				
			|||
 | 
				
			|||
    let (sc_prod_read_write_row_col, eval_read_write_row_col, r_read_write_row_col) =
 | 
				
			|||
      ProductArgumentBatched::prove(
 | 
				
			|||
        &[
 | 
				
			|||
          &MultilinearPolynomial::new(read_row),
 | 
				
			|||
          &MultilinearPolynomial::new(write_row),
 | 
				
			|||
          &MultilinearPolynomial::new(read_col),
 | 
				
			|||
          &MultilinearPolynomial::new(write_col),
 | 
				
			|||
        ],
 | 
				
			|||
        transcript,
 | 
				
			|||
      )?;
 | 
				
			|||
 | 
				
			|||
    // row-related claims of polynomial evaluations to aid the final check of the sum-check
 | 
				
			|||
    let eval_row = MultilinearPolynomial::evaluate_with(&poly.row, &r_read_write_row_col);
 | 
				
			|||
    let eval_row_read_ts =
 | 
				
			|||
      MultilinearPolynomial::evaluate_with(&poly.row_read_ts, &r_read_write_row_col);
 | 
				
			|||
    let eval_E_row2 = MultilinearPolynomial::evaluate_with(&E_row, &r_read_write_row_col);
 | 
				
			|||
    let eval_row_audit_ts =
 | 
				
			|||
      MultilinearPolynomial::evaluate_with(&poly.row_audit_ts, &r_init_audit_row);
 | 
				
			|||
 | 
				
			|||
    // col-related claims of polynomial evaluations to aid the final check of the sum-check
 | 
				
			|||
    let eval_col = MultilinearPolynomial::evaluate_with(&poly.col, &r_read_write_row_col);
 | 
				
			|||
    let eval_col_read_ts =
 | 
				
			|||
      MultilinearPolynomial::evaluate_with(&poly.col_read_ts, &r_read_write_row_col);
 | 
				
			|||
    let eval_E_col2 = MultilinearPolynomial::evaluate_with(&E_col, &r_read_write_row_col);
 | 
				
			|||
    let eval_col_audit_ts =
 | 
				
			|||
      MultilinearPolynomial::evaluate_with(&poly.col_audit_ts, &r_init_audit_col);
 | 
				
			|||
 | 
				
			|||
    // we can batch prove the first three claims
 | 
				
			|||
    transcript.absorb(
 | 
				
			|||
      b"e",
 | 
				
			|||
      &[
 | 
				
			|||
        eval_row,
 | 
				
			|||
        eval_row_read_ts,
 | 
				
			|||
        eval_E_row2,
 | 
				
			|||
        eval_col,
 | 
				
			|||
        eval_col_read_ts,
 | 
				
			|||
        eval_E_col2,
 | 
				
			|||
      ]
 | 
				
			|||
      .as_slice(),
 | 
				
			|||
    );
 | 
				
			|||
    let c = transcript.squeeze(b"c")?;
 | 
				
			|||
    let eval_joint = eval_row
 | 
				
			|||
      + c * eval_row_read_ts
 | 
				
			|||
      + c * c * eval_E_row2
 | 
				
			|||
      + c * c * c * eval_col
 | 
				
			|||
      + c * c * c * c * eval_col_read_ts
 | 
				
			|||
      + c * c * c * c * c * eval_E_col2;
 | 
				
			|||
    let comm_joint = comm.comm_row
 | 
				
			|||
      + comm.comm_row_read_ts * c
 | 
				
			|||
      + comm_E_row * c * c
 | 
				
			|||
      + comm.comm_col * c * c * c
 | 
				
			|||
      + comm.comm_col_read_ts * c * c * c * c
 | 
				
			|||
      + comm_E_col * c * c * c * c * c;
 | 
				
			|||
    let poly_joint = poly
 | 
				
			|||
      .row
 | 
				
			|||
      .iter()
 | 
				
			|||
      .zip(poly.row_read_ts.iter())
 | 
				
			|||
      .zip(E_row.into_iter())
 | 
				
			|||
      .zip(poly.col.iter())
 | 
				
			|||
      .zip(poly.col_read_ts.iter())
 | 
				
			|||
      .zip(E_col.into_iter())
 | 
				
			|||
      .map(|(((((x, y), z), m), n), q)| {
 | 
				
			|||
        *x + c * y + c * c * z + c * c * c * m + c * c * c * c * n + c * c * c * c * c * q
 | 
				
			|||
      })
 | 
				
			|||
      .collect::<Vec<_>>();
 | 
				
			|||
 | 
				
			|||
    let arg_row_col_joint = EE::prove(
 | 
				
			|||
      ck,
 | 
				
			|||
      pk_ee,
 | 
				
			|||
      transcript,
 | 
				
			|||
      &comm_joint,
 | 
				
			|||
      &poly_joint,
 | 
				
			|||
      &r_read_write_row_col,
 | 
				
			|||
      &eval_joint,
 | 
				
			|||
    )?;
 | 
				
			|||
 | 
				
			|||
    let arg_row_audit_ts = EE::prove(
 | 
				
			|||
      ck,
 | 
				
			|||
      pk_ee,
 | 
				
			|||
      transcript,
 | 
				
			|||
      &comm.comm_row_audit_ts,
 | 
				
			|||
      &poly.row_audit_ts,
 | 
				
			|||
      &r_init_audit_row,
 | 
				
			|||
      &eval_row_audit_ts,
 | 
				
			|||
    )?;
 | 
				
			|||
 | 
				
			|||
    let arg_col_audit_ts = EE::prove(
 | 
				
			|||
      ck,
 | 
				
			|||
      pk_ee,
 | 
				
			|||
      transcript,
 | 
				
			|||
      &comm.comm_col_audit_ts,
 | 
				
			|||
      &poly.col_audit_ts,
 | 
				
			|||
      &r_init_audit_col,
 | 
				
			|||
      &eval_col_audit_ts,
 | 
				
			|||
    )?;
 | 
				
			|||
 | 
				
			|||
    Ok(Self {
 | 
				
			|||
      // claimed evaluation
 | 
				
			|||
      eval,
 | 
				
			|||
 | 
				
			|||
      // oracles
 | 
				
			|||
      comm_E_row,
 | 
				
			|||
      comm_E_col,
 | 
				
			|||
 | 
				
			|||
      // proof of correct evaluation wrt oracles
 | 
				
			|||
      sc_proof_eval,
 | 
				
			|||
      eval_E_row: claims_eval[0],
 | 
				
			|||
      eval_E_col: claims_eval[1],
 | 
				
			|||
      eval_val: claims_eval[2],
 | 
				
			|||
      arg_eval,
 | 
				
			|||
 | 
				
			|||
      // proof that E_row and E_row are well-formed
 | 
				
			|||
      eval_init_row: eval_init_audit_row[0],
 | 
				
			|||
      eval_read_row: eval_read_write_row_col[0],
 | 
				
			|||
      eval_write_row: eval_read_write_row_col[1],
 | 
				
			|||
      eval_audit_row: eval_init_audit_row[1],
 | 
				
			|||
      eval_init_col: eval_init_audit_col[0],
 | 
				
			|||
      eval_read_col: eval_read_write_row_col[2],
 | 
				
			|||
      eval_write_col: eval_read_write_row_col[3],
 | 
				
			|||
      eval_audit_col: eval_init_audit_col[1],
 | 
				
			|||
      sc_prod_init_audit_row,
 | 
				
			|||
      sc_prod_read_write_row_col,
 | 
				
			|||
      sc_prod_init_audit_col,
 | 
				
			|||
      eval_row,
 | 
				
			|||
      eval_row_read_ts,
 | 
				
			|||
      eval_E_row2,
 | 
				
			|||
      eval_row_audit_ts,
 | 
				
			|||
      eval_col,
 | 
				
			|||
      eval_col_read_ts,
 | 
				
			|||
      eval_E_col2,
 | 
				
			|||
      eval_col_audit_ts,
 | 
				
			|||
      arg_row_col_joint,
 | 
				
			|||
      arg_row_audit_ts,
 | 
				
			|||
      arg_col_audit_ts,
 | 
				
			|||
    })
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn verify(
 | 
				
			|||
    &self,
 | 
				
			|||
    vk_ee: &EE::VerifierKey,
 | 
				
			|||
    comm: &SparsePolynomialCommitment<G>,
 | 
				
			|||
    r: &(&[G::Scalar], &[G::Scalar]),
 | 
				
			|||
    transcript: &mut G::TE,
 | 
				
			|||
  ) -> Result<G::Scalar, NovaError> {
 | 
				
			|||
    let (r_x, r_y) = r;
 | 
				
			|||
 | 
				
			|||
    // append the transcript and scalar
 | 
				
			|||
    transcript.absorb(b"E", &vec![self.comm_E_row, self.comm_E_col].as_slice());
 | 
				
			|||
    transcript.absorb(b"e", &self.eval);
 | 
				
			|||
 | 
				
			|||
    // (1) verify the correct evaluation of sparse polynomial
 | 
				
			|||
    let (claim_eval_final, r_eval) = self.sc_proof_eval.verify(
 | 
				
			|||
      self.eval,
 | 
				
			|||
      comm.size.next_power_of_two().log_2(),
 | 
				
			|||
      3,
 | 
				
			|||
      transcript,
 | 
				
			|||
    )?;
 | 
				
			|||
    // verify the last step of the sum-check
 | 
				
			|||
    if claim_eval_final != self.eval_E_row * self.eval_E_col * self.eval_val {
 | 
				
			|||
      return Err(NovaError::InvalidSumcheckProof);
 | 
				
			|||
    }
 | 
				
			|||
 | 
				
			|||
    // prove evaluations of E_row, E_col and val at r_eval
 | 
				
			|||
    let rho = transcript.squeeze(b"r")?;
 | 
				
			|||
    let comm_joint = self.comm_E_row + self.comm_E_col * rho + comm.comm_val * rho * rho;
 | 
				
			|||
    let eval_joint = self.eval_E_row + rho * self.eval_E_col + rho * rho * self.eval_val;
 | 
				
			|||
    EE::verify(
 | 
				
			|||
      vk_ee,
 | 
				
			|||
      transcript,
 | 
				
			|||
      &comm_joint,
 | 
				
			|||
      &r_eval,
 | 
				
			|||
      &eval_joint,
 | 
				
			|||
      &self.arg_eval,
 | 
				
			|||
    )?;
 | 
				
			|||
 | 
				
			|||
    // (2) verify if E_row and E_col are well formed
 | 
				
			|||
    let gamma_1 = transcript.squeeze(b"g1")?;
 | 
				
			|||
    let gamma_2 = transcript.squeeze(b"g2")?;
 | 
				
			|||
 | 
				
			|||
    // hash function
 | 
				
			|||
    let gamma_1_sqr = gamma_1 * gamma_1;
 | 
				
			|||
    let hash_func = |addr: &G::Scalar, val: &G::Scalar, ts: &G::Scalar| -> G::Scalar {
 | 
				
			|||
      (*ts * gamma_1_sqr + *val * gamma_1 + *addr) - gamma_2
 | 
				
			|||
    };
 | 
				
			|||
 | 
				
			|||
    // check the required multiset relationship
 | 
				
			|||
    // row
 | 
				
			|||
    if self.eval_init_row * self.eval_write_row != self.eval_read_row * self.eval_audit_row {
 | 
				
			|||
      return Err(NovaError::InvalidMultisetProof);
 | 
				
			|||
    }
 | 
				
			|||
    // col
 | 
				
			|||
    if self.eval_init_col * self.eval_write_col != self.eval_read_col * self.eval_audit_col {
 | 
				
			|||
      return Err(NovaError::InvalidMultisetProof);
 | 
				
			|||
    }
 | 
				
			|||
 | 
				
			|||
    // verify the product proofs
 | 
				
			|||
    let (claim_init_audit_row, r_init_audit_row) = self.sc_prod_init_audit_row.verify(
 | 
				
			|||
      &[self.eval_init_row, self.eval_audit_row],
 | 
				
			|||
      comm.ell.0.pow2(),
 | 
				
			|||
      transcript,
 | 
				
			|||
    )?;
 | 
				
			|||
    let (claim_init_audit_col, r_init_audit_col) = self.sc_prod_init_audit_col.verify(
 | 
				
			|||
      &[self.eval_init_col, self.eval_audit_col],
 | 
				
			|||
      comm.ell.1.pow2(),
 | 
				
			|||
      transcript,
 | 
				
			|||
    )?;
 | 
				
			|||
    let (claim_read_write_row_col, r_read_write_row_col) = self.sc_prod_read_write_row_col.verify(
 | 
				
			|||
      &[
 | 
				
			|||
        self.eval_read_row,
 | 
				
			|||
        self.eval_write_row,
 | 
				
			|||
        self.eval_read_col,
 | 
				
			|||
        self.eval_write_col,
 | 
				
			|||
      ],
 | 
				
			|||
      comm.size,
 | 
				
			|||
      transcript,
 | 
				
			|||
    )?;
 | 
				
			|||
 | 
				
			|||
    // finish the final step of the three sum-checks
 | 
				
			|||
    let (claim_init_expected_row, claim_audit_expected_row) = {
 | 
				
			|||
      let addr = IdentityPolynomial::new(r_init_audit_row.len()).evaluate(&r_init_audit_row);
 | 
				
			|||
      let val = EqPolynomial::new(r_x.to_vec()).evaluate(&r_init_audit_row);
 | 
				
			|||
 | 
				
			|||
      (
 | 
				
			|||
        hash_func(&addr, &val, &G::Scalar::zero()),
 | 
				
			|||
        hash_func(&addr, &val, &self.eval_row_audit_ts),
 | 
				
			|||
      )
 | 
				
			|||
    };
 | 
				
			|||
 | 
				
			|||
    let (claim_read_expected_row, claim_write_expected_row) = {
 | 
				
			|||
      (
 | 
				
			|||
        hash_func(&self.eval_row, &self.eval_E_row2, &self.eval_row_read_ts),
 | 
				
			|||
        hash_func(
 | 
				
			|||
          &self.eval_row,
 | 
				
			|||
          &self.eval_E_row2,
 | 
				
			|||
          &(self.eval_row_read_ts + G::Scalar::one()),
 | 
				
			|||
        ),
 | 
				
			|||
      )
 | 
				
			|||
    };
 | 
				
			|||
 | 
				
			|||
    // multiset check for the row
 | 
				
			|||
    if claim_init_expected_row != claim_init_audit_row[0]
 | 
				
			|||
      || claim_audit_expected_row != claim_init_audit_row[1]
 | 
				
			|||
      || claim_read_expected_row != claim_read_write_row_col[0]
 | 
				
			|||
      || claim_write_expected_row != claim_read_write_row_col[1]
 | 
				
			|||
    {
 | 
				
			|||
      return Err(NovaError::InvalidSumcheckProof);
 | 
				
			|||
    }
 | 
				
			|||
 | 
				
			|||
    let (claim_init_expected_col, claim_audit_expected_col) = {
 | 
				
			|||
      let addr = IdentityPolynomial::new(r_init_audit_col.len()).evaluate(&r_init_audit_col);
 | 
				
			|||
      let val = EqPolynomial::new(r_y.to_vec()).evaluate(&r_init_audit_col);
 | 
				
			|||
 | 
				
			|||
      (
 | 
				
			|||
        hash_func(&addr, &val, &G::Scalar::zero()),
 | 
				
			|||
        hash_func(&addr, &val, &self.eval_col_audit_ts),
 | 
				
			|||
      )
 | 
				
			|||
    };
 | 
				
			|||
 | 
				
			|||
    let (claim_read_expected_col, claim_write_expected_col) = {
 | 
				
			|||
      (
 | 
				
			|||
        hash_func(&self.eval_col, &self.eval_E_col2, &self.eval_col_read_ts),
 | 
				
			|||
        hash_func(
 | 
				
			|||
          &self.eval_col,
 | 
				
			|||
          &self.eval_E_col2,
 | 
				
			|||
          &(self.eval_col_read_ts + G::Scalar::one()),
 | 
				
			|||
        ),
 | 
				
			|||
      )
 | 
				
			|||
    };
 | 
				
			|||
 | 
				
			|||
    // multiset check for the col
 | 
				
			|||
    if claim_init_expected_col != claim_init_audit_col[0]
 | 
				
			|||
      || claim_audit_expected_col != claim_init_audit_col[1]
 | 
				
			|||
      || claim_read_expected_col != claim_read_write_row_col[2]
 | 
				
			|||
      || claim_write_expected_col != claim_read_write_row_col[3]
 | 
				
			|||
    {
 | 
				
			|||
      return Err(NovaError::InvalidSumcheckProof);
 | 
				
			|||
    }
 | 
				
			|||
 | 
				
			|||
    transcript.absorb(
 | 
				
			|||
      b"e",
 | 
				
			|||
      &[
 | 
				
			|||
        self.eval_row,
 | 
				
			|||
        self.eval_row_read_ts,
 | 
				
			|||
        self.eval_E_row2,
 | 
				
			|||
        self.eval_col,
 | 
				
			|||
        self.eval_col_read_ts,
 | 
				
			|||
        self.eval_E_col2,
 | 
				
			|||
      ]
 | 
				
			|||
      .as_slice(),
 | 
				
			|||
    );
 | 
				
			|||
    let c = transcript.squeeze(b"c")?;
 | 
				
			|||
    let eval_joint = self.eval_row
 | 
				
			|||
      + c * self.eval_row_read_ts
 | 
				
			|||
      + c * c * self.eval_E_row2
 | 
				
			|||
      + c * c * c * self.eval_col
 | 
				
			|||
      + c * c * c * c * self.eval_col_read_ts
 | 
				
			|||
      + c * c * c * c * c * self.eval_E_col2;
 | 
				
			|||
    let comm_joint = comm.comm_row
 | 
				
			|||
      + comm.comm_row_read_ts * c
 | 
				
			|||
      + self.comm_E_row * c * c
 | 
				
			|||
      + comm.comm_col * c * c * c
 | 
				
			|||
      + comm.comm_col_read_ts * c * c * c * c
 | 
				
			|||
      + self.comm_E_col * c * c * c * c * c;
 | 
				
			|||
 | 
				
			|||
    EE::verify(
 | 
				
			|||
      vk_ee,
 | 
				
			|||
      transcript,
 | 
				
			|||
      &comm_joint,
 | 
				
			|||
      &r_read_write_row_col,
 | 
				
			|||
      &eval_joint,
 | 
				
			|||
      &self.arg_row_col_joint,
 | 
				
			|||
    )?;
 | 
				
			|||
 | 
				
			|||
    EE::verify(
 | 
				
			|||
      vk_ee,
 | 
				
			|||
      transcript,
 | 
				
			|||
      &comm.comm_row_audit_ts,
 | 
				
			|||
      &r_init_audit_row,
 | 
				
			|||
      &self.eval_row_audit_ts,
 | 
				
			|||
      &self.arg_row_audit_ts,
 | 
				
			|||
    )?;
 | 
				
			|||
 | 
				
			|||
    EE::verify(
 | 
				
			|||
      vk_ee,
 | 
				
			|||
      transcript,
 | 
				
			|||
      &comm.comm_col_audit_ts,
 | 
				
			|||
      &r_init_audit_col,
 | 
				
			|||
      &self.eval_col_audit_ts,
 | 
				
			|||
      &self.arg_col_audit_ts,
 | 
				
			|||
    )?;
 | 
				
			|||
 | 
				
			|||
    Ok(self.eval)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||