#![allow(non_snake_case)]
use crate::{
errors::NovaError,
provider::traits::{CompressedGroup, DlogGroup, PairingGroup},
traits::{
commitment::{CommitmentEngineTrait, CommitmentTrait, Len},
evaluation::EvaluationEngineTrait,
AbsorbInROTrait, Engine, ROTrait, TranscriptEngineTrait, TranscriptReprTrait,
},
zip_with,
};
use core::{
marker::PhantomData,
ops::{Add, Mul, MulAssign},
};
use ff::Field;
use itertools::Itertools;
use rand_core::OsRng;
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
type G1Affine<E> = <<E as Engine>::GE as DlogGroup>::AffineGroupElement;
type G2Affine<E> = <<<E as Engine>::GE as PairingGroup>::G2 as DlogGroup>::AffineGroupElement;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CommitmentKey<E: Engine>
where
E::GE: PairingGroup,
{
ck: Vec<<E::GE as DlogGroup>::AffineGroupElement>,
tau_H: <<E::GE as PairingGroup>::G2 as DlogGroup>::AffineGroupElement, }
impl<E: Engine> Len for CommitmentKey<E>
where
E::GE: PairingGroup,
{
fn length(&self) -> usize {
self.ck.len()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(bound = "")]
pub struct Commitment<E>
where
E: Engine,
E::GE: PairingGroup,
{
comm: <E as Engine>::GE,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CompressedCommitment<E>
where
E: Engine,
E::GE: PairingGroup,
{
comm: <E::GE as DlogGroup>::CompressedGroupElement,
}
impl<E> CommitmentTrait<E> for Commitment<E>
where
E: Engine,
E::GE: PairingGroup,
{
type CompressedCommitment = CompressedCommitment<E>;
fn compress(&self) -> Self::CompressedCommitment {
CompressedCommitment {
comm: self.comm.compress(),
}
}
fn to_coordinates(&self) -> (E::Base, E::Base, bool) {
self.comm.to_coordinates()
}
fn decompress(c: &Self::CompressedCommitment) -> Result<Self, NovaError> {
let comm = <<E as Engine>::GE as DlogGroup>::CompressedGroupElement::decompress(&c.comm)?;
Ok(Commitment { comm })
}
}
impl<E> Default for Commitment<E>
where
E: Engine,
E::GE: PairingGroup,
{
fn default() -> Self {
Commitment {
comm: E::GE::zero(),
}
}
}
impl<E> TranscriptReprTrait<E::GE> for Commitment<E>
where
E: Engine,
E::GE: PairingGroup,
{
fn to_transcript_bytes(&self) -> Vec<u8> {
let (x, y, is_infinity) = self.comm.to_coordinates();
let is_infinity_byte = (!is_infinity).into();
[
x.to_transcript_bytes(),
y.to_transcript_bytes(),
[is_infinity_byte].to_vec(),
]
.concat()
}
}
impl<E> AbsorbInROTrait<E> for Commitment<E>
where
E: Engine,
E::GE: PairingGroup,
{
fn absorb_in_ro(&self, ro: &mut E::RO) {
let (x, y, is_infinity) = self.comm.to_coordinates();
ro.absorb(x);
ro.absorb(y);
ro.absorb(if is_infinity {
E::Base::ONE
} else {
E::Base::ZERO
});
}
}
impl<E: Engine> TranscriptReprTrait<E::GE> for CompressedCommitment<E>
where
E::GE: PairingGroup,
{
fn to_transcript_bytes(&self) -> Vec<u8> {
self.comm.to_transcript_bytes()
}
}
impl<E> MulAssign<E::Scalar> for Commitment<E>
where
E: Engine,
E::GE: PairingGroup,
{
fn mul_assign(&mut self, scalar: E::Scalar) {
let result = (self as &Commitment<E>).comm * scalar;
*self = Commitment { comm: result };
}
}
impl<'a, 'b, E> Mul<&'b E::Scalar> for &'a Commitment<E>
where
E: Engine,
E::GE: PairingGroup,
{
type Output = Commitment<E>;
fn mul(self, scalar: &'b E::Scalar) -> Commitment<E> {
Commitment {
comm: self.comm * scalar,
}
}
}
impl<E> Mul<E::Scalar> for Commitment<E>
where
E: Engine,
E::GE: PairingGroup,
{
type Output = Commitment<E>;
fn mul(self, scalar: E::Scalar) -> Commitment<E> {
Commitment {
comm: self.comm * scalar,
}
}
}
impl<E> Add for Commitment<E>
where
E: Engine,
E::GE: PairingGroup,
{
type Output = Commitment<E>;
fn add(self, other: Commitment<E>) -> Commitment<E> {
Commitment {
comm: self.comm + other.comm,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CommitmentEngine<E: Engine> {
_p: PhantomData<E>,
}
impl<E> CommitmentEngineTrait<E> for CommitmentEngine<E>
where
E: Engine,
E::GE: PairingGroup,
{
type Commitment = Commitment<E>;
type CommitmentKey = CommitmentKey<E>;
fn setup(_label: &'static [u8], n: usize) -> Self::CommitmentKey {
let tau = E::Scalar::random(OsRng);
let num_gens = n.next_power_of_two();
let mut powers_of_tau: Vec<E::Scalar> = Vec::with_capacity(num_gens);
powers_of_tau.insert(0, E::Scalar::ONE);
for i in 1..num_gens {
powers_of_tau.insert(i, powers_of_tau[i - 1] * tau);
}
let ck: Vec<G1Affine<E>> = (0..num_gens)
.into_par_iter()
.map(|i| (<E::GE as DlogGroup>::gen() * powers_of_tau[i]).affine())
.collect();
let tau_H = (<<E::GE as PairingGroup>::G2 as DlogGroup>::gen() * tau).affine();
Self::CommitmentKey { ck, tau_H }
}
fn commit(ck: &Self::CommitmentKey, v: &[E::Scalar]) -> Self::Commitment {
assert!(ck.ck.len() >= v.len());
Commitment {
comm: E::GE::vartime_multiscalar_mul(v, &ck.ck[..v.len()]),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(bound = "")]
pub struct ProverKey<E: Engine> {
_p: PhantomData<E>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(bound = "")]
pub struct VerifierKey<E: Engine>
where
E::GE: PairingGroup,
{
G: G1Affine<E>,
H: G2Affine<E>,
tau_H: G2Affine<E>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(bound = "")]
pub struct EvaluationArgument<E: Engine>
where
E::GE: PairingGroup,
{
com: Vec<G1Affine<E>>,
w: Vec<G1Affine<E>>,
v: Vec<Vec<E::Scalar>>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EvaluationEngine<E: Engine> {
_p: PhantomData<E>,
}
impl<E> EvaluationEngine<E>
where
E: Engine,
E::GE: PairingGroup,
{
fn compute_challenge(com: &[G1Affine<E>], transcript: &mut <E as Engine>::TE) -> E::Scalar {
transcript.absorb(b"c", &com.to_vec().as_slice());
transcript.squeeze(b"c").unwrap()
}
fn get_batch_challenge(v: &[Vec<E::Scalar>], transcript: &mut <E as Engine>::TE) -> E::Scalar {
transcript.absorb(
b"v",
&v.iter()
.flatten()
.cloned()
.collect::<Vec<E::Scalar>>()
.as_slice(),
);
transcript.squeeze(b"r").unwrap()
}
fn batch_challenge_powers(q: E::Scalar, k: usize) -> Vec<E::Scalar> {
let mut q_powers = vec![E::Scalar::ONE; k];
for i in 1..k {
q_powers[i] = q_powers[i - 1] * q;
}
q_powers
}
fn verifier_second_challenge(W: &[G1Affine<E>], transcript: &mut <E as Engine>::TE) -> E::Scalar {
transcript.absorb(b"W", &W.to_vec().as_slice());
transcript.squeeze(b"d").unwrap()
}
}
impl<E> EvaluationEngineTrait<E> for EvaluationEngine<E>
where
E: Engine<CE = CommitmentEngine<E>>,
E::GE: PairingGroup,
{
type EvaluationArgument = EvaluationArgument<E>;
type ProverKey = ProverKey<E>;
type VerifierKey = VerifierKey<E>;
fn setup(
ck: &<E::CE as CommitmentEngineTrait<E>>::CommitmentKey,
) -> (Self::ProverKey, Self::VerifierKey) {
let pk = ProverKey {
_p: Default::default(),
};
let vk = VerifierKey {
G: E::GE::gen().affine(),
H: <<E::GE as PairingGroup>::G2 as DlogGroup>::gen().affine(),
tau_H: ck.tau_H.clone(),
};
(pk, vk)
}
fn prove(
ck: &CommitmentKey<E>,
_pk: &Self::ProverKey,
transcript: &mut <E as Engine>::TE,
_C: &Commitment<E>,
hat_P: &[E::Scalar],
point: &[E::Scalar],
_eval: &E::Scalar,
) -> Result<Self::EvaluationArgument, NovaError> {
let x: Vec<E::Scalar> = point.to_vec();
let kzg_open = |f: &[E::Scalar], u: E::Scalar| -> G1Affine<E> {
let compute_witness_polynomial = |f: &[E::Scalar], u: E::Scalar| -> Vec<E::Scalar> {
let d = f.len();
let mut h = vec![E::Scalar::ZERO; d];
for i in (1..d).rev() {
h[i - 1] = f[i] + h[i] * u;
}
h
};
let h = compute_witness_polynomial(f, u);
E::CE::commit(ck, &h).comm.affine()
};
let kzg_open_batch = |f: &[Vec<E::Scalar>],
u: &[E::Scalar],
transcript: &mut <E as Engine>::TE|
-> (Vec<G1Affine<E>>, Vec<Vec<E::Scalar>>) {
let poly_eval = |f: &[E::Scalar], u: E::Scalar| -> E::Scalar {
let mut v = f[0];
let mut u_power = E::Scalar::ONE;
for fi in f.iter().skip(1) {
u_power *= u;
v += u_power * fi;
}
v
};
let scalar_vector_muladd = |a: &mut Vec<E::Scalar>, v: &Vec<E::Scalar>, s: E::Scalar| {
assert!(a.len() >= v.len());
for i in 0..v.len() {
a[i] += s * v[i];
}
};
let kzg_compute_batch_polynomial = |f: &[Vec<E::Scalar>], q: E::Scalar| -> Vec<E::Scalar> {
let k = f.len(); let q_powers = Self::batch_challenge_powers(q, k);
let mut B = f[0].clone();
for i in 1..k {
scalar_vector_muladd(&mut B, &f[i], q_powers[i]); }
B
};
let k = f.len();
let t = u.len();
let mut v = vec![vec!(E::Scalar::ZERO; k); t];
v.par_iter_mut().enumerate().for_each(|(i, v_i)| {
v_i.par_iter_mut().zip_eq(f).for_each(|(v_ij, f)| {
*v_ij = poly_eval(f, u[i]);
});
});
let q = Self::get_batch_challenge(&v, transcript);
let B = kzg_compute_batch_polynomial(f, q);
let w = u
.into_par_iter()
.map(|ui| kzg_open(&B, *ui))
.collect::<Vec<G1Affine<E>>>();
let _d_0 = Self::verifier_second_challenge(&w, transcript);
(w, v)
};
let ell = x.len();
let n = hat_P.len();
assert_eq!(n, 1 << ell); let mut polys: Vec<Vec<E::Scalar>> = Vec::new();
polys.push(hat_P.to_vec());
for i in 0..ell - 1 {
let Pi_len = polys[i].len() / 2;
let mut Pi = vec![E::Scalar::ZERO; Pi_len];
#[allow(clippy::needless_range_loop)]
Pi.par_iter_mut().enumerate().for_each(|(j, Pi_j)| {
*Pi_j = x[ell - i - 1] * (polys[i][2 * j + 1] - polys[i][2 * j]) + polys[i][2 * j];
});
polys.push(Pi);
}
let com: Vec<G1Affine<E>> = (1..polys.len())
.into_par_iter()
.map(|i| E::CE::commit(ck, &polys[i]).comm.affine())
.collect();
let r = Self::compute_challenge(&com, transcript);
let u = vec![r, -r, r * r];
let (w, v) = kzg_open_batch(&polys, &u, transcript);
Ok(EvaluationArgument { com, w, v })
}
fn verify(
vk: &Self::VerifierKey,
transcript: &mut <E as Engine>::TE,
C: &Commitment<E>,
point: &[E::Scalar],
P_of_x: &E::Scalar,
pi: &Self::EvaluationArgument,
) -> Result<(), NovaError> {
let x = point.to_vec();
let y = P_of_x;
let kzg_verify_batch = |vk: &VerifierKey<E>,
C: &Vec<G1Affine<E>>,
W: &Vec<G1Affine<E>>,
u: &Vec<E::Scalar>,
v: &Vec<Vec<E::Scalar>>,
transcript: &mut <E as Engine>::TE|
-> bool {
let k = C.len();
let t = u.len();
let q = Self::get_batch_challenge(v, transcript);
let q_powers = Self::batch_challenge_powers(q, k); let d_0 = Self::verifier_second_challenge(W, transcript);
let d_1 = d_0 * d_0;
let from_ppG1 = |P: &G1Affine<E>| <E::GE as DlogGroup>::group(P);
let from_ppG2 = |P: &G2Affine<E>| <<E::GE as PairingGroup>::G2 as DlogGroup>::group(P);
assert_eq!(t, 3);
assert_eq!(W.len(), 3);
let q_power_multiplier = E::Scalar::ONE + d_0 + d_1;
let q_powers_multiplied: Vec<E::Scalar> = q_powers
.par_iter()
.map(|q_power| *q_power * q_power_multiplier)
.collect();
let B_u = v
.into_par_iter()
.map(|v_i| zip_with!(iter, (q_powers, v_i), |a, b| *a * *b).sum())
.collect::<Vec<E::Scalar>>();
let L = E::GE::vartime_multiscalar_mul(
&[
&q_powers_multiplied[..k],
&[
u[0],
(u[1] * d_0),
(u[2] * d_1),
-(B_u[0] + d_0 * B_u[1] + d_1 * B_u[2]),
],
]
.concat(),
&[
&C[..k],
&[W[0].clone(), W[1].clone(), W[2].clone(), vk.G.clone()],
]
.concat(),
);
let R0 = from_ppG1(&W[0]);
let R1 = from_ppG1(&W[1]);
let R2 = from_ppG1(&W[2]);
let R = R0 + R1 * d_0 + R2 * d_1;
(<E::GE as PairingGroup>::pairing(&L, &from_ppG2(&vk.H)))
== (<E::GE as PairingGroup>::pairing(&R, &from_ppG2(&vk.tau_H)))
};
let ell = x.len();
let mut com = pi.com.clone();
let r = Self::compute_challenge(&com, transcript);
if r == E::Scalar::ZERO || C.comm == E::GE::zero() {
return Err(NovaError::ProofVerifyError);
}
com.insert(0, C.comm.affine()); let u = vec![r, -r, r * r];
let v = &pi.v;
if v.len() != 3 {
return Err(NovaError::ProofVerifyError);
}
if v[0].len() != ell || v[1].len() != ell || v[2].len() != ell {
return Err(NovaError::ProofVerifyError);
}
let ypos = &v[0];
let yneg = &v[1];
let mut Y = v[2].to_vec();
Y.push(*y);
let two = E::Scalar::from(2u64);
for i in 0..ell {
if two * r * Y[i + 1]
!= r * (E::Scalar::ONE - x[ell - i - 1]) * (ypos[i] + yneg[i])
+ x[ell - i - 1] * (ypos[i] - yneg[i])
{
return Err(NovaError::ProofVerifyError);
}
}
if !kzg_verify_batch(vk, &com, &pi.w, &u, &pi.v, transcript) {
return Err(NovaError::ProofVerifyError);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
provider::{keccak::Keccak256Transcript, Bn256EngineKZG},
spartan::polys::multilinear::MultilinearPolynomial,
};
use bincode::Options;
use rand::SeedableRng;
type E = Bn256EngineKZG;
type Fr = <E as Engine>::Scalar;
#[test]
fn test_hyperkzg_eval() {
let n = 4;
let ck: CommitmentKey<E> = CommitmentEngine::setup(b"test", n);
let (pk, vk): (ProverKey<E>, VerifierKey<E>) = EvaluationEngine::setup(&ck);
let poly = vec![Fr::from(1), Fr::from(2), Fr::from(2), Fr::from(4)];
let C = CommitmentEngine::commit(&ck, &poly);
let test_inner = |point: Vec<Fr>, eval: Fr| -> Result<(), NovaError> {
let mut tr = Keccak256Transcript::new(b"TestEval");
let proof = EvaluationEngine::prove(&ck, &pk, &mut tr, &C, &poly, &point, &eval).unwrap();
let mut tr = Keccak256Transcript::new(b"TestEval");
EvaluationEngine::verify(&vk, &mut tr, &C, &point, &eval, &proof)
};
let point = vec![Fr::from(0), Fr::from(0)];
let eval = Fr::ONE;
assert!(test_inner(point, eval).is_ok());
let point = vec![Fr::from(0), Fr::from(1)];
let eval = Fr::from(2);
assert!(test_inner(point, eval).is_ok());
let point = vec![Fr::from(1), Fr::from(1)];
let eval = Fr::from(4);
assert!(test_inner(point, eval).is_ok());
let point = vec![Fr::from(0), Fr::from(2)];
let eval = Fr::from(3);
assert!(test_inner(point, eval).is_ok());
let point = vec![Fr::from(2), Fr::from(2)];
let eval = Fr::from(9);
assert!(test_inner(point, eval).is_ok());
let point = vec![Fr::from(2), Fr::from(2)];
let eval = Fr::from(50);
assert!(test_inner(point, eval).is_err());
let point = vec![Fr::from(0), Fr::from(2)];
let eval = Fr::from(4);
assert!(test_inner(point, eval).is_err());
}
#[test]
fn test_hyperkzg_small() {
let n = 4;
let poly = vec![Fr::ONE, Fr::from(2), Fr::from(1), Fr::from(4)];
let point = vec![Fr::from(4), Fr::from(3)];
let eval = Fr::from(28);
let ck: CommitmentKey<E> = CommitmentEngine::setup(b"test", n);
let (pk, vk) = EvaluationEngine::setup(&ck);
let C = CommitmentEngine::commit(&ck, &poly);
let mut prover_transcript = Keccak256Transcript::new(b"TestEval");
let proof =
EvaluationEngine::<E>::prove(&ck, &pk, &mut prover_transcript, &C, &poly, &point, &eval)
.unwrap();
let post_c_p = prover_transcript.squeeze(b"c").unwrap();
let mut verifier_transcript = Keccak256Transcript::new(b"TestEval");
assert!(
EvaluationEngine::verify(&vk, &mut verifier_transcript, &C, &point, &eval, &proof).is_ok()
);
let post_c_v = verifier_transcript.squeeze(b"c").unwrap();
assert_eq!(post_c_p, post_c_v);
let proof_bytes = bincode::DefaultOptions::new()
.with_big_endian()
.with_fixint_encoding()
.serialize(&proof)
.unwrap();
assert_eq!(proof_bytes.len(), 368);
let mut bad_proof = proof.clone();
bad_proof.v[0] = bad_proof.v[1].clone();
let mut verifier_transcript2 = Keccak256Transcript::new(b"TestEval");
assert!(EvaluationEngine::verify(
&vk,
&mut verifier_transcript2,
&C,
&point,
&eval,
&bad_proof
)
.is_err());
}
#[test]
fn test_hyperkzg_large() {
for ell in [4, 5, 6] {
let mut rng = rand::rngs::StdRng::seed_from_u64(ell as u64);
let n = 1 << ell; let poly = (0..n).map(|_| Fr::random(&mut rng)).collect::<Vec<_>>();
let point = (0..ell).map(|_| Fr::random(&mut rng)).collect::<Vec<_>>();
let eval = MultilinearPolynomial::evaluate_with(&poly, &point);
let ck: CommitmentKey<E> = CommitmentEngine::setup(b"test", n);
let (pk, vk) = EvaluationEngine::setup(&ck);
let C = CommitmentEngine::commit(&ck, &poly);
let mut prover_transcript = Keccak256Transcript::new(b"TestEval");
let proof: EvaluationArgument<E> =
EvaluationEngine::prove(&ck, &pk, &mut prover_transcript, &C, &poly, &point, &eval)
.unwrap();
let mut verifier_tr = Keccak256Transcript::new(b"TestEval");
assert!(EvaluationEngine::verify(&vk, &mut verifier_tr, &C, &point, &eval, &proof).is_ok());
let mut bad_proof = proof.clone();
bad_proof.v[0] = bad_proof.v[1].clone();
let mut verifier_tr2 = Keccak256Transcript::new(b"TestEval");
assert!(
EvaluationEngine::verify(&vk, &mut verifier_tr2, &C, &point, &eval, &bad_proof).is_err()
);
}
}
}