#[cfg(feature = "hashing")]
use crate::encode;
use crate::error::{HoloHashError, HoloHashResult};
use crate::has_hash::HasHash;
use crate::HashType;
use crate::PrimitiveHashType;
pub const HOLO_HASH_PREFIX_LEN: usize = 3;
pub const HOLO_HASH_CORE_LEN: usize = 32;
pub const HOLO_HASH_LOC_LEN: usize = 4;
pub const HOLO_HASH_UNTYPED_LEN: usize = HOLO_HASH_CORE_LEN + HOLO_HASH_LOC_LEN;
pub const HOLO_HASH_FULL_LEN: usize = HOLO_HASH_PREFIX_LEN + HOLO_HASH_CORE_LEN + HOLO_HASH_LOC_LEN;
#[macro_export]
macro_rules! assert_length {
($len:expr, $hash:expr) => {
debug_assert_eq!(
$hash.len(),
$len,
"invalid byte count for HoloHash {:?}",
$hash
);
};
}
#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct HoloHash<T: HashType> {
hash: Vec<u8>,
hash_type: T,
}
impl<T: HashType> HoloHash<T> {
pub fn try_from_raw_39(hash: Vec<u8>) -> HoloHashResult<Self> {
if hash.len() != HOLO_HASH_FULL_LEN {
return Err(HoloHashError::BadSize);
}
let hash_type = T::try_from_prefix(&hash[0..3])?;
Ok(Self { hash, hash_type })
}
pub fn from_raw_39(hash: Vec<u8>) -> Self {
Self::try_from_raw_39(hash).unwrap()
}
pub fn try_from_raw_36_and_type(mut bytes: Vec<u8>, hash_type: T) -> HoloHashResult<Self> {
if bytes.len() != HOLO_HASH_UNTYPED_LEN {
return Err(HoloHashError::BadSize);
}
let mut hash = hash_type.get_prefix().to_vec();
hash.append(&mut bytes);
Ok(Self { hash, hash_type })
}
pub fn from_raw_36_and_type(bytes: Vec<u8>, hash_type: T) -> Self {
Self::try_from_raw_36_and_type(bytes, hash_type).unwrap()
}
pub(crate) fn retype<TT: HashType>(mut self, hash_type: TT) -> HoloHash<TT> {
let prefix = hash_type.get_prefix();
self.hash[0..HOLO_HASH_PREFIX_LEN].copy_from_slice(&prefix[0..HOLO_HASH_PREFIX_LEN]);
HoloHash {
hash: self.hash,
hash_type,
}
}
pub fn hash_type(&self) -> &T {
&self.hash_type
}
pub fn get_raw_39(&self) -> &[u8] {
&self.hash[..]
}
pub fn get_raw_36(&self) -> &[u8] {
let bytes = &self.hash[HOLO_HASH_PREFIX_LEN..];
assert_length!(HOLO_HASH_UNTYPED_LEN, bytes);
bytes
}
pub fn get_raw_32(&self) -> &[u8] {
let bytes = &self.hash[HOLO_HASH_PREFIX_LEN..HOLO_HASH_PREFIX_LEN + HOLO_HASH_CORE_LEN];
assert_length!(HOLO_HASH_CORE_LEN, bytes);
bytes
}
pub fn get_loc(&self) -> u32 {
bytes_to_loc(&self.hash[HOLO_HASH_FULL_LEN - HOLO_HASH_LOC_LEN..])
}
pub fn into_inner(self) -> Vec<u8> {
assert_length!(HOLO_HASH_FULL_LEN, &self.hash);
self.hash
}
pub fn to_hex(&self) -> String {
holochain_util::hex::bytes_to_hex(&self.hash, false)
}
}
#[cfg(feature = "kitsune2")]
impl crate::DnaHash {
pub fn to_k2_space(&self) -> kitsune2_api::SpaceId {
kitsune2_api::SpaceId::from(bytes::Bytes::copy_from_slice(self.get_raw_32()))
}
#[cfg(feature = "hashing")]
pub fn from_k2_space(space: &kitsune2_api::SpaceId) -> Self {
Self::from_raw_32(space.to_vec())
}
}
#[cfg(feature = "kitsune2")]
impl crate::AgentPubKey {
pub fn to_k2_agent(&self) -> kitsune2_api::AgentId {
kitsune2_api::AgentId::from(bytes::Bytes::copy_from_slice(self.get_raw_32()))
}
#[cfg(feature = "hashing")]
pub fn from_k2_agent(agent: &kitsune2_api::AgentId) -> Self {
Self::from_raw_32(agent.to_vec())
}
}
#[cfg(feature = "kitsune2")]
impl crate::DhtOpHash {
#[deprecated(since = "0.5.5", note = "Use `to_located_k2_op_id` instead")]
pub fn to_k2_op(&self) -> kitsune2_api::OpId {
kitsune2_api::OpId::from(bytes::Bytes::copy_from_slice(self.get_raw_36()))
}
pub fn to_located_k2_op_id(&self, op_basis: &crate::OpBasis) -> kitsune2_api::OpId {
let mut inner = bytes::BytesMut::with_capacity(HOLO_HASH_UNTYPED_LEN);
inner.extend_from_slice(self.get_raw_32());
inner.extend_from_slice(&op_basis.get_raw_36()[HOLO_HASH_CORE_LEN..]);
kitsune2_api::OpId::from(inner.freeze())
}
#[cfg(feature = "hashing")]
pub fn try_from_k2_op(op: &kitsune2_api::OpId) -> HoloHashResult<Self> {
if op.len() < HOLO_HASH_CORE_LEN {
return Err(HoloHashError::BadSize);
}
Ok(Self::from_raw_32(op[..HOLO_HASH_CORE_LEN].to_vec()))
}
}
#[cfg(feature = "hashing")]
impl<T: HashType> HoloHash<T> {
pub fn from_raw_32_and_type(mut hash: Vec<u8>, hash_type: T) -> Self {
assert_length!(HOLO_HASH_CORE_LEN, &hash);
hash.append(&mut encode::holo_dht_location_bytes(&hash));
assert_length!(HOLO_HASH_UNTYPED_LEN, &hash);
HoloHash::from_raw_36_and_type(hash, hash_type)
}
}
impl<P: PrimitiveHashType> HoloHash<P> {
pub fn from_raw_36(hash: Vec<u8>) -> Self {
assert_length!(HOLO_HASH_UNTYPED_LEN, &hash);
Self::from_raw_36_and_type(hash, P::new())
}
#[cfg(feature = "hashing")]
pub fn from_raw_32(hash: Vec<u8>) -> Self {
Self::from_raw_32_and_type(hash, P::new())
}
}
impl<T: HashType> AsRef<[u8]> for HoloHash<T> {
fn as_ref(&self) -> &[u8] {
assert_length!(HOLO_HASH_FULL_LEN, &self.hash);
&self.hash
}
}
#[cfg(any(feature = "sqlite", feature = "sqlite-encrypted"))]
impl<T: HashType> rusqlite::ToSql for HoloHash<T> {
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
Ok(rusqlite::types::ToSqlOutput::Borrowed(self.as_ref().into()))
}
}
#[cfg(any(feature = "sqlite", feature = "sqlite-encrypted"))]
impl<T: HashType> rusqlite::types::FromSql for HoloHash<T> {
fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
Vec::<u8>::column_result(value).and_then(|bytes| {
Self::try_from_raw_39(bytes).map_err(|_| rusqlite::types::FromSqlError::InvalidType)
})
}
}
impl<T: HashType> IntoIterator for HoloHash<T> {
type Item = u8;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.hash.into_iter()
}
}
impl<T: HashType> HasHash for HoloHash<T> {
type HashType = T;
fn as_hash(&self) -> &HoloHash<T> {
self
}
fn into_hash(self) -> HoloHash<T> {
self
}
}
impl<T: HashType> std::fmt::Debug for HoloHash<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{}({})", self.hash_type().hash_name(), self))?;
Ok(())
}
}
fn bytes_to_loc(bytes: &[u8]) -> u32 {
(bytes[0] as u32)
+ ((bytes[1] as u32) << 8)
+ ((bytes[2] as u32) << 16)
+ ((bytes[3] as u32) << 24)
}
#[cfg(test)]
mod tests {
use crate::*;
fn assert_type<T: HashType>(t: &str, h: HoloHash<T>) {
assert_eq!(3_688_618_971, h.get_loc());
assert_eq!(h.hash_type().hash_name(), t);
assert_eq!(
"[219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219]",
format!("{:?}", h.get_raw_32()),
);
}
#[test]
fn test_enum_types() {
assert_type(
"DnaHash",
DnaHash::from_raw_36(vec![0xdb; HOLO_HASH_UNTYPED_LEN]),
);
assert_type(
"AgentPubKey",
AgentPubKey::from_raw_36(vec![0xdb; HOLO_HASH_UNTYPED_LEN]),
);
assert_type(
"EntryHash",
EntryHash::from_raw_36(vec![0xdb; HOLO_HASH_UNTYPED_LEN]),
);
assert_type(
"DhtOpHash",
DhtOpHash::from_raw_36(vec![0xdb; HOLO_HASH_UNTYPED_LEN]),
);
assert_type(
"ExternalHash",
ExternalHash::from_raw_36(vec![0xdb; HOLO_HASH_UNTYPED_LEN]),
);
}
#[test]
#[should_panic]
fn test_from_raw_36_panics_with_bad_size() {
DnaHash::from_raw_36(vec![0xdb; 35]);
}
#[test]
fn test_try_from_raw_39_errors_with_bad_size() {
let mut raw = vec![132, 45, 36];
raw.extend(vec![0xdb; 35]);
let res = DnaHash::try_from_raw_39(raw);
assert_eq!(res, Err(HoloHashError::BadSize));
}
#[test]
fn test_try_from_raw_39_errors_with_bad_prefix() {
let res = DnaHash::try_from_raw_39(vec![0xdb; 39]);
assert!(matches!(res, Err(HoloHashError::BadPrefix { .. })));
}
#[test]
#[should_panic]
fn test_from_raw_39_panics_with_bad_size() {
let mut raw = vec![132, 45, 36];
raw.extend(vec![0xdb; 35]);
DnaHash::from_raw_39(raw);
}
#[test]
#[should_panic]
fn test_from_raw_39_panics_with_bad_prefix() {
DnaHash::from_raw_39(vec![0xdb; 39]);
}
#[test]
fn test_try_from_raw_36_and_type_errors_with_bad_size() {
let res = HoloHash::try_from_raw_36_and_type(vec![0xdb; 35], hash_type::Dna);
assert_eq!(res, Err(HoloHashError::BadSize));
}
#[test]
#[should_panic]
fn test_from_raw_36_and_type_panics_with_bad_size() {
HoloHash::from_raw_36_and_type(vec![0xdb; 35], hash_type::Dna);
}
#[test]
fn test_try_from_raw_36_and_type() {
let res = HoloHash::try_from_raw_36_and_type(vec![0xdb; 36], hash_type::Dna);
assert!(res.is_ok());
}
#[test]
fn test_from_raw_36_and_type() {
HoloHash::from_raw_36_and_type(vec![0xdb; 36], hash_type::Dna);
}
#[test]
fn test_try_from_raw_39() {
let mut raw = vec![132, 45, 36];
raw.extend(vec![0xdb; 36]);
let res = DnaHash::try_from_raw_39(raw);
assert!(res.is_ok());
}
#[test]
fn test_from_raw_39() {
let mut raw = vec![132, 45, 36];
raw.extend(vec![0xdb; 36]);
DnaHash::from_raw_39(raw);
}
}