Skip to content
Closed
299 changes: 250 additions & 49 deletions Cargo.lock

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,19 @@ 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 = "2774c09f8ab57199b03d277f8d74e4c9f5442bd9"

[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 = "2774c09f8ab57199b03d277f8d74e4c9f5442bd9"

[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 = "2774c09f8ab57199b03d277f8d74e4c9f5442bd9"

[workspace.dependencies.stellar-strkey]
version = "=0.0.13"
Expand All @@ -51,8 +51,8 @@ version = "=0.0.13"
version = "=23.0.0"
default-features = false
features = ["curr"]
#git = "https://github.com/stellar/rs-stellar-xdr"
#rev = "67be5955a15f1d3a4df83fe86e6ae107f687141b"
git = "https://github.com/stellar/rs-stellar-xdr"
rev = "8505f22833f4054227689c35f9cf253a6836cbac"

#[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
267 changes: 267 additions & 0 deletions soroban-sdk/src/crypto/bn254.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
#[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};

const FP_SERIALIZED_SIZE: usize = 32; // Size in bytes of a serialized Fp element in BN254.
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 G1 element in BN254. Each coordinate (X, Y) is 32 bytes.

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

// TODO: Add comments

#[derive(Clone)]
#[repr(transparent)]
pub struct G1Affine(BytesN<G1_SERIALIZED_SIZE>);

#[derive(Clone)]
#[repr(transparent)]
pub struct G2Affine(BytesN<G2_SERIALIZED_SIZE>);

#[derive(Clone)]
#[repr(transparent)]
pub struct Fr(U256);

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

struct Fp(BytesN<FP_SERIALIZED_SIZE>);

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

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
14 changes: 12 additions & 2 deletions soroban-sdk/src/tests/cost_estimate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,11 +235,21 @@ fn test_cost_estimate_budget() {
Bls12381FrMul 0 None 0 0 332 0 0 0
Bls12381FrPow 0 Some(0) 0 0 691 74558 0 128
Bls12381FrInv 0 None 0 0 35421 0 0 0
Bn254EncodeFp 0 None 0 0 345 0 0 0
Bn254DecodeFp 0 None 0 0 433 0 0 0
Bn254G1CheckPointOnCurve 0 None 0 0 858 0 0 0
Bn254G2CheckPointOnCurve 0 None 0 0 2642 0 0 0
Bn254G2CheckPointInSubgroup 0 None 0 0 2930793 0 0 0
Bn254G1ProjectiveToAffine 0 None 0 0 63 0 0 0
Bn254G1Add 0 None 0 0 3465 0 0 0
Bn254G1Mul 0 None 0 0 1034667 0 0 0
Bn254Pairing 0 Some(0) 0 0 5263357 391912693 1833 6231032
Bn254FrFromU256 0 None 0 0 1940 0 0 0
===============================================================================================================================================================================
Internal details (diagnostics info, does not affect fees)
Total # times meter was called: 197
Shadow cpu limit: 100000000; used: 31868
Shadow mem limit: 41943040; used: 27092
Shadow cpu limit: 100000000; used: 32431
Shadow mem limit: 41943040; used: 27108
===============================================================================================================================================================================


Expand Down
Loading
Loading