use super::{G1Point, G2Point, PairingPair};
use crate::{
bls12_381::{G1PointScalar, G2PointScalar},
bls12_381_const::{FP_LENGTH, G1_LENGTH, G2_LENGTH, SCALAR_LENGTH, SCALAR_LENGTH_BITS},
PrecompileHalt,
};
use blst::{
blst_bendian_from_fp, blst_final_exp, blst_fp, blst_fp12, blst_fp12_is_one, blst_fp12_mul,
blst_fp2, blst_fp_from_bendian, blst_map_to_g1, blst_map_to_g2, blst_miller_loop, blst_p1,
blst_p1_add_or_double_affine, blst_p1_affine, blst_p1_affine_in_g1, blst_p1_affine_on_curve,
blst_p1_from_affine, blst_p1_mult, blst_p1_to_affine, blst_p2, blst_p2_add_or_double_affine,
blst_p2_affine, blst_p2_affine_in_g2, blst_p2_affine_on_curve, blst_p2_from_affine,
blst_p2_mult, blst_p2_to_affine, blst_scalar, blst_scalar_from_bendian, MultiPoint,
};
use std::vec::Vec;
const MODULUS_REPR: [u8; 48] = [
0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, 0xac, 0xd7,
0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, 0x67, 0x30, 0xd2, 0xa0, 0xf6, 0xb0, 0xf6, 0x24,
0x1e, 0xab, 0xff, 0xfe, 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xab,
];
#[inline]
pub(crate) fn p1_to_affine(p: &blst_p1) -> blst_p1_affine {
let mut p_affine = blst_p1_affine::default();
unsafe { blst_p1_to_affine(&mut p_affine, p) };
p_affine
}
#[inline]
pub(crate) fn p1_from_affine(p_affine: &blst_p1_affine) -> blst_p1 {
let mut p = blst_p1::default();
unsafe { blst_p1_from_affine(&mut p, p_affine) };
p
}
#[inline]
pub(crate) fn p1_add_or_double(p: &blst_p1, p_affine: &blst_p1_affine) -> blst_p1 {
let mut result = blst_p1::default();
unsafe { blst_p1_add_or_double_affine(&mut result, p, p_affine) };
result
}
#[inline]
pub(crate) fn p2_to_affine(p: &blst_p2) -> blst_p2_affine {
let mut p_affine = blst_p2_affine::default();
unsafe { blst_p2_to_affine(&mut p_affine, p) };
p_affine
}
#[inline]
pub(crate) fn p2_from_affine(p_affine: &blst_p2_affine) -> blst_p2 {
let mut p = blst_p2::default();
unsafe { blst_p2_from_affine(&mut p, p_affine) };
p
}
#[inline]
pub(crate) fn p2_add_or_double(p: &blst_p2, p_affine: &blst_p2_affine) -> blst_p2 {
let mut result = blst_p2::default();
unsafe { blst_p2_add_or_double_affine(&mut result, p, p_affine) };
result
}
#[inline]
fn p1_add_affine(a: &blst_p1_affine, b: &blst_p1_affine) -> blst_p1_affine {
let a_jacobian = p1_from_affine(a);
let sum_jacobian = p1_add_or_double(&a_jacobian, b);
p1_to_affine(&sum_jacobian)
}
#[inline]
fn p2_add_affine(a: &blst_p2_affine, b: &blst_p2_affine) -> blst_p2_affine {
let a_jacobian = p2_from_affine(a);
let sum_jacobian = p2_add_or_double(&a_jacobian, b);
p2_to_affine(&sum_jacobian)
}
#[inline]
pub(crate) fn p1_scalar_mul(p: &blst_p1_affine, scalar: &blst_scalar) -> blst_p1_affine {
let p_jacobian = p1_from_affine(p);
let mut result = blst_p1::default();
unsafe {
blst_p1_mult(
&mut result,
&p_jacobian,
scalar.b.as_ptr(),
scalar.b.len() * 8,
)
};
p1_to_affine(&result)
}
#[inline]
pub(crate) fn p2_scalar_mul(p: &blst_p2_affine, scalar: &blst_scalar) -> blst_p2_affine {
let p_jacobian = p2_from_affine(p);
let mut result = blst_p2::default();
unsafe {
blst_p2_mult(
&mut result,
&p_jacobian,
scalar.b.as_ptr(),
scalar.b.len() * 8,
)
};
p2_to_affine(&result)
}
#[inline]
fn p1_msm(g1_points: Vec<blst_p1_affine>, scalars: Vec<blst_scalar>) -> blst_p1_affine {
assert_eq!(
g1_points.len(),
scalars.len(),
"number of scalars should equal the number of g1 points"
);
if g1_points.is_empty() {
return blst_p1_affine::default();
}
if g1_points.len() == 1 {
return p1_scalar_mul(&g1_points[0], &scalars[0]);
}
let scalars_bytes =
unsafe { core::slice::from_raw_parts(scalars.as_ptr() as *const u8, scalars.len() * 32) };
let multiexp = g1_points.mult(scalars_bytes, SCALAR_LENGTH_BITS);
p1_to_affine(&multiexp)
}
#[inline]
fn p2_msm(g2_points: Vec<blst_p2_affine>, scalars: Vec<blst_scalar>) -> blst_p2_affine {
assert_eq!(
g2_points.len(),
scalars.len(),
"number of scalars should equal the number of g2 points"
);
if g2_points.is_empty() {
return blst_p2_affine::default();
}
if g2_points.len() == 1 {
return p2_scalar_mul(&g2_points[0], &scalars[0]);
}
let scalars_bytes =
unsafe { core::slice::from_raw_parts(scalars.as_ptr() as *const u8, scalars.len() * 32) };
let multiexp = g2_points.mult(scalars_bytes, SCALAR_LENGTH_BITS);
p2_to_affine(&multiexp)
}
#[inline]
fn map_fp_to_g1(fp: &blst_fp) -> blst_p1_affine {
let mut p = blst_p1::default();
unsafe { blst_map_to_g1(&mut p, fp, core::ptr::null()) };
p1_to_affine(&p)
}
#[inline]
fn map_fp2_to_g2(fp2: &blst_fp2) -> blst_p2_affine {
let mut p = blst_p2::default();
unsafe { blst_map_to_g2(&mut p, fp2, core::ptr::null()) };
p2_to_affine(&p)
}
#[inline]
fn compute_miller_loop(g1: &blst_p1_affine, g2: &blst_p2_affine) -> blst_fp12 {
let mut result = blst_fp12::default();
unsafe { blst_miller_loop(&mut result, g2, g1) }
result
}
#[inline]
fn multiply_fp12(a: &blst_fp12, b: &blst_fp12) -> blst_fp12 {
let mut result = blst_fp12::default();
unsafe { blst_fp12_mul(&mut result, a, b) }
result
}
#[inline]
fn final_exp(f: &blst_fp12) -> blst_fp12 {
let mut result = blst_fp12::default();
unsafe { blst_final_exp(&mut result, f) }
result
}
#[inline]
fn is_fp12_one(f: &blst_fp12) -> bool {
unsafe { blst_fp12_is_one(f) }
}
#[inline]
pub(crate) fn pairing_check(pairs: &[(blst_p1_affine, blst_p2_affine)]) -> bool {
if pairs.is_empty() {
return true;
}
let (first_g1, first_g2) = &pairs[0];
let mut acc = compute_miller_loop(first_g1, first_g2);
for (g1, g2) in pairs.iter().skip(1) {
let ml = compute_miller_loop(g1, g2);
acc = multiply_fp12(&acc, &ml);
}
let final_result = final_exp(&acc);
is_fp12_one(&final_result)
}
fn encode_g1_point(input: &blst_p1_affine) -> [u8; G1_LENGTH] {
let mut out = [0u8; G1_LENGTH];
fp_to_bytes(&mut out[..FP_LENGTH], &input.x);
fp_to_bytes(&mut out[FP_LENGTH..], &input.y);
out
}
fn fp_to_bytes(out: &mut [u8], input: &blst_fp) {
if out.len() != FP_LENGTH {
return;
}
unsafe { blst_bendian_from_fp(out.as_mut_ptr(), input) };
}
fn decode_g1_on_curve(
p0_x: &[u8; FP_LENGTH],
p0_y: &[u8; FP_LENGTH],
) -> Result<blst_p1_affine, PrecompileHalt> {
let out = blst_p1_affine {
x: read_fp(p0_x)?,
y: read_fp(p0_y)?,
};
if unsafe { !blst_p1_affine_on_curve(&out) } {
return Err(PrecompileHalt::Bls12381G1NotOnCurve);
}
Ok(out)
}
fn read_g1(x: &[u8; FP_LENGTH], y: &[u8; FP_LENGTH]) -> Result<blst_p1_affine, PrecompileHalt> {
_extract_g1_input(x, y, true)
}
fn read_g1_no_subgroup_check(
x: &[u8; FP_LENGTH],
y: &[u8; FP_LENGTH],
) -> Result<blst_p1_affine, PrecompileHalt> {
_extract_g1_input(x, y, false)
}
fn _extract_g1_input(
x: &[u8; FP_LENGTH],
y: &[u8; FP_LENGTH],
subgroup_check: bool,
) -> Result<blst_p1_affine, PrecompileHalt> {
let out = decode_g1_on_curve(x, y)?;
if subgroup_check {
if unsafe { !blst_p1_affine_in_g1(&out) } {
return Err(PrecompileHalt::Bls12381G1NotInSubgroup);
}
}
Ok(out)
}
fn encode_g2_point(input: &blst_p2_affine) -> [u8; G2_LENGTH] {
let mut out = [0u8; G2_LENGTH];
fp_to_bytes(&mut out[..FP_LENGTH], &input.x.fp[0]);
fp_to_bytes(&mut out[FP_LENGTH..2 * FP_LENGTH], &input.x.fp[1]);
fp_to_bytes(&mut out[2 * FP_LENGTH..3 * FP_LENGTH], &input.y.fp[0]);
fp_to_bytes(&mut out[3 * FP_LENGTH..4 * FP_LENGTH], &input.y.fp[1]);
out
}
fn decode_g2_on_curve(
x1: &[u8; FP_LENGTH],
x2: &[u8; FP_LENGTH],
y1: &[u8; FP_LENGTH],
y2: &[u8; FP_LENGTH],
) -> Result<blst_p2_affine, PrecompileHalt> {
let out = blst_p2_affine {
x: read_fp2(x1, x2)?,
y: read_fp2(y1, y2)?,
};
if unsafe { !blst_p2_affine_on_curve(&out) } {
return Err(PrecompileHalt::Bls12381G2NotOnCurve);
}
Ok(out)
}
fn read_fp2(
input_1: &[u8; FP_LENGTH],
input_2: &[u8; FP_LENGTH],
) -> Result<blst_fp2, PrecompileHalt> {
let fp_1 = read_fp(input_1)?;
let fp_2 = read_fp(input_2)?;
let fp2 = blst_fp2 { fp: [fp_1, fp_2] };
Ok(fp2)
}
fn read_g2(
a_x_0: &[u8; FP_LENGTH],
a_x_1: &[u8; FP_LENGTH],
a_y_0: &[u8; FP_LENGTH],
a_y_1: &[u8; FP_LENGTH],
) -> Result<blst_p2_affine, PrecompileHalt> {
_extract_g2_input(a_x_0, a_x_1, a_y_0, a_y_1, true)
}
fn read_g2_no_subgroup_check(
a_x_0: &[u8; FP_LENGTH],
a_x_1: &[u8; FP_LENGTH],
a_y_0: &[u8; FP_LENGTH],
a_y_1: &[u8; FP_LENGTH],
) -> Result<blst_p2_affine, PrecompileHalt> {
_extract_g2_input(a_x_0, a_x_1, a_y_0, a_y_1, false)
}
fn _extract_g2_input(
a_x_0: &[u8; FP_LENGTH],
a_x_1: &[u8; FP_LENGTH],
a_y_0: &[u8; FP_LENGTH],
a_y_1: &[u8; FP_LENGTH],
subgroup_check: bool,
) -> Result<blst_p2_affine, PrecompileHalt> {
let out = decode_g2_on_curve(a_x_0, a_x_1, a_y_0, a_y_1)?;
if subgroup_check {
if unsafe { !blst_p2_affine_in_g2(&out) } {
return Err(PrecompileHalt::Bls12381G2NotInSubgroup);
}
}
Ok(out)
}
fn read_fp(input: &[u8; FP_LENGTH]) -> Result<blst_fp, PrecompileHalt> {
if !is_valid_be(input) {
return Err(PrecompileHalt::NonCanonicalFp);
}
let mut fp = blst_fp::default();
unsafe {
blst_fp_from_bendian(&mut fp, input.as_ptr());
}
Ok(fp)
}
fn read_scalar(input: &[u8]) -> Result<blst_scalar, PrecompileHalt> {
if input.len() != SCALAR_LENGTH {
return Err(PrecompileHalt::Bls12381ScalarInputLength);
}
let mut out = blst_scalar::default();
unsafe {
blst_scalar_from_bendian(&mut out, input.as_ptr())
};
Ok(out)
}
fn is_valid_be(input: &[u8; 48]) -> bool {
*input < MODULUS_REPR
}
#[inline]
pub(crate) fn p1_add_affine_bytes(
a: G1Point,
b: G1Point,
) -> Result<[u8; G1_LENGTH], crate::PrecompileHalt> {
let (a_x, a_y) = a;
let (b_x, b_y) = b;
let p1 = read_g1_no_subgroup_check(&a_x, &a_y)?;
let p2 = read_g1_no_subgroup_check(&b_x, &b_y)?;
let result = p1_add_affine(&p1, &p2);
Ok(encode_g1_point(&result))
}
#[inline]
pub(crate) fn p2_add_affine_bytes(
a: G2Point,
b: G2Point,
) -> Result<[u8; G2_LENGTH], crate::PrecompileHalt> {
let (a_x_0, a_x_1, a_y_0, a_y_1) = a;
let (b_x_0, b_x_1, b_y_0, b_y_1) = b;
let p1 = read_g2_no_subgroup_check(&a_x_0, &a_x_1, &a_y_0, &a_y_1)?;
let p2 = read_g2_no_subgroup_check(&b_x_0, &b_x_1, &b_y_0, &b_y_1)?;
let result = p2_add_affine(&p1, &p2);
Ok(encode_g2_point(&result))
}
#[inline]
pub(crate) fn map_fp_to_g1_bytes(
fp_bytes: &[u8; FP_LENGTH],
) -> Result<[u8; G1_LENGTH], crate::PrecompileHalt> {
let fp = read_fp(fp_bytes)?;
let result = map_fp_to_g1(&fp);
Ok(encode_g1_point(&result))
}
#[inline]
pub(crate) fn map_fp2_to_g2_bytes(
fp2_x: &[u8; FP_LENGTH],
fp2_y: &[u8; FP_LENGTH],
) -> Result<[u8; G2_LENGTH], crate::PrecompileHalt> {
let fp2 = read_fp2(fp2_x, fp2_y)?;
let result = map_fp2_to_g2(&fp2);
Ok(encode_g2_point(&result))
}
#[inline]
pub(crate) fn p1_msm_bytes(
point_scalar_pairs: impl Iterator<Item = Result<G1PointScalar, crate::PrecompileHalt>>,
) -> Result<[u8; G1_LENGTH], crate::PrecompileHalt> {
let (lower, _) = point_scalar_pairs.size_hint();
let mut g1_points = Vec::with_capacity(lower);
let mut scalars = Vec::with_capacity(lower);
for pair_result in point_scalar_pairs {
let ((x, y), scalar_bytes) = pair_result?;
let point = read_g1(&x, &y)?;
if scalar_bytes.iter().all(|&b| b == 0) {
continue;
}
let scalar = read_scalar(&scalar_bytes)?;
g1_points.push(point);
scalars.push(scalar);
}
if g1_points.is_empty() {
return Ok([0u8; G1_LENGTH]);
}
let result = p1_msm(g1_points, scalars);
Ok(encode_g1_point(&result))
}
#[inline]
pub(crate) fn p2_msm_bytes(
point_scalar_pairs: impl Iterator<Item = Result<G2PointScalar, crate::PrecompileHalt>>,
) -> Result<[u8; G2_LENGTH], crate::PrecompileHalt> {
let (lower, _) = point_scalar_pairs.size_hint();
let mut g2_points = Vec::with_capacity(lower);
let mut scalars = Vec::with_capacity(lower);
for pair_result in point_scalar_pairs {
let ((x_0, x_1, y_0, y_1), scalar_bytes) = pair_result?;
let point = read_g2(&x_0, &x_1, &y_0, &y_1)?;
if scalar_bytes.iter().all(|&b| b == 0) {
continue;
}
let scalar = read_scalar(&scalar_bytes)?;
g2_points.push(point);
scalars.push(scalar);
}
if g2_points.is_empty() {
return Ok([0u8; G2_LENGTH]);
}
let result = p2_msm(g2_points, scalars);
Ok(encode_g2_point(&result))
}
#[inline]
pub(crate) fn pairing_check_bytes(pairs: &[PairingPair]) -> Result<bool, crate::PrecompileHalt> {
super::pairing_common::pairing_check_bytes_generic(pairs, read_g1, read_g2, pairing_check)
}