Skip to content

Commit

Permalink
Added libfunc to constrain felt252 into one of two guarantees.
Browse files Browse the repository at this point in the history
commit-id:9653e0a0
  • Loading branch information
orizi committed Jun 25, 2024
1 parent 2f378b3 commit ba9f558
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 3 deletions.
18 changes: 18 additions & 0 deletions corelib/src/internal.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,21 @@ pub(crate) extern type BoundedIntGuarantee<BoundedIntType>;
pub(crate) extern fn bounded_int_into_guarantee<BoundedIntType>(
value: BoundedIntType
) -> BoundedIntGuarantee<BoundedIntType> nopanic;

trait MinusOne<const VALUE: felt252> {
const VALUE: felt252;
}
impl MinusOneU96 of MinusOne<0x1000000000000000000000000> {
const VALUE: felt252 = 0xffffffffffffffffffffffff;
}

enum ConstraitFelt252Result<const BOUNDARY_MINUS_1: felt252, const BOUNDARY: felt252> {
Under: BoundedIntGuarantee<BoundedInt<0, BOUNDARY_MINUS_1>>,
Over: BoundedIntGuarantee<BoundedInt<BOUNDARY, 0x800000000000011000000000000000000000000000000000000000000000000>>,
}

pub(crate) extern fn bounded_int_felt252_constrain<
const BOUNDARY: felt252, impl BoundaryMinusOne: MinusOne::<BOUNDARY>,
>(
value: felt252
) -> ConstraitFelt252Result<BoundaryMinusOne::VALUE, BOUNDARY> nopanic;
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,12 @@ pub fn core_libfunc_ap_change<InfoProvider: InvocationApChangeInfoProvider>(
vec![ApChange::Known(0)]
}
BoundedIntConcreteLibfunc::IntoGuarantee(_) => vec![ApChange::Known(0)],
BoundedIntConcreteLibfunc::Felt252Constrain(_) => {
vec![ApChange::Known(1), ApChange::Known(1)]
}
BoundedIntConcreteLibfunc::VerifyGuarantee(_) => {
todo!("Implement bounded_int_verify_guarantee")
}
},
Circuit(CircuitConcreteLibfunc::TryIntoCircuitModulus(_)) => {
vec![ApChange::Known(1), ApChange::Known(1)]
Expand Down
6 changes: 6 additions & 0 deletions crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,12 @@ pub fn core_libfunc_cost(
vec![ConstCost::steps(0).into()]
}
BoundedIntConcreteLibfunc::IntoGuarantee(_) => vec![ConstCost::steps(0).into()],
BoundedIntConcreteLibfunc::Felt252Constrain(_) => {
vec![ConstCost::steps(1).into(), ConstCost::steps(1).into()]
}
BoundedIntConcreteLibfunc::VerifyGuarantee(_) => {
todo!("Implement bounded_int_verify_guarantee")
}
},
Circuit(libfunc) => match libfunc {
CircuitConcreteLibfunc::FillInput(_) => {
Expand Down
29 changes: 29 additions & 0 deletions crates/cairo-lang-sierra-to-casm/src/invocations/int/bounded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ pub fn build(
BoundedIntConcreteLibfunc::IsZero(_) => build_is_zero(builder),
BoundedIntConcreteLibfunc::WrapNonZero(_) => build_identity(builder),
BoundedIntConcreteLibfunc::IntoGuarantee(_) => build_identity(builder),
BoundedIntConcreteLibfunc::Felt252Constrain(libfunc) => {
build_felt252_constrain(builder, &libfunc.boundary)
}
BoundedIntConcreteLibfunc::VerifyGuarantee(_) => {
todo!("Implement bounded_int_verify_guarantee")
}
}
}

Expand Down Expand Up @@ -222,3 +228,26 @@ fn build_constrain(
},
))
}

/// Build constrain on felt252 creating bounded int guarantees.
fn build_felt252_constrain(
builder: CompiledInvocationBuilder<'_>,
boundary: &BigInt,
) -> Result<CompiledInvocation, InvocationError> {
let [value] = builder.try_get_single_cells()?;

let mut casm_builder = CasmBuilder::default();
let value = casm_builder.add_var(value.clone());
casm_build_extend! {casm_builder,
const boundary_minus_1 = (boundary - 1) as BigInt;
tempvar is_over;
hint TestLessThanOrEqual {lhs: boundary_minus_1, rhs: value} into {dst: is_over};
jump Over if is_over != 0;
};
let target_statement_id = get_non_fallthrough_statement_id(&builder);
Ok(builder.build_from_casm_builder(
casm_builder,
[("Fallthrough", &[&[value]], None), ("Over", &[&[value]], Some(target_statement_id))],
Default::default(),
))
}
83 changes: 80 additions & 3 deletions crates/cairo-lang-sierra/src/extensions/modules/bounded_int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ use std::ops::Shl;
use cairo_lang_utils::require;
use itertools::Itertools;
use num_bigint::{BigInt, ToBigInt};
use num_traits::{One, Signed};
use num_traits::{One, Signed, Zero};
use starknet_types_core::felt::Felt as Felt252;

