use std::str::FromStr;
use blake2::{Blake2s256, Digest};
use num_bigint::BigInt;
use super::super::Hasher;
use crate::hasher::{byte_size, HasherError, HashingFunction};
#[derive(Debug, Clone)]
pub struct StarkBlakeHasher {
truncate: bool,
}
impl Hasher for StarkBlakeHasher {
fn get_name(&self) -> HashingFunction {
HashingFunction::Blake
}
fn hash(&self, data: Vec<String>) -> Result<String, HasherError> {
let mut hasher = Blake2s256::new();
for element in data {
let mut bytes = match element.strip_prefix("0x") {
Some(no_prefix) => {
let bytes =
hex::decode(no_prefix).map_err(|_| HasherError::InvalidElementsLength)?;
if bytes.len() % 4 != 0 {
return Err(HasherError::InvalidElementSize {
element_size: bytes.len(),
block_size_bits: self.get_block_size_bits(),
});
}
bytes
}
None => {
let bigint = BigInt::from_str(&element).unwrap();
let hex = format!(
"{:0>width$}",
bigint.to_str_radix(16),
width = self.get_block_size_bits() / 4
);
hex::decode(hex).unwrap()
}
};
bytes.chunks_exact_mut(4).for_each(|chunk| chunk.reverse());
hasher.update(&bytes);
}
let mut output = hasher.finalize().to_vec();
output.chunks_exact_mut(4).for_each(|chunk| chunk.reverse());
if self.truncate {
output[0] &= 0x0f
}
let hash = format!("0x{}", hex::encode(output));
Ok(hash)
}
fn is_element_size_valid(&self, element: &str) -> Result<bool, HasherError> {
let size = byte_size(element);
if size <= self.get_block_size_bits() / 8 {
Ok(true)
} else {
Err(HasherError::InvalidElementSize {
element_size: size,
block_size_bits: self.get_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 = "Chancellor on the brink of second bailout for banks.";
let hex_str = format!("0x{}", hex::encode(genesis_str));
self.hash_single(&hex_str)
}
fn get_block_size_bits(&self) -> usize {
256
}
}
impl StarkBlakeHasher {
pub fn new(truncate: bool) -> Self {
Self { truncate }
}
}
impl Default for StarkBlakeHasher {
fn default() -> Self {
Self::new(false)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_blake2s_hasher_empty() {
let hasher = StarkBlakeHasher::new(false);
let hash = hasher.hash(vec![]).unwrap();
assert_eq!(
hash,
"0x307a216994809079d02111e17c4a354248b6551f1ea5a12cfd0d251bf9eed01e"
);
}
#[test]
fn test_blake2s_hasher() {
let hasher = StarkBlakeHasher::new(false);
let hash = hasher
.hash(vec![
"0x0000000000000000000000000000000000000000000000000000000000000000".into(),
])
.unwrap();
assert_eq!(
hash,
"0xa95e0b32c23b659e41db93b54e0ad1304c0b3afd67a6e1c2718d672bad33bddf"
);
}
#[test]
fn test_blake2s_hasher_long() {
let hasher = StarkBlakeHasher::new(false);
let hash = hasher.hash(vec!["0xc713e33d89122b85e2f646cc518c2e6ef88b06d3b016104faa95f84f878dab66c713e33d89122b85e2f646cc518c2e6ef88b06d3b016104faa95f84f878dab66".into()]).unwrap();
assert_eq!(
hash,
"0x693aa1ab81c6362fe339fc4c7f6d8ddb1e515701e58c5bb2fb54a193c8287fdc"
);
}
#[test]
fn test_blake2s_hasher_pair() {
let hasher = StarkBlakeHasher::new(false);
let hash = hasher
.hash(vec![
"0xc713e33d89122b85e2f646cc518c2e6ef88b06d3b016104faa95f84f878dab66".into(),
"0xc713e33d89122b85e2f646cc518c2e6ef88b06d3b016104faa95f84f878dab66".into(),
])
.unwrap();
assert_eq!(
hash,
"0x693aa1ab81c6362fe339fc4c7f6d8ddb1e515701e58c5bb2fb54a193c8287fdc"
);
}
#[test]
fn test_blake2s_hasher_truncate() {
let hasher = StarkBlakeHasher::new(true);
let hash = hasher
.hash(vec![
"0x0000000000000000000000000000000000000000000000000000000000000000".into(),
])
.unwrap();
assert_eq!(
hash,
"0x095e0b32c23b659e41db93b54e0ad1304c0b3afd67a6e1c2718d672bad33bddf"
);
}
#[test]
fn test_blake2s_hasher_multiple_elements() {
let hasher = StarkBlakeHasher::new(false);
let data1: Vec<String> = vec![
"0x00000001".into(),
"0x000000002a22cfee1f2c846adbd12b3e183d4f97683f85dad08a79780a84bd55".into(),
"0x7dac2c5666815c17a3b36427de37bb9d2e2c5ccec3f8633eb91a4205cb4c10ff".into(),
"0x496ab951".into(),
"0x1d00ffff".into(),
"0x709e3e28".into(),
];
let hash1 = hasher.hash(data1.clone()).unwrap();
let data2: String = data1
.clone()
.iter()
.map(|e| e.strip_prefix("0x").unwrap_or(e).to_string())
.reduce(|acc, e| format!("{acc}{e}"))
.unwrap();
let data2 = vec![format!("0x{}", data2)];
let hash2 = hasher.hash(data2).unwrap();
assert_eq!(hash1, hash2);
assert_eq!(
hash2,
"0x0fcf8d89df790c8b0626b1ce495e22aca80b9332760b3c7bf9f46b7dd3b35556"
);
}
}