use starknet::core::crypto::pedersen_hash;
use starknet_crypto::Felt;
use crate::hasher::{byte_size, HasherError, HashingFunction};
use super::super::Hasher;
#[derive(Debug, Clone)]
pub struct StarkPedersenHasher {
block_size_bits: usize,
}
impl Hasher for StarkPedersenHasher {
fn get_name(&self) -> HashingFunction {
HashingFunction::Pedersen
}
fn hash(&self, data: Vec<String>) -> Result<String, HasherError> {
if data.len() < 2 {
return Err(HasherError::InvalidElementsLength);
}
let mut elements = data.into_iter();
let mut hash = elements.next().unwrap();
for element in elements {
hash = self.internal_hash(vec![hash, element])?;
}
Ok(hash)
}
fn is_element_size_valid(&self, element: &str) -> Result<bool, HasherError> {
let size = byte_size(element);
if size <= self.block_size_bits {
Ok(true)
} else {
Err(HasherError::InvalidElementSize {
element_size: size,
block_size_bits: self.block_size_bits,
})
}
}
fn hash_single(&self, data: &str) -> Result<String, HasherError> {
self.hash(vec![data.to_string(), "".to_string()])
}
fn get_genesis(&self) -> Result<String, HasherError> {
let genesis_str = "brave new world";
self.hash_single(genesis_str)
}
fn get_block_size_bits(&self) -> usize {
self.block_size_bits
}
}
impl StarkPedersenHasher {
fn internal_hash(&self, data: Vec<String>) -> Result<String, HasherError> {
if data.len() != 2 {
return Err(HasherError::InvalidElementsLength);
}
for element in &data {
self.is_element_size_valid(element)?;
}
let result =
pedersen_hash(&Felt::from_hex(&data[0])?, &Felt::from_hex(&data[1])?).to_bytes_be();
let padded_hex_str = format!("0x{:0>64}", hex::encode(result));
Ok(padded_hex_str)
}
pub fn new() -> Self {
Self {
block_size_bits: 252,
}
}
}
impl Default for StarkPedersenHasher {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stark_pedersen_hasher_cairo_playground_reference_1() {
let hasher = StarkPedersenHasher::new();
let data = vec![
"0x13a".to_string(),
"0x9f".to_string(),
];
let hash = hasher.hash(data).unwrap();
assert_eq!(
hash,
"0x00ae4c67cf8deb4f68f6bad0ce61be81097cf082b4bfa83c637af99a978fa9bd",
"Hashes do not match"
);
}
#[test]
fn test_stark_pedersen_hasher_cairo_playground_reference_2() {
let hasher = StarkPedersenHasher::new();
let data = vec![
"0x13a".to_string(),
"0x9f".to_string(),
"0x109".to_string(),
"0x166".to_string(),
];
let hash = hasher.hash(data).unwrap();
assert_eq!(
hash,
"0x040b0a3d05cf798d507d2b4b2725bb28b00d16c0ceefa2222aa04cc88518d030",
"Hashes do not match"
);
}
}