use crate::hasher::{byte_size, HasherError, HashingFunction};
use super::super::Hasher;
use starknet_crypto::{poseidon_hash, poseidon_hash_many, poseidon_hash_single, Felt};
#[derive(Debug, Clone)]
pub struct StarkPoseidonHasher {
block_size_bits: usize,
should_pad: bool,
}
impl Hasher for StarkPoseidonHasher {
fn get_name(&self) -> HashingFunction {
HashingFunction::Poseidon
}
fn hash(&self, data: Vec<String>) -> Result<String, HasherError> {
for element in &data {
self.is_element_size_valid(element)?;
}
let field_elements: Vec<Felt> =
data.iter().map(|e| e.parse().unwrap_or_default()).collect();
let hash_core = match field_elements.len() {
0 => return Err(HasherError::InvalidElementsLength),
1 => poseidon_hash_single(field_elements[0]),
2 => poseidon_hash(field_elements[0], field_elements[1]),
_ => poseidon_hash_many(&field_elements),
};
let mut hash = format!("{hash_core:x}");
if self.should_pad {
hash = format!("{hash:0>63}");
}
let hash = format!("0x{hash}");
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()])
}
fn get_genesis(&self) -> Result<String, HasherError> {
let genesis_str = "brave new world";
let hex_str = format!("0x{}", hex::encode(genesis_str));
self.hash_single(&hex_str)
}
fn get_block_size_bits(&self) -> usize {
self.block_size_bits
}
}
impl StarkPoseidonHasher {
pub fn new(should_pad: Option<bool>) -> Self {
Self {
block_size_bits: 252,
should_pad: should_pad.unwrap_or(false),
}
}
}
impl Default for StarkPoseidonHasher {
fn default() -> Self {
Self::new(None)
}
}