use crate::HexParseError;
use anyhow::{Context, Result};
use bc_envelope::prelude::*;
use std::{
fmt,
io::{self, Read, Write},
};
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct BlockHash([u8; 32]);
impl fmt::Debug for BlockHash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "BlockHash({})", self)
}
}
impl fmt::Display for BlockHash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut data = self.0;
data.reverse();
f.write_str(&hex::encode(data))
}
}
impl AsRef<[u8; 32]> for BlockHash {
fn as_ref(&self) -> &[u8; 32] {
&self.0
}
}
impl From<BlockHash> for [u8; 32] {
fn from(value: BlockHash) -> Self {
value.0
}
}
impl BlockHash {
pub fn from_bytes(bytes: [u8; 32]) -> Self {
BlockHash(bytes)
}
pub fn from_hex(hex: &str) -> Result<Self, HexParseError> {
let mut data = hex::decode(hex).map_err(crate::HexParseError::HexInvalid)?;
data.reverse();
Ok(Self(<[u8; 32]>::try_from(&data[..]).map_err(|_| {
crate::HexParseError::SliceInvalid {
expected: 64,
actual: hex.len(),
}
})?))
}
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let mut hash = [0u8; 32];
reader.read_exact(&mut hash)?;
Ok(BlockHash::from_bytes(hash))
}
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
writer.write_all(&self.0)?;
Ok(())
}
}
impl From<BlockHash> for CBOR {
fn from(value: BlockHash) -> Self {
CBOR::to_byte_string(value.0)
}
}
impl From<&BlockHash> for CBOR {
fn from(value: &BlockHash) -> Self {
CBOR::to_byte_string(value.0)
}
}
impl TryFrom<CBOR> for BlockHash {
type Error = dcbor::Error;
fn try_from(cbor: CBOR) -> dcbor::Result<Self> {
let bytes = cbor.try_into_byte_string()?;
if bytes.len() != 32 {
return Err(format!(
"Invalid BlockHash length: expected 32 bytes, got {}",
bytes.len()
)
.into());
}
let mut hash = [0u8; 32];
hash.copy_from_slice(&bytes);
Ok(BlockHash::from_bytes(hash))
}
}
impl From<BlockHash> for Envelope {
fn from(value: BlockHash) -> Self {
Envelope::new(CBOR::from(value))
}
}
impl TryFrom<Envelope> for BlockHash {
type Error = anyhow::Error;
fn try_from(envelope: Envelope) -> Result<Self, Self::Error> {
envelope.extract_subject().context("BlockHash")
}
}
#[cfg(test)]
mod tests {
use crate::{test_cbor_roundtrip, test_envelope_roundtrip};
use super::BlockHash;
impl crate::RandomInstance for BlockHash {
fn random() -> Self {
let mut rng = bc_rand::thread_rng();
Self(bc_rand::rng_random_array(&mut rng))
}
}
test_cbor_roundtrip!(BlockHash);
test_envelope_roundtrip!(BlockHash);
}