use super::felt252::Felt252Type;
use super::non_zero::{nonzero_ty, NonZeroType};
use super::range_check::RangeCheckType;
use super::utils::{reinterpret_cast_signature, Range};
Expand All @@ -20,8 +21,8 @@ use crate::extensions::types::{
GenericTypeArgGenericType, GenericTypeArgGenericTypeWrapper, TypeInfo,
};
use crate::extensions::{
args_as_single_type, args_as_two_types, ConcreteType, NamedLibfunc, NamedType,
OutputVarReferenceInfo, SignatureBasedConcreteLibfunc, SpecializationError,
args_as_single_type, args_as_single_value, args_as_two_types, ConcreteType, NamedLibfunc,
NamedType, OutputVarReferenceInfo, SignatureBasedConcreteLibfunc, SpecializationError,
};
use crate::ids::{ConcreteTypeId, GenericTypeId};
use crate::program::GenericArg;
Expand Down Expand Up @@ -114,6 +115,8 @@ define_libfunc_hierarchy! {
IsZero(BoundedIntIsZeroLibfunc),
WrapNonZero(BoundedIntWrapNonZeroLibfunc),
IntoGuarantee(BoundedIntIntoGuaranteeLibfunc),
Felt252Constrain(BoundedIntFelt252ConstrainLibfunc),
VerifyGuarantee(BoundedIntVerifyGuaranteeLibfunc),
}, BoundedIntConcreteLibfunc
}

Expand Down Expand Up @@ -519,6 +522,80 @@ impl SignatureOnlyGenericLibfunc for BoundedIntIntoGuaranteeLibfunc {
}
}

/// Libfunc for wrapping a given bounded int with a guarantee.
#[derive(Default)]
pub struct BoundedIntVerifyGuaranteeLibfunc {}
impl SignatureOnlyGenericLibfunc for BoundedIntVerifyGuaranteeLibfunc {
const STR_ID: &'static str = "bounded_int_verify_guarantee";

fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
args: &[GenericArg],
) -> Result<LibfuncSignature, SpecializationError> {
let ty = args_as_single_type(args)?;
let range_check_type = context.get_concrete_type(RangeCheckType::id(), &[])?;
Ok(LibfuncSignature::new_non_branch_ex(
vec![
ParamSignature::new(range_check_type.clone()).with_allow_add_const(),
ParamSignature::new(bounded_int_guarantee_ty(context, ty.clone())?),
],
vec![OutputVarInfo::new_builtin(range_check_type.clone(), 0)],
SierraApChange::Known { new_vars_only: false },
))
}
}

/// Libfunc for constraining a BoundedInt<Min, Max> to one of two non-empty ranges: [Min, Boundary)
/// or [Boundary, Max]. The libfunc is also applicable for standard types such as u* and i*.
#[derive(Default)]
pub struct BoundedIntFelt252ConstrainLibfunc {}
impl NamedLibfunc for BoundedIntFelt252ConstrainLibfunc {
type Concrete = BoundedIntConstrainConcreteLibfunc;

const STR_ID: &'static str = "bounded_int_felt252_constrain";

fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
args: &[GenericArg],
) -> Result<LibfuncSignature, SpecializationError> {
let boundary = args_as_single_value(args)?;
let zero = BigInt::zero();
let prime = Felt252::prime().into();
require(zero < boundary && boundary < prime)
.ok_or(SpecializationError::UnsupportedGenericArg)?;
let low_range = Range::half_open(zero, boundary.clone());
let high_range = Range::half_open(boundary.clone(), prime);
let felt252_ty = context.get_concrete_type(Felt252Type::ID, &[])?;
let branch_signature = |rng: Range| {
let bounded_int_ty = bounded_int_ty(context, rng.lower, rng.upper - 1)?;
Ok(BranchSignature {
vars: vec![OutputVarInfo {
ty: bounded_int_guarantee_ty(context, bounded_int_ty)?,
ref_info: OutputVarReferenceInfo::SameAsParam { param_idx: 0 },
}],
ap_change: SierraApChange::Known { new_vars_only: false },
})
};
Ok(LibfuncSignature {
param_signatures: vec![ParamSignature::new(felt252_ty)],
branch_signatures: vec![branch_signature(low_range)?, branch_signature(high_range)?],
fallthrough: Some(0),
})
}

fn specialize(
&self,
context: &dyn SpecializationContext,
args: &[GenericArg],
) -> Result<Self::Concrete, SpecializationError> {
let boundary = args_as_single_value(args)?;
let context = context.upcast();
Ok(Self::Concrete { boundary, signature: self.specialize_signature(context, args)? })
}
}

