use jam_types::Hash;
use scale::{Encode, Output};
use tiny_keccak::{Hasher, Keccak};
pub fn keccak(data: &[u8]) -> Hash {
let mut hasher = Keccak::v256();
hasher.update(data);
let mut output = [0u8; 32];
hasher.finalize(&mut output);
output
}
pub fn keccak_concat<I, T>(items: I) -> Hash
where
I: IntoIterator<Item = T>,
T: AsRef<[u8]>,
{
let mut hasher = Keccak::v256();
for item in items.into_iter() {
hasher.update(item.as_ref());
}
let mut output = [0u8; 32];
hasher.finalize(&mut output);
output
}
pub fn hash_raw(data: &[u8]) -> Hash {
let mut hasher = Blake2bHasher::new();
hasher.update(data);
hasher.into_hash()
}
pub fn hash_raw_concat<I, T>(items: I) -> Hash
where
I: IntoIterator<Item = T>,
T: AsRef<[u8]>,
{
let mut hasher = Blake2bHasher::new();
for item in items.into_iter() {
hasher.update(item.as_ref());
}
hasher.into_hash()
}
pub fn hash_encoded(what: &impl Encode) -> Hash {
let mut hasher = Blake2bHasher::new();
what.encode_to(&mut hasher);
hasher.into_hash()
}
struct Blake2bHasher {
state: blake2b_simd::State,
}
impl Blake2bHasher {
fn new() -> Self {
let state = blake2b_simd::Params::new().hash_length(32).to_state();
Self { state }
}
fn update(&mut self, data: &[u8]) {
self.state.update(data);
}
fn into_hash(self) -> Hash {
self.state.finalize().as_bytes().try_into().expect("Hash length set to 32")
}
}
impl Output for Blake2bHasher {
fn write(&mut self, bytes: &[u8]) {
self.update(bytes);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hash_encoded_works() {
#[derive(Encode)]
struct Dummy {
x: Vec<Dummy>,
y: u64,
z: String,
}
let dummy = Dummy {
x: vec![Dummy { x: Vec::new(), y: 123, z: "Hello".into() }],
y: 333,
z: "world".into(),
};
assert_eq!(hash_raw(&dummy.encode()), hash_encoded(&dummy));
assert_eq!(dummy.using_encoded(hash_raw), hash_encoded(&dummy));
}
#[test]
fn hash_raw_concat_works() {
assert_eq!(
hash_raw(&[b"x".as_ref(), b"y", b"z"].concat()),
hash_raw_concat([b"x", b"y", b"z"])
);
}
#[test]
fn keccak_concat_works() {
assert_eq!(
keccak(&[b"x".as_ref(), b"y", b"z"].concat()),
keccak_concat([b"x", b"y", b"z"])
);
}
}