use halo2curves::bn256::{Bn256, Fq as Bn256Fq, Fr as Bn256Fr, G1Affine, G2Affine, Gt, G1, G2};
use halo2curves::ff::{Field, FromUniformBytes, PrimeField};
use halo2curves::group::{prime::PrimeCurveAffine, Curve, Group};
use halo2curves::grumpkin::G1 as GrumpkinG1;
use halo2curves::msm::msm_best;
use halo2curves::pairing::Engine;
use halo2curves::secp256k1::Fq as Secp256k1Fq;
use num_bigint::BigUint;
use rand_chacha::ChaCha20Rng;
use rand_core::{RngCore, SeedableRng};
const SEED: u64 = 0x_5EED_C0DE_1234_5678;
const FIELD_ITERS: usize = 64;
const GROUP_ITERS: usize = 50;
const PAIRING_ITERS: usize = 16;
fn rng() -> ChaCha20Rng {
ChaCha20Rng::seed_from_u64(SEED)
}
fn field_properties<F: PrimeField + FromUniformBytes<64>>() {
let mut rng = rng();
assert!(bool::from(F::ZERO.invert().is_none()));
let p = BigUint::from_bytes_le((F::ZERO - F::ONE).to_repr().as_ref()) + BigUint::from(1u8);
for _ in 0..FIELD_ITERS {
let a = F::random(&mut rng);
let b = F::random(&mut rng);
if bool::from(!a.is_zero()) {
assert_eq!(a * a.invert().unwrap(), F::ONE);
}
assert_eq!(a * b, b * a);
assert_eq!((a + b) * (a - b), a.square() - b.square());
let mut x = [0u8; 64];
rng.fill_bytes(&mut x);
let fe = F::from_uniform_bytes(&x);
let expected = BigUint::from_bytes_le(&x) % &p;
let got = BigUint::from_bytes_le(fe.to_repr().as_ref());
assert_eq!(got, expected);
}
}
#[test]
fn field_bn256_fr() {
field_properties::<Bn256Fr>();
}
#[test]
fn field_bn256_fq() {
field_properties::<Bn256Fq>();
}
#[test]
fn field_secp256k1_fq() {
field_properties::<Secp256k1Fq>();
}
fn group_law<G: Group>() {
let mut rng = rng();
for _ in 0..GROUP_ITERS {
let p = G::random(&mut rng);
let q = G::random(&mut rng);
let r = G::random(&mut rng);
assert_eq!(p + (-p), G::identity());
assert_eq!(p + G::identity(), p);
assert_eq!(p.double(), p + p);
assert_eq!((p + q) + r, p + (q + r));
let k = G::Scalar::random(&mut rng);
assert_eq!((p + q) * k, p * k + q * k);
}
}
#[test]
fn group_law_bn256_g1() {
group_law::<G1>();
}
#[test]
fn group_law_grumpkin_g1() {
group_law::<GrumpkinG1>();
}
#[test]
fn pairing_properties() {
let mut rng = rng();
let p_aff = G1Affine::generator();
let q_aff = G2Affine::generator();
let g1 = G1::generator();
let g2 = G2::generator();
let base = Bn256::pairing(&p_aff, &q_aff);
assert_ne!(base, Gt::identity());
for _ in 0..PAIRING_ITERS {
let a = Bn256Fr::random(&mut rng);
let b = Bn256Fr::random(&mut rng);
let ap = (g1 * a).to_affine();
let bq = (g2 * b).to_affine();
assert_eq!(Bn256::pairing(&ap, &bq), base * (a * b));
let abp = (g1 * (a + b)).to_affine();
let lhs =
Bn256::pairing(&(g1 * a).to_affine(), &q_aff) + Bn256::pairing(&(g1 * b).to_affine(), &q_aff);
assert_eq!(lhs, Bn256::pairing(&abp, &q_aff));
}
}
fn naive_msm(scalars: &[Bn256Fr], bases: &[G1Affine]) -> G1 {
scalars
.iter()
.zip(bases.iter())
.fold(G1::identity(), |acc, (s, b)| acc + *b * s)
}
#[test]
fn msm_best_matches_naive() {
let mut rng = rng();
for &n in &[16usize, 100, 8104, 8200] {
let g = G1::generator();
let mut acc = g * Bn256Fr::random(&mut rng);
let mut proj = Vec::with_capacity(n);
for _ in 0..n {
proj.push(acc);
acc += g;
}
let mut bases = vec![G1Affine::identity(); n];
G1::batch_normalize(&proj, &mut bases);
let random_scalars: Vec<Bn256Fr> = (0..n).map(|_| Bn256Fr::random(&mut rng)).collect();
let equal_scalars = vec![Bn256Fr::random(&mut rng); n];
let r_minus_one = -Bn256Fr::ONE;
let mix_scalars: Vec<Bn256Fr> = (0..n)
.map(|i| {
if i % 2 == 0 {
Bn256Fr::ZERO
} else {
r_minus_one
}
})
.collect();
for scalars in [&random_scalars, &equal_scalars, &mix_scalars] {
assert_eq!(msm_best(scalars, &bases), naive_msm(scalars, &bases));
}
}
}
#[test]
fn constant_sanity() {
let x = Bn256Fr::ZERO - Bn256Fr::ONE;
let repr = x.to_repr();
let y = Bn256Fr::from_repr(repr).unwrap();
assert_eq!(x, y);
assert_eq!((-Bn256Fr::ONE) + Bn256Fr::ONE, Bn256Fr::ZERO);
}