Skip to content

Commit

Permalink
feat(hints): add ids.is_250 and ids.is_small hint implementations (
Browse files Browse the repository at this point in the history
…lambdaclass#1073)

* Add starknet_in_rust IS_250_BITS hint

* Add starknet_in_rust IS_ADDR_BOUNDED hint

* Update changelog

* Remove unnecessary `mod_floor`
  • Loading branch information
MegaRedHand authored and kariy committed Jun 23, 2023
1 parent a57695e commit 0bf8ff8
Show file tree
Hide file tree
Showing 6 changed files with 261 additions and 2 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,23 @@

```

* Add missing hints on whitelist [#1073](https://github.com/lambdaclass/cairo-rs/pull/1073):

`BuiltinHintProcessor` now supports the following hints:

```python
ids.is_250 = 1 if ids.addr < 2**250 else 0
```

```python
# Verify the assumptions on the relationship between 2**250, ADDR_BOUND and PRIME.
ADDR_BOUND = ids.ADDR_BOUND % PRIME
assert (2**250 < ADDR_BOUND <= 2**251) and (2 * 2**250 < PRIME) and (
ADDR_BOUND * 2 > PRIME), \
'normalize_address() cannot be used with the current constants.'
ids.is_small = 1 if ids.addr < ADDR_BOUND else 0
```

* Implement hint on ec_recover.json whitelist [#1038](https://github.com/lambdaclass/cairo-rs/pull/1038):

`BuiltinHintProcessor` now supports the following hint:
Expand Down
32 changes: 32 additions & 0 deletions cairo_programs/normalize_address.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
%builtins range_check

from starkware.starknet.common.storage import normalize_address
from starkware.cairo.common.math import assert_250_bit
from starkware.cairo.common.alloc import alloc

func normalize_address_element_array{range_check_ptr: felt}(
array: felt*, array_length: felt, iterator: felt
) {
if (iterator == array_length) {
return ();
}
normalize_address(array[iterator]);
return normalize_address_element_array(array, array_length, iterator + 1);
}

func fill_array(array: felt*, base: felt, step: felt, array_length: felt, iterator: felt) {
if (iterator == array_length) {
return ();
}
assert array[iterator] = base + step * iterator;
return fill_array(array, base, step, array_length, iterator + 1);
}

func main{range_check_ptr: felt}() {
alloc_locals;
tempvar array_length = 10;
let (array: felt*) = alloc();
fill_array(array, 70000000000000000000, 300000000000000000, array_length, 0);
normalize_address_element_array(array, array_length, 0);
return ();
}
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ impl HintProcessor for BuiltinHintProcessor {
hint_code::ASSERT_250_BITS => {
assert_250_bit(vm, &hint_data.ids_data, &hint_data.ap_tracking)
}
hint_code::IS_250_BITS => is_250_bits(vm, &hint_data.ids_data, &hint_data.ap_tracking),
hint_code::IS_ADDR_BOUNDED => {
is_addr_bounded(vm, &hint_data.ids_data, &hint_data.ap_tracking, constants)
}
hint_code::IS_POSITIVE => is_positive(vm, &hint_data.ids_data, &hint_data.ap_tracking),
hint_code::SPLIT_INT_ASSERT_RANGE => {
split_int_assert_range(vm, &hint_data.ids_data, &hint_data.ap_tracking)
Expand Down
9 changes: 9 additions & 0 deletions src/hint_processor/builtin_hint_processor/hint_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@ assert value < ids.UPPER_BOUND, f'{value} is outside of the range [0, 2**250).'
# Calculation for the assertion.
ids.high, ids.low = divmod(ids.value, ids.SHIFT)"#;

pub const IS_250_BITS: &str = r#"ids.is_250 = 1 if ids.addr < 2**250 else 0"#;

pub const IS_ADDR_BOUNDED: &str = r#"# Verify the assumptions on the relationship between 2**250, ADDR_BOUND and PRIME.
ADDR_BOUND = ids.ADDR_BOUND % PRIME
assert (2**250 < ADDR_BOUND <= 2**251) and (2 * 2**250 < PRIME) and (
ADDR_BOUND * 2 > PRIME), \
'normalize_address() cannot be used with the current constants.'
ids.is_small = 1 if ids.addr < ADDR_BOUND else 0"#;

pub const SPLIT_INT: &str = r#"memory[ids.output] = res = (int(ids.value) % PRIME) % ids.base
assert res < ids.bound, f'split_int(): Limb {res} is out of range.'"#;

Expand Down
194 changes: 192 additions & 2 deletions src/hint_processor/builtin_hint_processor/math_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ use num_traits::{Signed, Zero};

use super::hint_utils::get_maybe_relocatable_from_var_name;

const ADDR_BOUND: &str = "starkware.starknet.common.storage.ADDR_BOUND";

//Implements hint: memory[ap] = 0 if 0 <= (ids.a % PRIME) < range_check_builtin.bound else 1
pub fn is_nn(
vm: &mut VirtualMachine,
Expand Down Expand Up @@ -565,6 +567,65 @@ pub fn assert_250_bit(
insert_value_from_var_name("low", low, vm, ids_data, ap_tracking)
}

// Implements hint:
// %{ ids.is_250 = 1 if ids.addr < 2**250 else 0 %}
pub fn is_250_bits(
vm: &mut VirtualMachine,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let addr = get_integer_from_var_name("addr", vm, ids_data, ap_tracking)?;

// Main logic: ids.is_250 = 1 if ids.addr < 2**250 else 0
let is_250 = Felt252::from((addr.as_ref().bits() <= 250) as u8);

insert_value_from_var_name("is_250", is_250, vm, ids_data, ap_tracking)
}

/*
Implements hint:
%{
# Verify the assumptions on the relationship between 2**250, ADDR_BOUND and PRIME.
ADDR_BOUND = ids.ADDR_BOUND % PRIME
assert (2**250 < ADDR_BOUND <= 2**251) and (2 * 2**250 < PRIME) and (
ADDR_BOUND * 2 > PRIME), \
'normalize_address() cannot be used with the current constants.'
ids.is_small = 1 if ids.addr < ADDR_BOUND else 0
%}
*/
pub fn is_addr_bounded(
vm: &mut VirtualMachine,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
constants: &HashMap<String, Felt252>,
) -> Result<(), HintError> {
let addr = get_integer_from_var_name("addr", vm, ids_data, ap_tracking)?;
let prime = Felt252::prime();

let addr_bound = constants
.get(ADDR_BOUND)
.ok_or(HintError::MissingConstant(ADDR_BOUND))?
.to_biguint();

let lower_bound = BigUint::one() << 250_u32;
let upper_bound = BigUint::one() << 251_u32;

// assert (2**250 < ADDR_BOUND <= 2**251) and (2 * 2**250 < PRIME) and (
// ADDR_BOUND * 2 > PRIME), \
// 'normalize_address() cannot be used with the current constants.'
// The second check is not needed, as it's true for the CAIRO_PRIME
if !(lower_bound < addr_bound && addr_bound <= upper_bound && (&addr_bound << 1_u32) > prime) {
return Err(HintError::AssertionFailed(
"normalize_address() cannot be used with the current constants.".to_string(),
));
}

// Main logic: ids.is_small = 1 if ids.addr < ADDR_BOUND else 0
let is_small = Felt252::from((addr.as_ref() < &Felt252::from(addr_bound)) as u8);

insert_value_from_var_name("is_small", is_small, vm, ids_data, ap_tracking)
}

/*
Implements hint:
%{
Expand Down Expand Up @@ -1771,7 +1832,7 @@ mod tests {
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn run_assert_250_bit_valid() {
let hint_code = "from starkware.cairo.common.math_utils import as_int\n\n# Correctness check.\nvalue = as_int(ids.value, PRIME) % PRIME\nassert value < ids.UPPER_BOUND, f'{value} is outside of the range [0, 2**250).'\n\n# Calculation for the assertion.\nids.high, ids.low = divmod(ids.value, ids.SHIFT)";
let hint_code = hint_code::ASSERT_250_BITS;
let mut vm = vm!();
//Initialize fp
vm.run_context.fp = 3;
Expand All @@ -1789,7 +1850,7 @@ mod tests {
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn run_assert_250_bit_invalid() {
let hint_code = "from starkware.cairo.common.math_utils import as_int\n\n# Correctness check.\nvalue = as_int(ids.value, PRIME) % PRIME\nassert value < ids.UPPER_BOUND, f'{value} is outside of the range [0, 2**250).'\n\n# Calculation for the assertion.\nids.high, ids.low = divmod(ids.value, ids.SHIFT)";
let hint_code = hint_code::ASSERT_250_BITS;
let mut vm = vm!();
//Initialize fp
vm.run_context.fp = 3;
Expand All @@ -1811,6 +1872,135 @@ mod tests {
);
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn run_is_250_bits_valid() {
let hint_code = "ids.is_250 = 1 if ids.addr < 2**250 else 0";
let mut vm = vm!();
//Initialize fp
vm.run_context.fp = 2;
//Insert ids into memory
vm.segments = segments![((1, 0), 1152251)];
//Create ids
let ids_data = ids_data!["addr", "is_250"];
//Execute the hint
assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(()));
//Check ids.is_low
check_memory![vm.segments.memory, ((1, 1), 1)];
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn run_is_250_bits_invalid() {
let hint_code = "ids.is_250 = 1 if ids.addr < 2**250 else 0";
let mut vm = vm!();
//Initialize fp
vm.run_context.fp = 2;
//Insert ids into memory
//ids.value
vm.segments = segments![(
(1, 0),
(
"3618502788666131106986593281521497120414687020801267626233049500247285301248",
10
)
)];
//Create ids
let ids_data = ids_data!["addr", "is_250"];
//Execute the hint
assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(()));
//Check ids.is_low
check_memory![vm.segments.memory, ((1, 1), 0)];
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn run_is_addr_bounded_ok() {
let hint_code = hint_code::IS_ADDR_BOUNDED;
let mut vm = vm!();
let addr_bound = felt_str!(
"3618502788666131106986593281521497120414687020801267626233049500247285301000"
);
//Initialize fp
vm.run_context.fp = 2;
//Insert ids into memory
vm.segments = segments![(
(1, 0),
(
"1809251394333067160431340899751024102169435851563236335319518532916477952000",
10
)
),];
//Create ids
let ids_data = ids_data!["addr", "is_small"];
//Execute the hint
assert_matches!(
run_hint!(
vm,
ids_data,
hint_code,
exec_scopes_ref!(),
&[(ADDR_BOUND, addr_bound)]
.into_iter()
.map(|(k, v)| (k.to_string(), v))
.collect()
),
Ok(())
);
//Check ids.is_low
check_memory![vm.segments.memory, ((1, 1), 1)];
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn run_is_addr_bounded_assert_fail() {
let hint_code = hint_code::IS_ADDR_BOUNDED;
let mut vm = vm!();
let addr_bound = Felt252::one();
//Initialize fp
vm.run_context.fp = 2;
//Insert ids into memory
vm.segments = segments![(
(1, 0),
(
"3618502788666131106986593281521497120414687020801267626233049500247285301000",
10
)
),];
//Create ids
let ids_data = ids_data!["addr", "is_small"];
//Execute the hint
assert_matches!(
run_hint!(
vm,
ids_data,
hint_code,
exec_scopes_ref!(),
&HashMap::from([(ADDR_BOUND.to_string(), addr_bound)])
),
Err(HintError::AssertionFailed(msg))
if msg == "normalize_address() cannot be used with the current constants."
);
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn run_is_addr_bounded_missing_const() {
let hint_code = hint_code::IS_ADDR_BOUNDED;
let mut vm = vm!();
//Initialize fp
vm.run_context.fp = 2;
//Insert ids into memory
vm.segments = segments![((1, 0), 0),];
//Create ids
let ids_data = ids_data!["addr", "is_small"];
//Execute the hint
assert_matches!(
run_hint!(vm, ids_data, hint_code),
Err(HintError::MissingConstant(ADDR_BOUND))
);
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn run_split_felt_ok() {
Expand Down
7 changes: 7 additions & 0 deletions src/tests/cairo_run_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -918,3 +918,10 @@ fn cairo_run_ec_double_slope() {
let program_data = include_bytes!("../../cairo_programs/ec_double_slope.json");
run_program_simple_with_memory_holes(program_data.as_slice(), 0);
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn cairo_run_normalize_address() {
let program_data = include_bytes!("../../cairo_programs/normalize_address.json");
run_program_simple_with_memory_holes(program_data.as_slice(), 110);
}

0 comments on commit 0bf8ff8

Please sign in to comment.