use ark_ec::{AffineRepr, CurveGroup, Group, VariableBaseMSM};
use ark_ff::{One, PrimeField};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::{marker::PhantomData, rand::RngCore};
use ark_std::{vec, vec::Vec, UniformRand};
use digest::Digest;
use dock_crypto_utils::hashing_utils::field_elem_from_try_and_incr;
use crate::compressed_homomorphism;
use crate::error::CompSigmaError;
use crate::transforms::Homomorphism;
use crate::utils::get_n_powers;
#[derive(Clone, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize)]
pub struct RandomCommitment<G: AffineRepr> {
pub r: Vec<G::ScalarField>,
pub A: G,
pub t: G,
}
#[derive(Clone, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize)]
pub struct Response<G: AffineRepr> {
pub z: Vec<G::ScalarField>,
}
pub struct AmortizeHomomorphisms<G: AffineRepr, F: Homomorphism<G::ScalarField, Output = G>>(
PhantomData<G>,
PhantomData<F>,
);
impl<G: AffineRepr, F: Homomorphism<G::ScalarField, Output = G>> AmortizeHomomorphisms<G, F> {
pub fn new_homomorphism<D: Digest>(g: &[G], ys: &[G], fs: &[F]) -> F {
let randomness = Self::generate_randomness::<D>(g, ys);
Self::new_homomorphism_from_given_randomness(fs, &randomness)
}
pub fn new_homomorphism_and_evaluation<D: Digest>(
g: &[G],
ys: &[G],
fs: &[F],
) -> (F, G::Group) {
let randomness = Self::generate_randomness::<D>(g, ys);
(
Self::new_homomorphism_from_given_randomness(fs, &randomness),
Self::new_evaluation_from_given_randomness(ys, &randomness),
)
}
pub fn generate_randomness<D: Digest>(g: &[G], ys: &[G]) -> Vec<G::ScalarField> {
let mut bytes = vec![];
for g_ in g.iter() {
g_.serialize_compressed(&mut bytes).unwrap();
}
for y in ys.iter() {
y.serialize_compressed(&mut bytes).unwrap();
}
let rho = field_elem_from_try_and_incr::<G::ScalarField, D>(&bytes);
let mut rho_powers = get_n_powers(rho, ys.len() - 1);
rho_powers.insert(0, G::ScalarField::one());
rho_powers
}
pub fn new_evaluation_from_given_randomness(
ys: &[G],
rho_powers: &[G::ScalarField],
) -> G::Group {
G::Group::msm_unchecked(ys, &rho_powers[..ys.len()])
}
pub fn new_homomorphism_from_given_randomness(fs: &[F], rho_powers: &[G::ScalarField]) -> F {
fs.iter()
.zip(rho_powers.iter())
.map(|(f, r)| f.scale(r))
.reduce(|a, b| a.add(&b).unwrap())
.unwrap()
}
}
impl<G> RandomCommitment<G>
where
G: AffineRepr,
{
pub fn new<R: RngCore, D: Digest, F: Homomorphism<G::ScalarField, Output = G>>(
rng: &mut R,
g: &[G],
ys: &[G],
fs: &[F],
blindings: Option<Vec<G::ScalarField>>,
) -> Result<Self, CompSigmaError> {
if ys.len() != fs.len() {
return Err(CompSigmaError::VectorLenMismatch);
}
let f_rho = AmortizeHomomorphisms::<_, _>::new_homomorphism::<D>(g, ys, fs);
let r = if let Some(blindings) = blindings {
if blindings.len() != g.len() {
return Err(CompSigmaError::VectorLenMismatch);
}
blindings
} else {
(0..g.len()).map(|_| G::ScalarField::rand(rng)).collect()
};
let t = f_rho.eval(&r).unwrap();
let A = G::Group::msm_unchecked(g, &r);
Ok(Self {
r,
A: A.into_affine(),
t,
})
}
pub fn response(
&self,
x: &[G::ScalarField],
challenge: &G::ScalarField,
) -> Result<Response<G>, CompSigmaError> {
if self.r.len() != x.len() {
return Err(CompSigmaError::VectorLenMismatch);
}
Ok(Response {
z: self
.r
.iter()
.zip(x.iter())
.map(|(r, x)| *r + *x * challenge)
.collect(),
})
}
}
impl<G> Response<G>
where
G: AffineRepr,
{
pub fn is_valid<D: Digest, F: Homomorphism<G::ScalarField, Output = G> + Clone>(
&self,
g: &[G],
P: &G,
ys: &[G],
fs: &[F],
A: &G,
t: &G,
challenge: &G::ScalarField,
) -> Result<(), CompSigmaError> {
if ys.len() != fs.len() {
return Err(CompSigmaError::VectorLenMismatch);
}
let challenge_repr = challenge.into_bigint();
if G::Group::msm_unchecked(g, &self.z) != (P.mul_bigint(challenge_repr) + A) {
return Err(CompSigmaError::InvalidResponse);
}
let (f_rho, y_rho) =
AmortizeHomomorphisms::<_, _>::new_homomorphism_and_evaluation::<D>(g, ys, fs);
if f_rho.eval(&self.z).unwrap() != (y_rho.mul_bigint(challenge_repr) + t).into_affine() {
return Err(CompSigmaError::InvalidResponse);
}
Ok(())
}
pub fn compress<D: Digest, F: Homomorphism<G::ScalarField, Output = G> + Clone>(
self,
g: &[G],
ys: &[G],
fs: &[F],
) -> compressed_homomorphism::Response<G> {
let f_rho = AmortizeHomomorphisms::<_, _>::new_homomorphism::<D>(g, ys, fs);
compressed_homomorphism::RandomCommitment::compressed_response::<D, F>(
self.z,
g.to_vec(),
f_rho,
)
}
pub fn is_valid_compressed<D: Digest, F: Homomorphism<G::ScalarField, Output = G> + Clone>(
g: &[G],
fs: &[F],
P: &G,
ys: &[G],
A: &G,
t: &G,
challenge: &G::ScalarField,
compressed_resp: &compressed_homomorphism::Response<G>,
) -> Result<(), CompSigmaError> {
let (f_rho, y_rho) =
AmortizeHomomorphisms::<_, _>::new_homomorphism_and_evaluation::<D>(g, ys, fs);
let (Q, Y) = compressed_homomorphism::calculate_Q_and_Y::<G>(
P,
&y_rho.into_affine(),
A,
t,
challenge,
);
compressed_resp.validate_compressed::<D, F>(Q, Y, g.to_vec(), f_rho)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::pad_homomorphisms_to_have_same_size;
use ark_bls12_381::Bls12_381;
use ark_ec::pairing::Pairing;
use ark_std::{
rand::{rngs::StdRng, SeedableRng},
UniformRand, Zero,
};
use blake2::Blake2b512;
use std::time::Instant;
type Fr = <Bls12_381 as Pairing>::ScalarField;
type G1 = <Bls12_381 as Pairing>::G1Affine;
#[derive(Clone)]
struct TestHom<G1> {
pub constants: Vec<G1>,
}
impl TestHom<G1> {
fn new<R: RngCore>(rng: &mut R, size: usize) -> Self {
Self {
constants: (0..size)
.map(|_| <Bls12_381 as Pairing>::G1::rand(rng).into_affine())
.collect::<Vec<_>>(),
}
}
}
impl_simple_homomorphism!(TestHom, Fr, G1);
#[test]
fn amortization() {
let size = 8;
let mut rng = StdRng::seed_from_u64(0u64);
let hom1 = TestHom::new(&mut rng, size - 1);
let hom2 = TestHom::new(&mut rng, size);
let hom3 = TestHom::new(&mut rng, size - 2);
let hom4 = TestHom::new(&mut rng, size + 5);
let hom5 = TestHom::new(&mut rng, size + 1);
let homs = [hom1, hom2, hom3, hom4, hom5];
let x = (0..size).map(|_| Fr::rand(&mut rng)).collect::<Vec<_>>();
let g = (0..size)
.map(|_| <Bls12_381 as Pairing>::G1::rand(&mut rng).into_affine())
.collect::<Vec<_>>();
let comm = <Bls12_381 as Pairing>::G1::msm_unchecked(&g, &x).into_affine();
let fs = pad_homomorphisms_to_have_same_size(&homs);
let ys = fs.iter().map(|f| f.eval(&x).unwrap()).collect::<Vec<_>>();
let rand_comm =
RandomCommitment::new::<_, Blake2b512, _>(&mut rng, &g, &ys, &fs, None).unwrap();
let challenge = Fr::rand(&mut rng);
let response = rand_comm.response(&x, &challenge).unwrap();
response
.is_valid::<Blake2b512, _>(&g, &comm, &ys, &fs, &rand_comm.A, &rand_comm.t, &challenge)
.unwrap();
}
#[test]
fn amortization_and_compression() {
let mut rng = StdRng::seed_from_u64(0u64);
let size = 8;
let hom1 = TestHom::new(&mut rng, size);
let hom2 = TestHom::new(&mut rng, size);
let hom3 = TestHom::new(&mut rng, size);
let hom4 = TestHom::new(&mut rng, size);
let hom5 = TestHom::new(&mut rng, size);
let homs = [hom1, hom2, hom3, hom4, hom5];
let x = (0..size).map(|_| Fr::rand(&mut rng)).collect::<Vec<_>>();
let g = (0..size)
.map(|_| <Bls12_381 as Pairing>::G1::rand(&mut rng).into_affine())
.collect::<Vec<_>>();
let comm = <Bls12_381 as Pairing>::G1::msm_unchecked(&g, &x).into_affine();
let fs = pad_homomorphisms_to_have_same_size(&homs);
let ys = fs.iter().map(|f| f.eval(&x).unwrap()).collect::<Vec<_>>();
let rand_comm =
RandomCommitment::new::<_, Blake2b512, _>(&mut rng, &g, &ys, &fs, None).unwrap();
let challenge = Fr::rand(&mut rng);
let response = rand_comm.response(&x, &challenge).unwrap();
let start = Instant::now();
response
.is_valid::<Blake2b512, _>(&g, &comm, &ys, &fs, &rand_comm.A, &rand_comm.t, &challenge)
.unwrap();
println!(
"Verification of uncompressed response of {} homomorphisms, each of size {} takes: {:?}",
fs.len(),
size,
start.elapsed()
);
let start = Instant::now();
let comp_resp = response.compress::<Blake2b512, _>(&g, &ys, &fs);
println!(
"Compressing response of {} homomorphisms, each of size {} takes: {:?}",
homs.len(),
size,
start.elapsed()
);
let start = Instant::now();
Response::is_valid_compressed::<Blake2b512, _>(
&g,
&fs,
&comm,
&ys,
&rand_comm.A,
&rand_comm.t,
&challenge,
&comp_resp,
)
.unwrap();
println!(
"Verification of compressed response of {} homomorphisms, each of size {} takes: {:?}",
fs.len(),
size,
start.elapsed()
);
}
#[test]
fn amortization_and_compression_with_amortization_done_beforehand() {
let mut rng = StdRng::seed_from_u64(0u64);
let size = 8;
let hom1 = TestHom::new(&mut rng, size);
let hom2 = TestHom::new(&mut rng, size);
let hom3 = TestHom::new(&mut rng, size);
let hom4 = TestHom::new(&mut rng, size);
let hom5 = TestHom::new(&mut rng, size);
let homs = [hom1, hom2, hom3, hom4, hom5];
let x = (0..size).map(|_| Fr::rand(&mut rng)).collect::<Vec<_>>();
let g = (0..size)
.map(|_| <Bls12_381 as Pairing>::G1::rand(&mut rng).into_affine())
.collect::<Vec<_>>();
let comm = <Bls12_381 as Pairing>::G1::msm_unchecked(&g, &x).into_affine();
let fs = pad_homomorphisms_to_have_same_size(&homs);
let ys = fs.iter().map(|f| f.eval(&x).unwrap()).collect::<Vec<_>>();
let start = Instant::now();
let (f_rho, y_rho) = AmortizeHomomorphisms::<_, _>::new_homomorphism_and_evaluation::<
Blake2b512,
>(&g, &ys, &fs);
println!(
"Time to amortize {} homomorphisms, each of size {} takes: {:?}",
fs.len(),
size,
start.elapsed()
);
let rand_comm =
compressed_homomorphism::RandomCommitment::new(&mut rng, &g, &f_rho, None).unwrap();
let challenge = Fr::rand(&mut rng);
let response = rand_comm
.response::<Blake2b512, _>(&g, &f_rho, &x, &challenge)
.unwrap();
response
.is_valid::<Blake2b512, _>(
&g,
&comm,
&y_rho.into_affine(),
&f_rho,
&rand_comm.A_hat,
&rand_comm.t,
&challenge,
)
.unwrap();
}
}