diff --git a/crates/blockifier/src/execution/native/syscall_handler.rs b/crates/blockifier/src/execution/native/syscall_handler.rs index d0cabe9c10..21943382ee 100644 --- a/crates/blockifier/src/execution/native/syscall_handler.rs +++ b/crates/blockifier/src/execution/native/syscall_handler.rs @@ -452,47 +452,13 @@ impl<'state> StarknetSyscallHandler for &mut NativeSyscallHandler<'state> { fn keccak(&mut self, input: &[u64], remaining_gas: &mut u64) -> SyscallResult<U256> { 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(input, 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(err) => Err(self.handle_error(remaining_gas, err)), } - - 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 d902bcbfd6..b1d6b0be39 100644 --- a/crates/blockifier/src/execution/syscalls/mod.rs +++ b/crates/blockifier/src/execution/syscalls/mod.rs @@ -30,7 +30,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}; @@ -613,45 +612,23 @@ pub fn keccak( ) -> SyscallResult<KeccakResponse> { 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::<Result<Vec<u64>, _>>()?; - return Err(SyscallExecutionError::SyscallError { error_data: vec![out_of_gas_error] }); - } - *remaining_gas -= gas_cost; + let (state, n_rounds) = syscall_handler.base.keccak(data_u64, 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 5ca3fa8675..99da673ea9 100644 --- a/crates/blockifier/src/execution/syscalls/syscall_base.rs +++ b/crates/blockifier/src/execution/syscalls/syscall_base.rs @@ -21,11 +21,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<T> = Result<T, SyscallExecutionError>; +pub const KECCAK_FULL_RATE_IN_WORDS: usize = 17; /// This file is for sharing common logic between Native and VM syscall implementations. @@ -241,6 +244,47 @@ impl<'state> SyscallHandlerBase<'state> { Ok(raw_retdata) } + pub fn keccak( + &mut self, + input: &[u64], + remaining_gas: &mut u64, + ) -> SyscallResult<([u64; 4], usize)> { + 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(SyscallExecutionError::SyscallError { + error_data: vec![ + Felt::from_hex(INVALID_INPUT_LENGTH_ERROR) + .expect("Failed to parse INVALID_INPUT_LENGTH_ERROR hex string"), + ], + }); + } + // 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) + .expect("Failed to parse OUT_OF_GAS_ERROR hex string"); + + 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)) + } + pub fn finalize(&mut self) { self.context .revert_infos