#[cfg(any(feature = "kzg", feature = "aplonk"))]
use ark_ec::pairing::Pairing;
#[cfg(feature = "aplonk")]
use ark_ec::pairing::PairingOutput;
use ark_ff::{BigInteger, PrimeField};
#[cfg(any(feature = "kzg", feature = "aplonk"))]
use ark_poly::DenseUVPolynomial;
#[cfg(any(feature = "kzg", feature = "aplonk"))]
use ark_std::One;
#[cfg(any(feature = "kzg", feature = "aplonk"))]
use std::ops::{Div, Mul};
pub mod linalg;
pub fn split_data_into_field_elements<F: PrimeField>(bytes: &[u8], modulus: usize) -> Vec<F> {
let bytes_per_element = (F::MODULUS_BIT_SIZE as usize) / 8;
let mut elements = Vec::new();
for chunk in bytes.chunks(bytes_per_element) {
elements.push(F::from_le_bytes_mod_order(chunk));
}
if elements.len() % modulus != 0 {
elements.resize((elements.len() / modulus + 1) * modulus, F::one());
}
elements
}
pub(crate) fn merge_elements_into_bytes<F: PrimeField>(elements: &[F]) -> Vec<u8> {
let mut bytes = vec![];
for e in elements {
let mut b = e.into_bigint().to_bytes_le();
b.pop();
bytes.append(&mut b);
}
bytes
}
#[cfg(any(feature = "kzg", feature = "aplonk"))]
pub(crate) fn scalar_product_polynomial<E, P>(lhs: &[E::ScalarField], rhs: &[P]) -> P
where
E: Pairing,
P: DenseUVPolynomial<E::ScalarField, Point = E::ScalarField>,
for<'a, 'b> &'a P: Div<&'b P, Output = P>,
{
let mut polynomial = P::from_coefficients_vec(Vec::new());
for (p, s) in rhs.iter().zip(lhs.iter()) {
let coefficients: Vec<E::ScalarField> = p
.coeffs()
.iter()
.map(|coefficient| coefficient.mul(s))
.collect();
polynomial = polynomial.add(P::from_coefficients_vec(coefficients));
}
polynomial
}
#[cfg(feature = "aplonk")]
pub(super) fn scalar_product_pairing<E: Pairing>(lhs: &[E::G1], rhs: &[E::G2]) -> PairingOutput<E> {
lhs.iter()
.zip(rhs.iter())
.map(|(l, r)| E::pairing(l, r))
.sum()
}
#[cfg(feature = "aplonk")]
pub(super) fn scalar_product<E: Pairing>(
lhs: &[E::ScalarField],
rhs: &[E::ScalarField],
) -> E::ScalarField {
lhs.iter().zip(rhs.iter()).map(|(l, r)| l.mul(r)).sum()
}
#[cfg(feature = "aplonk")]
pub(super) fn scalar_product_g1<E: Pairing>(lhs: &[E::G1], rhs: &[E::ScalarField]) -> E::G1 {
lhs.iter().zip(rhs.iter()).map(|(l, r)| l.mul(r)).sum()
}
#[cfg(feature = "aplonk")]
pub(super) fn scalar_product_g2<E: Pairing>(lhs: &[E::G2], rhs: &[E::ScalarField]) -> E::G2 {
lhs.iter().zip(rhs.iter()).map(|(l, r)| l.mul(r)).sum()
}
#[cfg(feature = "aplonk")]
pub(super) mod vector {
use ark_ff::Zero;
pub fn zero<Z: Zero + Clone>(capacity: usize) -> Vec<Z> {
let mut vector = Vec::with_capacity(capacity);
vector.resize(capacity, Z::zero());
vector
}
}
#[cfg(any(feature = "kzg", feature = "aplonk"))]
pub(crate) fn powers_of<E: Pairing>(step: E::ScalarField, nb_powers: usize) -> Vec<E::ScalarField> {
let mut powers = Vec::with_capacity(nb_powers);
powers.push(E::ScalarField::one());
for j in 1..nb_powers {
powers.push(powers[j - 1].mul(step));
}
powers
}
#[cfg(test)]
mod tests {
#[cfg(any(feature = "kzg", feature = "aplonk"))]
use ark_bls12_381::Bls12_381;
use ark_bls12_381::Fr;
#[cfg(any(feature = "kzg", feature = "aplonk"))]
use ark_ec::pairing::Pairing;
#[cfg(any(feature = "kzg", feature = "aplonk"))]
use ark_ff::Field;
use ark_ff::PrimeField;
#[cfg(any(feature = "kzg", feature = "aplonk"))]
use ark_std::{test_rng, UniformRand};
fn bytes() -> Vec<u8> {
include_bytes!("../../assets/dragoon_32x32.png").to_vec()
}
fn split_data_template<F: PrimeField>(
bytes: &[u8],
modulus: usize,
exact_length: Option<usize>,
) {
let test_case = format!(
"TEST | modulus: {}, exact_length: {:?}",
modulus, exact_length
);
let elements = super::split_data_into_field_elements::<F>(bytes, modulus);
assert!(
elements.len() % modulus == 0,
"number of elements should be divisible by {}, found {}\n{test_case}",
modulus,
elements.len(),
);
if let Some(length) = exact_length {
assert!(
elements.len() == length,
"number of elements should be exactly {}, found {}\n{test_case}",
length,
elements.len(),
);
}
assert!(
!elements.iter().any(|&e| e == F::zero()),
"elements should not contain any 0\n{test_case}"
);
}
#[test]
fn split_data() {
split_data_template::<Fr>(&bytes(), 1, None);
split_data_template::<Fr>(&bytes(), 8, None);
split_data_template::<Fr>(&[], 1, None);
split_data_template::<Fr>(&[], 8, None);
let nb_bytes = 11 * (Fr::MODULUS_BIT_SIZE as usize / 8);
split_data_template::<Fr>(&bytes()[..nb_bytes], 1, Some(11));
split_data_template::<Fr>(&bytes()[..nb_bytes], 8, Some(16));
let nb_bytes = 11 * (Fr::MODULUS_BIT_SIZE as usize / 8) - 10;
split_data_template::<Fr>(&bytes()[..nb_bytes], 1, Some(11));
split_data_template::<Fr>(&bytes()[..nb_bytes], 8, Some(16));
}
fn split_and_merge_template<F: PrimeField>(bytes: &[u8], modulus: usize) {
let elements: Vec<F> = super::split_data_into_field_elements(bytes, modulus);
let mut actual = super::merge_elements_into_bytes(&elements);
actual.resize(bytes.len(), 0);
assert_eq!(bytes, actual, "TEST | modulus: {modulus}");
}
#[test]
fn split_and_merge() {
split_and_merge_template::<Fr>(&bytes(), 1);
split_and_merge_template::<Fr>(&bytes(), 8);
split_and_merge_template::<Fr>(&bytes(), 64);
split_and_merge_template::<Fr>(&bytes(), 4096);
}
#[cfg(any(feature = "kzg", feature = "aplonk"))]
fn powers_of_template<E: Pairing>() {
let rng = &mut test_rng();
const POWER: usize = 10;
let r = E::ScalarField::rand(rng);
assert_eq!(
super::powers_of::<E>(r, POWER + 1).last().unwrap(),
&r.pow([POWER as u64])
);
}
#[cfg(any(feature = "kzg", feature = "aplonk"))]
#[test]
fn powers_of() {
powers_of_template::<Bls12_381>();
}
#[cfg(any(feature = "kzg", feature = "aplonk"))]
mod scalar_product {
use ark_bls12_381::Bls12_381;
use ark_ec::pairing::Pairing;
use ark_ff::PrimeField;
use ark_poly::univariate::DensePolynomial;
use ark_poly::DenseUVPolynomial;
#[cfg(feature = "aplonk")]
use ark_std::test_rng;
#[cfg(feature = "aplonk")]
use ark_std::UniformRand;
#[cfg(feature = "aplonk")]
use std::ops::Add;
use std::ops::Div;
type UniPoly381 = DensePolynomial<<Bls12_381 as Pairing>::ScalarField>;
fn vec_to_elements<E: Pairing>(elements: Vec<u8>) -> Vec<E::ScalarField> {
elements
.iter()
.map(|&x| E::ScalarField::from_le_bytes_mod_order(&[x]))
.collect()
}
fn polynomial_template<E, P>()
where
E: Pairing,
P: DenseUVPolynomial<E::ScalarField, Point = E::ScalarField>,
for<'a, 'b> &'a P: Div<&'b P, Output = P>,
{
let polynomials = vec![
P::from_coefficients_vec(vec_to_elements::<E>(vec![1])),
P::from_coefficients_vec(vec_to_elements::<E>(vec![0, 1])),
P::from_coefficients_vec(vec_to_elements::<E>(vec![0, 0, 1])),
P::from_coefficients_vec(vec_to_elements::<E>(vec![0, 0, 0, 1])),
];
let coeffs = vec_to_elements::<E>(vec![2, 3, 4, 5]);
assert_eq!(
super::super::scalar_product_polynomial::<E, P>(&coeffs, &polynomials),
P::from_coefficients_vec(coeffs)
)
}
#[test]
fn polynomial() {
polynomial_template::<Bls12_381, UniPoly381>();
}
#[cfg(feature = "aplonk")]
fn scalar_template<E: Pairing>(lhs: Vec<u8>, rhs: Vec<u8>, result: u8) {
let lhs = lhs
.iter()
.map(|x| E::ScalarField::from_le_bytes_mod_order(&[*x]))
.collect::<Vec<_>>();
let rhs = rhs
.iter()
.map(|x| E::ScalarField::from_le_bytes_mod_order(&[*x]))
.collect::<Vec<_>>();
let result = E::ScalarField::from_le_bytes_mod_order(&[result]);
assert_eq!(super::super::scalar_product::<E>(&lhs, &rhs), result);
}
#[cfg(feature = "aplonk")]
#[test]
fn scalar() {
scalar_template::<Bls12_381>(vec![1, 2], vec![3, 4], 11);
scalar_template::<Bls12_381>(vec![5, 6], vec![7, 8], 83);
}
#[cfg(feature = "aplonk")]
#[ignore = "scalar_product_g1 is a clone of scalar_product"]
#[test]
fn g_1() {}
#[cfg(feature = "aplonk")]
#[ignore = "scalar_product_g2 is a clone of scalar_product"]
#[test]
fn g_2() {}
#[cfg(feature = "aplonk")]
fn pairing_template<E: Pairing>() {
let rng = &mut test_rng();
let g_1 = E::G1::rand(rng);
let g_2 = E::G2::rand(rng);
let pairing = E::pairing(g_1, g_2);
let two_pairings = pairing.add(pairing);
assert_eq!(
super::super::scalar_product_pairing::<E>(&[g_1, g_1], &[g_2, g_2]),
two_pairings
);
}
#[cfg(feature = "aplonk")]
#[test]
fn pairing() {
pairing_template::<Bls12_381>();
}
}
}