soroban-sdk: `Fr` scalar field equality comparison bypasses modular reduction
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.
| Package | Affected versions | Patched versions |
|---|---|---|
soroban-sdkcrates.io | >= 25.0.0, < 25.3.0 | 25.3.0 |
soroban-sdkcrates.io | >= 23.0.0, < 23.5.3 | 23.5.3 |
soroban-sdkcrates.io | < 22.0.11 | 22.0.11 |
Affected products
1- Range: >= 25.0.0, < 25.3.0
Patches
1082424b30bf2Fix `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- github.com/advisories/GHSA-x2hw-px52-wp4mghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-32322ghsaADVISORY
- github.com/stellar/rs-soroban-sdk/commit/082424b30bf22ea7fb8c79f16ccd135e0ae9f3dbghsaWEB
- github.com/stellar/rs-soroban-sdk/pull/1750ghsaWEB
- github.com/stellar/rs-soroban-sdk/releases/tag/v22.0.11ghsaWEB
- github.com/stellar/rs-soroban-sdk/releases/tag/v23.5.3ghsaWEB
- github.com/stellar/rs-soroban-sdk/releases/tag/v25.3.0ghsaWEB
- github.com/stellar/rs-soroban-sdk/security/advisories/GHSA-x2hw-px52-wp4mghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.