Skip to content
Closed
301 changes: 251 additions & 50 deletions Cargo.lock

Large diffs are not rendered by default.

24 changes: 12 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,29 @@ soroban-token-spec = { version = "23.1.0", path = "soroban-token-spec" }
stellar-asset-spec = { version = "23.1.0", path = "stellar-asset-spec" }

[workspace.dependencies.soroban-env-common]
version = "=23.0.1"
#git = "https://github.com/stellar/rs-soroban-env"
#rev = "bd0c80a1fe171e75f8d745f17975a73927d44ecd"
version = "=25.0.0"
git = "https://github.com/stellar/rs-soroban-env"
rev = "0a0c2df704edeb3cdafabfcc759eddccd6775337"

[workspace.dependencies.soroban-env-guest]
version = "=23.0.1"
#git = "https://github.com/stellar/rs-soroban-env"
#rev = "bd0c80a1fe171e75f8d745f17975a73927d44ecd"
version = "=25.0.0"
git = "https://github.com/stellar/rs-soroban-env"
rev = "0a0c2df704edeb3cdafabfcc759eddccd6775337"

[workspace.dependencies.soroban-env-host]
version = "=23.0.1"
#git = "https://github.com/stellar/rs-soroban-env"
#rev = "bd0c80a1fe171e75f8d745f17975a73927d44ecd"
version = "=25.0.0"
git = "https://github.com/stellar/rs-soroban-env"
rev = "0a0c2df704edeb3cdafabfcc759eddccd6775337"

[workspace.dependencies.stellar-strkey]
version = "=0.0.13"

[workspace.dependencies.stellar-xdr]
version = "=23.0.0"
version = "=24.0.1"
default-features = false
features = ["curr"]
#git = "https://github.com/stellar/rs-stellar-xdr"
#rev = "67be5955a15f1d3a4df83fe86e6ae107f687141b"
git = "https://github.com/stellar/rs-stellar-xdr"
rev = "89cc1cbadf1b9a16843826954dede7fec514d8e7"

