From 25274c80123596730cd15281a6ae11775fe8361d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= Date: Wed, 26 Apr 2023 13:02:30 -0300 Subject: [PATCH 1/4] Add starknet_in_rust IS_250_BITS hint --- cairo_programs/normalize_address.cairo | 32 ++++++++++ .../builtin_hint_processor_definition.rs | 1 + .../builtin_hint_processor/hint_code.rs | 2 + .../builtin_hint_processor/math_utils.rs | 60 ++++++++++++++++++- src/tests/cairo_run_test.rs | 8 +++ 5 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 cairo_programs/normalize_address.cairo diff --git a/cairo_programs/normalize_address.cairo b/cairo_programs/normalize_address.cairo new file mode 100644 index 0000000000..b54c4daac4 --- /dev/null +++ b/cairo_programs/normalize_address.cairo @@ -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 (); +} diff --git a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index c3c9a2bcea..bc12866708 100644 --- a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -193,6 +193,7 @@ 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_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) diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index 612a88b690..5c4ef9626c 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -98,6 +98,8 @@ 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 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.'"#; diff --git a/src/hint_processor/builtin_hint_processor/math_utils.rs b/src/hint_processor/builtin_hint_processor/math_utils.rs index 54a1797cc0..18eeafb5d7 100644 --- a/src/hint_processor/builtin_hint_processor/math_utils.rs +++ b/src/hint_processor/builtin_hint_processor/math_utils.rs @@ -565,6 +565,21 @@ 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, + 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: %{ @@ -1771,7 +1786,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; @@ -1789,7 +1804,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; @@ -1811,6 +1826,47 @@ mod tests { ); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn run_is_250_bit_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_bit_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_split_felt_ok() { diff --git a/src/tests/cairo_run_test.rs b/src/tests/cairo_run_test.rs index 9d8c6d4e41..c22730b980 100644 --- a/src/tests/cairo_run_test.rs +++ b/src/tests/cairo_run_test.rs @@ -897,3 +897,11 @@ fn ec_double_assign_new_x_v3() { let program_data = include_bytes!("../../cairo_programs/ec_double_assign_new_x_v3.json"); run_program_simple(program_data.as_slice()); } + +// TODO: fails because "is_small" hint isn't implemented +// #[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); +// } From d99c17d336008a2e10b3132940a15f46b04d9b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= Date: Wed, 26 Apr 2023 15:34:50 -0300 Subject: [PATCH 2/4] Add starknet_in_rust IS_ADDR_BOUNDED hint --- .../builtin_hint_processor_definition.rs | 3 + .../builtin_hint_processor/hint_code.rs | 7 + .../builtin_hint_processor/math_utils.rs | 139 +++++++++++++++++- src/tests/cairo_run_test.rs | 13 +- 4 files changed, 153 insertions(+), 9 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index bc12866708..86d93dd0e0 100644 --- a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -194,6 +194,9 @@ impl HintProcessor for BuiltinHintProcessor { 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) diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index 5c4ef9626c..c60718d619 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -100,6 +100,13 @@ 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.'"#; diff --git a/src/hint_processor/builtin_hint_processor/math_utils.rs b/src/hint_processor/builtin_hint_processor/math_utils.rs index 18eeafb5d7..ab958d1690 100644 --- a/src/hint_processor/builtin_hint_processor/math_utils.rs +++ b/src/hint_processor/builtin_hint_processor/math_utils.rs @@ -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, @@ -580,6 +582,51 @@ pub fn is_250_bits( 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, + ap_tracking: &ApTracking, + constants: &HashMap, +) -> 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() + .mod_floor(&prime); + + 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: %{ @@ -1828,7 +1875,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn run_is_250_bit_valid() { + 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 @@ -1845,7 +1892,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn run_is_250_bit_invalid() { + 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 @@ -1867,6 +1914,94 @@ mod tests { 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() { diff --git a/src/tests/cairo_run_test.rs b/src/tests/cairo_run_test.rs index c22730b980..14ae1f7d55 100644 --- a/src/tests/cairo_run_test.rs +++ b/src/tests/cairo_run_test.rs @@ -898,10 +898,9 @@ fn ec_double_assign_new_x_v3() { run_program_simple(program_data.as_slice()); } -// TODO: fails because "is_small" hint isn't implemented -// #[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); -// } +#[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); +} From ec4970b0d14e43e10fbe53fc0430f6e8d0c823ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= Date: Wed, 26 Apr 2023 15:37:25 -0300 Subject: [PATCH 3/4] Update changelog --- CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e0f90c404..4b3275e20c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -147,6 +147,23 @@ Add missing hint on vrf.json lib [#1053](https://github.com/lambdaclass/cairo-rs ``` +* 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: From 590cf597564bf6a235995049e941bfa8fa08d012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= Date: Thu, 27 Apr 2023 10:47:00 -0300 Subject: [PATCH 4/4] Remove unnecessary `mod_floor` --- src/hint_processor/builtin_hint_processor/math_utils.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/math_utils.rs b/src/hint_processor/builtin_hint_processor/math_utils.rs index ab958d1690..04e291ec97 100644 --- a/src/hint_processor/builtin_hint_processor/math_utils.rs +++ b/src/hint_processor/builtin_hint_processor/math_utils.rs @@ -605,8 +605,7 @@ pub fn is_addr_bounded( let addr_bound = constants .get(ADDR_BOUND) .ok_or(HintError::MissingConstant(ADDR_BOUND))? - .to_biguint() - .mod_floor(&prime); + .to_biguint(); let lower_bound = BigUint::one() << 250_u32; let upper_bound = BigUint::one() << 251_u32;