use crate::util::hash::{b58_decode, b58_encode, Hash, Hashable}; use anyhow::{anyhow, Result}; use multihash::{Code, Multihash, MultihashDigest}; use serde::de::Visitor; use serde::{de, ser, Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; use std::str::FromStr; use url::Url; use uuid::Uuid; #[derive(Clone, Eq, PartialEq, Hash)] pub enum Address { Hash(Hash), Uuid(Uuid), Attribute(String), Url(Url), } // multihash SHA2-256 const SHA2_256: u64 = 0x12; // multihash identity const IDENTITY: u64 = 0x00; impl Address { pub fn encode(&self) -> Result> { let hash = match self { Self::Hash(hash) => Multihash::wrap(SHA2_256, &hash.0).map_err(|err| anyhow!(err))?, Self::Uuid(uuid) => { Code::Identity.digest(&[vec![b'U'], uuid.as_bytes().to_vec()].concat()) } Self::Attribute(attribute) => { Code::Identity.digest(&[&[b'A'], attribute.as_bytes()].concat()) } Self::Url(url) => { Code::Identity.digest(&[&[b'X'], url.to_string().as_bytes()].concat()) } }; Ok(hash.to_bytes()) } pub fn decode(buffer: &[u8]) -> Result { let multihash = Multihash::from_bytes(buffer) .map_err(|err| anyhow!("Error decoding address: {}", err))?; match multihash.code() { SHA2_256 => Ok(Self::Hash(Hash(multihash.digest().to_vec()))), IDENTITY => { let digest = multihash.digest().to_owned(); let digest_content: Vec = digest.clone().into_iter().skip(1).collect(); match digest[0] { b'U' => Ok(Self::Uuid(uuid::Uuid::from_slice( digest_content.as_slice(), )?)), b'A' => Ok(Self::Attribute(String::from_utf8(digest_content)?)), b'X' => Ok(Self::Url(Url::parse(&String::from_utf8(digest_content)?)?)), _ => Err(anyhow!("Error decoding address: Unknown identity marker.")), } } _ => Err(anyhow!( "Error decoding address: Unknown hash function type." )), } } } impl Serialize for Address { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: Serializer, { serializer.serialize_str(b58_encode(self.encode().map_err(ser::Error::custom)?).as_str()) } } struct AddressVisitor; impl<'de> Visitor<'de> for AddressVisitor { type Value = Address; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid UpEnd address (hash/UUID) as a multi-hashed string") } fn visit_str(self, str: &str) -> Result where E: de::Error, { let bytes = b58_decode(str) .map_err(|e| de::Error::custom(format!("Error deserializing address: {}", e)))?; Address::decode(bytes.as_ref()) .map_err(|e| de::Error::custom(format!("Error deserializing address: {}", e))) } } impl<'de> Deserialize<'de> for Address { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_str(AddressVisitor) } } impl FromStr for Address { type Err = anyhow::Error; fn from_str(s: &str) -> Result { Address::decode( b58_decode(s) .map_err(|e| anyhow!("Error deserializing address: {}", e))? .as_ref(), ) } } impl std::fmt::Display for Address { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", b58_encode(self.encode().map_err(|_| std::fmt::Error)?) ) } } impl std::fmt::Debug for Address { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "Address<{}>: {}", match self { Address::Hash(_) => "Hash", Address::Uuid(_) => "UUID", Address::Attribute(_) => "Attribute", Address::Url(_) => "URL", }, self ) } } pub trait Addressable: Hashable { fn address(&self) -> Result
{ Ok(Address::Hash(self.hash()?)) } } #[cfg(test)] mod tests { use anyhow::Result; use url::Url; use uuid::Uuid; use crate::addressing::Address; use crate::util::hash::Hash; #[test] fn test_hash_codec() -> Result<()> { let addr = Address::Hash(Hash(vec![1, 2, 3, 4, 5])); let encoded = addr.encode()?; let decoded = Address::decode(&encoded)?; assert_eq!(addr, decoded); Ok(()) } #[test] fn test_uuid_codec() -> Result<()> { let addr = Address::Uuid(Uuid::new_v4()); let encoded = addr.encode()?; let decoded = Address::decode(&encoded)?; assert_eq!(addr, decoded); Ok(()) } #[test] fn test_attribute_codec() -> Result<()> { let addr = Address::Attribute(String::from("ATTRIBUTE")); let encoded = addr.encode()?; let decoded = Address::decode(&encoded)?; assert_eq!(addr, decoded); Ok(()) } #[test] fn test_url_codec() -> Result<()> { let addr = Address::Url(Url::parse("https://upend.dev").unwrap()); let encoded = addr.encode()?; let decoded = Address::decode(&encoded)?; assert_eq!(addr, decoded); Ok(()) } }