upend/src/util/hash.rs

120 lines
2.7 KiB
Rust

use crate::addressing::Address;
use actix::prelude::*;
use anyhow::{anyhow, Result};
use diesel::backend::Backend;
use diesel::deserialize::FromSql;
use diesel::sqlite::Sqlite;
use diesel::{deserialize, sql_types};
use filebuffer::FileBuffer;
use serde::{ser, Serialize, Serializer};
use std::path::PathBuf;
use tiny_keccak::{Hasher, KangarooTwelve};
#[derive(Debug, Clone, PartialEq, FromSqlRow)]
pub struct Hash(pub Vec<u8>);
impl AsRef<[u8]> for Hash {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl FromSql<sql_types::Binary, Sqlite> for Hash {
fn from_sql(bytes: Option<&<Sqlite as Backend>::RawValue>) -> deserialize::Result<Self> {
Ok(Hash(Vec::from(not_none!(bytes).read_blob())))
}
}
impl Serialize for Hash {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
serializer.serialize_str(
encode(
Address::Hash(self.clone())
.encode()
.map_err(ser::Error::custom)?,
)
.as_str(),
)
}
}
pub trait Hashable {
fn hash(&self) -> Result<Hash>;
}
pub struct HasherWorker;
impl Actor for HasherWorker {
type Context = SyncContext<Self>;
}
#[derive(Message)]
#[rtype(result = "Result<Hash>")]
pub struct ComputeHash {
pub path: PathBuf,
}
impl Handler<ComputeHash> for HasherWorker {
type Result = Result<Hash>;
fn handle(&mut self, msg: ComputeHash, _: &mut Self::Context) -> Self::Result {
msg.path.hash()
}
}
impl Hashable for PathBuf {
fn hash(self: &PathBuf) -> Result<Hash> {
let fbuffer = FileBuffer::open(self)?;
Ok(hash(&fbuffer))
}
}
pub fn hash<T: AsRef<[u8]>>(input: T) -> Hash {
let mut k12 = KangarooTwelve::new(b"");
k12.update(input.as_ref());
let mut result = [0u8; 256 / 8];
k12.finalize(&mut result);
Hash(Vec::from(result))
}
pub fn encode<T: AsRef<[u8]>>(vec: T) -> String {
// multibase base58
format!("z{}", bs58::encode(vec).into_string())
}
pub fn decode<T: AsRef<str>>(string: T) -> Result<Vec<u8>> {
let string = string.as_ref();
let (base, data) = string.split_at(1);
// multibase base58
if base != "z" {
Err(anyhow!(format!(
"data not base58 encoded, bailing... ({})",
string
)))
} else {
Ok(bs58::decode(data).into_vec()?)
}
}
#[cfg(test)]
mod tests {
use crate::util::hash::{decode, encode};
#[test]
fn test_encode_decode() {
let content = "Hello, World!".as_bytes();
let encoded = encode(content);
let decoded = decode(encoded);
assert!(decoded.is_ok());
assert_eq!(content, decoded.unwrap());
}
}