#[cfg(not(target_family = "wasm"))]
use crate::xdr::ScVal;
use crate::{
crypto::utils::BigInt,
env::internal::{self, BytesObject, U256Val},
impl_bytesn_repr_without_from_bytes,
unwrap::{UnwrapInfallible, UnwrapOptimized},
Bytes, BytesN, ConversionError, Env, IntoVal, TryFromVal, Val, Vec, U256,
};
use core::{
cmp::Ordering,
fmt::Debug,
ops::{Add, Mul, Neg},
};
pub const BN254_FP_SERIALIZED_SIZE: usize = 32; pub const BN254_G1_SERIALIZED_SIZE: usize = BN254_FP_SERIALIZED_SIZE * 2; pub const BN254_G2_SERIALIZED_SIZE: usize = BN254_G1_SERIALIZED_SIZE * 2;
pub struct Bn254 {
env: Env,
}
#[derive(Clone)]
#[repr(transparent)]
pub struct Bn254G1Affine(BytesN<BN254_G1_SERIALIZED_SIZE>);
#[derive(Clone)]
#[repr(transparent)]
pub struct Bn254G2Affine(BytesN<BN254_G2_SERIALIZED_SIZE>);
#[derive(Clone)]
#[repr(transparent)]
pub struct Fr(U256);
#[derive(Clone)]
#[repr(transparent)]
pub struct Bn254Fp(BytesN<BN254_FP_SERIALIZED_SIZE>);
impl_bytesn_repr_without_from_bytes!(Bn254G1Affine, BN254_G1_SERIALIZED_SIZE);
impl_bytesn_repr_without_from_bytes!(Bn254G2Affine, BN254_G2_SERIALIZED_SIZE);
impl_bytesn_repr_without_from_bytes!(Bn254Fp, BN254_FP_SERIALIZED_SIZE);
const BN254_FP_MODULUS_BE: [u8; BN254_FP_SERIALIZED_SIZE] = [
0x30, 0x64, 0x4e, 0x72, 0xe1, 0x31, 0xa0, 0x29, 0xb8, 0x50, 0x45, 0xb6, 0x81, 0x81, 0x58, 0x5d,
0x97, 0x81, 0x6a, 0x91, 0x68, 0x71, 0xca, 0x8d, 0x3c, 0x20, 0x8c, 0x16, 0xd8, 0x7c, 0xfd, 0x47,
];
fn validate_bn254_fp(bytes: &[u8; BN254_FP_SERIALIZED_SIZE]) {
if bytes >= &BN254_FP_MODULUS_BE {
sdk_panic!("Bn254: Invalid Fp");
}
}
impl Bn254G1Affine {
pub fn from_bytes(bytes: BytesN<BN254_G1_SERIALIZED_SIZE>) -> Self {
Self(bytes)
}
}
impl Bn254G2Affine {
pub fn from_bytes(bytes: BytesN<BN254_G2_SERIALIZED_SIZE>) -> Self {
Self(bytes)
}
}
impl Bn254Fp {
pub fn from_bytes(bytes: BytesN<BN254_FP_SERIALIZED_SIZE>) -> Self {
validate_bn254_fp(&bytes.to_array());
Self(bytes)
}
}
impl Bn254G1Affine {
pub fn env(&self) -> &Env {
self.0.env()
}
}
impl Bn254Fp {
pub fn env(&self) -> &Env {
self.0.env()
}
fn checked_neg(&self) -> Option<Bn254Fp> {
let fq_bigint: BigInt<4> = (&self.0).into();
if fq_bigint.is_zero() {
return Some(self.clone());
}
const BN254_MODULUS: [u64; 4] = [
4332616871279656263,
10917124144477883021,
13281191951274694749,
3486998266802970665,
];
let mut res = BigInt(BN254_MODULUS);
let borrow = res.sub_with_borrow(&fq_bigint);
if borrow {
return None;
}
let mut bytes = [0u8; BN254_FP_SERIALIZED_SIZE];
res.copy_into_array(&mut bytes);
Some(Bn254Fp::from_array(self.env(), &bytes))
}
}
impl Neg for &Bn254Fp {
type Output = Bn254Fp;
fn neg(self) -> Self::Output {
match self.checked_neg() {
Some(v) => v,
None => sdk_panic!("invalid input - Bn254Fp is larger than the field modulus"),
}
}
}
impl Neg for Bn254Fp {
type Output = Bn254Fp;
fn neg(self) -> Self::Output {
(&self).neg()
}
}
impl Add for Bn254G1Affine {
type Output = Bn254G1Affine;
fn add(self, rhs: Self) -> Self::Output {
self.env().crypto().bn254().g1_add(&self, &rhs)
}
}
impl Mul<Fr> for Bn254G1Affine {
type Output = Bn254G1Affine;
fn mul(self, rhs: Fr) -> Self::Output {
self.env().crypto().bn254().g1_mul(&self, &rhs)
}
}
impl Neg for &Bn254G1Affine {
type Output = Bn254G1Affine;
fn neg(self) -> Self::Output {
let mut inner: Bytes = (&self.0).into();
let y = Bn254Fp::try_from_val(
inner.env(),
inner.slice(BN254_FP_SERIALIZED_SIZE as u32..).as_val(),
)
.unwrap_optimized();
let neg_y = -y;
inner.copy_from_slice(BN254_FP_SERIALIZED_SIZE as u32, &neg_y.to_array());
Bn254G1Affine::from_bytes(
BytesN::try_from_val(inner.env(), inner.as_val()).unwrap_optimized(),
)
}
}
impl Neg for Bn254G1Affine {
type Output = Bn254G1Affine;
fn neg(self) -> Self::Output {
(&self).neg()
}
}
impl Bn254G2Affine {
pub fn env(&self) -> &Env {
self.0.env()
}
}
impl Fr {
pub fn env(&self) -> &Env {
self.0.env()
}
pub fn from_u256(value: U256) -> Self {
value.into()
}
pub fn to_u256(&self) -> U256 {
self.0.clone()
}
pub fn as_u256(&self) -> &U256 {
&self.0
}
pub fn from_bytes(bytes: BytesN<32>) -> Self {
U256::from_be_bytes(bytes.env(), bytes.as_ref()).into()
}
pub fn to_bytes(&self) -> BytesN<32> {
self.as_u256().to_be_bytes().try_into().unwrap_optimized()
}
pub fn as_val(&self) -> &Val {
self.0.as_val()
}
pub fn to_val(&self) -> Val {
self.0.to_val()
}
}
const BN254_FR_MODULUS_BE: [u8; 32] = [
0x30, 0x64, 0x4e, 0x72, 0xe1, 0x31, 0xa0, 0x29, 0xb8, 0x50, 0x45, 0xb6, 0x81, 0x81, 0x58, 0x5d,
0x28, 0x33, 0xe8, 0x48, 0x79, 0xb9, 0x70, 0x91, 0x43, 0xe1, 0xf5, 0x93, 0xf0, 0x00, 0x00, 0x01,
];
fn fr_modulus(env: &Env) -> U256 {
U256::from_be_bytes(env, &Bytes::from_array(env, &BN254_FR_MODULUS_BE))
}
impl From<U256> for Fr {
fn from(value: U256) -> Self {
let modulus = fr_modulus(value.env());
if value >= modulus {
Self(value.rem_euclid(&modulus))
} else {
Self(value)
}
}
}
impl From<&Fr> for U256Val {
fn from(value: &Fr) -> Self {
value.as_u256().into()
}
}
impl TryFromVal<Env, Val> for Fr {
type Error = ConversionError;
fn try_from_val(env: &Env, val: &Val) -> Result<Self, Self::Error> {
let u = U256::try_from_val(env, val)?;
Ok(u.into())
}
}
impl TryFromVal<Env, Fr> for Val {
type Error = ConversionError;
fn try_from_val(_env: &Env, fr: &Fr) -> Result<Self, Self::Error> {
Ok(fr.to_val())
}
}
impl TryFromVal<Env, &Fr> for Val {
type Error = ConversionError;
fn try_from_val(_env: &Env, fr: &&Fr) -> Result<Self, Self::Error> {
Ok(fr.to_val())
}
}
#[cfg(not(target_family = "wasm"))]
impl From<&Fr> for ScVal {
fn from(v: &Fr) -> Self {
Self::from(&v.0)
}
}
#[cfg(not(target_family = "wasm"))]
impl From<Fr> for ScVal {
fn from(v: Fr) -> Self {
(&v).into()
}
}
impl Eq for Fr {}
impl PartialEq for Fr {
fn eq(&self, other: &Self) -> bool {
self.as_u256().partial_cmp(other.as_u256()) == Some(core::cmp::Ordering::Equal)
}
}
impl Debug for Fr {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Fr({:?})", self.as_u256())
}
}
impl Bn254 {
pub(crate) fn new(env: &Env) -> Bn254 {
Bn254 { env: env.clone() }
}
pub fn env(&self) -> &Env {
&self.env
}
pub fn g1_add(&self, p0: &Bn254G1Affine, p1: &Bn254G1Affine) -> Bn254G1Affine {
let env = self.env();
let bin =
internal::Env::bn254_g1_add(env, p0.to_object(), p1.to_object()).unwrap_infallible();
unsafe { Bn254G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
}
pub fn g1_mul(&self, p0: &Bn254G1Affine, scalar: &Fr) -> Bn254G1Affine {
let env = self.env();
let bin =
internal::Env::bn254_g1_mul(env, p0.to_object(), scalar.into()).unwrap_infallible();
unsafe { Bn254G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
}
pub fn pairing_check(&self, vp1: Vec<Bn254G1Affine>, vp2: Vec<Bn254G2Affine>) -> bool {
let env = self.env();
internal::Env::bn254_multi_pairing_check(env, vp1.into(), vp2.into())
.unwrap_infallible()
.into()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::bytesn;
#[test]
fn test_g1affine_to_val() {
let env = Env::default();
let g1 = Bn254G1Affine::from_bytes(BytesN::from_array(&env, &[1; 64]));
let val: Val = g1.clone().into_val(&env);
let rt: Bn254G1Affine = val.into_val(&env);
assert_eq!(g1, rt);
}
#[test]
fn test_ref_g1affine_to_val() {
let env = Env::default();
let g1 = Bn254G1Affine::from_bytes(BytesN::from_array(&env, &[1; 64]));
let val: Val = (&g1).into_val(&env);
let rt: Bn254G1Affine = val.into_val(&env);
assert_eq!(g1, rt);
}
#[test]
fn test_double_ref_g1affine_to_val() {
let env = Env::default();
let g1 = Bn254G1Affine::from_bytes(BytesN::from_array(&env, &[1; 64]));
let val: Val = (&&g1).into_val(&env);
let rt: Bn254G1Affine = val.into_val(&env);
assert_eq!(g1, rt);
}
#[test]
fn test_fr_to_val() {
let env = Env::default();
let fr = Fr::from_bytes(BytesN::from_array(&env, &[1; 32]));
let val: Val = fr.clone().into_val(&env);
let rt: Fr = val.into_val(&env);
assert_eq!(fr, rt);
}
#[test]
fn test_ref_fr_to_val() {
let env = Env::default();
let fr = Fr::from_bytes(BytesN::from_array(&env, &[1; 32]));
let val: Val = (&fr).into_val(&env);
let rt: Fr = val.into_val(&env);
assert_eq!(fr, rt);
}
#[test]
fn test_double_ref_fr_to_val() {
let env = Env::default();
let fr = Fr::from_bytes(BytesN::from_array(&env, &[1; 32]));
let val: Val = (&&fr).into_val(&env);
let rt: Fr = val.into_val(&env);
assert_eq!(fr, rt);
}
#[test]
fn test_fr_eq_both_unreduced() {
let env = Env::default();
let r = fr_modulus(&env);
let one = U256::from_u32(&env, 1);
let a = Fr::from_u256(r.add(&one)); let b = Fr::from_u256(one.clone()); assert_eq!(a, b);
let two_r_plus_one = r.add(&r).add(&one);
let c = Fr::from_u256(two_r_plus_one); assert_eq!(a, c);
assert_eq!(b, c);
}
#[test]
fn test_fr_eq_unreduced_vs_zero() {
let env = Env::default();
let r = fr_modulus(&env);
let zero = U256::from_u32(&env, 0);
let a = Fr::from_u256(r);
let b = Fr::from_u256(zero);
assert_eq!(a, b);
}
#[test]
fn test_fr_reduced_value_unchanged() {
let env = Env::default();
let r = fr_modulus(&env);
let val = r.sub(&U256::from_u32(&env, 1));
let fr = Fr::from_u256(val.clone());
assert_eq!(fr.to_u256(), val);
let fr42 = Fr::from_u256(U256::from_u32(&env, 42));
assert_eq!(fr42.to_u256(), U256::from_u32(&env, 42));
}
#[test]
fn test_fr_from_bytes_reduces() {
let env = Env::default();
let one_fr = Fr::from_u256(U256::from_u32(&env, 1));
let fr_from_bytes = Fr::from_bytes(bytesn!(
&env,
0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000002
));
assert_eq!(fr_from_bytes, one_fr);
}
#[test]
fn test_fr_try_from_val_reduces() {
let env = Env::default();
let r = fr_modulus(&env);
let one = U256::from_u32(&env, 1);
let unreduced_u256 = r.add(&one);
let val: Val = unreduced_u256.into_val(&env);
let fr_from_val: Fr = val.into_val(&env);
let fr_one = Fr::from_u256(one);
assert_eq!(fr_from_val, fr_one);
}
#[test]
fn test_fr_u256_into_reduces() {
let env = Env::default();
let r = fr_modulus(&env);
let one = U256::from_u32(&env, 1);
let fr: Fr = r.add(&one).into(); let fr_one: Fr = one.into();
assert_eq!(fr, fr_one);
}
#[test]
fn test_bn254_fp_max_valid_accepted() {
let env = Env::default();
let mut p_minus_1 = BN254_FP_MODULUS_BE;
p_minus_1[BN254_FP_SERIALIZED_SIZE - 1] -= 1;
let _ = Bn254Fp::from_array(&env, &p_minus_1);
}
#[test]
#[should_panic(expected = "Bn254: Invalid Fp")]
fn test_bn254_fp_at_modulus_panics() {
let env = Env::default();
let _ = Bn254Fp::from_array(&env, &BN254_FP_MODULUS_BE);
}
#[test]
#[should_panic(expected = "Bn254: Invalid Fp")]
fn test_bn254_fp_above_modulus_panics() {
let env = Env::default();
let mut above = BN254_FP_MODULUS_BE;
above[BN254_FP_SERIALIZED_SIZE - 1] += 1; let _ = Bn254Fp::from_array(&env, &above);
}
#[test]
fn test_bn254_fp_from_bytes_validates() {
let env = Env::default();
let _ = Bn254Fp::from_bytes(BytesN::from_array(&env, &[0u8; BN254_FP_SERIALIZED_SIZE]));
}
#[test]
#[should_panic(expected = "Bn254: Invalid Fp")]
fn test_bn254_fp_from_bytes_rejects_modulus() {
let env = Env::default();
let _ = Bn254Fp::from_bytes(BytesN::from_array(&env, &BN254_FP_MODULUS_BE));
}
#[test]
#[should_panic(expected = "Bn254: Invalid Fp")]
fn test_bn254_fp_try_from_val_rejects_modulus() {
let env = Env::default();
let bytes = BytesN::from_array(&env, &BN254_FP_MODULUS_BE);
let val: Val = bytes.into_val(&env);
let _: Bn254Fp = val.into_val(&env);
}
#[test]
fn test_bn254_fp_modulus_matches_arkworks() {
use ark_bn254::Fq;
use ark_ff::{BigInteger, PrimeField};
let be_bytes = Fq::MODULUS.to_bytes_be();
assert_eq!(
be_bytes.as_slice(),
&BN254_FP_MODULUS_BE,
"BN254 Fp modulus does not match arkworks"
);
}
#[test]
fn test_bn254_fr_modulus_matches_arkworks() {
use ark_bn254::Fr as ArkFr;
use ark_ff::{BigInteger, PrimeField};
let be_bytes = ArkFr::MODULUS.to_bytes_be();
assert_eq!(
be_bytes.as_slice(),
&BN254_FR_MODULUS_BE,
"BN254 Fr modulus does not match arkworks"
);
}
}