diff --git a/crates/blockifier/src/execution/native/syscall_handler.rs b/crates/blockifier/src/execution/native/syscall_handler.rs index 0c0869d535..7b8dc99334 100644 --- a/crates/blockifier/src/execution/native/syscall_handler.rs +++ b/crates/blockifier/src/execution/native/syscall_handler.rs @@ -486,47 +486,15 @@ impl<'state> StarknetSyscallHandler for &mut NativeSyscallHandler<'state> { fn keccak(&mut self, input: &[u64], remaining_gas: &mut u64) -> SyscallResult { self.pre_execute_syscall(remaining_gas, self.gas_costs().keccak_gas_cost)?; - const KECCAK_FULL_RATE_IN_WORDS: usize = 17; - let input_length = input.len(); - let (n_rounds, remainder) = num_integer::div_rem(input_length, KECCAK_FULL_RATE_IN_WORDS); - - if remainder != 0 { - return Err(self.handle_error( - remaining_gas, - SyscallExecutionError::SyscallError { - error_data: vec![Felt::from_hex(INVALID_INPUT_LENGTH_ERROR).unwrap()], - }, - )); - } - - // TODO(Ori, 1/2/2024): Write an indicative expect message explaining why the conversion - // works. - let n_rounds = u64::try_from(n_rounds).expect("Failed to convert usize to u64."); - let gas_cost = n_rounds * self.gas_costs().keccak_round_cost_gas_cost; - - if gas_cost > *remaining_gas { - return Err(self.handle_error( - remaining_gas, - SyscallExecutionError::SyscallError { - error_data: vec![Felt::from_hex(OUT_OF_GAS_ERROR).unwrap()], - }, - )); - } - *remaining_gas -= gas_cost; - let mut state = [0u64; 25]; - for chunk in input.chunks(KECCAK_FULL_RATE_IN_WORDS) { - for (i, val) in chunk.iter().enumerate() { - state[i] ^= val; - } - keccak::f1600(&mut state) + match self.base.keccak_base(input, input_length, remaining_gas) { + Ok((state, _n_rounds)) => Ok(U256 { + hi: u128::from(state[2]) | (u128::from(state[3]) << 64), + lo: u128::from(state[0]) | (u128::from(state[1]) << 64), + }), + Err(e) => Err(self.handle_error(remaining_gas, e.into())), } - - Ok(U256 { - hi: u128::from(state[2]) | (u128::from(state[3]) << 64), - lo: u128::from(state[0]) | (u128::from(state[1]) << 64), - }) } fn secp256k1_new( diff --git a/crates/blockifier/src/execution/syscalls/mod.rs b/crates/blockifier/src/execution/syscalls/mod.rs index 13d4dd117a..1f11fd8999 100644 --- a/crates/blockifier/src/execution/syscalls/mod.rs +++ b/crates/blockifier/src/execution/syscalls/mod.rs @@ -37,7 +37,6 @@ use crate::execution::execution_utils::{ write_maybe_relocatable, ReadOnlySegment, }; -use crate::execution::syscalls::hint_processor::{INVALID_INPUT_LENGTH_ERROR, OUT_OF_GAS_ERROR}; use crate::execution::syscalls::syscall_base::SyscallResult; use crate::versioned_constants::{EventLimits, VersionedConstants}; @@ -639,45 +638,24 @@ pub fn keccak( ) -> SyscallResult { let input_length = (request.input_end - request.input_start)?; - const KECCAK_FULL_RATE_IN_WORDS: usize = 17; - let (n_rounds, remainder) = num_integer::div_rem(input_length, KECCAK_FULL_RATE_IN_WORDS); - - if remainder != 0 { - return Err(SyscallExecutionError::SyscallError { - error_data: vec![ - Felt::from_hex(INVALID_INPUT_LENGTH_ERROR).map_err(SyscallExecutionError::from)?, - ], - }); - } - - // TODO(Ori, 1/2/2024): Write an indicative expect message explaining why the conversion works. - let n_rounds_as_u64 = u64::try_from(n_rounds).expect("Failed to convert usize to u64."); - let gas_cost = n_rounds_as_u64 * syscall_handler.gas_costs().keccak_round_cost_gas_cost; - if gas_cost > *remaining_gas { - let out_of_gas_error = - Felt::from_hex(OUT_OF_GAS_ERROR).map_err(SyscallExecutionError::from)?; + let data = vm.get_integer_range(request.input_start, input_length)?; + let data_u64: &[u64] = &data + .iter() + .map(|felt| { + felt.to_u64().ok_or_else(|| SyscallExecutionError::InvalidSyscallInput { + input: **felt, + info: "Invalid input for the keccak syscall.".to_string(), + }) + }) + .collect::, _>>()?; - return Err(SyscallExecutionError::SyscallError { error_data: vec![out_of_gas_error] }); - } - *remaining_gas -= gas_cost; + let (state, n_rounds) = + syscall_handler.base.keccak_base(data_u64, input_length, remaining_gas)?; // For the keccak system call we want to count the number of rounds rather than the number of // syscall invocations. syscall_handler.increment_syscall_count_by(&SyscallSelector::Keccak, n_rounds); - let data = vm.get_integer_range(request.input_start, input_length)?; - - let mut state = [0u64; 25]; - for chunk in data.chunks(KECCAK_FULL_RATE_IN_WORDS) { - for (i, val) in chunk.iter().enumerate() { - state[i] ^= val.to_u64().ok_or_else(|| SyscallExecutionError::InvalidSyscallInput { - input: **val, - info: String::from("Invalid input for the keccak syscall."), - })?; - } - keccak::f1600(&mut state) - } - Ok(KeccakResponse { result_low: (Felt::from(state[1]) * Felt::TWO.pow(64_u128)) + Felt::from(state[0]), result_high: (Felt::from(state[3]) * Felt::TWO.pow(64_u128)) + Felt::from(state[2]), diff --git a/crates/blockifier/src/execution/syscalls/syscall_base.rs b/crates/blockifier/src/execution/syscalls/syscall_base.rs index 54bc82d1ce..c05b479925 100644 --- a/crates/blockifier/src/execution/syscalls/syscall_base.rs +++ b/crates/blockifier/src/execution/syscalls/syscall_base.rs @@ -15,11 +15,14 @@ use crate::execution::syscalls::hint_processor::{ SyscallExecutionError, BLOCK_NUMBER_OUT_OF_RANGE_ERROR, ENTRYPOINT_FAILED_ERROR, + INVALID_INPUT_LENGTH_ERROR, + OUT_OF_GAS_ERROR, }; use crate::state::state_api::State; use crate::transaction::account_transaction::is_cairo1; pub type SyscallResult = Result; +pub const KECCAK_FULL_RATE_IN_WORDS: usize = 17; /// This file is for sharing common logic between Native and VM syscall implementations. @@ -207,4 +210,44 @@ impl<'state> SyscallHandlerBase<'state> { .expect("Missing contract revert info.") .original_values = std::mem::take(&mut self.original_values); } + + pub fn keccak_base( + &mut self, + input: &[u64], + input_length: usize, + remaining_gas: &mut u64, + ) -> SyscallResult<([u64; 4], usize)> { + let (n_rounds, remainder) = num_integer::div_rem(input_length, KECCAK_FULL_RATE_IN_WORDS); + + if remainder != 0 { + return Err(SyscallExecutionError::SyscallError { + error_data: vec![ + Felt::from_hex(INVALID_INPUT_LENGTH_ERROR) + .map_err(SyscallExecutionError::from)?, + ], + }); + } + // TODO(Ori, 1/2/2024): Write an indicative expect message explaining why the conversion + // works. + let n_rounds_as_u64 = u64::try_from(n_rounds).expect("Failed to convert usize to u64."); + let gas_cost = n_rounds_as_u64 * self.context.gas_costs().keccak_round_cost_gas_cost; + + if gas_cost > *remaining_gas { + let out_of_gas_error = + Felt::from_hex(OUT_OF_GAS_ERROR).map_err(SyscallExecutionError::from)?; + + return Err(SyscallExecutionError::SyscallError { error_data: vec![out_of_gas_error] }); + } + *remaining_gas -= gas_cost; + + let mut state = [0u64; 25]; + for chunk in input.chunks(KECCAK_FULL_RATE_IN_WORDS) { + for (i, val) in chunk.iter().enumerate() { + state[i] ^= val; + } + keccak::f1600(&mut state) + } + + Ok((state[..4].try_into().expect("Slice with incorrect length"), n_rounds)) + } }