From 3c62eae9bcac1db75e45c45ba120400424c58c86 Mon Sep 17 00:00:00 2001 From: Ori Ziv Date: Mon, 30 Mar 2026 10:28:22 +0300 Subject: [PATCH] Added suppport for u128 guarantee in `bounded_int_guarantee_verify`. SIERRA_UPDATE_NO_CHANGE_TAG=Changes to non-audited libfunc. --- .../src/core_libfunc_cost_base.rs | 8 ++-- .../src/invocations/int/bounded.rs | 14 ++++--- .../src/extensions/modules/bounded_int.rs | 19 +++++---- tests/e2e_test_data/libfuncs/bounded_int | 39 +++++++++++++++++++ 4 files changed, 64 insertions(+), 16 deletions(-) diff --git a/crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs b/crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs index c4abe20f167..30878bd7798 100644 --- a/crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs +++ b/crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs @@ -555,10 +555,12 @@ pub fn core_libfunc_cost( vec![ConstCost::steps(0).into()] } BoundedIntConcreteLibfunc::GuaranteeVerify(libfunc) => { - let steps = 2 + let u128_bound = BigInt::one().shl(128); + let range_checks = if libfunc.range.size() == u128_bound { 1 } else { 2 }; + let steps = range_checks + if libfunc.range.lower.is_zero() { 0 } else { 1 } - + if &libfunc.range.upper - 1 == u128::MAX.into() { 0 } else { 1 }; - vec![ConstCost { steps, holes: 0, range_checks: 2, range_checks96: 0 }.into()] + + if libfunc.range.upper == u128_bound { 0 } else { 1 }; + vec![ConstCost { steps, holes: 0, range_checks, range_checks96: 0 }.into()] } BoundedIntConcreteLibfunc::U128ToU32Guarantees(_) => { vec![ConstCost::steps(7).into()] diff --git a/crates/cairo-lang-sierra-to-casm/src/invocations/int/bounded.rs b/crates/cairo-lang-sierra-to-casm/src/invocations/int/bounded.rs index b6bedd443a7..10c5656c1ae 100644 --- a/crates/cairo-lang-sierra-to-casm/src/invocations/int/bounded.rs +++ b/crates/cairo-lang-sierra-to-casm/src/invocations/int/bounded.rs @@ -9,10 +9,11 @@ use cairo_lang_sierra::extensions::felt252::Felt252BinaryOperator; use cairo_lang_sierra::extensions::gas::CostTokenType; use cairo_lang_sierra::extensions::utils::Range; use num_bigint::BigInt; -use num_traits::One; +use num_traits::{One, Zero}; use crate::invocations::casts::{validate_ge, validate_lt}; use crate::invocations::felt252::build_felt252_op_with_var; +use crate::invocations::int::u128_bound; use crate::invocations::misc::{build_identity, build_is_zero}; use crate::invocations::{ BuiltinInfo, CompiledInvocation, CompiledInvocationBuilder, CostValidationInfo, @@ -259,15 +260,18 @@ fn build_guarantee_verify( libfunc: &BoundedIntGuaranteeVerifyConcreteLibfunc, ) -> Result { let [range_check, value] = builder.try_get_single_cells()?; - - let mut casm_builder = CasmBuilder::with_capacity(4, 2); + let mut casm_builder = CasmBuilder::with_capacity(4, 0); add_input_variables! {casm_builder, buffer(2) range_check; deref value; }; casm_build_extend!(casm_builder, let orig_range_check = range_check;); - validate_ge(&mut casm_builder, range_check, value, &libfunc.range.lower); - validate_lt(&mut casm_builder, range_check, value, &libfunc.range.upper); + if libfunc.range.lower.is_zero() && &libfunc.range.upper == u128_bound() { + casm_build_extend!(casm_builder, assert value = *(range_check++);); + } else { + validate_ge(&mut casm_builder, range_check, value, &libfunc.range.lower); + validate_lt(&mut casm_builder, range_check, value, &libfunc.range.upper); + } Ok(builder.build_from_casm_builder( casm_builder, [("Fallthrough", &[&[range_check]], None)], diff --git a/crates/cairo-lang-sierra/src/extensions/modules/bounded_int.rs b/crates/cairo-lang-sierra/src/extensions/modules/bounded_int.rs index 4ad745e733e..5a65f1939a9 100644 --- a/crates/cairo-lang-sierra/src/extensions/modules/bounded_int.rs +++ b/crates/cairo-lang-sierra/src/extensions/modules/bounded_int.rs @@ -46,7 +46,7 @@ impl NamedType for BoundedIntType { /// A guarantee that a value is within the specified bounds. /// Unlike BoundedInt, this type is non-duplicatable and non-droppable. /// It must be verified via `bounded_int_guarantee_verify` to be consumed. -/// Currently only supports u32 bounds (0 to 2^32-1). +/// Currently only supports u32 and u128 bounds. #[derive(Default)] pub struct BoundedIntGuaranteeType {} impl NamedType for BoundedIntGuaranteeType { @@ -60,7 +60,8 @@ impl NamedType for BoundedIntGuaranteeType { ) -> Result { let (min, max) = extract_bounds(args)?; // Currently only u32 guarantees are supported. - require(is_u32_range(min, max)).ok_or(SpecializationError::UnsupportedGenericArg)?; + require(is_valid_guarantee_range(min, max)) + .ok_or(SpecializationError::UnsupportedGenericArg)?; specialize_bounded_int_type(Self::ID, args, false, false) } } @@ -554,7 +555,7 @@ impl SignatureOnlyGenericLibfunc for BoundedIntWrapNonZeroLibfunc { /// Libfunc for verifying a BoundedIntGuarantee. /// Consumes the guarantee and returns the underlying value as a BoundedInt. /// Generic args: [min, max] - the bounds of the guarantee. -/// Currently only supports u32 bounds (0 to 2^32-1). +/// Currently only supports u32 and u128 bounds. #[derive(Default)] pub struct BoundedIntGuaranteeVerifyLibfunc {} impl NamedLibfunc for BoundedIntGuaranteeVerifyLibfunc { @@ -569,7 +570,8 @@ impl NamedLibfunc for BoundedIntGuaranteeVerifyLibfunc { ) -> Result { let (min, max) = extract_bounds(args)?; // Currently only u32 guarantees are supported. - require(is_u32_range(min, max)).ok_or(SpecializationError::UnsupportedGenericArg)?; + require(is_valid_guarantee_range(min, max)) + .ok_or(SpecializationError::UnsupportedGenericArg)?; let range_check_ty = context.get_concrete_type(RangeCheckType::ID, &[])?; let guarantee_ty = bounded_int_guarantee_ty(context, min.clone(), max.clone())?; @@ -591,7 +593,8 @@ impl NamedLibfunc for BoundedIntGuaranteeVerifyLibfunc { ) -> Result { let (min, max) = extract_bounds(args)?; // Currently only u32 guarantees are supported. - require(is_u32_range(min, max)).ok_or(SpecializationError::UnsupportedGenericArg)?; + require(is_valid_guarantee_range(min, max)) + .ok_or(SpecializationError::UnsupportedGenericArg)?; let range = Range::closed(min.clone(), max.clone()); Ok(Self::Concrete { range, signature: self.specialize_signature(context, args)? }) @@ -678,9 +681,9 @@ fn extract_bounds(args: &[GenericArg]) -> Result<(&BigInt, &BigInt), Specializat } } -/// Checks if the given bounds match the u32 range. -fn is_u32_range(min: &BigInt, max: &BigInt) -> bool { - min.is_zero() && max.to_u32() == Some(u32::MAX) +/// Checks if the given bounds match allowed bounded int guarantee ranges. +fn is_valid_guarantee_range(min: &BigInt, max: &BigInt) -> bool { + min.is_zero() && matches!(max.to_u128(), Some(0xffffffff | u128::MAX)) } /// Helper to specialize bounded int types. diff --git a/tests/e2e_test_data/libfuncs/bounded_int b/tests/e2e_test_data/libfuncs/bounded_int index 8cfe5984d3e..f01a95aa517 100644 --- a/tests/e2e_test_data/libfuncs/bounded_int +++ b/tests/e2e_test_data/libfuncs/bounded_int @@ -1133,6 +1133,45 @@ test::foo@F0([0]: RangeCheck, [1]: BoundedIntGuarantee<0, 4294967295>) -> (Range //! > ========================================================================== +//! > bounded_int_guarantee_verify libfunc for u128. + +//! > test_runner_name +SmallE2ETestRunner + +//! > cairo_code +extern type BoundedIntGuarantee; +extern fn bounded_int_guarantee_verify( + value: BoundedIntGuarantee, +) implicits(RangeCheck) nopanic; + +fn foo(value: BoundedIntGuarantee<0, 0xffffffffffffffffffffffffffffffff>) { + bounded_int_guarantee_verify(value) +} + +//! > casm +[fp + -3] = [[fp + -4] + 0]; +[ap + 0] = [fp + -4] + 1, ap++; +ret; + +//! > sierra_code +type RangeCheck = RangeCheck [storable: true, drop: false, dup: false, zero_sized: false]; +type BoundedIntGuarantee<0, 340282366920938463463374607431768211455> = BoundedIntGuarantee<0, 340282366920938463463374607431768211455> [storable: true, drop: false, dup: false, zero_sized: false]; + +libfunc bounded_int_guarantee_verify<0, 340282366920938463463374607431768211455> = bounded_int_guarantee_verify<0, 340282366920938463463374607431768211455>; +libfunc store_temp = store_temp; + +F0: +bounded_int_guarantee_verify<0, 340282366920938463463374607431768211455>([0], [1]) -> ([2]); +store_temp([2]) -> ([2]); +return([2]); + +test::foo@F0([0]: RangeCheck, [1]: BoundedIntGuarantee<0, 340282366920938463463374607431768211455>) -> (RangeCheck); + +//! > function_costs +test::foo: SmallOrderedMap({Const: 270}) + +//! > ========================================================================== + //! > u128_to_u32_guarantees libfunc. //! > test_runner_name