diff --git a/soroban-sdk/src/_migrating.rs b/soroban-sdk/src/_migrating.rs index a298de57e..74f6c5b1b 100644 --- a/soroban-sdk/src/_migrating.rs +++ b/soroban-sdk/src/_migrating.rs @@ -8,7 +8,9 @@ //! 2. Add support for [CAP-82: Checked 256-bit integer arithmetic host functions](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0082.md). //! New `checked_*` methods on [`U256`] and [`I256`] (`checked_add`, `checked_sub`, //! `checked_mul`, `checked_div`, `checked_pow`, `checked_rem_euclid`, `checked_shl`, -//! `checked_shr`) return `Option` instead of panicking on overflow. +//! `checked_shr`) return `Option` instead of panicking on overflow. Also adds +//! `min_value` and `max_value` methods on [`U256`] and [`I256`] to fetch the +//! value bounds of each type. //! //! 3. Add support for [CAP-80: Host functions for efficient ZK BN254 use cases](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0080.md). //! [`BN254`] gains scalar field arithmetic (`Fr` `Add`/`Sub`/`Mul` traits, `pow`, `inv`), diff --git a/soroban-sdk/src/num.rs b/soroban-sdk/src/num.rs index fcdb39c00..e96b729af 100644 --- a/soroban-sdk/src/num.rs +++ b/soroban-sdk/src/num.rs @@ -205,6 +205,16 @@ impl_num_wrapping_val_type!(U256, U256Val, U256Small); impl U256 { pub const BITS: u32 = 256; + /// Returns the smallest value that can be represented by this type (0). + pub fn min_value(env: &Env) -> Self { + Self::from_u32(env, 0) + } + + /// Returns the largest value that can be represented by this type (2^256 - 1). + pub fn max_value(env: &Env) -> Self { + Self::from_parts(env, u64::MAX, u64::MAX, u64::MAX, u64::MAX) + } + fn is_zero(&self) -> bool { *self == U256::from_u32(&self.env, 0) } @@ -438,6 +448,16 @@ impl_num_wrapping_val_type!(I256, I256Val, I256Small); impl I256 { pub const BITS: u32 = 256; + /// Returns the smallest value that can be represented by this type (-2^255). + pub fn min_value(env: &Env) -> Self { + Self::from_parts(env, i64::MIN, 0, 0, 0) + } + + /// Returns the largest value that can be represented by this type (2^255 - 1). + pub fn max_value(env: &Env) -> Self { + Self::from_parts(env, i64::MAX, u64::MAX, u64::MAX, u64::MAX) + } + fn is_zero(&self) -> bool { *self == I256::from_i32(&self.env, 0) } @@ -446,11 +466,6 @@ impl I256 { *self == I256::from_i32(&self.env, -1) } - /// Returns the minimum value of I256 (-2^255). - pub fn min_value(env: &Env) -> Self { - I256::from_parts(env, i64::MIN, 0, 0, 0) - } - pub fn from_i32(env: &Env, i: i32) -> Self { I256 { env: env.clone(), @@ -847,6 +862,36 @@ mod test { assert_eq!(u3.rem_euclid(&u4), U256::from_u32(&env, 3)); } + #[test] + fn test_u256_min() { + let env = Env::default(); + + let min = U256::min_value(&env); + assert_eq!(min, U256::from_u32(&env, 0)); + + let one = U256::from_u32(&env, 1); + assert_eq!(min.checked_sub(&one), None); + assert!(min.checked_add(&one).is_some()); + } + + #[test] + fn test_u256_max() { + let env = Env::default(); + + let max = U256::max_value(&env); + assert_eq!( + max, + U256::from_parts(&env, u64::MAX, u64::MAX, u64::MAX, u64::MAX) + ); + + let u128_max = U256::from_u128(&env, u128::MAX); + assert!(max > u128_max); + + let one = U256::from_u32(&env, 1); + assert_eq!(max.checked_add(&one), None); + assert!(max.checked_sub(&one).is_some()); + } + #[test] fn test_u256_checked_arith() { let env = Env::default(); @@ -873,7 +918,7 @@ mod test { let zero = U256::from_u32(&env, 0); let one = U256::from_u32(&env, 1); let two = U256::from_u32(&env, 2); - let max = U256::from_parts(&env, u64::MAX, u64::MAX, u64::MAX, u64::MAX); + let max = U256::max_value(&env); assert_eq!(max.checked_add(&one), None); assert_eq!(zero.checked_sub(&one), None); assert_eq!(max.checked_mul(&two), None); @@ -921,6 +966,39 @@ mod test { assert_eq!(u3.rem_euclid(&u4), I256::from_i32(&env, 1)); } + #[test] + fn test_i256_min() { + let env = Env::default(); + + let min = I256::min_value(&env); + assert_eq!(min, I256::from_parts(&env, i64::MIN, 0, 0, 0)); + + let i128_min = I256::from_i128(&env, i128::MIN); + assert!(min < i128_min); + + let one = I256::from_i32(&env, 1); + assert_eq!(min.checked_sub(&one), None); + assert!(min.checked_add(&one).is_some()); + } + + #[test] + fn test_i256_max() { + let env = Env::default(); + + let max = I256::max_value(&env); + assert_eq!( + max, + I256::from_parts(&env, i64::MAX, u64::MAX, u64::MAX, u64::MAX) + ); + + let i128_max = I256::from_i128(&env, i128::MAX); + assert!(max > i128_max); + + let one = I256::from_i32(&env, 1); + assert_eq!(max.checked_add(&one), None); + assert!(max.checked_sub(&one).is_some()); + } + #[test] fn test_i256_checked_arith() { let env = Env::default(); @@ -948,8 +1026,8 @@ mod test { let one = I256::from_i32(&env, 1); let negative_one = I256::from_i32(&env, -1); let two = I256::from_i32(&env, 2); - let max = I256::from_parts(&env, i64::MAX, u64::MAX, u64::MAX, u64::MAX); - let min = I256::from_parts(&env, i64::MIN, 0, 0, 0); + let max = I256::max_value(&env); + let min = I256::min_value(&env); assert_eq!(max.checked_add(&one), None); assert_eq!(min.checked_sub(&one), None); assert_eq!(max.checked_mul(&two), None);