VYPR
Moderate severityNVD Advisory· Published Mar 12, 2026· Updated Mar 13, 2026

soroban-sdk: `Fr` scalar field equality comparison bypasses modular reduction

CVE-2026-32322

Description

soroban-sdk is a Rust SDK for Soroban contracts. Prior to 22.0.11, 23.5.3, and 25.3.0, The Fr (scalar field) types for BN254 and BLS12-381 in soroban-sdk compared values using their raw U256 representation without first reducing modulo the field modulus r. This caused mathematically equal field elements to compare as not-equal when one or both values were unreduced (i.e., >= r). The vulnerability requires an attacker to supply crafted Fr values through contract inputs, and compare them directly without going through host-side arithmetic operations. Smart contracts that rely on Fr equality checks for security-critical logic could produce incorrect results. The impact depends on how the affected contract uses Fr equality comparisons, but can result in incorrect authorization decisions or validation bypasses in contracts that perform equality checks on user-supplied scalar values. This vulnerability is fixed in 22.0.11, 23.5.3, and 25.3.0.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
soroban-sdkcrates.io
>= 25.0.0, < 25.3.025.3.0
soroban-sdkcrates.io
>= 23.0.0, < 23.5.323.5.3
soroban-sdkcrates.io
< 22.0.1122.0.11

Affected products

1

Patches

1
082424b30bf2

