use anyhow::Result;
use bao_tree::blake3;
use postcard::experimental::max_size::MaxSize;
use serde::{
de::{self, SeqAccess},
ser::SerializeTuple,
Deserialize, Deserializer, Serialize, Serializer,
};
use std::{fmt, result, str::FromStr};
use thiserror::Error;
pub mod io;
pub mod progress;
pub mod runtime;
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
pub struct Hash(blake3::Hash);
impl Hash {
pub fn new(buf: impl AsRef<[u8]>) -> Self {
let val = blake3::hash(buf.as_ref());
Hash(val)
}
pub fn as_bytes(&self) -> &[u8; 32] {
self.0.as_bytes()
}
pub fn as_cid_bytes(&self) -> [u8; 36] {
let hash = self.0.as_bytes();
let mut res = [0u8; 36];
res[0..4].copy_from_slice(&CID_PREFIX);
res[4..36].copy_from_slice(hash);
res
}
pub fn from_cid_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
anyhow::ensure!(
bytes.len() == 36,
"invalid cid length, expected 36, got {}",
bytes.len()
);
anyhow::ensure!(bytes[0..4] == CID_PREFIX, "invalid cid prefix");
let mut hash = [0u8; 32];
hash.copy_from_slice(&bytes[4..36]);
Ok(Self::from(hash))
}
pub fn to_hex(&self) -> String {
self.0.to_hex().to_string()
}
}
impl AsRef<[u8]> for Hash {
fn as_ref(&self) -> &[u8] {
self.0.as_bytes()
}
}
impl From<Hash> for blake3::Hash {
fn from(value: Hash) -> Self {
value.0
}
}
impl From<blake3::Hash> for Hash {
fn from(value: blake3::Hash) -> Self {
Hash(value)
}
}
impl From<[u8; 32]> for Hash {
fn from(value: [u8; 32]) -> Self {
Hash(blake3::Hash::from(value))
}
}
impl From<Hash> for [u8; 32] {
fn from(value: Hash) -> Self {
*value.as_bytes()
}
}
impl From<&[u8; 32]> for Hash {
fn from(value: &[u8; 32]) -> Self {
Hash(blake3::Hash::from(*value))
}
}
impl PartialOrd for Hash {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.0.as_bytes().cmp(other.0.as_bytes()))
}
}
impl Ord for Hash {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.as_bytes().cmp(other.0.as_bytes())
}
}
impl fmt::Display for Hash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut res = [b'b'; 59];
data_encoding::BASE32_NOPAD.encode_mut(&self.as_cid_bytes(), &mut res[1..]);
let t = std::str::from_utf8_mut(res.as_mut()).unwrap();
t.make_ascii_lowercase();
f.write_str(t)
}
}
impl FromStr for Hash {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let sb = s.as_bytes();
if sb.len() == 59 && sb[0] == b'b' {
let mut t = [0u8; 58];
t.copy_from_slice(&sb[1..]);
std::str::from_utf8_mut(t.as_mut())
.unwrap()
.make_ascii_uppercase();
let mut res = [0u8; 36];
data_encoding::BASE32_NOPAD
.decode_mut(&t, &mut res)
.map_err(|_e| anyhow::anyhow!("invalid base32"))?;
Self::from_cid_bytes(&res)
} else {
let (_base, bytes) = multibase::decode(s)?;
Self::from_cid_bytes(bytes.as_ref())
}
}
}
impl Serialize for Hash {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut s = serializer.serialize_tuple(32)?;
for item in self.0.as_bytes() {
s.serialize_element(item)?;
}
s.end()
}
}
impl<'de> Deserialize<'de> for Hash {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_tuple(32, HashVisitor)
}
}
struct HashVisitor;
impl<'de> de::Visitor<'de> for HashVisitor {
type Value = Hash;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "an array of 32 bytes containing hash data")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut arr = [0u8; 32];
let mut i = 0;
while let Some(val) = seq.next_element()? {
arr[i] = val;
i += 1;
if i > 32 {
return Err(de::Error::invalid_length(i, &self));
}
}
Ok(Hash::from(arr))
}
}
impl MaxSize for Hash {
const POSTCARD_MAX_SIZE: usize = 32;
}
const CID_PREFIX: [u8; 4] = [
0x01, 0x55, 0x1e, 0x20, ];
#[derive(Serialize, Deserialize, Debug, Error)]
pub struct RpcError(serde_error::Error);
impl fmt::Display for RpcError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl From<anyhow::Error> for RpcError {
fn from(e: anyhow::Error) -> Self {
RpcError(serde_error::Error::new(&*e))
}
}
impl From<std::io::Error> for RpcError {
fn from(e: std::io::Error) -> Self {
RpcError(serde_error::Error::new(&e))
}
}
#[allow(dead_code)]
pub type RpcResult<T> = result::Result<T, RpcError>;
#[derive(Debug)]
pub(crate) struct NonSend {
_marker: std::marker::PhantomData<std::rc::Rc<()>>,
}
impl NonSend {
#[allow(dead_code)]
pub const fn new() -> Self {
Self {
_marker: std::marker::PhantomData,
}
}
}
#[cfg(test)]
mod tests {
use iroh_test::{assert_eq_hex, hexdump::parse_hexdump};
use super::*;
use serde_test::{assert_tokens, Token};
#[test]
fn test_hash() {
let data = b"hello world";
let hash = Hash::new(data);
let encoded = hash.to_string();
assert_eq!(encoded.parse::<Hash>().unwrap(), hash);
}
#[test]
fn hash_wire_format() {
let hash = Hash::from([0xab; 32]);
let serialized = postcard::to_stdvec(&hash).unwrap();
let expected = parse_hexdump(r"
ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab # hash
").unwrap();
assert_eq_hex!(serialized, expected);
}
#[test]
fn test_hash_serde() {
let hash = Hash::new("hello");
let mut tokens = Vec::new();
tokens.push(Token::Tuple { len: 32 });
for byte in hash.as_bytes() {
tokens.push(Token::U8(*byte));
}
tokens.push(Token::TupleEnd);
assert_eq!(tokens.len(), 34);
assert_tokens(&hash, &tokens);
}
#[test]
fn test_hash_postcard() {
let hash = Hash::new("hello");
let ser = postcard::to_stdvec(&hash).unwrap();
let de = postcard::from_bytes(&ser).unwrap();
assert_eq!(hash, de);
assert_eq!(ser.len(), 32);
}
}