#[patch.crates-io]
#soroban-env-common = { path = "../rs-soroban-env/soroban-env-common" }
Expand Down
2 changes: 1 addition & 1 deletion soroban-ledger-snapshot/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ impl LedgerSnapshot {
impl Default for LedgerSnapshot {
fn default() -> Self {
Self {
protocol_version: 23,
protocol_version: 25,
sequence_number: Default::default(),
timestamp: Default::default(),
network_id: Default::default(),
Expand Down
7 changes: 7 additions & 0 deletions soroban-sdk/src/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{
};

pub mod bls12_381;
pub mod bn254;
/// A `BytesN<N>` generated by a cryptographic hash function.
///
/// The `Hash<N>` type contains a `BytesN<N>` and can only be constructed in
Expand Down Expand Up @@ -182,6 +183,12 @@ impl Crypto {
pub fn bls12_381(&self) -> bls12_381::Bls12_381 {
bls12_381::Bls12_381::new(self.env())
}

/// Get a [Bn254][bn254::Bn254] for accessing the bn254
/// functions.
pub fn bn254(&self) -> bn254::Bn254 {
bn254::Bn254::new(self.env())
}
}

/// # ⚠️ Hazardous Materials
Expand Down
307 changes: 307 additions & 0 deletions soroban-sdk/src/crypto/bn254.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
#[cfg(not(target_family = "wasm"))]
use crate::xdr::ScVal;
use crate::{
env::internal::{self, BytesObject, U256Val},
impl_bytesn_repr,
unwrap::{UnwrapInfallible, UnwrapOptimized},
Bytes, BytesN, ConversionError, Env, IntoVal, TryFromVal, Val, Vec, U256,
};
use core::{
cmp::Ordering,
fmt::Debug,
ops::{Add, Mul},
};

const FP_SERIALIZED_SIZE: usize = 32; // Size in bytes of a serialized Fp element in BN254. The field modulus is 254 bits, requiring 32 bytes (256 bits).
pub const G1_SERIALIZED_SIZE: usize = FP_SERIALIZED_SIZE * 2; // Size in bytes of a serialized G1 element in BN254. Each coordinate (X, Y) is 32 bytes.
pub const G2_SERIALIZED_SIZE: usize = G1_SERIALIZED_SIZE * 2; // Size in bytes of a serialized G2 element in BN254. Each coordinate (X, Y) is 64 bytes (2 Fp elements per coordinate).

/// Bn254 provides access to curve and pairing operations on the BN254
/// (also known as alt_bn128) curve.
pub struct Bn254 {
env: Env,
}

/// `G1Affine` is a point in the G1 group (subgroup defined over the base field
/// `Fq` with prime order `q =
/// 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47`) of the
/// BN254 elliptic curve
///
/// # Serialization:
/// - The 64 bytes represent the **uncompressed encoding** of a point in G1. The
/// bytes consist of `be_bytes(X) || be_bytes(Y)` (`||` is concatenation),
/// where 'X' and 'Y' are the two coordinates, each being a base field element
/// `Fp` (32 bytes each).
#[derive(Clone)]
#[repr(transparent)]
pub struct G1Affine(BytesN<G1_SERIALIZED_SIZE>);

/// `G2Affine` is a point in the G2 group (subgroup defined over the quadratic
/// extension field `Fq2`) of the BN254 elliptic curve
///
/// # Serialization:
/// - The 128 bytes represent the **uncompressed encoding** of a point in G2.
/// The bytes consist of `be_bytes(X_im) || be_bytes(X_re) || be_bytes(Y_im)
/// || be_bytes(Y_re)` (`||` is concatenation), where 'X' and 'Y' are the two
/// coordinates, each being an extension field element `Fp2`. Each component
/// (real and imaginary parts) is an `Fp` element (32 bytes each).
#[derive(Clone)]
#[repr(transparent)]
pub struct G2Affine(BytesN<G2_SERIALIZED_SIZE>);

/// `Fr` represents an element in the BN254 scalar field, which is a prime field
/// of order `r =
/// 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001`. The
/// struct is internally represented with a `U256`, all arithmetic operations
/// follow modulo `r`.
#[derive(Clone)]
#[repr(transparent)]
pub struct Fr(U256);

impl_bytesn_repr!(G1Affine, G1_SERIALIZED_SIZE);
impl_bytesn_repr!(G2Affine, G2_SERIALIZED_SIZE);

impl G1Affine {
pub fn env(&self) -> &Env {
self.0.env()
}
}

impl Add for G1Affine {
type Output = G1Affine;

fn add(self, rhs: Self) -> Self::Output {
self.env().crypto().bn254().g1_add(&self, &rhs)
}
}

impl Mul<Fr> for G1Affine {
type Output = G1Affine;

fn mul(self, rhs: Fr) -> Self::Output {
self.env().crypto().bn254().g1_mul(&self, &rhs)
}
}

impl G2Affine {
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()
}
}

impl From<U256> for Fr {
fn from(value: U256) -> Self {
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(Fr(u))
}
}

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
}

/// Adds two points `p0` and `p1` in G1.
pub fn g1_add(&self, p0: &G1Affine, p1: &G1Affine) -> G1Affine {
let env = self.env();
let bin =
internal::Env::bn254_g1_add(env, p0.to_object(), p1.to_object()).unwrap_infallible();
unsafe { G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
}

/// Multiplies a point `p0` in G1 by a scalar.
pub fn g1_mul(&self, p0: &G1Affine, scalar: &Fr) -> G1Affine {
let env = self.env();
let bin =
internal::Env::bn254_g1_mul(env, p0.to_object(), scalar.into()).unwrap_infallible();
unsafe { G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
}

// pairing

/// Performs a multi-pairing check between vectors of points in G1 and G2.
///
/// This function computes the pairing for each pair of points in the
/// provided vectors `vp1` (G1 points) and `vp2` (G2 points) and verifies if
/// the product of all pairings is equal to 1 in the target group Fq12.
///
/// # Returns:
/// - `true` if the pairing check holds (i.e., the product of pairings equals 1),
/// otherwise `false`.
///
/// # Panics:
/// - If the lengths of `vp1` and `vp2` are not equal or if they are empty.
pub fn pairing_check(&self, vp1: Vec<G1Affine>, vp2: Vec<G2Affine>) -> 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::*;

#[test]
fn test_g1affine_to_val() {
let env = Env::default();

let g1 = G1Affine::from_bytes(BytesN::from_array(&env, &[1; 64]));
let val: Val = g1.clone().into_val(&env);
let rt: G1Affine = val.into_val(&env);

assert_eq!(g1, rt);
}

#[test]
fn test_ref_g1affine_to_val() {
let env = Env::default();

let g1 = G1Affine::from_bytes(BytesN::from_array(&env, &[1; 64]));
let val: Val = (&g1).into_val(&env);
let rt: G1Affine = val.into_val(&env);

assert_eq!(g1, rt);
}

#[test]
fn test_double_ref_g1affine_to_val() {
let env = Env::default();

let g1 = G1Affine::from_bytes(BytesN::from_array(&env, &[1; 64]));
let val: Val = (&&g1).into_val(&env);
let rt: G1Affine = 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);
}
}
2 changes: 1 addition & 1 deletion soroban-sdk/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ impl Env {

let rf = Rc::new(EmptySnapshotSource());
let info = internal::LedgerInfo {
protocol_version: 23,
protocol_version: 25,
sequence_number: 0,
timestamp: 0,
network_id: [0; 32],
Expand Down
Loading