diff --git a/crates/cairo-lang-sierra-ap-change/src/core_libfunc_ap_change.rs b/crates/cairo-lang-sierra-ap-change/src/core_libfunc_ap_change.rs index 7e77c3d815e..d8509ca5079 100644 --- a/crates/cairo-lang-sierra-ap-change/src/core_libfunc_ap_change.rs +++ b/crates/cairo-lang-sierra-ap-change/src/core_libfunc_ap_change.rs @@ -433,9 +433,9 @@ pub fn core_libfunc_ap_change( vec![ApChange::Known(ap_change), ApChange::Known(ap_change)] } BoundedIntConcreteLibfunc::IsZero(_) => vec![ApChange::Known(0), ApChange::Known(0)], - BoundedIntConcreteLibfunc::WrapNonZero(_) => { - vec![ApChange::Known(0)] - } + BoundedIntConcreteLibfunc::WrapNonZero(_) => vec![ApChange::Known(0)], + BoundedIntConcreteLibfunc::ToGuarantee(_) => vec![ApChange::Known(0)], + BoundedIntConcreteLibfunc::GuaranteeContent(_) => vec![ApChange::Known(0)], BoundedIntConcreteLibfunc::GuaranteeVerify(libfunc) => { let ap_change = if libfunc.range.lower.is_zero() { 0 } else { 1 } + if &libfunc.range.upper - 1 == u128::MAX.into() { 0 } else { 1 }; 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 30878bd7798..86329911fd2 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 @@ -551,9 +551,9 @@ pub fn core_libfunc_cost( BoundedIntConcreteLibfunc::IsZero(_) => { vec![ConstCost::steps(1).into(), ConstCost::steps(1).into()] } - BoundedIntConcreteLibfunc::WrapNonZero(_) => { - vec![ConstCost::steps(0).into()] - } + BoundedIntConcreteLibfunc::WrapNonZero(_) => vec![ConstCost::steps(0).into()], + BoundedIntConcreteLibfunc::ToGuarantee(_) => vec![ConstCost::steps(0).into()], + BoundedIntConcreteLibfunc::GuaranteeContent(_) => vec![ConstCost::steps(0).into()], BoundedIntConcreteLibfunc::GuaranteeVerify(libfunc) => { let u128_bound = BigInt::one().shl(128); let range_checks = if libfunc.range.size() == u128_bound { 1 } else { 2 }; 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 10c5656c1ae..ffb9c51c8fa 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 @@ -14,7 +14,7 @@ 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::misc::{build_dup, build_identity, build_is_zero}; use crate::invocations::{ BuiltinInfo, CompiledInvocation, CompiledInvocationBuilder, CostValidationInfo, InvocationError, add_input_variables, get_non_fallthrough_statement_id, @@ -47,6 +47,8 @@ pub fn build( } BoundedIntConcreteLibfunc::IsZero(_) => build_is_zero(builder), BoundedIntConcreteLibfunc::WrapNonZero(_) => build_identity(builder), + BoundedIntConcreteLibfunc::ToGuarantee(_) => build_identity(builder), + BoundedIntConcreteLibfunc::GuaranteeContent(_) => build_dup(builder), BoundedIntConcreteLibfunc::GuaranteeVerify(libfunc) => { build_guarantee_verify(builder, libfunc) } 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 5a65f1939a9..5499f1b3a11 100644 --- a/crates/cairo-lang-sierra/src/extensions/modules/bounded_int.rs +++ b/crates/cairo-lang-sierra/src/extensions/modules/bounded_int.rs @@ -89,6 +89,8 @@ define_libfunc_hierarchy! { TrimMax(BoundedIntTrimLibfunc), IsZero(BoundedIntIsZeroLibfunc), WrapNonZero(BoundedIntWrapNonZeroLibfunc), + ToGuarantee(BoundedIntToGuaranteeLibfunc), + GuaranteeContent(BoundedIntGuaranteeContentLibfunc), GuaranteeVerify(BoundedIntGuaranteeVerifyLibfunc), U128ToU32Guarantees(U128ToU32GuaranteesLibfunc), }, BoundedIntConcreteLibfunc @@ -552,6 +554,72 @@ impl SignatureOnlyGenericLibfunc for BoundedIntWrapNonZeroLibfunc { } } +/// Libfunc for converting a bounded int into a guarantee - used for converting into interfaces +/// expecting guarantees. +#[derive(Default)] +pub struct BoundedIntToGuaranteeLibfunc {} +impl SignatureOnlyGenericLibfunc for BoundedIntToGuaranteeLibfunc { + const STR_ID: &'static str = "bounded_int_to_guarantee"; + + fn specialize_signature( + &self, + context: &dyn SignatureSpecializationContext, + args: &[GenericArg], + ) -> Result { + let (min, max) = extract_bounds(args)?; + // Currently only u32 guarantees are supported. + require(is_valid_guarantee_range(min, max)) + .ok_or(SpecializationError::UnsupportedGenericArg)?; + + Ok(LibfuncSignature::new_non_branch_ex( + vec![ + ParamSignature::new(bounded_int_ty(context, min.clone(), max.clone())?) + .with_allow_all(), + ], + vec![OutputVarInfo { + ty: bounded_int_guarantee_ty(context, min.clone(), max.clone())?, + ref_info: OutputVarReferenceInfo::SameAsParam { param_idx: 0 }, + }], + SierraApChange::Known { new_vars_only: false }, + )) + } +} + +/// Libfunc for extracting a bounded int from its corresponding guarantee - used to read its +/// internal value, while deferring the validation into further down the Sierra program. +#[derive(Default)] +pub struct BoundedIntGuaranteeContentLibfunc {} +impl SignatureOnlyGenericLibfunc for BoundedIntGuaranteeContentLibfunc { + const STR_ID: &'static str = "bounded_int_guarantee_content"; + + fn specialize_signature( + &self, + context: &dyn SignatureSpecializationContext, + args: &[GenericArg], + ) -> Result { + let (min, max) = extract_bounds(args)?; + // Currently only u32 guarantees are supported. + require(is_valid_guarantee_range(min, max)) + .ok_or(SpecializationError::UnsupportedGenericArg)?; + let guarantee_ty = bounded_int_guarantee_ty(context, min.clone(), max.clone())?; + + Ok(LibfuncSignature::new_non_branch_ex( + vec![ParamSignature::new(guarantee_ty.clone()).with_allow_all()], + vec![ + OutputVarInfo { + ty: guarantee_ty, + ref_info: OutputVarReferenceInfo::SameAsParam { param_idx: 0 }, + }, + OutputVarInfo { + ty: bounded_int_ty(context, min.clone(), max.clone())?, + ref_info: OutputVarReferenceInfo::SameAsParam { param_idx: 0 }, + }, + ], + SierraApChange::Known { new_vars_only: false }, + )) + } +} + /// 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. diff --git a/crates/cairo-lang-starknet-classes/src/allowed_libfuncs_lists/all.json b/crates/cairo-lang-starknet-classes/src/allowed_libfuncs_lists/all.json index 87b2637ae2b..59cabf2be2b 100644 --- a/crates/cairo-lang-starknet-classes/src/allowed_libfuncs_lists/all.json +++ b/crates/cairo-lang-starknet-classes/src/allowed_libfuncs_lists/all.json @@ -26,10 +26,12 @@ "bounded_int_add", "bounded_int_constrain", "bounded_int_div_rem", + "bounded_int_guarantee_content", "bounded_int_guarantee_verify", "bounded_int_is_zero", "bounded_int_mul", "bounded_int_sub", + "bounded_int_to_guarantee", "bounded_int_trim_max", "bounded_int_trim_min", "bounded_int_wrap_non_zero", diff --git a/tests/e2e_test_data/libfuncs/bounded_int b/tests/e2e_test_data/libfuncs/bounded_int index f01a95aa517..36370b69180 100644 --- a/tests/e2e_test_data/libfuncs/bounded_int +++ b/tests/e2e_test_data/libfuncs/bounded_int @@ -1092,6 +1092,172 @@ test::foo: SmallOrderedMap({Const: 400}) //! > ========================================================================== +//! > bounded_int_to_guarantee libfunc for u32. + +//! > test_runner_name +SmallE2ETestRunner + +//! > cairo_code +extern type BoundedInt; +extern type BoundedIntGuarantee; +extern fn bounded_int_to_guarantee( + value: BoundedInt, +) -> BoundedIntGuarantee implicits() nopanic; + +fn foo(value: BoundedInt<0, 0xffffffff>) -> BoundedIntGuarantee<0, 0xffffffff> { + bounded_int_to_guarantee(value) +} + +//! > casm +[ap + 0] = [fp + -3], ap++; +ret; + +//! > sierra_code +type BoundedInt<0, 4294967295> = BoundedInt<0, 4294967295> [storable: true, drop: true, dup: true, zero_sized: false]; +type BoundedIntGuarantee<0, 4294967295> = BoundedIntGuarantee<0, 4294967295> [storable: true, drop: false, dup: false, zero_sized: false]; + +libfunc bounded_int_to_guarantee<0, 4294967295> = bounded_int_to_guarantee<0, 4294967295>; +libfunc store_temp> = store_temp>; + +F0: +bounded_int_to_guarantee<0, 4294967295>([0]) -> ([1]); +store_temp>([1]) -> ([1]); +return([1]); + +test::foo@F0([0]: BoundedInt<0, 4294967295>) -> (BoundedIntGuarantee<0, 4294967295>); + +//! > function_costs +test::foo: SmallOrderedMap({Const: 100}) + +//! > ========================================================================== + +//! > bounded_int_to_guarantee libfunc for u128. + +//! > test_runner_name +SmallE2ETestRunner + +//! > cairo_code +extern type BoundedInt; +extern type BoundedIntGuarantee; +extern fn bounded_int_to_guarantee( + value: BoundedInt, +) -> BoundedIntGuarantee implicits() nopanic; + +fn foo( + value: BoundedInt<0, 0xffffffffffffffffffffffffffffffff>, +) -> BoundedIntGuarantee<0, 0xffffffffffffffffffffffffffffffff> { + bounded_int_to_guarantee(value) +} + +//! > casm +[ap + 0] = [fp + -3], ap++; +ret; + +//! > sierra_code +type BoundedInt<0, 340282366920938463463374607431768211455> = BoundedInt<0, 340282366920938463463374607431768211455> [storable: true, drop: true, dup: true, zero_sized: false]; +type BoundedIntGuarantee<0, 340282366920938463463374607431768211455> = BoundedIntGuarantee<0, 340282366920938463463374607431768211455> [storable: true, drop: false, dup: false, zero_sized: false]; + +libfunc bounded_int_to_guarantee<0, 340282366920938463463374607431768211455> = bounded_int_to_guarantee<0, 340282366920938463463374607431768211455>; +libfunc store_temp> = store_temp>; + +F0: +bounded_int_to_guarantee<0, 340282366920938463463374607431768211455>([0]) -> ([1]); +store_temp>([1]) -> ([1]); +return([1]); + +test::foo@F0([0]: BoundedInt<0, 340282366920938463463374607431768211455>) -> (BoundedIntGuarantee<0, 340282366920938463463374607431768211455>); + +//! > function_costs +test::foo: SmallOrderedMap({Const: 100}) + +//! > ========================================================================== + +//! > bounded_int_guarantee_content libfunc for u32. + +//! > test_runner_name +SmallE2ETestRunner + +//! > cairo_code +extern type BoundedInt; +extern type BoundedIntGuarantee; +extern fn bounded_int_guarantee_content( + ref value: BoundedIntGuarantee, +) -> BoundedInt implicits() nopanic; + +fn foo(ref value: BoundedIntGuarantee<0, 0xffffffff>) -> BoundedInt<0, 0xffffffff> { + bounded_int_guarantee_content(ref value) +} + +//! > casm +[ap + 0] = [fp + -3], ap++; +[ap + 0] = [fp + -3], ap++; +ret; + +//! > sierra_code +type BoundedIntGuarantee<0, 4294967295> = BoundedIntGuarantee<0, 4294967295> [storable: true, drop: false, dup: false, zero_sized: false]; +type BoundedInt<0, 4294967295> = BoundedInt<0, 4294967295> [storable: true, drop: true, dup: true, zero_sized: false]; + +libfunc bounded_int_guarantee_content<0, 4294967295> = bounded_int_guarantee_content<0, 4294967295>; +libfunc store_temp> = store_temp>; +libfunc store_temp> = store_temp>; + +F0: +bounded_int_guarantee_content<0, 4294967295>([0]) -> ([1], [2]); +store_temp>([1]) -> ([1]); +store_temp>([2]) -> ([2]); +return([1], [2]); + +test::foo@F0([0]: BoundedIntGuarantee<0, 4294967295>) -> (BoundedIntGuarantee<0, 4294967295>, BoundedInt<0, 4294967295>); + +//! > function_costs +test::foo: SmallOrderedMap({Const: 200}) + +//! > ========================================================================== + +//! > bounded_int_guarantee_content libfunc for u128. + +//! > test_runner_name +SmallE2ETestRunner + +//! > cairo_code +extern type BoundedInt; +extern type BoundedIntGuarantee; +extern fn bounded_int_guarantee_content( + ref value: BoundedIntGuarantee, +) -> BoundedInt implicits() nopanic; + +fn foo( + ref value: BoundedIntGuarantee<0, 0xffffffffffffffffffffffffffffffff>, +) -> BoundedInt<0, 0xffffffffffffffffffffffffffffffff> { + bounded_int_guarantee_content(ref value) +} + +//! > casm +[ap + 0] = [fp + -3], ap++; +[ap + 0] = [fp + -3], ap++; +ret; + +//! > sierra_code +type BoundedIntGuarantee<0, 340282366920938463463374607431768211455> = BoundedIntGuarantee<0, 340282366920938463463374607431768211455> [storable: true, drop: false, dup: false, zero_sized: false]; +type BoundedInt<0, 340282366920938463463374607431768211455> = BoundedInt<0, 340282366920938463463374607431768211455> [storable: true, drop: true, dup: true, zero_sized: false]; + +libfunc bounded_int_guarantee_content<0, 340282366920938463463374607431768211455> = bounded_int_guarantee_content<0, 340282366920938463463374607431768211455>; +libfunc store_temp> = store_temp>; +libfunc store_temp> = store_temp>; + +F0: +bounded_int_guarantee_content<0, 340282366920938463463374607431768211455>([0]) -> ([1], [2]); +store_temp>([1]) -> ([1]); +store_temp>([2]) -> ([2]); +return([1], [2]); + +test::foo@F0([0]: BoundedIntGuarantee<0, 340282366920938463463374607431768211455>) -> (BoundedIntGuarantee<0, 340282366920938463463374607431768211455>, BoundedInt<0, 340282366920938463463374607431768211455>); + +//! > function_costs +test::foo: SmallOrderedMap({Const: 200}) + +//! > ========================================================================== + //! > bounded_int_guarantee_verify libfunc for u32. //! > test_runner_name