diff --git a/crates/contracts/src/cairo1_helpers.cairo b/crates/contracts/src/cairo1_helpers.cairo index 9f56c7e7d..500559ef2 100644 --- a/crates/contracts/src/cairo1_helpers.cairo +++ b/crates/contracts/src/cairo1_helpers.cairo @@ -33,10 +33,27 @@ trait IHelpers { /// `Block number out of range` - If the block number is greater than `current_block - 10`. /// `0`: The block number is inferior to `first_v0_12_0_block`. fn get_block_hash(self: @T, block_number: u64) -> felt252; + + /// Computes the keccak hash of the provided data. + /// + /// The data is expected to be an array of full 64-bit words. + /// The last u64-word to hash may be incomplete and is provided separately. + /// # Arguments + /// + /// * `words` - The full 64-bit words to hash. + /// * `last_input_word` - The last word to hash. + /// * `last_input_num_bytes` - The number of bytes in the last word. + /// + /// # Returns + /// The EVM-compatible keccak hash of the provided data. + fn keccak( + self: @T, words: Array, last_input_word: u64, last_input_num_bytes: usize + ) -> u256; } #[starknet::contract] mod Cairo1Helpers { + use core::keccak::cairo_keccak; use core::traits::Into; use core::{starknet, starknet::SyscallResultTrait}; use evm::errors::EVMError; @@ -47,6 +64,7 @@ mod Cairo1Helpers { use evm::precompiles::sha256::Sha256; use super::{IPrecompiles, IHelpers}; + use utils::helpers::U256Trait; #[storage] struct Storage {} @@ -76,5 +94,14 @@ mod Cairo1Helpers { fn get_block_hash(self: @ContractState, block_number: u64) -> felt252 { starknet::get_block_hash_syscall(block_number).unwrap_syscall() } + + fn keccak( + self: @ContractState, + mut words: Array, + last_input_word: u64, + last_input_num_bytes: usize + ) -> u256 { + cairo_keccak(ref words, last_input_word, last_input_num_bytes).reverse_endianness() + } } } diff --git a/crates/contracts/src/tests.cairo b/crates/contracts/src/tests.cairo index fba50831d..1a1ba89c4 100644 --- a/crates/contracts/src/tests.cairo +++ b/crates/contracts/src/tests.cairo @@ -1,3 +1,4 @@ +mod test_cairo1_helpers; mod test_contract_account; mod test_data; diff --git a/crates/contracts/src/tests/test_cairo1_helpers.cairo b/crates/contracts/src/tests/test_cairo1_helpers.cairo new file mode 100644 index 000000000..6c4654665 --- /dev/null +++ b/crates/contracts/src/tests/test_cairo1_helpers.cairo @@ -0,0 +1,20 @@ +use contracts::cairo1_helpers::Cairo1Helpers; +use utils::helpers::BytesUsedTrait; + +#[test] +fn test_keccak() { + // "Hello world!" + // where: + // 8031924123371070792 == int.from_bytes(b'Hello wo', 'little') + // 560229490 == int.from_bytes(b'rld!', 'little') + let input = array![8031924123371070792]; + let last_input_word: u64 = 560229490; + let last_input_num_bytes = last_input_word.bytes_used(); + let state = Cairo1Helpers::contract_state_for_testing(); + + let res = Cairo1Helpers::Helpers::keccak( + @state, input, last_input_word, last_input_num_bytes.into() + ); + + assert_eq!(res, 0xecd0e108a98e192af1d2c25055f4e3bed784b5c877204e73219a5203251feaab); +}