Fix `Fr` scalar field to reduce modulo `r` on construction (#1750)

19 files changed · +1964 87
  • Cargo.lock+231 30 modified
    @@ -14,6 +14,12 @@ dependencies = [
      "zerocopy",
     ]
     
    +[[package]]
    +name = "allocator-api2"
    +version = "0.2.21"
    +source = "registry+https://github.com/rust-lang/crates.io-index"
    +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
    +
     [[package]]
     name = "android-tzdata"
     version = "0.1.1"
    @@ -44,10 +50,22 @@ version = "0.4.0"
     source = "registry+https://github.com/rust-lang/crates.io-index"
     checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488"
     dependencies = [
    - "ark-ec",
    - "ark-ff",
    - "ark-serialize",
    - "ark-std",
    + "ark-ec 0.4.2",
    + "ark-ff 0.4.2",
    + "ark-serialize 0.4.2",
    + "ark-std 0.4.0",
    +]
    +
    +[[package]]
    +name = "ark-bls12-381"
    +version = "0.5.0"
    +source = "registry+https://github.com/rust-lang/crates.io-index"
    +checksum = "3df4dcc01ff89867cd86b0da835f23c3f02738353aaee7dde7495af71363b8d5"
    +dependencies = [
    + "ark-ec 0.5.0",
    + "ark-ff 0.5.0",
    + "ark-serialize 0.5.0",
    + "ark-std 0.5.0",
     ]
     
     [[package]]
    @@ -56,9 +74,20 @@ version = "0.4.0"
     source = "registry+https://github.com/rust-lang/crates.io-index"
     checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f"
     dependencies = [
    - "ark-ec",
    - "ark-ff",
    - "ark-std",
    + "ark-ec 0.4.2",
    + "ark-ff 0.4.2",
    + "ark-std 0.4.0",
    +]
    +
    +[[package]]
    +name = "ark-bn254"
    +version = "0.5.0"
    +source = "registry+https://github.com/rust-lang/crates.io-index"
    +checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc"
    +dependencies = [
    + "ark-ec 0.5.0",
    + "ark-ff 0.5.0",
    + "ark-std 0.5.0",
     ]
     
     [[package]]
    @@ -67,13 +96,34 @@ version = "0.4.2"
     source = "registry+https://github.com/rust-lang/crates.io-index"
     checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba"
     dependencies = [
    - "ark-ff",
    - "ark-poly",
    - "ark-serialize",
    - "ark-std",
    + "ark-ff 0.4.2",
    + "ark-poly 0.4.2",
    + "ark-serialize 0.4.2",
    + "ark-std 0.4.0",
      "derivative",
      "hashbrown 0.13.2",
    - "itertools",
    + "itertools 0.10.5",
    + "num-traits",
    + "zeroize",
    +]
    +
    +[[package]]
    +name = "ark-ec"
    +version = "0.5.0"
    +source = "registry+https://github.com/rust-lang/crates.io-index"
    +checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce"
    +dependencies = [
    + "ahash",
    + "ark-ff 0.5.0",
    + "ark-poly 0.5.0",
    + "ark-serialize 0.5.0",
    + "ark-std 0.5.0",
    + "educe",
    + "fnv",
    + "hashbrown 0.15.5",
    + "itertools 0.13.0",
    + "num-bigint",
    + "num-integer",
      "num-traits",
      "zeroize",
     ]
    @@ -84,20 +134,40 @@ version = "0.4.2"
     source = "registry+https://github.com/rust-lang/crates.io-index"
     checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba"
     dependencies = [
    - "ark-ff-asm",
    - "ark-ff-macros",
    - "ark-serialize",
    - "ark-std",
    + "ark-ff-asm 0.4.2",
    + "ark-ff-macros 0.4.2",
    + "ark-serialize 0.4.2",
    + "ark-std 0.4.0",
      "derivative",
      "digest",
    - "itertools",
    + "itertools 0.10.5",
      "num-bigint",
      "num-traits",
      "paste",
      "rustc_version",
      "zeroize",
     ]
     
    +[[package]]
    +name = "ark-ff"
    +version = "0.5.0"
    +source = "registry+https://github.com/rust-lang/crates.io-index"
    +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70"
    +dependencies = [
    + "ark-ff-asm 0.5.0",
    + "ark-ff-macros 0.5.0",
    + "ark-serialize 0.5.0",
    + "ark-std 0.5.0",
    + "arrayvec",
    + "digest",
    + "educe",
    + "itertools 0.13.0",
    + "num-bigint",
    + "num-traits",
    + "paste",
    + "zeroize",
    +]
    +
     [[package]]
     name = "ark-ff-asm"
     version = "0.4.2"
    @@ -108,6 +178,16 @@ dependencies = [
      "syn 1.0.109",
     ]
     
    +[[package]]
    +name = "ark-ff-asm"
    +version = "0.5.0"
    +source = "registry+https://github.com/rust-lang/crates.io-index"
    +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60"
    +dependencies = [
    + "quote",
    + "syn 2.0.103",
    +]
    +
     [[package]]
     name = "ark-ff-macros"
     version = "0.4.2"
    @@ -121,27 +201,68 @@ dependencies = [
      "syn 1.0.109",
     ]
     
    +[[package]]
    +name = "ark-ff-macros"
    +version = "0.5.0"
    +source = "registry+https://github.com/rust-lang/crates.io-index"
    +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3"
    +dependencies = [
    + "num-bigint",
    + "num-traits",
    + "proc-macro2",
    + "quote",
    + "syn 2.0.103",
    +]
    +
     [[package]]
     name = "ark-poly"
     version = "0.4.2"
     source = "registry+https://github.com/rust-lang/crates.io-index"
     checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf"
     dependencies = [
    - "ark-ff",
    - "ark-serialize",
    - "ark-std",
    + "ark-ff 0.4.2",
    + "ark-serialize 0.4.2",
    + "ark-std 0.4.0",
      "derivative",
      "hashbrown 0.13.2",
     ]
     
    +[[package]]
    +name = "ark-poly"
    +version = "0.5.0"
    +source = "registry+https://github.com/rust-lang/crates.io-index"
    +checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27"
    +dependencies = [
    + "ahash",
    + "ark-ff 0.5.0",
    + "ark-serialize 0.5.0",
    + "ark-std 0.5.0",
    + "educe",
    + "fnv",
    + "hashbrown 0.15.5",
    +]
    +
     [[package]]
     name = "ark-serialize"
     version = "0.4.2"
     source = "registry+https://github.com/rust-lang/crates.io-index"
     checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5"
     dependencies = [
    - "ark-serialize-derive",
    - "ark-std",
    + "ark-serialize-derive 0.4.2",
    + "ark-std 0.4.0",
    + "digest",
    + "num-bigint",
    +]
    +
    +[[package]]
    +name = "ark-serialize"
    +version = "0.5.0"
    +source = "registry+https://github.com/rust-lang/crates.io-index"
    +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7"
    +dependencies = [
    + "ark-serialize-derive 0.5.0",
    + "ark-std 0.5.0",
    + "arrayvec",
      "digest",
      "num-bigint",
     ]
    @@ -157,6 +278,17 @@ dependencies = [
      "syn 1.0.109",
     ]
     
    +[[package]]
    +name = "ark-serialize-derive"
    +version = "0.5.0"
    +source = "registry+https://github.com/rust-lang/crates.io-index"
    +checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d"
    +dependencies = [
    + "proc-macro2",
    + "quote",
    + "syn 2.0.103",
    +]
    +
     [[package]]
     name = "ark-std"
     version = "0.4.0"
    @@ -167,6 +299,22 @@ dependencies = [
      "rand",
     ]
     
    +[[package]]
    +name = "ark-std"
    +version = "0.5.0"
    +source = "registry+https://github.com/rust-lang/crates.io-index"
    +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a"
    +dependencies = [
    + "num-traits",
    + "rand",
    +]
    +
    +[[package]]
    +name = "arrayvec"
    +version = "0.7.6"
    +source = "registry+https://github.com/rust-lang/crates.io-index"
    +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
    +
     [[package]]
     name = "autocfg"
     version = "1.3.0"
    @@ -555,6 +703,18 @@ dependencies = [
      "zeroize",
     ]
     
    +[[package]]
    +name = "educe"
    +version = "0.6.0"
    +source = "registry+https://github.com/rust-lang/crates.io-index"
    +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417"
    +dependencies = [
    + "enum-ordinalize",
    + "proc-macro2",
    + "quote",
    + "syn 2.0.103",
    +]
    +
     [[package]]
     name = "either"
     version = "1.13.0"
    @@ -579,6 +739,26 @@ dependencies = [
      "zeroize",
     ]
     
    +[[package]]
    +name = "enum-ordinalize"
    +version = "4.3.2"
    +source = "registry+https://github.com/rust-lang/crates.io-index"
    +checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0"
    +dependencies = [
    + "enum-ordinalize-derive",
    +]
    +
    +[[package]]
    +name = "enum-ordinalize-derive"
    +version = "4.3.2"
    +source = "registry+https://github.com/rust-lang/crates.io-index"
    +checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631"
    +dependencies = [
    + "proc-macro2",
    + "quote",
    + "syn 2.0.103",
    +]
    +
     [[package]]
     name = "equivalent"
     version = "1.0.1"
    @@ -710,6 +890,15 @@ version = "0.14.5"
     source = "registry+https://github.com/rust-lang/crates.io-index"
     checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
     
    +[[package]]
    +name = "hashbrown"
    +version = "0.15.5"
    +source = "registry+https://github.com/rust-lang/crates.io-index"
    +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
    +dependencies = [
    + "allocator-api2",
    +]
    +
     [[package]]
     name = "heapless"
     version = "0.8.0"
    @@ -816,6 +1005,15 @@ dependencies = [
      "either",
     ]
     
    +[[package]]
    +name = "itertools"
    +version = "0.13.0"
    +source = "registry+https://github.com/rust-lang/crates.io-index"
    +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
    +dependencies = [
    + "either",
    +]
    +
     [[package]]
     name = "itoa"
     version = "1.0.11"
    @@ -1342,7 +1540,7 @@ version = "25.0.1"
     source = "registry+https://github.com/rust-lang/crates.io-index"
     checksum = "7192e3a5551a7aeee90d2110b11b615798e81951fd8c8293c87ea7f88b0168f5"
     dependencies = [
    - "itertools",
    + "itertools 0.10.5",
      "proc-macro2",
      "quote",
      "syn 2.0.103",
    @@ -1383,11 +1581,11 @@ version = "25.0.1"
     source = "registry+https://github.com/rust-lang/crates.io-index"
     checksum = "43af5d53c57bc2f546e122adc0b1cca6f93942c718977379aa19ddd04f06fcec"
     dependencies = [
    - "ark-bls12-381",
    - "ark-bn254",
    - "ark-ec",
    - "ark-ff",
    - "ark-serialize",
    + "ark-bls12-381 0.4.0",
    + "ark-bn254 0.4.0",
    + "ark-ec 0.4.2",
    + "ark-ff 0.4.2",
    + "ark-serialize 0.4.2",
      "curve25519-dalek",
      "ecdsa",
      "ed25519-dalek",
    @@ -1420,7 +1618,7 @@ version = "25.0.1"
     source = "registry+https://github.com/rust-lang/crates.io-index"
     checksum = "a989167512e3592d455b1e204d703cfe578a36672a77ed2f9e6f7e1bbfd9cc5c"
     dependencies = [
    - "itertools",
    + "itertools 0.10.5",
      "proc-macro2",
      "quote",
      "serde",
    @@ -1456,6 +1654,9 @@ name = "soroban-sdk"
     version = "25.2.0"
     dependencies = [
      "arbitrary",
    + "ark-bls12-381 0.5.0",
    + "ark-bn254 0.5.0",
    + "ark-ff 0.5.0",
      "bytes-lit",
      "crate-git-revision",
      "ctor",
    @@ -1488,7 +1689,7 @@ version = "25.2.0"
     dependencies = [
      "darling",
      "heck",
    - "itertools",
    + "itertools 0.10.5",
      "macro-string",
      "proc-macro2",
      "quote",
    
  • soroban-sdk/Cargo.toml+3 0 modified
    @@ -59,6 +59,9 @@ proptest-arbitrary-interop = "0.1.0"
     libfuzzer-sys = "0.4.7"
     expect-test = "1.4.1"
     sha2 = "0.10.7"
    +ark-bn254 = { version = "0.5", default-features = false, features = ["curve"] }
    +ark-bls12-381 = { version = "0.5", default-features = false, features = ["curve"] }
    +ark-ff = { version = "0.5", default-features = false }
     
     [features]
     alloc = []
    
  • soroban-sdk/src/bytes.rs+32 8 modified
    @@ -102,14 +102,19 @@ macro_rules! bytesn {
         };
     }
     
    +/// Internal macro that generates all `BytesN` wrapper methods and trait impls
    +/// *except* `from_bytes`. Types using this macro must provide their own
    +/// `from_bytes(BytesN<$size>) -> Self` (e.g. to add validation).
    +///
    +/// This macro exists for backward compatibility: `impl_bytesn_repr` was
    +/// accidentally exported via `#[macro_export]` and cannot be changed until the
    +/// next protocol boundary. Once we remove the `#[macro_export]` from
    +/// `impl_bytesn_repr`, this macro should be consolidated back into it.
    +#[doc(hidden)]
     #[macro_export]
    -macro_rules! impl_bytesn_repr {
    +macro_rules! impl_bytesn_repr_without_from_bytes {
         ($elem: ident, $size: expr) => {
             impl $elem {
    -            pub fn from_bytes(bytes: BytesN<$size>) -> Self {
    -                Self(bytes)
    -            }
    -
                 pub fn into_bytes(self) -> BytesN<$size> {
                     self.0
                 }
    @@ -127,7 +132,7 @@ macro_rules! impl_bytesn_repr {
                 }
     
                 pub fn from_array(env: &Env, array: &[u8; $size]) -> Self {
    -                Self(<BytesN<$size>>::from_array(env, array))
    +                Self::from_bytes(BytesN::from_array(env, array))
                 }
     
                 pub fn as_val(&self) -> &Val {
    @@ -151,8 +156,8 @@ macro_rules! impl_bytesn_repr {
                 type Error = ConversionError;
     
                 fn try_from_val(env: &Env, val: &Val) -> Result<Self, Self::Error> {
    -                let bytes = <BytesN<$size>>::try_from_val(env, val)?;
    -                Ok($elem(bytes))
    +                let bytes = BytesN::try_from_val(env, val)?;
    +                Ok(Self::from_bytes(bytes))
                 }
             }
     
    @@ -226,6 +231,25 @@ macro_rules! impl_bytesn_repr {
         };
     }
     
    +/// Generates all `BytesN` wrapper methods and trait impls including a default
    +/// `from_bytes` that wraps the bytes without validation.
    +///
    +/// NOTE: This macro was not intended to be exported. Remove the
    +/// `#[macro_export]` at the next protocol boundary, and consolidate
    +/// `impl_bytesn_repr_without_from_bytes` back into this macro.
    +#[macro_export]
    +macro_rules! impl_bytesn_repr {
    +    ($elem: ident, $size: expr) => {
    +        impl $elem {
    +            pub fn from_bytes(bytes: BytesN<$size>) -> Self {
    +                Self(bytes)
    +            }
    +        }
    +
    +        impl_bytesn_repr_without_from_bytes!($elem, $size);
    +    };
    +}
    +
     /// Bytes is a contiguous growable array type containing `u8`s.
     ///
     /// The array is stored in the Host and available to the Guest through the
    
  • soroban-sdk/src/crypto/bls12_381.rs+277 7 modified
    @@ -3,7 +3,7 @@ use crate::xdr::ScVal;
     use crate::{
         crypto::utils::BigInt,
         env::internal::{self, BytesObject, U256Val, U64Val},
    -    impl_bytesn_repr,
    +    impl_bytesn_repr_without_from_bytes,
         unwrap::{UnwrapInfallible, UnwrapOptimized},
         Bytes, BytesN, ConversionError, Env, IntoVal, TryFromVal, Val, Vec, U256,
     };
    @@ -116,10 +116,55 @@ pub type Fp2 = Bls12381Fp2;
     #[repr(transparent)]
     pub struct Fr(U256);
     
    -impl_bytesn_repr!(Bls12381G1Affine, G1_SERIALIZED_SIZE);
    -impl_bytesn_repr!(Bls12381G2Affine, G2_SERIALIZED_SIZE);
    -impl_bytesn_repr!(Bls12381Fp, FP_SERIALIZED_SIZE);
    -impl_bytesn_repr!(Bls12381Fp2, FP2_SERIALIZED_SIZE);
    +impl_bytesn_repr_without_from_bytes!(Bls12381G1Affine, G1_SERIALIZED_SIZE);
    +impl_bytesn_repr_without_from_bytes!(Bls12381G2Affine, G2_SERIALIZED_SIZE);
    +impl_bytesn_repr_without_from_bytes!(Bls12381Fp, FP_SERIALIZED_SIZE);
    +impl_bytesn_repr_without_from_bytes!(Bls12381Fp2, FP2_SERIALIZED_SIZE);
    +
    +// BLS12-381 base field modulus p in big-endian bytes.
    +// p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
    +const BLS12_381_FP_MODULUS_BE: [u8; FP_SERIALIZED_SIZE] = [
    +    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,
    +];
    +
    +fn validate_fp(bytes: &[u8; FP_SERIALIZED_SIZE]) {
    +    if bytes >= &BLS12_381_FP_MODULUS_BE {
    +        sdk_panic!("Bls12-381: Invalid Fp");
    +    }
    +}
    +
    +fn validate_fp2(bytes: &[u8; FP2_SERIALIZED_SIZE]) {
    +    validate_fp(bytes[0..FP_SERIALIZED_SIZE].try_into().unwrap());
    +    validate_fp(bytes[FP_SERIALIZED_SIZE..].try_into().unwrap());
    +}
    +
    +impl Bls12381G1Affine {
    +    pub fn from_bytes(bytes: BytesN<G1_SERIALIZED_SIZE>) -> Self {
    +        Self(bytes)
    +    }
    +}
    +
    +impl Bls12381G2Affine {
    +    pub fn from_bytes(bytes: BytesN<G2_SERIALIZED_SIZE>) -> Self {
    +        Self(bytes)
    +    }
    +}
    +
    +impl Bls12381Fp {
    +    pub fn from_bytes(bytes: BytesN<FP_SERIALIZED_SIZE>) -> Self {
    +        validate_fp(&bytes.to_array());
    +        Self(bytes)
    +    }
    +}
    +
    +impl Bls12381Fp2 {
    +    pub fn from_bytes(bytes: BytesN<FP2_SERIALIZED_SIZE>) -> Self {
    +        validate_fp2(&bytes.to_array());
    +        Self(bytes)
    +    }
    +}
     
     impl Fp {
         pub fn env(&self) -> &Env {
    @@ -430,9 +475,29 @@ impl Fr {
         }
     }
     
    +// BLS12-381 scalar field modulus r in big-endian bytes.
    +// r = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001
    +const BLS12_381_FR_MODULUS_BE: [u8; 32] = [
    +    0x73, 0xed, 0xa7, 0x53, 0x29, 0x9d, 0x7d, 0x48, 0x33, 0x39, 0xd8, 0x08, 0x09, 0xa1, 0xd8, 0x05,
    +    0x53, 0xbd, 0xa4, 0x02, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
    +];
    +
    +fn fr_modulus(env: &Env) -> U256 {
    +    U256::from_be_bytes(env, &Bytes::from_array(env, &BLS12_381_FR_MODULUS_BE))
    +}
    +
     impl From<U256> for Fr {
         fn from(value: U256) -> Self {
    -        Self(value)
    +        // Keep all Fr construction paths canonical by reducing modulo r here.
    +        // Constructors and deserialization paths should route through this impl.
    +        // Skip the expensive rem_euclid when value is already canonical (< r),
    +        // which is always the case for host-returned arithmetic results.
    +        let modulus = fr_modulus(value.env());
    +        if value >= modulus {
    +            Self(value.rem_euclid(&modulus))
    +        } else {
    +            Self(value)
    +        }
         }
     }
     
    @@ -447,7 +512,7 @@ impl TryFromVal<Env, Val> for Fr {
     
         fn try_from_val(env: &Env, val: &Val) -> Result<Self, Self::Error> {
             let u = U256::try_from_val(env, val)?;
    -        Ok(Fr(u))
    +        Ok(u.into())
         }
     }
     
    @@ -727,6 +792,7 @@ impl Bls12_381 {
     #[cfg(test)]
     mod test {
         use super::*;
    +    use crate::bytesn;
     
         #[test]
         fn test_g1affine_to_val() {
    @@ -793,4 +859,208 @@ mod test {
     
             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));
    +
    +        // BLS12-381 r+1 as big-endian bytes
    +        let fr_from_bytes = Fr::from_bytes(bytesn!(
    +            &env,
    +            0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000002
    +        ));
    +        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() {
    +        // Direct From<U256>::from / .into() path must reduce
    +        let env = Env::default();
    +        let r = fr_modulus(&env);
    +        let one = U256::from_u32(&env, 1);
    +
    +        let fr: Fr = r.add(&one).into(); // r+1 via .into()
    +        let fr_one: Fr = one.into();
    +        assert_eq!(fr, fr_one);
    +    }
    +
    +    #[test]
    +    fn test_fr_eq_unreduced_vs_host_computed() {
    +        // User-provided unreduced Fr vs host-computed Fr
    +        let env = Env::default();
    +        let bls = Bls12_381::new(&env);
    +        let r = fr_modulus(&env);
    +        let five = U256::from_u32(&env, 5);
    +
    +        // User provides r+5 (unreduced)
    +        let user_fr = Fr::from_u256(r.add(&five));
    +        // Host computes 2+3 = 5 (always reduced)
    +        let host_fr = bls.fr_add(
    +            &Fr::from_u256(U256::from_u32(&env, 2)),
    +            &Fr::from_u256(U256::from_u32(&env, 3)),
    +        );
    +        assert_eq!(user_fr, host_fr);
    +    }
    +
    +    // Fp validation tests
    +
    +    #[test]
    +    fn test_fp_max_valid_accepted() {
    +        let env = Env::default();
    +        // p - 1 (last byte 0xaa instead of 0xab)
    +        let mut p_minus_1 = BLS12_381_FP_MODULUS_BE;
    +        p_minus_1[FP_SERIALIZED_SIZE - 1] -= 1;
    +        let _ = Fp::from_array(&env, &p_minus_1);
    +    }
    +
    +    #[test]
    +    #[should_panic(expected = "Bls12-381: Invalid Fp")]
    +    fn test_fp_at_modulus_panics() {
    +        let env = Env::default();
    +        let _ = Fp::from_array(&env, &BLS12_381_FP_MODULUS_BE);
    +    }
    +
    +    #[test]
    +    #[should_panic(expected = "Bls12-381: Invalid Fp")]
    +    fn test_fp_above_modulus_panics() {
    +        let env = Env::default();
    +        let mut above = BLS12_381_FP_MODULUS_BE;
    +        above[FP_SERIALIZED_SIZE - 1] += 1; // p + 1
    +        let _ = Fp::from_array(&env, &above);
    +    }
    +
    +    #[test]
    +    fn test_fp_from_bytes_validates() {
    +        let env = Env::default();
    +        // Zero should be valid
    +        let _ = Fp::from_bytes(BytesN::from_array(&env, &[0u8; FP_SERIALIZED_SIZE]));
    +    }
    +
    +    #[test]
    +    #[should_panic(expected = "Bls12-381: Invalid Fp")]
    +    fn test_fp_from_bytes_rejects_modulus() {
    +        let env = Env::default();
    +        let _ = Fp::from_bytes(BytesN::from_array(&env, &BLS12_381_FP_MODULUS_BE));
    +    }
    +
    +    #[test]
    +    #[should_panic(expected = "Bls12-381: Invalid Fp")]
    +    fn test_fp_try_from_val_rejects_modulus() {
    +        let env = Env::default();
    +        let bytes = BytesN::from_array(&env, &BLS12_381_FP_MODULUS_BE);
    +        let val: Val = bytes.into_val(&env);
    +        let _: Fp = val.into_val(&env);
    +    }
    +
    +    #[test]
    +    #[should_panic(expected = "Bls12-381: Invalid Fp")]
    +    fn test_fp2_component_above_modulus_panics() {
    +        let env = Env::default();
    +        // First Fp component is the modulus (invalid), second is zero (valid)
    +        let mut fp2_bytes = [0u8; FP2_SERIALIZED_SIZE];
    +        fp2_bytes[0..FP_SERIALIZED_SIZE].copy_from_slice(&BLS12_381_FP_MODULUS_BE);
    +        let _ = Fp2::from_array(&env, &fp2_bytes);
    +    }
    +
    +    #[test]
    +    #[should_panic(expected = "Bls12-381: Invalid Fp")]
    +    fn test_fp2_second_component_above_modulus_panics() {
    +        let env = Env::default();
    +        // First Fp component is zero (valid), second is the modulus (invalid)
    +        let mut fp2_bytes = [0u8; FP2_SERIALIZED_SIZE];
    +        fp2_bytes[FP_SERIALIZED_SIZE..].copy_from_slice(&BLS12_381_FP_MODULUS_BE);
    +        let _ = Fp2::from_array(&env, &fp2_bytes);
    +    }
    +
    +    #[test]
    +    fn test_fp2_max_valid_accepted() {
    +        let env = Env::default();
    +        // Both components are p-1 (valid)
    +        let mut p_minus_1 = BLS12_381_FP_MODULUS_BE;
    +        p_minus_1[FP_SERIALIZED_SIZE - 1] -= 1;
    +        let mut fp2_bytes = [0u8; FP2_SERIALIZED_SIZE];
    +        fp2_bytes[0..FP_SERIALIZED_SIZE].copy_from_slice(&p_minus_1);
    +        fp2_bytes[FP_SERIALIZED_SIZE..].copy_from_slice(&p_minus_1);
    +        let _ = Fp2::from_array(&env, &fp2_bytes);
    +    }
    +
    +    #[test]
    +    fn test_bls12_381_fp_modulus_matches_arkworks() {
    +        use ark_bls12_381::Fq;
    +        use ark_ff::{BigInteger, PrimeField};
    +
    +        let be_bytes = Fq::MODULUS.to_bytes_be();
    +        assert_eq!(
    +            be_bytes.as_slice(),
    +            &BLS12_381_FP_MODULUS_BE,
    +            "BLS12-381 Fp modulus does not match arkworks"
    +        );
    +    }
    +
    +    #[test]
    +    fn test_bls12_381_fr_modulus_matches_arkworks() {
    +        use ark_bls12_381::Fr as ArkFr;
    +        use ark_ff::{BigInteger, PrimeField};
    +
    +        let be_bytes = ArkFr::MODULUS.to_bytes_be();
    +        assert_eq!(
    +            be_bytes.as_slice(),
    +            &BLS12_381_FR_MODULUS_BE,
    +            "BLS12-381 Fr modulus does not match arkworks"
    +        );
    +    }
     }
    
  • soroban-sdk/src/crypto/bn254.rs+221 6 modified
    @@ -3,7 +3,7 @@ use crate::xdr::ScVal;
     use crate::{
         crypto::utils::BigInt,
         env::internal::{self, BytesObject, U256Val},
    -    impl_bytesn_repr,
    +    impl_bytesn_repr_without_from_bytes,
         unwrap::{UnwrapInfallible, UnwrapOptimized},
         Bytes, BytesN, ConversionError, Env, IntoVal, TryFromVal, Val, Vec, U256,
     };
    @@ -74,9 +74,41 @@ pub struct Fr(U256);
     #[repr(transparent)]
     pub struct Bn254Fp(BytesN<BN254_FP_SERIALIZED_SIZE>);
     
    -impl_bytesn_repr!(Bn254G1Affine, BN254_G1_SERIALIZED_SIZE);
    -impl_bytesn_repr!(Bn254G2Affine, BN254_G2_SERIALIZED_SIZE);
    -impl_bytesn_repr!(Bn254Fp, 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);
    +
    +// BN254 base field modulus p in big-endian bytes.
    +// p = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47
    +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 {
    @@ -224,9 +256,29 @@ impl Fr {
         }
     }
     
    +// BN254 scalar field modulus r in big-endian bytes.
    +// r = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
    +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 {
    -        Self(value)
    +        // Keep all Fr construction paths canonical by reducing modulo r here.
    +        // Constructors and deserialization paths should route through this impl.
    +        // Skip the expensive rem_euclid when value is already canonical (< r),
    +        // which is always the case for host-returned arithmetic results.
    +        let modulus = fr_modulus(value.env());
    +        if value >= modulus {
    +            Self(value.rem_euclid(&modulus))
    +        } else {
    +            Self(value)
    +        }
         }
     }
     
    @@ -241,7 +293,7 @@ impl TryFromVal<Env, Val> for Fr {
     
         fn try_from_val(env: &Env, val: &Val) -> Result<Self, Self::Error> {
             let u = U256::try_from_val(env, val)?;
    -        Ok(Fr(u))
    +        Ok(u.into())
         }
     }
     
    @@ -339,6 +391,7 @@ impl Bn254 {
     #[cfg(test)]
     mod test {
         use super::*;
    +    use crate::bytesn;
     
         #[test]
         fn test_g1affine_to_val() {
    @@ -405,4 +458,166 @@ mod test {
     
             assert_eq!(fr, rt);
         }
    +
    +    #[test]
    +    fn test_fr_eq_both_unreduced() {
    +        // Both inputs are user-provided unreduced values representing the same field element
    +        let env = Env::default();
    +        let r = fr_modulus(&env);
    +        let one = U256::from_u32(&env, 1);
    +
    +        let a = Fr::from_u256(r.add(&one)); // r+1 ≡ 1 (mod r)
    +        let b = Fr::from_u256(one.clone()); // 1
    +        assert_eq!(a, b);
    +
    +        // Both unreduced by different multiples of r
    +        let two_r_plus_one = r.add(&r).add(&one);
    +        let c = Fr::from_u256(two_r_plus_one); // 2r+1 ≡ 1 (mod r)
    +        assert_eq!(a, c);
    +        assert_eq!(b, c);
    +    }
    +
    +    #[test]
    +    fn test_fr_eq_unreduced_vs_zero() {
    +        // value == r should reduce to 0
    +        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() {
    +        // value < r should be preserved as-is
    +        let env = Env::default();
    +        let r = fr_modulus(&env);
    +        let val = r.sub(&U256::from_u32(&env, 1)); // r-1
    +
    +        let fr = Fr::from_u256(val.clone());
    +        assert_eq!(fr.to_u256(), val);
    +
    +        // small values
    +        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() {
    +        // from_bytes should also reduce since it goes through From<U256>
    +        let env = Env::default();
    +        let one_fr = Fr::from_u256(U256::from_u32(&env, 1));
    +
    +        // BN254 r+1 as big-endian bytes
    +        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() {
    +        // TryFromVal<Env, Val> path must also reduce
    +        let env = Env::default();
    +        let r = fr_modulus(&env);
    +        let one = U256::from_u32(&env, 1);
    +
    +        // Create an unreduced U256 value (r+1), convert to Val, then to Fr
    +        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() {
    +        // Direct From<U256>::from / .into() path must reduce
    +        let env = Env::default();
    +        let r = fr_modulus(&env);
    +        let one = U256::from_u32(&env, 1);
    +
    +        let fr: Fr = r.add(&one).into(); // r+1 via .into()
    +        let fr_one: Fr = one.into();
    +        assert_eq!(fr, fr_one);
    +    }
    +
    +    // Bn254Fp validation tests
    +
    +    #[test]
    +    fn test_bn254_fp_max_valid_accepted() {
    +        let env = Env::default();
    +        // p - 1 (last byte 0x46 instead of 0x47)
    +        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; // p + 1
    +        let _ = Bn254Fp::from_array(&env, &above);
    +    }
    +
    +    #[test]
    +    fn test_bn254_fp_from_bytes_validates() {
    +        let env = Env::default();
    +        // Zero should be valid
    +        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"
    +        );
    +    }
     }
    
  • soroban-sdk/src/testutils/arbitrary.rs+10 2 modified
    @@ -703,7 +703,11 @@ mod objects {
             type Error = ConversionError;
     
             fn try_from_val(env: &Env, v: &ArbitraryFp) -> Result<Self, Self::Error> {
    -            Ok(Fp::from_array(env, &v.bytes))
    +            let mut bytes = v.bytes;
    +            // Ensure the value is strictly less than the BLS12-381 base field modulus
    +            // p = 0x1a0111ea... by restricting the most significant byte.
    +            bytes[0] %= 0x1a;
    +            Ok(Fp::from_array(env, &bytes))
             }
         }
     
    @@ -721,7 +725,11 @@ mod objects {
             type Error = ConversionError;
     
             fn try_from_val(env: &Env, v: &ArbitraryFp2) -> Result<Self, Self::Error> {
    -            Ok(Fp2::from_array(env, &v.bytes))
    +            let mut bytes = v.bytes;
    +            // Ensure both Fp components are strictly less than the modulus
    +            bytes[0] %= 0x1a;
    +            bytes[FP_SERIALIZED_SIZE] %= 0x1a;
    +            Ok(Fp2::from_array(env, &bytes))
             }
         }
     
    
  • soroban-sdk/test_snapshots/tests/crypto_bls12_381/test_invoke_contract.1.json+9 9 modified
    @@ -74,7 +74,7 @@
                   "val": {
                     "contract_instance": {
                       "executable": {
    -                    "wasm": "cf2ec4405dbfcf217a8dc7f7f30939bbf83b8a2cdda3299493064a66d5392362"
    +                    "wasm": "62476431b18352c831c9c89ff7b96db7f034076f578fff279998bdea50463ac7"
                       },
                       "storage": null
                     }
    @@ -95,21 +95,21 @@
                       "ext": "v0",
                       "cost_inputs": {
                         "ext": "v0",
    -                    "n_instructions": 379,
    -                    "n_functions": 7,
    +                    "n_instructions": 1228,
    +                    "n_functions": 18,
                         "n_globals": 4,
                         "n_table_entries": 0,
    -                    "n_types": 5,
    +                    "n_types": 9,
                         "n_data_segments": 1,
                         "n_elem_segments": 0,
    -                    "n_imports": 10,
    -                    "n_exports": 7,
    -                    "n_data_segment_bytes": 52
    +                    "n_imports": 17,
    +                    "n_exports": 8,
    +                    "n_data_segment_bytes": 132
                       }
                     }
                   },
    -              "hash": "cf2ec4405dbfcf217a8dc7f7f30939bbf83b8a2cdda3299493064a66d5392362",
    -              "code": "0061736d01000000011e0560027e7e017e60047e7e7e7e017e60017e017e60027f7e0060017f017e023d0a016301360000016301630000016d016100010162013800020163013800020163013400020163016500020163016100020163016700000176016700000308070003000302030405030100110621047f01418080c0000b7f0041b480c0000b7f0041b480c0000b7f0041c080c0000b074a07066d656d6f727902000667315f6d756c000a0667325f6d756c000c0c64756d6d795f766572696679000e015f03010a5f5f646174615f656e6403020b5f5f686561705f6261736503030a9807076701027f23808080800041106b220224808080800020022000108b80808000024020022802004101460d002002290308210002402001a741ff01712203410c460d00200341c600470d010b200020011080808080002101200241106a24808080800020010f0b000b4d01017f23808080800041106b220224808080800020022001108f8080800042012101024020022802000d0020002002290308370308420021010b20002001370300200241106a2480808080000b6701027f23808080800041106b220224808080800020022000108d80808000024020022802004101460d002002290308210002402001a741ff01712203410c460d00200341c600470d010b200020011081808080002101200241106a24808080800020010f0b000b4201017e420121020240200142ff018342c800520d0020011083808080004280808080708342808080808018520d0020002001370308420021020b200020023703000bd90303027f047e017f23808080800041c0006b2201248080808000410021020240034020024128460d01200141086a20026a4202370300200241086a21020c000b0b0240200042ff018342cc00520d002000418c80c08000ad422086420484200141086aad4220864204844284808080d0001082808080001a2001290308220042ff018342c800520d0020001083808080004280808080708342808080808006520d00200141306a2001290310108f8080800020012802300d0020012903382103024020012903182204a741ff01712202410c460d00200241c600470d010b200141306a2001290320108b8080800020012802300d0020012903382105200141306a2001290328108d8080800020012802300d002001290338210620001084808080001085808080001a20031086808080001087808080001a200520041080808080002103200620041081808080002104420221004101210203402000210520024101712107410021022003210020070d000b20012005370308200141086a1090808080002105420221004101210203402000210320024101712107410021022004210020070d000b200120033703082005200141086a1090808080001088808080002100200141c0006a2480808080002000420151ad0f0b000b4201017e420121020240200142ff018342c800520d002001108380808000428080808070834280808080800c520d0020002001370308420021020b200020023703000b17002000ad4220864204844284808080101089808080000b0b3d0100418080c0000b346670667032667267316732000000100002000000020010000300000005001000020000000700100002000000090010000200000000e7020e636f6e74726163747370656376300000000100000000000000000000000a44756d6d7950726f6f66000000000005000000000000000266700000000003ee00000030000000000000000366703200000003ee000000600000000000000002667200000000000c000000000000000267310000000003ee00000060000000000000000267320000000003ee000000c000000000000000000000000667315f6d756c000000000002000000000000000170000000000003ee000000600000000000000001730000000000000c00000001000003ee0000006000000000000000000000000667325f6d756c000000000002000000000000000170000000000003ee000000c00000000000000001730000000000000c00000001000003ee000000c000000000000000000000000c64756d6d795f76657269667900000001000000000000000570726f6f66000000000007d00000000a44756d6d7950726f6f6600000000000100000001001e11636f6e7472616374656e766d6574617630000000000000001900000000002b0e636f6e74726163746d65746176300000000000000005727376657200000000000006312e38342e300000"
    +              "hash": "62476431b18352c831c9c89ff7b96db7f034076f578fff279998bdea50463ac7",
    +              "code": "0061736d0100000001320960027e7e017e60047e7e7e7e017e60017e017e60027f7e0060037e7f7f0060017f0060017f017e60000060037f7f7f017f026711016301360000016301630000016d0161000101620138000201630138000201630134000201630165000201630161000201630167000001760133000201760131000001620131000101760167000001620133000001690161000201780130000001690172000003131200030300030204050306000707070708080805030100110621047f01418080c0000b7f0041b480c0000b7f00418481c0000b7f00419081c0000b075708066d656d6f727902000667315f6d756c00110667325f6d756c00140c64756d6d795f76657269667900160a66725f7665635f676574001b015f03010a5f5f646174615f656e6403020b5f5f686561705f6261736503030ae914126501017f23808080800041106b220224808080800020022000109280808000024020022802004101460d00200229030821002002200110938080800020022802004101460d00200020022903081080808080002100200241106a24808080800020000f0b000b4d01017f23808080800041106b22022480808080002002200110998080800042012101024020022802000d0020002002290308370308420021010b20002001370300200241106a2480808080000ba60102017f017e024002402001a741ff0171220241c600460d00420121032002410c470d010b41e480c08000ad42208642048442848080808004108d80808000108e8080800021030240024002400240200142ce0083420c520d00200342ff0183420c510d010b20012003108f808080004200590d010c020b20014208882003420888540d010b2001200310908080800021010b20002001370308420021030b200020033703000b6501017f23808080800041106b220224808080800020022000109580808000024020022802004101460d00200229030821002002200110938080800020022802004101460d00200020022903081081808080002100200241106a24808080800020000f0b000b4201017e420121020240200142ff018342c800520d0020011083808080004280808080708342808080808018520d0020002001370308420021020b200020023703000bfa0403027f047e017f23808080800041f0016b2201248080808000410021020240034020024128460d01200141086a20026a4202370300200241086a21020c000b0b0240200042ff018342cc00520d002000418c80c08000ad422086420484200141086aad4220864204844284808080d0001082808080001a2001290308220042ff018342c800520d0020001083808080004280808080708342808080808006520d0020014190016a4100413010a2808080001a200020014190016a4130109780808000200141306a20014190016a413010a1808080001a200141306a10988080800020014190016a20012903101099808080002001280290010d00200129039801210320014190016a410041e00010a2808080001a200320014190016a41e000109780808000200141306a20014190016a41e00010a1808080001a200141306a109880808000200141306a41306a10988080800020014190016a20012903181093808080002001280290010d00200129039801210420014190016a20012903201092808080002001280290010d00200129039801210520014190016a20012903281095808080002001280290010d00200129039801210620001084808080001085808080001a20031086808080001087808080001a200520041080808080002103200620041081808080002104420221004101210203402000210520024101712107410021022003210020070d000b200120053703900120014190016a109a808080002105420221004101210203402000210320024101712107410021022004210020070d000b2001200337039001200520014190016a109a808080001088808080002100200141f0016a2480808080002000420151ad0f0b000b1f00200042042001ad4220864204842002ad422086420484108b808080001a0b22000240200041b480c08000413010a080808000417f4a0d000f0b109f80808000000b4201017e420121020240200142ff018342c800520d002001108380808000428080808070834280808080800c520d0020002001370308420021020b200020023703000b17002000ad422086420484428480808010108c808080000b870101017f23808080800041106b2202248080808000024002400240200042ff018342cb00520d00200142ff01834204520d0020001089808080004220882001422088580d0120022000200142848080807083108a8080800010938080800020022802004101470d020b000b109c80808000000b20022903082100200241106a24808080800020000b0900109e80808000000b0300000b0900109d80808000000b0900109e80808000000b4a01037f4100210302402002450d000240034020002d0000220420012d00002205470d01200041016a2100200141016a21012002417f6a2202450d020c000b0b200420056b21030b20030ba50501087f02400240200241104f0d00200021030c010b02402000410020006b41037122046a220520004d0d002004417f6a2106200021032001210702402004450d002004210820002103200121070340200320072d00003a0000200741016a2107200341016a21032008417f6a22080d000b0b20064107490d000340200320072d00003a0000200341016a200741016a2d00003a0000200341026a200741026a2d00003a0000200341036a200741036a2d00003a0000200341046a200741046a2d00003a0000200341056a200741056a2d00003a0000200341066a200741066a2d00003a0000200341076a200741076a2d00003a0000200741086a2107200341086a22032005470d000b0b2005200220046b2208417c7122066a210302400240200120046a22074103710d00200520034f0d0120072101034020052001280200360200200141046a2101200541046a22052003490d000c020b0b200520034f0d002007410374220241187121042007417c71220941046a2101410020026b411871210a2009280200210203402005200220047620012802002202200a7472360200200141046a2101200541046a22052003490d000b0b20084103712102200720066a21010b02402003200320026a22054f0d002002417f6a2108024020024107712207450d000340200320012d00003a0000200141016a2101200341016a21032007417f6a22070d000b0b20084107490d000340200320012d00003a0000200341016a200141016a2d00003a0000200341026a200141026a2d00003a0000200341036a200141036a2d00003a0000200341046a200141046a2d00003a0000200341056a200141056a2d00003a0000200341066a200141066a2d00003a0000200341076a200141076a2d00003a0000200141086a2101200341086a22032005470d000b0b20000baa0301057f02400240200241104f0d00200021030c010b02402000410020006b41037122046a220520004d0d002004417f6a21062000210302402004450d0020042107200021030340200320013a0000200341016a21032007417f6a22070d000b0b20064107490d000340200320013a0000200341076a20013a0000200341066a20013a0000200341056a20013a0000200341046a20013a0000200341036a20013a0000200341026a20013a0000200341016a20013a0000200341086a22032005470d000b0b024020052005200220046b2202417c716a22034f0d00200141ff017141818284086c2107034020052007360200200541046a22052003490d000b0b200241037121020b02402003200320026a22074f0d002002417f6a2104024020024107712205450d000340200320013a0000200341016a21032005417f6a22050d000b0b20044107490d000340200320013a0000200341076a20013a0000200341066a20013a0000200341056a20013a0000200341046a20013a0000200341036a20013a0000200341026a20013a0000200341016a20013a0000200341086a22032007470d000b0b20000b0b8e010100418080c0000b8401667066703266726731673200000010000200000002001000030000000500100002000000070010000200000009001000020000001a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff0000000100b7030e636f6e74726163747370656376300000000100000000000000000000000a44756d6d7950726f6f66000000000005000000000000000266700000000003ee00000030000000000000000366703200000003ee000000600000000000000002667200000000000c000000000000000267310000000003ee00000060000000000000000267320000000003ee000000c000000000000000000000000667315f6d756c000000000002000000000000000170000000000003ee000000600000000000000001730000000000000c00000001000003ee0000006000000000000000000000000667325f6d756c000000000002000000000000000170000000000003ee000000c00000000000000001730000000000000c00000001000003ee000000c000000000000000000000000c64756d6d795f76657269667900000001000000000000000570726f6f66000000000007d00000000a44756d6d7950726f6f660000000000010000000100000000000000000000000a66725f7665635f676574000000000002000000000000000676616c7565730000000003ea0000000c0000000000000005696e64657800000000000004000000010000000c001e11636f6e7472616374656e766d6574617630000000000000001900000000002b0e636f6e74726163746d65746176300000000000000005727376657200000000000006312e38342e300000"
                 }
               },
               "ext": "v0"
    
  • tests/bls/src/lib.rs+43 2 modified
    @@ -2,7 +2,7 @@
     use soroban_sdk::{
         contract, contractimpl, contracttype,
         crypto::bls12_381::{Bls12381Fp, Bls12381Fp2, Bls12381G1Affine, Bls12381G2Affine, Fr},
    -    log, Env,
    +    log, Env, Vec,
     };
     
     #[derive(Clone)]
    @@ -43,12 +43,16 @@ impl Contract {
             let vp2 = soroban_sdk::Vec::from_array(&env, [g2_mul]);
             env.crypto().bls12_381().pairing_check(vp1, vp2)
         }
    +
    +    pub fn fr_vec_get(_env: Env, values: Vec<Fr>, index: u32) -> Fr {
    +        values.get(index).unwrap()
    +    }
     }
     
     #[cfg(test)]
     mod test {
         use super::*;
    -    use soroban_sdk::{bytesn, Env};
    +    use soroban_sdk::{bytesn, vec, Env, IntoVal, Symbol, Val, U256};
     
         use crate::{Contract, ContractClient};
     
    @@ -114,4 +118,41 @@ mod test {
             let res = client.dummy_verify(&proof);
             assert!(!res); // The pairing of generator points multiplied by the same scalar should not be the identity
         }
    +
    +    #[test]
    +    fn test_fr_decode_reduces_unreduced_scalar_and_vec_elements() {
    +        let env = Env::default();
    +        let contract_id = env.register(Contract, ());
    +
    +        let modulus = U256::from_be_bytes(
    +            &env,
    +            &bytesn!(
    +                &env,
    +                0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001
    +            )
    +            .into(),
    +        );
    +        let two = U256::from_u32(&env, 2);
    +        let nine = U256::from_u32(&env, 9);
    +
    +        let raw_vals: Vec<Val> = vec![
    +            &env,
    +            modulus.add(&two).into_val(&env),
    +            modulus.add(&nine).into_val(&env),
    +        ];
    +
    +        let first: Fr = env.invoke_contract(
    +            &contract_id,
    +            &Symbol::new(&env, "fr_vec_get"),
    +            vec![&env, raw_vals.clone().into_val(&env), 0_u32.into_val(&env)],
    +        );
    +        let second: Fr = env.invoke_contract(
    +            &contract_id,
    +            &Symbol::new(&env, "fr_vec_get"),
    +            vec![&env, raw_vals.into_val(&env), 1_u32.into_val(&env)],
    +        );
    +
    +        assert_eq!(first, Fr::from_u256(two));
    +        assert_eq!(second, Fr::from_u256(nine));
    +    }
     }
    
  • tests/bls/test_snapshots/test/test_fr_arg_decode_reduces_unreduced_u256.1.json+61 0 added
    @@ -0,0 +1,61 @@
    +{
    +  "generators": {
    +    "address": 1,
    +    "nonce": 0,
    +    "mux_id": 0
    +  },
    +  "auth": [
    +    [],
    +    []
    +  ],
    +  "ledger": {
    +    "protocol_version": 25,
    +    "sequence_number": 0,
    +    "timestamp": 0,
    +    "network_id": "0000000000000000000000000000000000000000000000000000000000000000",
    +    "base_reserve": 0,
    +    "min_persistent_entry_ttl": 4096,
    +    "min_temp_entry_ttl": 16,
    +    "max_entry_ttl": 6312000,
    +    "ledger_entries": [
    +      {
    +        "entry": {
    +          "last_modified_ledger_seq": 0,
    +          "data": {
    +            "contract_data": {
    +              "ext": "v0",
    +              "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
    +              "key": "ledger_key_contract_instance",
    +              "durability": "persistent",
    +              "val": {
    +                "contract_instance": {
    +                  "executable": {
    +                    "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
    +                  },
    +                  "storage": null
    +                }
    +              }
    +            }
    +          },
    +          "ext": "v0"
    +        },
    +        "live_until": 4095
    +      },
    +      {
    +        "entry": {
    +          "last_modified_ledger_seq": 0,
    +          "data": {
    +            "contract_code": {
    +              "ext": "v0",
    +              "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    +              "code": ""
    +            }
    +          },
    +          "ext": "v0"
    +        },
    +        "live_until": 4095
    +      }
    +    ]
    +  },
    +  "events": []
    +}
    \ No newline at end of file
    
  • tests/bls/test_snapshots/test/test_fr_decode_reduces_unreduced_scalar_and_vec_elements.1.json+62 0 added
    @@ -0,0 +1,62 @@
    +{
    +  "generators": {
    +    "address": 1,
    +    "nonce": 0,
    +    "mux_id": 0
    +  },
    +  "auth": [
    +    [],
    +    [],
    +    []
    +  ],
    +  "ledger": {
    +    "protocol_version": 25,
    +    "sequence_number": 0,
    +    "timestamp": 0,
    +    "network_id": "0000000000000000000000000000000000000000000000000000000000000000",
    +    "base_reserve": 0,
    +    "min_persistent_entry_ttl": 4096,
    +    "min_temp_entry_ttl": 16,
    +    "max_entry_ttl": 6312000,
    +    "ledger_entries": [
    +      {
    +        "entry": {
    +          "last_modified_ledger_seq": 0,
    +          "data": {
    +            "contract_data": {
    +              "ext": "v0",
    +              "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
    +              "key": "ledger_key_contract_instance",
    +              "durability": "persistent",
    +              "val": {
    +                "contract_instance": {
    +                  "executable": {
    +                    "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
    +                  },
    +                  "storage": null
    +                }
    +              }
    +            }
    +          },
    +          "ext": "v0"
    +        },
    +        "live_until": 4095
    +      },
    +      {
    +        "entry": {
    +          "last_modified_ledger_seq": 0,
    +          "data": {
    +            "contract_code": {
    +              "ext": "v0",
    +              "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    +              "code": ""
    +            }
    +          },
    +          "ext": "v0"
    +        },
    +        "live_until": 4095
    +      }
    +    ]
    +  },
    +  "events": []
    +}
    \ No newline at end of file
    
  • tests/bls/test_snapshots/test/test_fr_vec_arg_decode_reduces_each_element.1.json+61 0 added
    @@ -0,0 +1,61 @@
    +{
    +  "generators": {
    +    "address": 1,
    +    "nonce": 0,
    +    "mux_id": 0
    +  },
    +  "auth": [
    +    [],
    +    []
    +  ],
    +  "ledger": {
    +    "protocol_version": 25,
    +    "sequence_number": 0,
    +    "timestamp": 0,
    +    "network_id": "0000000000000000000000000000000000000000000000000000000000000000",
    +    "base_reserve": 0,
    +    "min_persistent_entry_ttl": 4096,
    +    "min_temp_entry_ttl": 16,
    +    "max_entry_ttl": 6312000,
    +    "ledger_entries": [
    +      {
    +        "entry": {
    +          "last_modified_ledger_seq": 0,
    +          "data": {
    +            "contract_data": {
    +              "ext": "v0",
    +              "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
    +              "key": "ledger_key_contract_instance",
    +              "durability": "persistent",
    +              "val": {
    +                "contract_instance": {
    +                  "executable": {
    +                    "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
    +                  },
    +                  "storage": null
    +                }
    +              }
    +            }
    +          },
    +          "ext": "v0"
    +        },
    +        "live_until": 4095
    +      },
    +      {
    +        "entry": {
    +          "last_modified_ledger_seq": 0,
    +          "data": {
    +            "contract_code": {
    +              "ext": "v0",
    +              "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    +              "code": ""
    +            }
    +          },
    +          "ext": "v0"
    +        },
    +        "live_until": 4095
    +      }
    +    ]
    +  },
    +  "events": []
    +}
    \ No newline at end of file
    
  • tests/bn254/src/lib.rs+42 1 modified
    @@ -28,12 +28,16 @@ impl Contract {
         pub fn g1_mul(p: Bn254G1Affine, s: Fr) -> Bn254G1Affine {
             p * s
         }
    +
    +    pub fn fr_vec_get(_env: Env, values: Vec<Fr>, index: u32) -> Fr {
    +        values.get(index).unwrap()
    +    }
     }
     
     #[cfg(test)]
     mod test {
         use super::*;
    -    use soroban_sdk::{vec, Env, U256};
    +    use soroban_sdk::{bytesn, vec, Env, IntoVal, Symbol, Val, U256};
         extern crate std;
     
         use crate::{Contract, ContractClient};
    @@ -143,4 +147,41 @@ mod test {
     
             assert_eq!(-g1, g1_negated);
         }
    +
    +    #[test]
    +    fn test_fr_decode_reduces_unreduced_scalar_and_vec_elements() {
    +        let env = Env::default();
    +        let contract_id = env.register(Contract, ());
    +
    +        let modulus = U256::from_be_bytes(
    +            &env,
    +            &bytesn!(
    +                &env,
    +                0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
    +            )
    +            .into(),
    +        );
    +        let three = U256::from_u32(&env, 3);
    +        let seven = U256::from_u32(&env, 7);
    +
    +        let raw_vals: Vec<Val> = vec![
    +            &env,
    +            modulus.add(&three).into_val(&env),
    +            modulus.add(&seven).into_val(&env),
    +        ];
    +
    +        let first: Fr = env.invoke_contract(
    +            &contract_id,
    +            &Symbol::new(&env, "fr_vec_get"),
    +            vec![&env, raw_vals.clone().into_val(&env), 0_u32.into_val(&env)],
    +        );
    +        let second: Fr = env.invoke_contract(
    +            &contract_id,
    +            &Symbol::new(&env, "fr_vec_get"),
    +            vec![&env, raw_vals.into_val(&env), 1_u32.into_val(&env)],
    +        );
    +
    +        assert_eq!(first, Fr::from_u256(three));
    +        assert_eq!(second, Fr::from_u256(seven));
    +    }
     }
    
  • tests/bn254/test_snapshots/test/test_fr_arg_decode_reduces_unreduced_u256.1.json+61 0 added
    @@ -0,0 +1,61 @@
    +{
    +  "generators": {
    +    "address": 1,
    +    "nonce": 0,
    +    "mux_id": 0
    +  },
    +  "auth": [
    +    [],
    +    []
    +  ],
    +  "ledger": {
    +    "protocol_version": 25,
    +    "sequence_number": 0,
    +    "timestamp": 0,
    +    "network_id": "0000000000000000000000000000000000000000000000000000000000000000",
    +    "base_reserve": 0,
    +    "min_persistent_entry_ttl": 4096,
    +    "min_temp_entry_ttl": 16,
    +    "max_entry_ttl": 6312000,
    +    "ledger_entries": [
    +      {
    +        "entry": {
    +          "last_modified_ledger_seq": 0,
    +          "data": {
    +            "contract_data": {
    +              "ext": "v0",
    +              "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
    +              "key": "ledger_key_contract_instance",
    +              "durability": "persistent",
    +              "val": {
    +                "contract_instance": {
    +                  "executable": {
    +                    "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
    +                  },
    +                  "storage": null
    +                }
    +              }
    +            }
    +          },
    +          "ext": "v0"
    +        },
    +        "live_until": 4095
    +      },
    +      {
    +        "entry": {
    +          "last_modified_ledger_seq": 0,
    +          "data": {
    +            "contract_code": {
    +              "ext": "v0",
    +              "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    +              "code": ""
    +            }
    +          },
    +          "ext": "v0"
    +        },
    +        "live_until": 4095
    +      }
    +    ]
    +  },
    +  "events": []
    +}
    \ No newline at end of file
    
  • tests/bn254/test_snapshots/test/test_fr_decode_reduces_unreduced_scalar_and_vec_elements.1.json+62 0 added
    @@ -0,0 +1,62 @@
    +{
    +  "generators": {
    +    "address": 1,
    +    "nonce": 0,
    +    "mux_id": 0
    +  },
    +  "auth": [
    +    [],
    +    [],
    +    []
    +  ],
    +  "ledger": {
    +    "protocol_version": 25,
    +    "sequence_number": 0,
    +    "timestamp": 0,
    +    "network_id": "0000000000000000000000000000000000000000000000000000000000000000",
    +    "base_reserve": 0,
    +    "min_persistent_entry_ttl": 4096,
    +    "min_temp_entry_ttl": 16,
    +    "max_entry_ttl": 6312000,
    +    "ledger_entries": [
    +      {
    +        "entry": {
    +          "last_modified_ledger_seq": 0,
    +          "data": {
    +            "contract_data": {
    +              "ext": "v0",
    +              "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
    +              "key": "ledger_key_contract_instance",
    +              "durability": "persistent",
    +              "val": {
    +                "contract_instance": {
    +                  "executable": {
    +                    "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
    +                  },
    +                  "storage": null
    +                }
    +              }
    +            }
    +          },
    +          "ext": "v0"
    +        },
    +        "live_until": 4095
    +      },
    +      {
    +        "entry": {
    +          "last_modified_ledger_seq": 0,
    +          "data": {
    +            "contract_code": {
    +              "ext": "v0",
    +              "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    +              "code": ""
    +            }
    +          },
    +          "ext": "v0"
    +        },
    +        "live_until": 4095
    +      }
    +    ]
    +  },
    +  "events": []
    +}
    \ No newline at end of file
    
  • tests/bn254/test_snapshots/test/test_fr_vec_arg_decode_reduces_each_element.1.json+61 0 added
    @@ -0,0 +1,61 @@
    +{
    +  "generators": {
    +    "address": 1,
    +    "nonce": 0,
    +    "mux_id": 0
    +  },
    +  "auth": [
    +    [],
    +    []
    +  ],
    +  "ledger": {
    +    "protocol_version": 25,
    +    "sequence_number": 0,
    +    "timestamp": 0,
    +    "network_id": "0000000000000000000000000000000000000000000000000000000000000000",
    +    "base_reserve": 0,
    +    "min_persistent_entry_ttl": 4096,
    +    "min_temp_entry_ttl": 16,
    +    "max_entry_ttl": 6312000,
    +    "ledger_entries": [
    +      {
    +        "entry": {
    +          "last_modified_ledger_seq": 0,
    +          "data": {
    +            "contract_data": {
    +              "ext": "v0",
    +              "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
    +              "key": "ledger_key_contract_instance",
    +              "durability": "persistent",
    +              "val": {
    +                "contract_instance": {
    +                  "executable": {
    +                    "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
    +                  },
    +                  "storage": null
    +                }
    +              }
    +            }
    +          },
    +          "ext": "v0"
    +        },
    +        "live_until": 4095
    +      },
    +      {
    +        "entry": {
    +          "last_modified_ledger_seq": 0,
    +          "data": {
    +            "contract_code": {
    +              "ext": "v0",
    +              "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    +              "code": ""
    +            }
    +          },
    +          "ext": "v0"
    +        },
    +        "live_until": 4095
    +      }
    +    ]
    +  },
    +  "events": []
    +}
    \ No newline at end of file
    
  • tests-expanded/test_bls_tests.rs+270 11 modified
    @@ -8,7 +8,7 @@ extern crate compiler_builtins as _;
     use soroban_sdk::{
         contract, contractimpl, contracttype,
         crypto::bls12_381::{Bls12381Fp, Bls12381Fp2, Bls12381G1Affine, Bls12381G2Affine, Fr},
    -    log, Env,
    +    log, Env, Vec,
     };
     pub struct DummyProof {
         pub fp: Bls12381Fp,
    @@ -727,6 +727,9 @@ impl Contract {
             let vp2 = soroban_sdk::Vec::from_array(&env, [g2_mul]);
             env.crypto().bls12_381().pairing_check(vp1, vp2)
         }
    +    pub fn fr_vec_get(_env: Env, values: Vec<Fr>, index: u32) -> Fr {
    +        values.get(index).unwrap()
    +    }
     }
     #[doc(hidden)]
     #[allow(non_snake_case)]
    @@ -770,6 +773,20 @@ impl Contract {
             *b"\0\0\0\0\0\0\0\0\0\0\0\x0cdummy_verify\0\0\0\x01\0\0\0\0\0\0\0\x05proof\0\0\0\0\0\x07\xd0\0\0\0\nDummyProof\0\0\0\0\0\x01\0\0\0\x01"
         }
     }
    +#[doc(hidden)]
    +#[allow(non_snake_case)]
    +pub mod __Contract__fr_vec_get__spec {
    +    #[doc(hidden)]
    +    #[allow(non_snake_case)]
    +    #[allow(non_upper_case_globals)]
    +    pub static __SPEC_XDR_FN_FR_VEC_GET: [u8; 80usize] = super::Contract::spec_xdr_fr_vec_get();
    +}
    +impl Contract {
    +    #[allow(non_snake_case)]
    +    pub const fn spec_xdr_fr_vec_get() -> [u8; 80usize] {
    +        *b"\0\0\0\0\0\0\0\0\0\0\0\nfr_vec_get\0\0\0\0\0\x02\0\0\0\0\0\0\0\x06values\0\0\0\0\x03\xea\0\0\0\x0c\0\0\0\0\0\0\0\x05index\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x0c"
    +    }
    +}
     impl<'a> ContractClient<'a> {
         pub fn g1_mul(&self, p: &Bls12381G1Affine, s: &Fr) -> Bls12381G1Affine {
             use core::ops::Not;
    @@ -1017,6 +1034,81 @@ impl<'a> ContractClient<'a> {
             }
             res
         }
    +    pub fn fr_vec_get(&self, values: &Vec<Fr>, index: &u32) -> Fr {
    +        use core::ops::Not;
    +        let old_auth_manager = self
    +            .env
    +            .in_contract()
    +            .not()
    +            .then(|| self.env.host().snapshot_auth_manager().unwrap());
    +        {
    +            if let Some(set_auths) = self.set_auths {
    +                self.env.set_auths(set_auths);
    +            }
    +            if let Some(mock_auths) = self.mock_auths {
    +                self.env.mock_auths(mock_auths);
    +            }
    +            if self.mock_all_auths {
    +                if self.allow_non_root_auth {
    +                    self.env.mock_all_auths_allowing_non_root_auth();
    +                } else {
    +                    self.env.mock_all_auths();
    +                }
    +            }
    +        }
    +        use soroban_sdk::{FromVal, IntoVal};
    +        let res = self.env.invoke_contract(
    +            &self.address,
    +            &{ soroban_sdk::Symbol::new(&self.env, "fr_vec_get") },
    +            ::soroban_sdk::Vec::from_array(
    +                &self.env,
    +                [values.into_val(&self.env), index.into_val(&self.env)],
    +            ),
    +        );
    +        if let Some(old_auth_manager) = old_auth_manager {
    +            self.env.host().set_auth_manager(old_auth_manager).unwrap();
    +        }
    +        res
    +    }
    +    pub fn try_fr_vec_get(
    +        &self,
    +        values: &Vec<Fr>,
    +        index: &u32,
    +    ) -> Result<
    +        Result<Fr, <Fr as soroban_sdk::TryFromVal<soroban_sdk::Env, soroban_sdk::Val>>::Error>,
    +        Result<soroban_sdk::Error, soroban_sdk::InvokeError>,
    +    > {
    +        use core::ops::Not;
    +        let old_auth_manager = self
    +            .env
    +            .in_contract()
    +            .not()
    +            .then(|| self.env.host().snapshot_auth_manager().unwrap());
    +        {
    +            if let Some(set_auths) = self.set_auths {
    +                self.env.set_auths(set_auths);
    +            }
    +            if let Some(mock_auths) = self.mock_auths {
    +                self.env.mock_auths(mock_auths);
    +            }
    +            if self.mock_all_auths {
    +                self.env.mock_all_auths();
    +            }
    +        }
    +        use soroban_sdk::{FromVal, IntoVal};
    +        let res = self.env.try_invoke_contract(
    +            &self.address,
    +            &{ soroban_sdk::Symbol::new(&self.env, "fr_vec_get") },
    +            ::soroban_sdk::Vec::from_array(
    +                &self.env,
    +                [values.into_val(&self.env), index.into_val(&self.env)],
    +            ),
    +        );
    +        if let Some(old_auth_manager) = old_auth_manager {
    +            self.env.host().set_auth_manager(old_auth_manager).unwrap();
    +        }
    +        res
    +    }
     }
     impl ContractArgs {
         #[inline(always)]
    @@ -1034,6 +1126,11 @@ impl ContractArgs {
         pub fn dummy_verify<'i>(proof: &'i DummyProof) -> (&'i DummyProof,) {
             (proof,)
         }
    +    #[inline(always)]
    +    #[allow(clippy::unused_unit)]
    +    pub fn fr_vec_get<'i>(values: &'i Vec<Fr>, index: &'i u32) -> (&'i Vec<Fr>, &'i u32) {
    +        (values, index)
    +    }
     }
     #[doc(hidden)]
     #[allow(non_snake_case)]
    @@ -1200,8 +1297,65 @@ pub extern "C" fn __Contract__dummy_verify__invoke_raw_extern(
     }
     #[doc(hidden)]
     #[allow(non_snake_case)]
    +#[deprecated(note = "use `ContractClient::new(&env, &contract_id).fr_vec_get` instead")]
    +#[allow(deprecated)]
    +pub fn __Contract__fr_vec_get__invoke_raw(
    +    env: soroban_sdk::Env,
    +    arg_0: soroban_sdk::Val,
    +    arg_1: soroban_sdk::Val,
    +) -> soroban_sdk::Val {
    +    soroban_sdk::IntoValForContractFn::into_val_for_contract_fn(
    +        <Contract>::fr_vec_get(
    +            env.clone(),
    +            <_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
    +                <_ as soroban_sdk::TryFromValForContractFn<
    +                    soroban_sdk::Env,
    +                    soroban_sdk::Val,
    +                >>::try_from_val_for_contract_fn(&env, &arg_0),
    +            ),
    +            <_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
    +                <_ as soroban_sdk::TryFromValForContractFn<
    +                    soroban_sdk::Env,
    +                    soroban_sdk::Val,
    +                >>::try_from_val_for_contract_fn(&env, &arg_1),
    +            ),
    +        ),
    +        &env,
    +    )
    +}
    +#[doc(hidden)]
    +#[allow(non_snake_case)]
    +#[deprecated(note = "use `ContractClient::new(&env, &contract_id).fr_vec_get` instead")]
    +pub fn __Contract__fr_vec_get__invoke_raw_slice(
    +    env: soroban_sdk::Env,
    +    args: &[soroban_sdk::Val],
    +) -> soroban_sdk::Val {
    +    if args.len() != 2usize {
    +        {
    +            ::core::panicking::panic_fmt(format_args!(
    +                "invalid number of input arguments: {0} expected, got {1}",
    +                2usize,
    +                args.len(),
    +            ));
    +        };
    +    }
    +    #[allow(deprecated)]
    +    __Contract__fr_vec_get__invoke_raw(env, args[0usize], args[1usize])
    +}
    +#[doc(hidden)]
    +#[allow(non_snake_case)]
    +#[deprecated(note = "use `ContractClient::new(&env, &contract_id).fr_vec_get` instead")]
    +pub extern "C" fn __Contract__fr_vec_get__invoke_raw_extern(
    +    arg_0: soroban_sdk::Val,
    +    arg_1: soroban_sdk::Val,
    +) -> soroban_sdk::Val {
    +    #[allow(deprecated)]
    +    __Contract__fr_vec_get__invoke_raw(soroban_sdk::Env::default(), arg_0, arg_1)
    +}
    +#[doc(hidden)]
    +#[allow(non_snake_case)]
     #[allow(unused)]
    -fn __Contract____492d85ed5a5d2cb14995f41e785b3df9c7fcf4af92b6d50ce2c9fa7c9b1e350d_ctor() {
    +fn __Contract____9d5f96fce19df1a5d4c4527aba1995b76016454af03e47b778c4fa6ece8e5b9c_ctor() {
         #[allow(unsafe_code)]
         {
             #[link_section = ".init_array"]
    @@ -1213,7 +1367,7 @@ fn __Contract____492d85ed5a5d2cb14995f41e785b3df9c7fcf4af92b6d50ce2c9fa7c9b1e350
                 #[allow(non_snake_case)]
                 extern "C" fn f() -> ::ctor::__support::CtorRetType {
                     unsafe {
    -                    __Contract____492d85ed5a5d2cb14995f41e785b3df9c7fcf4af92b6d50ce2c9fa7c9b1e350d_ctor();
    +                    __Contract____9d5f96fce19df1a5d4c4527aba1995b76016454af03e47b778c4fa6ece8e5b9c_ctor();
                     };
                     core::default::Default::default()
                 }
    @@ -1236,13 +1390,18 @@ fn __Contract____492d85ed5a5d2cb14995f41e785b3df9c7fcf4af92b6d50ce2c9fa7c9b1e350
                 #[allow(deprecated)]
                 &__Contract__dummy_verify__invoke_raw_slice,
             );
    +        <Contract as soroban_sdk::testutils::ContractFunctionRegister>::register(
    +            "fr_vec_get",
    +            #[allow(deprecated)]
    +            &__Contract__fr_vec_get__invoke_raw_slice,
    +        );
         }
     }
     #[cfg(test)]
     mod test {
         use super::*;
         use crate::{Contract, ContractClient};
    -    use soroban_sdk::{bytesn, Env};
    +    use soroban_sdk::{bytesn, vec, Env, IntoVal, Symbol, Val, U256};
         extern crate test;
         #[cfg(test)]
         #[rustc_test_marker = "test::test_g1_mul"]
    @@ -1253,9 +1412,9 @@ mod test {
                 ignore: false,
                 ignore_message: ::core::option::Option::None,
                 source_file: "tests/bls/src/lib.rs",
    -            start_line: 56usize,
    +            start_line: 60usize,
                 start_col: 8usize,
    -            end_line: 56usize,
    +            end_line: 60usize,
                 end_col: 19usize,
                 compile_fail: false,
                 no_run: false,
    @@ -1327,9 +1486,9 @@ mod test {
                 ignore: false,
                 ignore_message: ::core::option::Option::None,
                 source_file: "tests/bls/src/lib.rs",
    -            start_line: 73usize,
    +            start_line: 77usize,
                 start_col: 8usize,
    -            end_line: 73usize,
    +            end_line: 77usize,
                 end_col: 19usize,
                 compile_fail: false,
                 no_run: false,
    @@ -1415,9 +1574,9 @@ mod test {
                 ignore: false,
                 ignore_message: ::core::option::Option::None,
                 source_file: "tests/bls/src/lib.rs",
    -            start_line: 90usize,
    +            start_line: 94usize,
                 start_col: 8usize,
    -            end_line: 90usize,
    +            end_line: 94usize,
                 end_col: 25usize,
                 compile_fail: false,
                 no_run: false,
    @@ -1505,11 +1664,111 @@ mod test {
                 ::core::panicking::panic("assertion failed: !res")
             }
         }
    +    extern crate test;
    +    #[cfg(test)]
    +    #[rustc_test_marker = "test::test_fr_decode_reduces_unreduced_scalar_and_vec_elements"]
    +    #[doc(hidden)]
    +    pub const test_fr_decode_reduces_unreduced_scalar_and_vec_elements: test::TestDescAndFn =
    +        test::TestDescAndFn {
    +            desc: test::TestDesc {
    +                name: test::StaticTestName(
    +                    "test::test_fr_decode_reduces_unreduced_scalar_and_vec_elements",
    +                ),
    +                ignore: false,
    +                ignore_message: ::core::option::Option::None,
    +                source_file: "tests/bls/src/lib.rs",
    +                start_line: 123usize,
    +                start_col: 8usize,
    +                end_line: 123usize,
    +                end_col: 64usize,
    +                compile_fail: false,
    +                no_run: false,
    +                should_panic: test::ShouldPanic::No,
    +                test_type: test::TestType::UnitTest,
    +            },
    +            testfn: test::StaticTestFn(
    +                #[coverage(off)]
    +                || {
    +                    test::assert_test_result(
    +                        test_fr_decode_reduces_unreduced_scalar_and_vec_elements(),
    +                    )
    +                },
    +            ),
    +        };
    +    fn test_fr_decode_reduces_unreduced_scalar_and_vec_elements() {
    +        let env = Env::default();
    +        let contract_id = env.register(Contract, ());
    +        let modulus = U256::from_be_bytes(
    +            &env,
    +            &::soroban_sdk::BytesN::from_array(
    +                &env,
    +                &[
    +                    115u8, 237u8, 167u8, 83u8, 41u8, 157u8, 125u8, 72u8, 51u8, 57u8, 216u8, 8u8,
    +                    9u8, 161u8, 216u8, 5u8, 83u8, 189u8, 164u8, 2u8, 255u8, 254u8, 91u8, 254u8,
    +                    255u8, 255u8, 255u8, 255u8, 0u8, 0u8, 0u8, 1u8,
    +                ],
    +            )
    +            .into(),
    +        );
    +        let two = U256::from_u32(&env, 2);
    +        let nine = U256::from_u32(&env, 9);
    +        let raw_vals: Vec<Val> = ::soroban_sdk::Vec::from_array(
    +            &env,
    +            [
    +                modulus.add(&two).into_val(&env),
    +                modulus.add(&nine).into_val(&env),
    +            ],
    +        );
    +        let first: Fr = env.invoke_contract(
    +            &contract_id,
    +            &Symbol::new(&env, "fr_vec_get"),
    +            ::soroban_sdk::Vec::from_array(
    +                &env,
    +                [raw_vals.clone().into_val(&env), 0_u32.into_val(&env)],
    +            ),
    +        );
    +        let second: Fr = env.invoke_contract(
    +            &contract_id,
    +            &Symbol::new(&env, "fr_vec_get"),
    +            ::soroban_sdk::Vec::from_array(&env, [raw_vals.into_val(&env), 1_u32.into_val(&env)]),
    +        );
    +        match (&first, &Fr::from_u256(two)) {
    +            (left_val, right_val) => {
    +                if !(*left_val == *right_val) {
    +                    let kind = ::core::panicking::AssertKind::Eq;
    +                    ::core::panicking::assert_failed(
    +                        kind,
    +                        &*left_val,
    +                        &*right_val,
    +                        ::core::option::Option::None,
    +                    );
    +                }
    +            }
    +        };
    +        match (&second, &Fr::from_u256(nine)) {
    +            (left_val, right_val) => {
    +                if !(*left_val == *right_val) {
    +                    let kind = ::core::panicking::AssertKind::Eq;
    +                    ::core::panicking::assert_failed(
    +                        kind,
    +                        &*left_val,
    +                        &*right_val,
    +                        ::core::option::Option::None,
    +                    );
    +                }
    +            }
    +        };
    +    }
     }
     #[rustc_main]
     #[coverage(off)]
     #[doc(hidden)]
     pub fn main() -> () {
         extern crate test;
    -    test::test_main_static(&[&test_dummy_verify, &test_g1_mul, &test_g2_mul])
    +    test::test_main_static(&[
    +        &test_dummy_verify,
    +        &test_fr_decode_reduces_unreduced_scalar_and_vec_elements,
    +        &test_g1_mul,
    +        &test_g2_mul,
    +    ])
     }
    
  • tests-expanded/test_bls_wasm32v1-none.rs+95 1 modified
    @@ -8,7 +8,7 @@ extern crate compiler_builtins as _;
     use soroban_sdk::{
         contract, contractimpl, contracttype,
         crypto::bls12_381::{Bls12381Fp, Bls12381Fp2, Bls12381G1Affine, Bls12381G2Affine, Fr},
    -    log, Env,
    +    log, Env, Vec,
     };
     pub struct DummyProof {
         pub fp: Bls12381Fp,
    @@ -148,6 +148,9 @@ impl Contract {
             let vp2 = soroban_sdk::Vec::from_array(&env, [g2_mul]);
             env.crypto().bls12_381().pairing_check(vp1, vp2)
         }
    +    pub fn fr_vec_get(_env: Env, values: Vec<Fr>, index: u32) -> Fr {
    +        values.get(index).unwrap()
    +    }
     }
     #[doc(hidden)]
     #[allow(non_snake_case)]
    @@ -194,6 +197,21 @@ impl Contract {
             *b"\0\0\0\0\0\0\0\0\0\0\0\x0cdummy_verify\0\0\0\x01\0\0\0\0\0\0\0\x05proof\0\0\0\0\0\x07\xd0\0\0\0\nDummyProof\0\0\0\0\0\x01\0\0\0\x01"
         }
     }
    +#[doc(hidden)]
    +#[allow(non_snake_case)]
    +pub mod __Contract__fr_vec_get__spec {
    +    #[doc(hidden)]
    +    #[allow(non_snake_case)]
    +    #[allow(non_upper_case_globals)]
    +    #[link_section = "contractspecv0"]
    +    pub static __SPEC_XDR_FN_FR_VEC_GET: [u8; 80usize] = super::Contract::spec_xdr_fr_vec_get();
    +}
    +impl Contract {
    +    #[allow(non_snake_case)]
    +    pub const fn spec_xdr_fr_vec_get() -> [u8; 80usize] {
    +        *b"\0\0\0\0\0\0\0\0\0\0\0\nfr_vec_get\0\0\0\0\0\x02\0\0\0\0\0\0\0\x06values\0\0\0\0\x03\xea\0\0\0\x0c\0\0\0\0\0\0\0\x05index\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x0c"
    +    }
    +}
     impl<'a> ContractClient<'a> {
         pub fn g1_mul(&self, p: &Bls12381G1Affine, s: &Fr) -> Bls12381G1Affine {
             use core::ops::Not;
    @@ -312,6 +330,38 @@ impl<'a> ContractClient<'a> {
             );
             res
         }
    +    pub fn fr_vec_get(&self, values: &Vec<Fr>, index: &u32) -> Fr {
    +        use core::ops::Not;
    +        use soroban_sdk::{FromVal, IntoVal};
    +        let res = self.env.invoke_contract(
    +            &self.address,
    +            &{ soroban_sdk::Symbol::new(&self.env, "fr_vec_get") },
    +            ::soroban_sdk::Vec::from_array(
    +                &self.env,
    +                [values.into_val(&self.env), index.into_val(&self.env)],
    +            ),
    +        );
    +        res
    +    }
    +    pub fn try_fr_vec_get(
    +        &self,
    +        values: &Vec<Fr>,
    +        index: &u32,
    +    ) -> Result<
    +        Result<Fr, <Fr as soroban_sdk::TryFromVal<soroban_sdk::Env, soroban_sdk::Val>>::Error>,
    +        Result<soroban_sdk::Error, soroban_sdk::InvokeError>,
    +    > {
    +        use soroban_sdk::{FromVal, IntoVal};
    +        let res = self.env.try_invoke_contract(
    +            &self.address,
    +            &{ soroban_sdk::Symbol::new(&self.env, "fr_vec_get") },
    +            ::soroban_sdk::Vec::from_array(
    +                &self.env,
    +                [values.into_val(&self.env), index.into_val(&self.env)],
    +            ),
    +        );
    +        res
    +    }
     }
     impl ContractArgs {
         #[inline(always)]
    @@ -329,6 +379,11 @@ impl ContractArgs {
         pub fn dummy_verify<'i>(proof: &'i DummyProof) -> (&'i DummyProof,) {
             (proof,)
         }
    +    #[inline(always)]
    +    #[allow(clippy::unused_unit)]
    +    pub fn fr_vec_get<'i>(values: &'i Vec<Fr>, index: &'i u32) -> (&'i Vec<Fr>, &'i u32) {
    +        (values, index)
    +    }
     }
     #[doc(hidden)]
     #[allow(non_snake_case)]
    @@ -439,3 +494,42 @@ pub extern "C" fn __Contract__dummy_verify__invoke_raw_extern(
         #[allow(deprecated)]
         __Contract__dummy_verify__invoke_raw(soroban_sdk::Env::default(), arg_0)
     }
    +#[doc(hidden)]
    +#[allow(non_snake_case)]
    +#[deprecated(note = "use `ContractClient::new(&env, &contract_id).fr_vec_get` instead")]
    +#[allow(deprecated)]
    +pub fn __Contract__fr_vec_get__invoke_raw(
    +    env: soroban_sdk::Env,
    +    arg_0: soroban_sdk::Val,
    +    arg_1: soroban_sdk::Val,
    +) -> soroban_sdk::Val {
    +    soroban_sdk::IntoValForContractFn::into_val_for_contract_fn(
    +        <Contract>::fr_vec_get(
    +            env.clone(),
    +            <_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
    +                <_ as soroban_sdk::TryFromValForContractFn<
    +                    soroban_sdk::Env,
    +                    soroban_sdk::Val,
    +                >>::try_from_val_for_contract_fn(&env, &arg_0),
    +            ),
    +            <_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
    +                <_ as soroban_sdk::TryFromValForContractFn<
    +                    soroban_sdk::Env,
    +                    soroban_sdk::Val,
    +                >>::try_from_val_for_contract_fn(&env, &arg_1),
    +            ),
    +        ),
    +        &env,
    +    )
    +}
    +#[doc(hidden)]
    +#[allow(non_snake_case)]
    +#[deprecated(note = "use `ContractClient::new(&env, &contract_id).fr_vec_get` instead")]
    +#[export_name = "fr_vec_get"]
    +pub extern "C" fn __Contract__fr_vec_get__invoke_raw_extern(
    +    arg_0: soroban_sdk::Val,
    +    arg_1: soroban_sdk::Val,
    +) -> soroban_sdk::Val {
    +    #[allow(deprecated)]
    +    __Contract__fr_vec_get__invoke_raw(soroban_sdk::Env::default(), arg_0, arg_1)
    +}
    
  • tests-expanded/test_bn254_tests.rs+269 10 modified
    @@ -542,6 +542,9 @@ impl Contract {
         pub fn g1_mul(p: Bn254G1Affine, s: Fr) -> Bn254G1Affine {
             p * s
         }
    +    pub fn fr_vec_get(_env: Env, values: Vec<Fr>, index: u32) -> Fr {
    +        values.get(index).unwrap()
    +    }
     }
     #[doc(hidden)]
     #[allow(non_snake_case)]
    @@ -586,6 +589,20 @@ impl Contract {
             *b"\0\0\0\0\0\0\0\0\0\0\0\x06g1_mul\0\0\0\0\0\x02\0\0\0\0\0\0\0\x01p\0\0\0\0\0\x03\xee\0\0\0@\0\0\0\0\0\0\0\x01s\0\0\0\0\0\0\x0c\0\0\0\x01\0\0\x03\xee\0\0\0@"
         }
     }
    +#[doc(hidden)]
    +#[allow(non_snake_case)]
    +pub mod __Contract__fr_vec_get__spec {
    +    #[doc(hidden)]
    +    #[allow(non_snake_case)]
    +    #[allow(non_upper_case_globals)]
    +    pub static __SPEC_XDR_FN_FR_VEC_GET: [u8; 80usize] = super::Contract::spec_xdr_fr_vec_get();
    +}
    +impl Contract {
    +    #[allow(non_snake_case)]
    +    pub const fn spec_xdr_fr_vec_get() -> [u8; 80usize] {
    +        *b"\0\0\0\0\0\0\0\0\0\0\0\nfr_vec_get\0\0\0\0\0\x02\0\0\0\0\0\0\0\x06values\0\0\0\0\x03\xea\0\0\0\x0c\0\0\0\0\0\0\0\x05index\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x0c"
    +    }
    +}
     impl<'a> ContractClient<'a> {
         pub fn verify_pairing(&self, proof: &MockProof) -> bool {
             use core::ops::Not;
    @@ -827,6 +844,81 @@ impl<'a> ContractClient<'a> {
             }
             res
         }
    +    pub fn fr_vec_get(&self, values: &Vec<Fr>, index: &u32) -> Fr {
    +        use core::ops::Not;
    +        let old_auth_manager = self
    +            .env
    +            .in_contract()
    +            .not()
    +            .then(|| self.env.host().snapshot_auth_manager().unwrap());
    +        {
    +            if let Some(set_auths) = self.set_auths {
    +                self.env.set_auths(set_auths);
    +            }
    +            if let Some(mock_auths) = self.mock_auths {
    +                self.env.mock_auths(mock_auths);
    +            }
    +            if self.mock_all_auths {
    +                if self.allow_non_root_auth {
    +                    self.env.mock_all_auths_allowing_non_root_auth();
    +                } else {
    +                    self.env.mock_all_auths();
    +                }
    +            }
    +        }
    +        use soroban_sdk::{FromVal, IntoVal};
    +        let res = self.env.invoke_contract(
    +            &self.address,
    +            &{ soroban_sdk::Symbol::new(&self.env, "fr_vec_get") },
    +            ::soroban_sdk::Vec::from_array(
    +                &self.env,
    +                [values.into_val(&self.env), index.into_val(&self.env)],
    +            ),
    +        );
    +        if let Some(old_auth_manager) = old_auth_manager {
    +            self.env.host().set_auth_manager(old_auth_manager).unwrap();
    +        }
    +        res
    +    }
    +    pub fn try_fr_vec_get(
    +        &self,
    +        values: &Vec<Fr>,
    +        index: &u32,
    +    ) -> Result<
    +        Result<Fr, <Fr as soroban_sdk::TryFromVal<soroban_sdk::Env, soroban_sdk::Val>>::Error>,
    +        Result<soroban_sdk::Error, soroban_sdk::InvokeError>,
    +    > {
    +        use core::ops::Not;
    +        let old_auth_manager = self
    +            .env
    +            .in_contract()
    +            .not()
    +            .then(|| self.env.host().snapshot_auth_manager().unwrap());
    +        {
    +            if let Some(set_auths) = self.set_auths {
    +                self.env.set_auths(set_auths);
    +            }
    +            if let Some(mock_auths) = self.mock_auths {
    +                self.env.mock_auths(mock_auths);
    +            }
    +            if self.mock_all_auths {
    +                self.env.mock_all_auths();
    +            }
    +        }
    +        use soroban_sdk::{FromVal, IntoVal};
    +        let res = self.env.try_invoke_contract(
    +            &self.address,
    +            &{ soroban_sdk::Symbol::new(&self.env, "fr_vec_get") },
    +            ::soroban_sdk::Vec::from_array(
    +                &self.env,
    +                [values.into_val(&self.env), index.into_val(&self.env)],
    +            ),
    +        );
    +        if let Some(old_auth_manager) = old_auth_manager {
    +            self.env.host().set_auth_manager(old_auth_manager).unwrap();
    +        }
    +        res
    +    }
     }
     impl ContractArgs {
         #[inline(always)]
    @@ -847,6 +939,11 @@ impl ContractArgs {
         pub fn g1_mul<'i>(p: &'i Bn254G1Affine, s: &'i Fr) -> (&'i Bn254G1Affine, &'i Fr) {
             (p, s)
         }
    +    #[inline(always)]
    +    #[allow(clippy::unused_unit)]
    +    pub fn fr_vec_get<'i>(values: &'i Vec<Fr>, index: &'i u32) -> (&'i Vec<Fr>, &'i u32) {
    +        (values, index)
    +    }
     }
     #[doc(hidden)]
     #[allow(non_snake_case)]
    @@ -1011,8 +1108,65 @@ pub extern "C" fn __Contract__g1_mul__invoke_raw_extern(
     }
     #[doc(hidden)]
     #[allow(non_snake_case)]
    +#[deprecated(note = "use `ContractClient::new(&env, &contract_id).fr_vec_get` instead")]
    +#[allow(deprecated)]
    +pub fn __Contract__fr_vec_get__invoke_raw(
    +    env: soroban_sdk::Env,
    +    arg_0: soroban_sdk::Val,
    +    arg_1: soroban_sdk::Val,
    +) -> soroban_sdk::Val {
    +    soroban_sdk::IntoValForContractFn::into_val_for_contract_fn(
    +        <Contract>::fr_vec_get(
    +            env.clone(),
    +            <_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
    +                <_ as soroban_sdk::TryFromValForContractFn<
    +                    soroban_sdk::Env,
    +                    soroban_sdk::Val,
    +                >>::try_from_val_for_contract_fn(&env, &arg_0),
    +            ),
    +            <_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
    +                <_ as soroban_sdk::TryFromValForContractFn<
    +                    soroban_sdk::Env,
    +                    soroban_sdk::Val,
    +                >>::try_from_val_for_contract_fn(&env, &arg_1),
    +            ),
    +        ),
    +        &env,
    +    )
    +}
    +#[doc(hidden)]
    +#[allow(non_snake_case)]
    +#[deprecated(note = "use `ContractClient::new(&env, &contract_id).fr_vec_get` instead")]
    +pub fn __Contract__fr_vec_get__invoke_raw_slice(
    +    env: soroban_sdk::Env,
    +    args: &[soroban_sdk::Val],
    +) -> soroban_sdk::Val {
    +    if args.len() != 2usize {
    +        {
    +            ::core::panicking::panic_fmt(format_args!(
    +                "invalid number of input arguments: {0} expected, got {1}",
    +                2usize,
    +                args.len(),
    +            ));
    +        };
    +    }
    +    #[allow(deprecated)]
    +    __Contract__fr_vec_get__invoke_raw(env, args[0usize], args[1usize])
    +}
    +#[doc(hidden)]
    +#[allow(non_snake_case)]
    +#[deprecated(note = "use `ContractClient::new(&env, &contract_id).fr_vec_get` instead")]
    +pub extern "C" fn __Contract__fr_vec_get__invoke_raw_extern(
    +    arg_0: soroban_sdk::Val,
    +    arg_1: soroban_sdk::Val,
    +) -> soroban_sdk::Val {
    +    #[allow(deprecated)]
    +    __Contract__fr_vec_get__invoke_raw(soroban_sdk::Env::default(), arg_0, arg_1)
    +}
    +#[doc(hidden)]
    +#[allow(non_snake_case)]
     #[allow(unused)]
    -fn __Contract____0bbda83869b96862f040597ce6f7f1153fcf92be3a6565cd371a4485bb1a9c8d_ctor() {
    +fn __Contract____791f30cbe20fdd8dd82fcdc2b6465e800581173658019a4bee49e2305925f4e0_ctor() {
         #[allow(unsafe_code)]
         {
             #[link_section = ".init_array"]
    @@ -1024,7 +1178,7 @@ fn __Contract____0bbda83869b96862f040597ce6f7f1153fcf92be3a6565cd371a4485bb1a9c8
                 #[allow(non_snake_case)]
                 extern "C" fn f() -> ::ctor::__support::CtorRetType {
                     unsafe {
    -                    __Contract____0bbda83869b96862f040597ce6f7f1153fcf92be3a6565cd371a4485bb1a9c8d_ctor();
    +                    __Contract____791f30cbe20fdd8dd82fcdc2b6465e800581173658019a4bee49e2305925f4e0_ctor();
                     };
                     core::default::Default::default()
                 }
    @@ -1047,12 +1201,17 @@ fn __Contract____0bbda83869b96862f040597ce6f7f1153fcf92be3a6565cd371a4485bb1a9c8
                 #[allow(deprecated)]
                 &__Contract__g1_mul__invoke_raw_slice,
             );
    +        <Contract as soroban_sdk::testutils::ContractFunctionRegister>::register(
    +            "fr_vec_get",
    +            #[allow(deprecated)]
    +            &__Contract__fr_vec_get__invoke_raw_slice,
    +        );
         }
     }
     #[cfg(test)]
     mod test {
         use super::*;
    -    use soroban_sdk::{vec, Env, U256};
    +    use soroban_sdk::{bytesn, vec, Env, IntoVal, Symbol, Val, U256};
         extern crate std;
         use crate::{Contract, ContractClient};
         fn parse_ethereum_g1_add_input(input: &str) -> ([u8; 64], [u8; 64]) {
    @@ -1111,9 +1270,9 @@ mod test {
                 ignore: false,
                 ignore_message: ::core::option::Option::None,
                 source_file: "tests/bn254/src/lib.rs",
    -            start_line: 70usize,
    +            start_line: 74usize,
                 start_col: 8usize,
    -            end_line: 70usize,
    +            end_line: 74usize,
                 end_col: 24usize,
                 compile_fail: false,
                 no_run: false,
    @@ -1181,9 +1340,9 @@ mod test {
                 ignore: false,
                 ignore_message: ::core::option::Option::None,
                 source_file: "tests/bn254/src/lib.rs",
    -            start_line: 98usize,
    +            start_line: 102usize,
                 start_col: 8usize,
    -            end_line: 98usize,
    +            end_line: 102usize,
                 end_col: 20usize,
                 compile_fail: false,
                 no_run: false,
    @@ -1233,9 +1392,9 @@ mod test {
                 ignore: false,
                 ignore_message: ::core::option::Option::None,
                 source_file: "tests/bn254/src/lib.rs",
    -            start_line: 131usize,
    +            start_line: 135usize,
                 start_col: 8usize,
    -            end_line: 131usize,
    +            end_line: 135usize,
                 end_col: 24usize,
                 compile_fail: false,
                 no_run: false,
    @@ -1282,11 +1441,111 @@ mod test {
                 }
             };
         }
    +    extern crate test;
    +    #[cfg(test)]
    +    #[rustc_test_marker = "test::test_fr_decode_reduces_unreduced_scalar_and_vec_elements"]
    +    #[doc(hidden)]
    +    pub const test_fr_decode_reduces_unreduced_scalar_and_vec_elements: test::TestDescAndFn =
    +        test::TestDescAndFn {
    +            desc: test::TestDesc {
    +                name: test::StaticTestName(
    +                    "test::test_fr_decode_reduces_unreduced_scalar_and_vec_elements",
    +                ),
    +                ignore: false,
    +                ignore_message: ::core::option::Option::None,
    +                source_file: "tests/bn254/src/lib.rs",
    +                start_line: 152usize,
    +                start_col: 8usize,
    +                end_line: 152usize,
    +                end_col: 64usize,
    +                compile_fail: false,
    +                no_run: false,
    +                should_panic: test::ShouldPanic::No,
    +                test_type: test::TestType::UnitTest,
    +            },
    +            testfn: test::StaticTestFn(
    +                #[coverage(off)]
    +                || {
    +                    test::assert_test_result(
    +                        test_fr_decode_reduces_unreduced_scalar_and_vec_elements(),
    +                    )
    +                },
    +            ),
    +        };
    +    fn test_fr_decode_reduces_unreduced_scalar_and_vec_elements() {
    +        let env = Env::default();
    +        let contract_id = env.register(Contract, ());
    +        let modulus = U256::from_be_bytes(
    +            &env,
    +            &::soroban_sdk::BytesN::from_array(
    +                &env,
    +                &[
    +                    48u8, 100u8, 78u8, 114u8, 225u8, 49u8, 160u8, 41u8, 184u8, 80u8, 69u8, 182u8,
    +                    129u8, 129u8, 88u8, 93u8, 40u8, 51u8, 232u8, 72u8, 121u8, 185u8, 112u8, 145u8,
    +                    67u8, 225u8, 245u8, 147u8, 240u8, 0u8, 0u8, 1u8,
    +                ],
    +            )
    +            .into(),
    +        );
    +        let three = U256::from_u32(&env, 3);
    +        let seven = U256::from_u32(&env, 7);
    +        let raw_vals: Vec<Val> = ::soroban_sdk::Vec::from_array(
    +            &env,
    +            [
    +                modulus.add(&three).into_val(&env),
    +                modulus.add(&seven).into_val(&env),
    +            ],
    +        );
    +        let first: Fr = env.invoke_contract(
    +            &contract_id,
    +            &Symbol::new(&env, "fr_vec_get"),
    +            ::soroban_sdk::Vec::from_array(
    +                &env,
    +                [raw_vals.clone().into_val(&env), 0_u32.into_val(&env)],
    +            ),
    +        );
    +        let second: Fr = env.invoke_contract(
    +            &contract_id,
    +            &Symbol::new(&env, "fr_vec_get"),
    +            ::soroban_sdk::Vec::from_array(&env, [raw_vals.into_val(&env), 1_u32.into_val(&env)]),
    +        );
    +        match (&first, &Fr::from_u256(three)) {
    +            (left_val, right_val) => {
    +                if !(*left_val == *right_val) {
    +                    let kind = ::core::panicking::AssertKind::Eq;
    +                    ::core::panicking::assert_failed(
    +                        kind,
    +                        &*left_val,
    +                        &*right_val,
    +                        ::core::option::Option::None,
    +                    );
    +                }
    +            }
    +        };
    +        match (&second, &Fr::from_u256(seven)) {
    +            (left_val, right_val) => {
    +                if !(*left_val == *right_val) {
    +                    let kind = ::core::panicking::AssertKind::Eq;
    +                    ::core::panicking::assert_failed(
    +                        kind,
    +                        &*left_val,
    +                        &*right_val,
    +                        ::core::option::Option::None,
    +                    );
    +                }
    +            }
    +        };
    +    }
     }
     #[rustc_main]
     #[coverage(off)]
     #[doc(hidden)]
     pub fn main() -> () {
         extern crate test;
    -    test::test_main_static(&[&test_add_and_mul, &test_g1_negation, &test_pairing])
    +    test::test_main_static(&[
    +        &test_add_and_mul,
    +        &test_fr_decode_reduces_unreduced_scalar_and_vec_elements,
    +        &test_g1_negation,
    +        &test_pairing,
    +    ])
     }
    
  • tests-expanded/test_bn254_wasm32v1-none.rs+94 0 modified
    @@ -110,6 +110,9 @@ impl Contract {
         pub fn g1_mul(p: Bn254G1Affine, s: Fr) -> Bn254G1Affine {
             p * s
         }
    +    pub fn fr_vec_get(_env: Env, values: Vec<Fr>, index: u32) -> Fr {
    +        values.get(index).unwrap()
    +    }
     }
     #[doc(hidden)]
     #[allow(non_snake_case)]
    @@ -157,6 +160,21 @@ impl Contract {
             *b"\0\0\0\0\0\0\0\0\0\0\0\x06g1_mul\0\0\0\0\0\x02\0\0\0\0\0\0\0\x01p\0\0\0\0\0\x03\xee\0\0\0@\0\0\0\0\0\0\0\x01s\0\0\0\0\0\0\x0c\0\0\0\x01\0\0\x03\xee\0\0\0@"
         }
     }
    +#[doc(hidden)]
    +#[allow(non_snake_case)]
    +pub mod __Contract__fr_vec_get__spec {
    +    #[doc(hidden)]
    +    #[allow(non_snake_case)]
    +    #[allow(non_upper_case_globals)]
    +    #[link_section = "contractspecv0"]
    +    pub static __SPEC_XDR_FN_FR_VEC_GET: [u8; 80usize] = super::Contract::spec_xdr_fr_vec_get();
    +}
    +impl Contract {
    +    #[allow(non_snake_case)]
    +    pub const fn spec_xdr_fr_vec_get() -> [u8; 80usize] {
    +        *b"\0\0\0\0\0\0\0\0\0\0\0\nfr_vec_get\0\0\0\0\0\x02\0\0\0\0\0\0\0\x06values\0\0\0\0\x03\xea\0\0\0\x0c\0\0\0\0\0\0\0\x05index\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x0c"
    +    }
    +}
     impl<'a> ContractClient<'a> {
         pub fn verify_pairing(&self, proof: &MockProof) -> bool {
             use core::ops::Not;
    @@ -269,6 +287,38 @@ impl<'a> ContractClient<'a> {
             );
             res
         }
    +    pub fn fr_vec_get(&self, values: &Vec<Fr>, index: &u32) -> Fr {
    +        use core::ops::Not;
    +        use soroban_sdk::{FromVal, IntoVal};
    +        let res = self.env.invoke_contract(
    +            &self.address,
    +            &{ soroban_sdk::Symbol::new(&self.env, "fr_vec_get") },
    +            ::soroban_sdk::Vec::from_array(
    +                &self.env,
    +                [values.into_val(&self.env), index.into_val(&self.env)],
    +            ),
    +        );
    +        res
    +    }
    +    pub fn try_fr_vec_get(
    +        &self,
    +        values: &Vec<Fr>,
    +        index: &u32,
    +    ) -> Result<
    +        Result<Fr, <Fr as soroban_sdk::TryFromVal<soroban_sdk::Env, soroban_sdk::Val>>::Error>,
    +        Result<soroban_sdk::Error, soroban_sdk::InvokeError>,
    +    > {
    +        use soroban_sdk::{FromVal, IntoVal};
    +        let res = self.env.try_invoke_contract(
    +            &self.address,
    +            &{ soroban_sdk::Symbol::new(&self.env, "fr_vec_get") },
    +            ::soroban_sdk::Vec::from_array(
    +                &self.env,
    +                [values.into_val(&self.env), index.into_val(&self.env)],
    +            ),
    +        );
    +        res
    +    }
     }
     impl ContractArgs {
         #[inline(always)]
    @@ -289,6 +339,11 @@ impl ContractArgs {
         pub fn g1_mul<'i>(p: &'i Bn254G1Affine, s: &'i Fr) -> (&'i Bn254G1Affine, &'i Fr) {
             (p, s)
         }
    +    #[inline(always)]
    +    #[allow(clippy::unused_unit)]
    +    pub fn fr_vec_get<'i>(values: &'i Vec<Fr>, index: &'i u32) -> (&'i Vec<Fr>, &'i u32) {
    +        (values, index)
    +    }
     }
     #[doc(hidden)]
     #[allow(non_snake_case)]
    @@ -397,3 +452,42 @@ pub extern "C" fn __Contract__g1_mul__invoke_raw_extern(
         #[allow(deprecated)]
         __Contract__g1_mul__invoke_raw(soroban_sdk::Env::default(), arg_0, arg_1)
     }
    +#[doc(hidden)]
    +#[allow(non_snake_case)]
    +#[deprecated(note = "use `ContractClient::new(&env, &contract_id).fr_vec_get` instead")]
    +#[allow(deprecated)]
    +pub fn __Contract__fr_vec_get__invoke_raw(
    +    env: soroban_sdk::Env,
    +    arg_0: soroban_sdk::Val,
    +    arg_1: soroban_sdk::Val,
    +) -> soroban_sdk::Val {
    +    soroban_sdk::IntoValForContractFn::into_val_for_contract_fn(
    +        <Contract>::fr_vec_get(
    +            env.clone(),
    +            <_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
    +                <_ as soroban_sdk::TryFromValForContractFn<
    +                    soroban_sdk::Env,
    +                    soroban_sdk::Val,
    +                >>::try_from_val_for_contract_fn(&env, &arg_0),
    +            ),
    +            <_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
    +                <_ as soroban_sdk::TryFromValForContractFn<
    +                    soroban_sdk::Env,
    +                    soroban_sdk::Val,
    +                >>::try_from_val_for_contract_fn(&env, &arg_1),
    +            ),
    +        ),
    +        &env,
    +    )
    +}
    +#[doc(hidden)]
    +#[allow(non_snake_case)]
    +#[deprecated(note = "use `ContractClient::new(&env, &contract_id).fr_vec_get` instead")]
    +#[export_name = "fr_vec_get"]
    +pub extern "C" fn __Contract__fr_vec_get__invoke_raw_extern(
    +    arg_0: soroban_sdk::Val,
    +    arg_1: soroban_sdk::Val,
    +) -> soroban_sdk::Val {
    +    #[allow(deprecated)]
    +    __Contract__fr_vec_get__invoke_raw(soroban_sdk::Env::default(), arg_0, arg_1)
    +}
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.