/// Returns the concrete type for a BoundedInt<min, max>.
pub fn bounded_int_ty(
context: &dyn SignatureSpecializationContext,
Expand Down
56 changes: 56 additions & 0 deletions tests/e2e_test_data/libfuncs/bounded_int
Original file line number Diff line number Diff line change
Expand Up @@ -858,3 +858,59 @@ test::foo@0([0]: u8) -> (BoundedIntGuarantee<u8>);

//! > function_costs
test::foo: OrderedHashMap({Const: 100})

//! > ==========================================================================

//! > bounded_int_felt252_constrain

//! > test_runner_name
SmallE2ETestRunner

//! > cairo
use core::internal::{ConstraitFelt252Result, bounded_int_felt252_constrain};


fn foo(val: felt252) -> ConstraitFelt252Result<
0xffffffffffffffffffffffff, 0x1000000000000000000000000
> {
bounded_int_felt252_constrain(val)
}

//! > casm
%{ memory[ap + 0] = 79228162514264337593543950335 <= memory[fp + -3] %}
jmp rel 6 if [ap + 0] != 0, ap++;
[ap + 0] = 0, ap++;
[ap + 0] = [fp + -3], ap++;
ret;
[ap + 0] = 1, ap++;
[ap + 0] = [fp + -3], ap++;
ret;

//! > sierra_code
type felt252 = felt252 [storable: true, drop: true, dup: true, zero_sized: false];
type BoundedIntGuarantee<BoundedInt<0, 79228162514264337593543950335>> = BoundedIntGuarantee<BoundedInt<0, 79228162514264337593543950335>> [storable: true, drop: false, dup: false, zero_sized: false];
type BoundedIntGuarantee<BoundedInt<79228162514264337593543950336, 3618502788666131213697322783095070105623107215331596699973092056135872020480>> = BoundedIntGuarantee<BoundedInt<79228162514264337593543950336, 3618502788666131213697322783095070105623107215331596699973092056135872020480>> [storable: true, drop: false, dup: false, zero_sized: false];
type core::internal::ConstraitFelt252Result::<79228162514264337593543950335, 79228162514264337593543950336> = Enum<ut@core::internal::ConstraitFelt252Result::<79228162514264337593543950335, 79228162514264337593543950336>, BoundedIntGuarantee<BoundedInt<0, 79228162514264337593543950335>>, BoundedIntGuarantee<BoundedInt<79228162514264337593543950336, 3618502788666131213697322783095070105623107215331596699973092056135872020480>>> [storable: true, drop: false, dup: false, zero_sized: false];
type BoundedInt<79228162514264337593543950336, 3618502788666131213697322783095070105623107215331596699973092056135872020480> = BoundedInt<79228162514264337593543950336, 3618502788666131213697322783095070105623107215331596699973092056135872020480> [storable: true, drop: true, dup: true, zero_sized: false];
type BoundedInt<0, 79228162514264337593543950335> = BoundedInt<0, 79228162514264337593543950335> [storable: true, drop: true, dup: true, zero_sized: false];

libfunc bounded_int_felt252_constrain<79228162514264337593543950336> = bounded_int_felt252_constrain<79228162514264337593543950336>;
libfunc branch_align = branch_align;
libfunc enum_init<core::internal::ConstraitFelt252Result::<79228162514264337593543950335, 79228162514264337593543950336>, 0> = enum_init<core::internal::ConstraitFelt252Result::<79228162514264337593543950335, 79228162514264337593543950336>, 0>;
libfunc store_temp<core::internal::ConstraitFelt252Result::<79228162514264337593543950335, 79228162514264337593543950336>> = store_temp<core::internal::ConstraitFelt252Result::<79228162514264337593543950335, 79228162514264337593543950336>>;
libfunc enum_init<core::internal::ConstraitFelt252Result::<79228162514264337593543950335, 79228162514264337593543950336>, 1> = enum_init<core::internal::ConstraitFelt252Result::<79228162514264337593543950335, 79228162514264337593543950336>, 1>;

bounded_int_felt252_constrain<79228162514264337593543950336>([0]) { fallthrough([1]) 5([2]) }; // 0
branch_align() -> (); // 1
enum_init<core::internal::ConstraitFelt252Result::<79228162514264337593543950335, 79228162514264337593543950336>, 0>([1]) -> ([3]); // 2
store_temp<core::internal::ConstraitFelt252Result::<79228162514264337593543950335, 79228162514264337593543950336>>([3]) -> ([3]); // 3
return([3]); // 4
branch_align() -> (); // 5
enum_init<core::internal::ConstraitFelt252Result::<79228162514264337593543950335, 79228162514264337593543950336>, 1>([2]) -> ([4]); // 6
store_temp<core::internal::ConstraitFelt252Result::<79228162514264337593543950335, 79228162514264337593543950336>>([4]) -> ([4]); // 7
return([4]); // 8

test::foo@0([0]: felt252) -> (core::internal::ConstraitFelt252Result::<79228162514264337593543950335, 79228162514264337593543950336>);

//! > function_costs
test::foo: OrderedHashMap({Const: 300})

0 comments on commit ba9f558

Please sign in to comment.