diff --git a/Cargo.toml b/Cargo.toml index 1613229a..cc91ac9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ indexmap = { version = "1.9.2", features = ["serde"] } once_cell = { version = "1.16.0" } serde = { version = "1.0.130", features = ["derive", "rc"] } serde_json = { version = "1.0.81" } +sha3 = { version = "0.10.6"} starknet-crypto = { version = "0.2.0" } thiserror = { version = "1.0.31" } web3 = { version = "0.18.0" } diff --git a/src/hash.rs b/src/hash.rs index 1c6ed00b..83953984 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -6,7 +6,9 @@ use std::fmt::{Debug, Display}; use std::io::Error; use serde::{Deserialize, Serialize}; +use sha3::Digest; use starknet_crypto::{pedersen_hash as starknet_crypto_pedersen_hash, FieldElement}; +use web3::types::U256; use crate::serde_utils::{ bytes_from_hex_str, hex_str_from_bytes, BytesAsHex, NonPrefixedBytesAsHex, PrefixedBytesAsHex, @@ -19,6 +21,9 @@ pub const GENESIS_HASH: &str = "0x0"; // Felt encoding constants. const CHOOSER_FULL: u8 = 15; const CHOOSER_HALF: u8 = 14; +/// The MASK equals to U256::pow(U256::from(2), U256::from(250)) - U256::from(1) +const MASK: U256 = + U256([18446744073709551615, 18446744073709551615, 18446744073709551615, 288230376151711743]); /// An alias for [`StarkFelt`]. /// The output of the [Pedersen hash](https://docs.starknet.io/documentation/architecture_and_concepts/Hashing/hash-functions/#pedersen_hash). @@ -43,6 +48,17 @@ pub fn pedersen_hash_array(felts: &[StarkFelt]) -> StarkHash { pedersen_hash(¤t_hash, &data_len) } +/// Computes Starknet Keccak Hash, as defined +/// in +pub fn starknet_keccak(data: &[u8]) -> Result { + let keccak256 = sha3::Keccak256::digest(data); + let number = U256::from_big_endian(keccak256.as_slice()); + let masked_number = number & MASK; + let mut res_bytes: [u8; 32] = [0; 32]; + masked_number.to_big_endian(&mut res_bytes); + StarkFelt::new(res_bytes) +} + // TODO: Move to a different crate. /// The StarkNet [field element](https://docs.starknet.io/documentation/architecture_and_concepts/Hashing/hash-functions/#domain_and_range). #[derive(Copy, Clone, Eq, PartialEq, Default, Hash, Deserialize, Serialize, PartialOrd, Ord)] diff --git a/src/hash_test.rs b/src/hash_test.rs index 207a9809..d65f9c36 100644 --- a/src/hash_test.rs +++ b/src/hash_test.rs @@ -1,4 +1,6 @@ -use crate::hash::{pedersen_hash, pedersen_hash_array, StarkFelt}; +use web3::types::U256; + +use crate::hash::{pedersen_hash, pedersen_hash_array, starknet_keccak, StarkFelt, MASK}; use crate::stark_felt; #[test] @@ -67,3 +69,20 @@ fn hash_serde() { assert_eq!(bytes, d.0); } } + +#[test] +fn starknet_keccak_mask() { + let mask: U256 = U256::pow(U256::from(2), U256::from(250)) - U256::from(1); + assert_eq!(mask, MASK); +} + +#[test] +fn starknet_keccak_calculation() { + // Test result is taken from tutorial. + // increase_balance function selector can be found in contract_compiled.json -> + // entry_points_by_type object. + let expected_keccak_felt = + stark_felt!("0x362398bec32bc0ebb411203221a35a0301193a96f317ebe5e40be9f60d15320"); + let increase_balance_keccak_felt = starknet_keccak("increase_balance".as_bytes()).unwrap(); + assert_eq!(increase_balance_keccak_felt, expected_keccak_felt); +}