Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()]
Expand Down
14 changes: 9 additions & 5 deletions crates/cairo-lang-sierra-to-casm/src/invocations/int/bounded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -259,15 +260,18 @@ fn build_guarantee_verify(
libfunc: &BoundedIntGuaranteeVerifyConcreteLibfunc,
) -> Result<CompiledInvocation, InvocationError> {
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)],
Expand Down
19 changes: 11 additions & 8 deletions crates/cairo-lang-sierra/src/extensions/modules/bounded_int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -60,7 +60,8 @@ impl NamedType for BoundedIntGuaranteeType {
) -> Result<Self::Concrete, SpecializationError> {
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)
}
}
Expand Down Expand Up @@ -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 {
Expand All @@ -569,7 +570,8 @@ impl NamedLibfunc for BoundedIntGuaranteeVerifyLibfunc {
) -> Result<LibfuncSignature, SpecializationError> {
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())?;
Expand All @@ -591,7 +593,8 @@ impl NamedLibfunc for BoundedIntGuaranteeVerifyLibfunc {
) -> Result<Self::Concrete, SpecializationError> {
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)? })
Expand Down Expand Up @@ -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.
Expand Down
39 changes: 39 additions & 0 deletions tests/e2e_test_data/libfuncs/bounded_int
Original file line number Diff line number Diff line change
Expand Up @@ -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<const MIN: felt252, const MAX: felt252>;
extern fn bounded_int_guarantee_verify<const MIN: felt252, const MAX: felt252>(
value: BoundedIntGuarantee<MIN, MAX>,
) 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<RangeCheck> = store_temp<RangeCheck>;

F0:
bounded_int_guarantee_verify<0, 340282366920938463463374607431768211455>([0], [1]) -> ([2]);
store_temp<RangeCheck>([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
Expand Down
Loading