use crate::{
setup::ChunkedCommitmentGens,
utils::{chunks_count, decompose},
};
use ark_ec::{AffineRepr, CurveGroup, Group, VariableBaseMSM};
use ark_ff::{Field, One, PrimeField};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::{vec, vec::Vec};
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use dock_crypto_utils::{msm::multiply_field_elems_with_same_group_elem, serde_utils::*};
#[serde_as]
#[derive(
Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize,
)]
pub struct ChunkedCommitment<G: AffineRepr>(
#[serde_as(as = "ArkObjectBytes")] pub G,
#[serde_as(as = "Vec<ArkObjectBytes>")] pub Vec<G>,
);
impl<G: AffineRepr> ChunkedCommitment<G> {
pub fn new(
message: &G::ScalarField,
blinding: &G::ScalarField,
chunk_bit_size: u8,
gens: &ChunkedCommitmentGens<G>,
) -> crate::Result<Self> {
let decomposed = Self::get_values_to_commit(message, blinding, chunk_bit_size)?;
let gs = Self::commitment_key(gens, chunk_bit_size);
Ok(Self(
G::Group::msm_bigint(&gs, &decomposed).into_affine(),
gs,
))
}
pub fn get_commitment_given_commitment_key(
message: &G::ScalarField,
blinding: &G::ScalarField,
chunk_bit_size: u8,
comm_key: &[G],
) -> crate::Result<G> {
let decomposed = Self::get_values_to_commit(message, blinding, chunk_bit_size)?;
Ok(G::Group::msm_bigint(comm_key, &decomposed).into_affine())
}
pub fn commitment_key(gens: &ChunkedCommitmentGens<G>, chunk_bit_size: u8) -> Vec<G> {
let radix = (1 << chunk_bit_size) as u64;
let chunks = chunks_count::<G::ScalarField>(chunk_bit_size);
let gs = if radix.is_power_of_two() {
Self::commitment_key_for_radix_power_of_2(gens.G.into_group(), chunks, radix)
} else {
Self::commitment_key_for_radix_non_power_of_2(gens.G.into_group(), chunks, radix)
};
let mut ck = G::Group::normalize_batch(&gs);
ck.push(gens.H);
ck
}
fn get_values_to_commit(
message: &G::ScalarField,
blinding: &G::ScalarField,
chunk_bit_size: u8,
) -> crate::Result<Vec<<G::ScalarField as PrimeField>::BigInt>> {
let mut decomposed = decompose(message, chunk_bit_size)?
.into_iter()
.map(|m| <G::ScalarField as PrimeField>::BigInt::from(m as u64))
.collect::<Vec<_>>();
decomposed.push(blinding.into_bigint());
Ok(decomposed)
}
fn commitment_key_for_radix_power_of_2(
g: G::Group,
chunks_count: u8,
radix: u64,
) -> Vec<G::Group> {
let mut gs = vec![g];
let log2 = radix.trailing_zeros();
for i in 1..chunks_count {
let mut curr = gs[i as usize - 1];
for _ in 0..log2 {
curr.double_in_place();
}
gs.push(curr);
}
gs.reverse();
gs
}
fn commitment_key_for_radix_non_power_of_2(
g: G::Group,
chunks_count: u8,
radix: u64,
) -> Vec<G::Group> {
let radix = G::ScalarField::from(radix);
let mut factors = vec![];
for i in 1..chunks_count {
factors.push(radix.pow([(chunks_count - i) as u64]));
}
factors.push(G::ScalarField::one());
multiply_field_elems_with_same_group_elem(g, &factors)
}
}
#[cfg(test)]
mod tests {
use super::*;
use ark_bls12_381::{Bls12_381, Fr};
use ark_ec::pairing::Pairing;
use ark_std::{
collections::BTreeSet,
ops::Add,
rand::{prelude::StdRng, SeedableRng},
UniformRand,
};
use std::time::{Duration, Instant};
use crate::encryption::{tests::enc_setup, Encryption};
use blake2::Blake2b512;
use proof_system::{
prelude::{
EqualWitnesses, MetaStatement, MetaStatements, Proof, ProofSpec, Statements, Witness,
WitnessRef, Witnesses,
},
statement::ped_comm::PedersenCommitment as PedersenCommitmentStmt,
};
#[test]
fn commitment_key_creation() {
fn check(chunk_bit_size: u8) {
let mut rng = StdRng::seed_from_u64(0u64);
let g = <Bls12_381 as Pairing>::G1::rand(&mut rng);
let chunks_count = chunks_count::<Fr>(chunk_bit_size);
let start = Instant::now();
let gs_1 = ChunkedCommitment::<<Bls12_381 as Pairing>::G1Affine>::commitment_key_for_radix_power_of_2(g, chunks_count, 1 << chunk_bit_size);
println!(
"commitment_key_for_radix_power_of_2 time {:?}",
start.elapsed()
);
let start = Instant::now();
let gs_2 = ChunkedCommitment::<<Bls12_381 as Pairing>::G1Affine>::commitment_key_for_radix_non_power_of_2(g, chunks_count, 1 << chunk_bit_size);
println!(
"commitment_key_for_radix_non_power_of_2 time {:?}",
start.elapsed()
);
assert_eq!(gs_1, gs_2);
}
check(4);
check(8);
check(16);
}
#[test]
fn commitment_transform_works() {
fn check(chunk_bit_size: u8) {
let mut rng = StdRng::seed_from_u64(0u64);
let n = chunks_count::<Fr>(chunk_bit_size) as usize;
let (_, g_i, _, ek, _) = enc_setup(chunk_bit_size, &mut rng);
let gens =
ChunkedCommitmentGens::<<Bls12_381 as Pairing>::G1Affine>::new_using_rng(&mut rng);
let count = 10;
let mut total_prove = Duration::default();
let mut total_verify = Duration::default();
for _ in 0..count {
let m = Fr::rand(&mut rng);
let blinding = Fr::rand(&mut rng);
let comm_1 = gens
.G
.mul_bigint(m.into_bigint())
.add(&(gens.H.mul_bigint(blinding.into_bigint())));
let comm_2 = ChunkedCommitment::<<Bls12_381 as Pairing>::G1Affine>::new(
&m,
&blinding,
chunk_bit_size,
&gens,
)
.unwrap()
.0;
assert_eq!(comm_1, comm_2);
let (ct, r) = Encryption::encrypt(&mut rng, &m, &ek, &g_i, chunk_bit_size).unwrap();
let comm_ct = ct.commitment;
let mut decomposed = decompose(&m, chunk_bit_size)
.unwrap()
.into_iter()
.map(|m| Fr::from(m as u64))
.collect::<Vec<_>>();
let gs = ChunkedCommitment::<<Bls12_381 as Pairing>::G1Affine>::commitment_key(
&gens,
chunk_bit_size,
);
decomposed.push(blinding);
assert_eq!(gs.len(), decomposed.len());
let bases = ek.commitment_key();
let mut wit2 = decomposed.clone();
wit2[n as usize] = r;
let start = Instant::now();
let mut statements = Statements::<Bls12_381>::new();
statements.add(PedersenCommitmentStmt::new_statement_from_params(
gs.clone(),
comm_2.clone(),
));
statements.add(PedersenCommitmentStmt::new_statement_from_params(
bases.clone(),
comm_ct.clone(),
));
let mut meta_statements = MetaStatements::new();
for i in 0..n as usize {
meta_statements.add(MetaStatement::WitnessEquality(EqualWitnesses(
vec![(0, i), (1, i)]
.into_iter()
.collect::<BTreeSet<WitnessRef>>(),
)));
}
let proof_spec =
ProofSpec::new(statements.clone(), meta_statements.clone(), vec![], None);
let mut witnesses = Witnesses::new();
witnesses.add(Witness::PedersenCommitment(decomposed));
witnesses.add(Witness::PedersenCommitment(wit2));
let proof = Proof::new::<StdRng, Blake2b512>(
&mut rng,
proof_spec.clone(),
witnesses.clone(),
None,
Default::default(),
)
.unwrap()
.0;
total_prove += start.elapsed();
let start = Instant::now();
proof
.verify::<StdRng, Blake2b512>(&mut rng, proof_spec, None, Default::default())
.unwrap();
total_verify += start.elapsed();
}
println!(
"Time taken for {} iterations and {} chunk size:",
count, chunk_bit_size
);
println!("Proving {:?}", total_prove);
println!("Verifying {:?}", total_verify);
}
check(4);
check(8);
check(16);
}
}