From 1feaba0de832e01bfd09ba56aa2c1655a853e82c Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Wed, 26 Apr 2023 00:54:21 -0300 Subject: [PATCH] feat(hints): NewHint#67 cairo_keccak.json `_block_permutation` (#1046) * feat(hint): implement whitelisted hint #67 * Fix typo * Fix CHANGELOG * Fix test --- CHANGELOG.md | 17 +++- cairo_programs/_keccak_alternative_hint.cairo | 80 +++++++++++++++---- .../builtin_hint_processor_definition.rs | 13 +-- .../cairo_keccak/keccak_hints.rs | 64 +++++++++++++-- .../builtin_hint_processor/hint_code.rs | 9 ++- src/tests/cairo_run_test.rs | 2 +- 6 files changed, 155 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59cfd6cff5..128205ee8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ #### Upcoming Changes +* Implement hint for `starkware.cairo.common.cairo_keccak.keccak._block_permutation` as described by whitelist `starknet/security/whitelists/cairo_keccak.json` [#1046](https://github.com/lambdaclass/cairo-rs/pull/1046) + + `BuiltinHintProcessor` now supports the following hint: + + ```python + %{ + from starkware.cairo.common.cairo_keccak.keccak_utils import keccak_func + _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) + assert 0 <= _keccak_state_size_felts < 100 + output_values = keccak_func(memory.get_range( + ids.keccak_ptr_start, _keccak_state_size_felts)) + segments.write_arg(ids.output, output_values) + %} + ``` + * Implement hint on cairo_blake2s whitelist [#1040](https://github.com/lambdaclass/cairo-rs/pull/1040) `BuiltinHintProcessor` now supports the following hint: @@ -1089,4 +1104,4 @@ * `pub fn from_vm_error(runner: &CairoRunner, error: VirtualMachineError, pc: usize) -> Self` is now `pub fn from_vm_error(runner: &CairoRunner, vm: &VirtualMachine, error: VirtualMachineError) -> Self` * `pub fn get_location(pc: &usize, runner: &CairoRunner) -> Option` is now `pub fn get_location(pc: usize, runner: &CairoRunner) -> Option` * `pub fn decode_instruction(encoded_instr: i64, mut imm: Option) -> Result` is now `pub fn decode_instruction(encoded_instr: i64, mut imm: Option<&BigInt>) -> Result` - * `VmExcepion` field's string format now mirror their cairo-lang conterparts. + * `VmException` fields' string format now mirrors their cairo-lang counterparts. diff --git a/cairo_programs/_keccak_alternative_hint.cairo b/cairo_programs/_keccak_alternative_hint.cairo index d36504d874..d3f725af38 100644 --- a/cairo_programs/_keccak_alternative_hint.cairo +++ b/cairo_programs/_keccak_alternative_hint.cairo @@ -11,15 +11,15 @@ from starkware.cairo.common.cairo_builtins import BitwiseBuiltin from starkware.cairo.common.alloc import alloc from starkware.cairo.common.serialize import serialize_word -func _keccak{range_check_ptr, bitwise_ptr: BitwiseBuiltin*, keccak_ptr: felt*}( +func _keccak_0_10_3{range_check_ptr, bitwise_ptr: BitwiseBuiltin*, keccak_ptr: felt*}( inputs: felt*, n_bytes: felt, state: felt* ) -> (output: felt*) { alloc_locals; if (nondet %{ ids.n_bytes >= ids.KECCAK_FULL_RATE_IN_BYTES %} != 0) { _prepare_block(inputs=inputs, n_bytes=KECCAK_FULL_RATE_IN_BYTES, state=state); - _block_permutation(); + _block_permutation_0_10_3(); - return _keccak( + return _keccak_0_10_3( inputs=inputs + KECCAK_FULL_RATE_IN_WORDS, n_bytes=n_bytes - KECCAK_FULL_RATE_IN_BYTES, state=keccak_ptr - KECCAK_STATE_SIZE_FELTS, @@ -29,12 +29,12 @@ func _keccak{range_check_ptr, bitwise_ptr: BitwiseBuiltin*, keccak_ptr: felt*}( assert_nn_le(n_bytes, KECCAK_FULL_RATE_IN_BYTES - 1); _prepare_block(inputs=inputs, n_bytes=n_bytes, state=state); - _block_permutation(); + _block_permutation_0_10_3(); return (output=keccak_ptr - KECCAK_STATE_SIZE_FELTS); } -func _block_permutation{keccak_ptr: felt*}() { +func _block_permutation_0_10_3{keccak_ptr: felt*}() { %{ from starkware.cairo.common.cairo_keccak.keccak_utils import keccak_func _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) @@ -49,17 +49,7 @@ func _block_permutation{keccak_ptr: felt*}() { return (); } -func fill_array(array: felt*, base: felt, array_length: felt, iterator: felt) { - if (iterator == array_length) { - return (); - } - - assert array[iterator] = base; - - return fill_array(array, base, array_length, iterator + 1); -} - -func main{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() { +func run_0_10_3{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() { alloc_locals; let (output: felt*) = alloc(); @@ -75,7 +65,7 @@ func main{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() { let n_bytes = 24; - let (res: felt*) = _keccak{keccak_ptr=keccak_output}( + let (res: felt*) = _keccak_0_10_3{keccak_ptr=keccak_output}( inputs=inputs_start, n_bytes=n_bytes, state=state_start ); @@ -86,3 +76,59 @@ func main{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() { return (); } + +func _block_permutation_cairo_keccak{output_ptr: felt*, keccak_ptr: felt*}() { + alloc_locals; + let output = output_ptr; + let keccak_ptr_start = keccak_ptr - KECCAK_STATE_SIZE_FELTS; + %{ + from starkware.cairo.common.cairo_keccak.keccak_utils import keccak_func + _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) + assert 0 <= _keccak_state_size_felts < 100 + output_values = keccak_func(memory.get_range( + ids.keccak_ptr_start, _keccak_state_size_felts)) + segments.write_arg(ids.output, output_values) + %} + let keccak_ptr = keccak_ptr + KECCAK_STATE_SIZE_FELTS; + + return (); +} + +func run_cairo_keccak{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() { + alloc_locals; + + let (output: felt*) = alloc(); + let keccak_output = output; + + let (inputs: felt*) = alloc(); + let inputs_start = inputs; + fill_array(inputs, 9, 3, 0); + + let (state: felt*) = alloc(); + let state_start = state; + fill_array(state, 5, 25, 0); + + let n_bytes = 24; + + _prepare_block{keccak_ptr=output_ptr}(inputs=inputs, n_bytes=n_bytes, state=state); + _block_permutation_cairo_keccak{keccak_ptr=output_ptr}(); + + return (); +} + +func fill_array(array: felt*, base: felt, array_length: felt, iterator: felt) { + if (iterator == array_length) { + return (); + } + + assert array[iterator] = base; + + return fill_array(array, base, array_length, iterator + 1); +} + +func main{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() { + run_0_10_3(); + run_cairo_keccak(); + + 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 1265ae020e..6589272b46 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 @@ -21,9 +21,9 @@ use crate::{ blake2s_add_uint256, blake2s_add_uint256_bigend, compute_blake2s, finalize_blake2s, }, cairo_keccak::keccak_hints::{ - block_permutation, cairo_keccak_finalize_v1, cairo_keccak_finalize_v2, - compare_bytes_in_word_nondet, compare_keccak_full_rate_in_bytes_nondet, - keccak_write_args, + block_permutation_v1, block_permutation_v2, cairo_keccak_finalize_v1, + cairo_keccak_finalize_v2, compare_bytes_in_word_nondet, + compare_keccak_full_rate_in_bytes_nondet, keccak_write_args, }, dict_hint_utils::{ default_dict_new, dict_new, dict_read, dict_squash_copy_dict, @@ -530,8 +530,11 @@ impl HintProcessor for BuiltinHintProcessor { constants, ) } - hint_code::BLOCK_PERMUTATION | hint_code::BLOCK_PERMUTATION_WHITELIST => { - block_permutation(vm, &hint_data.ids_data, &hint_data.ap_tracking, constants) + hint_code::BLOCK_PERMUTATION | hint_code::BLOCK_PERMUTATION_WHITELIST_V1 => { + block_permutation_v1(vm, &hint_data.ids_data, &hint_data.ap_tracking, constants) + } + hint_code::BLOCK_PERMUTATION_WHITELIST_V2 => { + block_permutation_v2(vm, &hint_data.ids_data, &hint_data.ap_tracking, constants) } hint_code::CAIRO_KECCAK_FINALIZE_V1 => { cairo_keccak_finalize_v1(vm, &hint_data.ids_data, &hint_data.ap_tracking, constants) diff --git a/src/hint_processor/builtin_hint_processor/cairo_keccak/keccak_hints.rs b/src/hint_processor/builtin_hint_processor/cairo_keccak/keccak_hints.rs index 6edfd73dff..1df1b0dcdc 100644 --- a/src/hint_processor/builtin_hint_processor/cairo_keccak/keccak_hints.rs +++ b/src/hint_processor/builtin_hint_processor/cairo_keccak/keccak_hints.rs @@ -4,6 +4,7 @@ use crate::stdlib::{ prelude::*, }; use crate::{ + felt::Felt252, hint_processor::{ builtin_hint_processor::hint_utils::{ get_integer_from_var_name, get_ptr_from_var_name, insert_value_into_ap, @@ -17,7 +18,6 @@ use crate::{ vm_core::VirtualMachine, }, }; -use felt::Felt252; use num_traits::{ToPrimitive, Zero}; // Constants in package "starkware.cairo.common.cairo_keccak.keccak". @@ -123,7 +123,16 @@ pub fn compare_keccak_full_rate_in_bytes_nondet( } /* -Implements hint: +Implements hints: + %{ + from starkware.cairo.common.cairo_keccak.keccak_utils import keccak_func + _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) + assert 0 <= _keccak_state_size_felts < 100 + + output_values = keccak_func(memory.get_range( + ids.keccak_ptr - _keccak_state_size_felts, _keccak_state_size_felts)) + segments.write_arg(ids.keccak_ptr, output_values) + %} %{ from starkware.cairo.common.cairo_keccak.keccak_utils import keccak_func _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) @@ -134,7 +143,7 @@ Implements hint: segments.write_arg(ids.keccak_ptr, output_values) %} */ -pub fn block_permutation( +pub(crate) fn block_permutation_v1( vm: &mut VirtualMachine, ids_data: &HashMap, ap_tracking: &ApTracking, @@ -143,7 +152,6 @@ pub fn block_permutation( let keccak_state_size_felts = constants .get(KECCAK_STATE_SIZE_FELTS) .ok_or(HintError::MissingConstant(KECCAK_STATE_SIZE_FELTS))?; - if keccak_state_size_felts >= &Felt252::new(100_i32) { return Err(HintError::InvalidKeccakStateSizeFelt252s( keccak_state_size_felts.clone(), @@ -157,7 +165,6 @@ pub fn block_permutation( (keccak_ptr - keccak_state_size_felts)?, keccak_state_size_felts, ); - let mut u64_values = maybe_reloc_vec_to_u64_array(&values)? .try_into() .map_err(|_| VirtualMachineError::SliceToArrayError)?; @@ -174,6 +181,53 @@ pub fn block_permutation( Ok(()) } +/* +Implements hint: + %{ + from starkware.cairo.common.cairo_keccak.keccak_utils import keccak_func + _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) + assert 0 <= _keccak_state_size_felts < 100 + output_values = keccak_func(memory.get_range( + ids.keccak_ptr_start, _keccak_state_size_felts)) + segments.write_arg(ids.output, output_values) + %} +*/ +pub(crate) fn block_permutation_v2( + vm: &mut VirtualMachine, + ids_data: &HashMap, + ap_tracking: &ApTracking, + constants: &HashMap, +) -> Result<(), HintError> { + let keccak_state_size_felts = constants + .get(KECCAK_STATE_SIZE_FELTS) + .ok_or(HintError::MissingConstant(KECCAK_STATE_SIZE_FELTS))?; + if keccak_state_size_felts >= &Felt252::from(100_i32) { + return Err(HintError::InvalidKeccakStateSizeFelt252s( + keccak_state_size_felts.clone(), + )); + } + + let keccak_ptr = get_ptr_from_var_name("keccak_ptr_start", vm, ids_data, ap_tracking)?; + + let keccak_state_size_felts = keccak_state_size_felts.to_usize().unwrap(); + let values = vm.get_range(keccak_ptr, keccak_state_size_felts); + let mut u64_values = maybe_reloc_vec_to_u64_array(&values)? + .try_into() + .map_err(|_| VirtualMachineError::SliceToArrayError)?; + + // this function of the keccak crate is the one used instead of keccak_func from + // keccak_utils.py + keccak::f1600(&mut u64_values); + + let bigint_values = u64_array_to_mayberelocatable_vec(&u64_values); + + let output = get_ptr_from_var_name("output", vm, ids_data, ap_tracking)?; + vm.write_arg(output, &bigint_values) + .map_err(HintError::Memory)?; + + Ok(()) +} + fn cairo_keccak_finalize( vm: &mut VirtualMachine, ids_data: &HashMap, diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index 5fec60e473..1bb2a0b63e 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -735,7 +735,7 @@ segments.write_arg(ids.keccak_ptr, output_values)"#; // The 0.10.3 whitelist uses this variant (instead of the one used by the common library), but both hints have the same behaviour // We should check for future refactors that may discard one of the variants -pub const BLOCK_PERMUTATION_WHITELIST: &str = r#"from starkware.cairo.common.cairo_keccak.keccak_utils import keccak_func +pub const BLOCK_PERMUTATION_WHITELIST_V1: &str = r#"from starkware.cairo.common.cairo_keccak.keccak_utils import keccak_func _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) assert 0 <= _keccak_state_size_felts < 100 @@ -743,6 +743,13 @@ output_values = keccak_func(memory.get_range( ids.keccak_ptr - _keccak_state_size_felts, _keccak_state_size_felts)) segments.write_arg(ids.keccak_ptr, output_values)"#; +pub const BLOCK_PERMUTATION_WHITELIST_V2: &str = r#"from starkware.cairo.common.cairo_keccak.keccak_utils import keccak_func +_keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) +assert 0 <= _keccak_state_size_felts < 100 +output_values = keccak_func(memory.get_range( + ids.keccak_ptr_start, _keccak_state_size_felts)) +segments.write_arg(ids.output, output_values)"#; + pub const CAIRO_KECCAK_FINALIZE_V1: &str = r#"# Add dummy pairs of input and output. _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) _block_size = int(ids.BLOCK_SIZE) diff --git a/src/tests/cairo_run_test.rs b/src/tests/cairo_run_test.rs index f0a4273c9a..6c0cf3858f 100644 --- a/src/tests/cairo_run_test.rs +++ b/src/tests/cairo_run_test.rs @@ -713,7 +713,7 @@ fn mul_s_inv() { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn keccak_alternative_hint() { let program_data = include_bytes!("../../cairo_programs/_keccak_alternative_hint.json"); - run_program_simple_with_memory_holes(program_data.as_slice(), 23); + run_program_simple_with_memory_holes(program_data.as_slice(), 24); } #[test]