use std::hash::{Hash, Hasher};
use std::io::{BufRead, Write};
use std::str::FromStr;
use std::{error, fmt, result};
use cbor_event::{self, de::Deserializer, se::Serializer};
use cryptoxide::blake2b::Blake2b;
use cryptoxide::digest::Digest as _;
use cryptoxide::sha3;
use hex::FromHexError;
use crate::chain_crypto::bech32::{self, Bech32};
#[derive(Debug, PartialEq, Clone)]
pub enum Error {
InvalidHashSize(usize, usize),
InvalidHexEncoding(FromHexError),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::InvalidHashSize(sz, expected) => write!(
f,
"invalid hash size, expected {expected} but received {sz} bytes."
),
Error::InvalidHexEncoding(_) => write!(f, "invalid hex encoding for hash value"),
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::InvalidHashSize(..) => None,
Error::InvalidHexEncoding(err) => Some(err),
}
}
}
impl From<FromHexError> for Error {
fn from(err: FromHexError) -> Self {
Error::InvalidHexEncoding(err)
}
}
pub type Result<T> = result::Result<T, Error>;
macro_rules! define_blake2b_new {
($hash_ty:ty) => {
impl $hash_ty {
pub fn new(buf: &[u8]) -> Self {
let mut b2b = Blake2b::new(Self::HASH_SIZE);
let mut out = [0; Self::HASH_SIZE];
b2b.input(buf);
b2b.result(&mut out);
Self::from(out)
}
}
};
}
macro_rules! define_hash_object {
($hash_ty:ty, $constructor:expr, $hash_size:ident, $bech32_hrp:expr) => {
impl $hash_ty {
pub const HASH_SIZE: usize = $hash_size;
pub fn as_hash_bytes(&self) -> &[u8; Self::HASH_SIZE] {
&self.0
}
pub fn try_from_slice(slice: &[u8]) -> Result<Self> {
if slice.len() != Self::HASH_SIZE {
return Err(Error::InvalidHashSize(slice.len(), Self::HASH_SIZE));
}
let mut buf = [0; Self::HASH_SIZE];
buf[0..Self::HASH_SIZE].clone_from_slice(slice);
Ok(Self::from(buf))
}
}
impl AsRef<[u8]> for $hash_ty {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl From<$hash_ty> for [u8; $hash_size] {
fn from(bytes: $hash_ty) -> Self {
bytes.0
}
}
impl<'a> From<&'a $hash_ty> for &'a [u8; $hash_size] {
fn from(bytes: &'a $hash_ty) -> Self {
&bytes.0
}
}
impl From<[u8; Self::HASH_SIZE]> for $hash_ty {
fn from(bytes: [u8; Self::HASH_SIZE]) -> Self {
$constructor(bytes)
}
}
#[allow(clippy::derived_hash_with_manual_eq)]
impl Hash for $hash_ty {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state)
}
}
impl FromStr for $hash_ty {
type Err = Error;
fn from_str(s: &str) -> result::Result<Self, Self::Err> {
let bytes = hex::decode(s)?;
Self::try_from_slice(&bytes)
}
}
impl fmt::Display for $hash_ty {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", hex::encode(self.as_ref()))
}
}
impl fmt::Debug for $hash_ty {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(concat!(stringify!($hash_ty), "(0x"))?;
write!(f, "{}", hex::encode(self.as_ref()))?;
f.write_str(")")
}
}
impl Bech32 for $hash_ty {
const BECH32_HRP: &'static str = $bech32_hrp;
fn try_from_bech32_str(bech32_str: &str) -> bech32::Result<Self> {
let bytes = bech32::try_from_bech32_to_bytes::<Self>(bech32_str)?;
Self::try_from_slice(&bytes).map_err(bech32::Error::data_invalid)
}
fn to_bech32_str(&self) -> String {
bech32::to_bech32_from_bytes::<Self>(self.as_ref())
}
}
impl cbor_event::de::Deserialize for $hash_ty {
fn deserialize<R: BufRead>(reader: &mut Deserializer<R>) -> cbor_event::Result<Self> {
let bytes = reader.bytes()?;
match Self::try_from_slice(&bytes) {
Ok(digest) => Ok(digest),
Err(Error::InvalidHashSize(sz, expected)) => {
Err(cbor_event::Error::NotEnough(sz, expected))
}
Err(err) => Err(cbor_event::Error::CustomError(format!(
"unexpected error: {:?}",
err
))),
}
}
}
impl cbor_event::se::Serialize for $hash_ty {
fn serialize<'se, W: Write>(
&self,
serializer: &'se mut Serializer<W>,
) -> cbor_event::Result<&'se mut Serializer<W>> {
serializer.write_bytes(self.as_ref())
}
}
#[cfg(feature = "generic-serialization")]
impl serde::Serialize for $hash_ty {
#[inline]
fn serialize<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
serializer.serialize_str(&hex::encode(self.as_ref()))
} else {
serializer.serialize_bytes(&self.as_ref())
}
}
}
#[cfg(feature = "generic-serialization")]
impl<'de> serde::Deserialize<'de> for $hash_ty {
fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct HashVisitor;
impl<'de> serde::de::Visitor<'de> for HashVisitor {
type Value = $hash_ty;
fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "Expecting a Blake2b_256 hash (`Hash`)")
}
fn visit_str<'a, E>(self, v: &'a str) -> result::Result<Self::Value, E>
where
E: serde::de::Error,
{
match Self::Value::from_str(&v) {
Err(Error::HexadecimalError(err)) => Err(E::custom(format!("{}", err))),
Err(Error::InvalidHashSize(sz, _)) => {
Err(E::invalid_length(sz, &"32 bytes"))
}
Ok(h) => Ok(h),
}
}
fn visit_bytes<'a, E>(self, v: &'a [u8]) -> result::Result<Self::Value, E>
where
E: serde::de::Error,
{
match Self::Value::try_from_slice(v) {
Err(Error::InvalidHashSize(sz, _)) => {
Err(E::invalid_length(sz, &"32 bytes"))
}
Err(err) => panic!("unexpected error: {}", err),
Ok(h) => Ok(h),
}
}
}
if deserializer.is_human_readable() {
deserializer.deserialize_str(HashVisitor)
} else {
deserializer.deserialize_bytes(HashVisitor)
}
}
}
};
}
pub const HASH_SIZE_224: usize = 28;
pub const HASH_SIZE_256: usize = 32;
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
pub struct Blake2b256([u8; HASH_SIZE_256]);
define_hash_object!(Blake2b256, Blake2b256, HASH_SIZE_256, "blake2b256");
define_blake2b_new!(Blake2b256);
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
pub struct Blake2b224([u8; HASH_SIZE_224]);
define_hash_object!(Blake2b224, Blake2b224, HASH_SIZE_224, "blake2b224");
define_blake2b_new!(Blake2b224);
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
pub struct Sha3_256([u8; HASH_SIZE_256]);
define_hash_object!(Sha3_256, Sha3_256, HASH_SIZE_256, "sha3256");
impl Sha3_256 {
pub fn new(buf: &[u8]) -> Self {
let mut sh3 = sha3::Sha3_256::new();
let mut out = [0; Self::HASH_SIZE];
sh3.input(buf.as_ref());
sh3.result(&mut out);
Self::from(out)
}
}