upend/src/addressing.rs

199 lines
5.6 KiB
Rust

use crate::util::hash::{b58_decode, b58_encode, Hash, Hashable};
use anyhow::{anyhow, Result};
use serde::de::Visitor;
use serde::{de, ser, Deserialize, Deserializer, Serialize, Serializer};
use std::fmt;
use std::io::prelude::*;
use std::io::Cursor;
use std::str::FromStr;
use thiserror::private::DisplayAsDisplay;
use unsigned_varint::encode;
use uuid::Uuid;
#[derive(Clone, Eq, PartialEq, Hash)]
pub enum Address {
Hash(Hash),
Uuid(Uuid),
Attribute(String),
Url(String),
}
// multihash KangarooTwelve
const KANGAROO_TWELVE: u128 = 0x1d01;
// multihash identity
const IDENTITY: u128 = 0x00;
impl Address {
pub fn encode(&self) -> Result<Vec<u8>> {
let (hash_func_type, digest) = match self {
Self::Hash(hash) => (KANGAROO_TWELVE, hash.0.clone()),
Self::Uuid(uuid) => (IDENTITY, [vec![b'U'], uuid.as_bytes().to_vec()].concat()),
Self::Attribute(attribute) => (IDENTITY, [&[b'A'], attribute.as_bytes()].concat()),
Self::Url(url) => (IDENTITY, [&[b'X'], url.as_bytes()].concat()),
};
let mut result = Cursor::new(vec![0u8; 0]);
result.write_all(encode::u128(hash_func_type, &mut encode::u128_buffer()))?;
result.write_all(encode::usize(digest.len(), &mut encode::usize_buffer()))?;
result.write_all(digest.as_slice())?;
Ok(result.get_ref().clone())
}
pub fn decode(buffer: &[u8]) -> Result<Self> {
let (hash_func_type, rest) = unsigned_varint::decode::u128(buffer)?;
let (digest_len, rest) = unsigned_varint::decode::usize(rest)?;
let digest = rest.to_vec();
if digest_len != digest.len() {
Err(anyhow!(format!(
"Actual digest length ({}) does not match declared digest length ({}).",
digest.len(),
digest_len
)))
} else {
match hash_func_type {
KANGAROO_TWELVE => Ok(Self::Hash(Hash(digest))),
IDENTITY => {
let digest_content: Vec<u8> = 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(String::from_utf8(digest_content)?)),
_ => Err(anyhow!("Unknown identity marker.")),
}
}
_ => Err(anyhow!("Unknown hash function type.")),
}
}
}
}
impl Serialize for Address {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::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<E>(self, str: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
let bytes = b58_decode(str).map_err(de::Error::custom)?;
Address::decode(bytes.as_ref()).map_err(de::Error::custom)
}
}
impl<'de> Deserialize<'de> for Address {
fn deserialize<D>(deserializer: D) -> Result<Address, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(AddressVisitor)
}
}
impl FromStr for Address {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Address::decode(b58_decode(s)?.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, "{}", self.as_display())
}
}
pub trait Addressable: Hashable {
fn address(&self) -> Result<Address> {
Ok(Address::Hash(self.hash()?))
}
}
#[cfg(test)]
mod tests {
use uuid::Uuid;
use crate::addressing::Address;
use crate::util::hash::Hash;
#[test]
fn test_hash_codec() {
let addr = Address::Hash(Hash(vec![1, 2, 3, 4, 5]));
let encoded = addr.encode();
assert!(encoded.is_ok());
let decoded = Address::decode(&encoded.unwrap());
assert!(decoded.is_ok());
assert_eq!(addr, decoded.unwrap());
}
#[test]
fn test_uuid_codec() {
let addr = Address::Uuid(Uuid::new_v4());
let encoded = addr.encode();
assert!(encoded.is_ok());
let decoded = Address::decode(&encoded.unwrap());
assert!(decoded.is_ok());
assert_eq!(addr, decoded.unwrap());
}
#[test]
fn test_attribute_codec() {
let addr = Address::Attribute(String::from("ATTRIBUTE"));
let encoded = addr.encode();
assert!(encoded.is_ok());
let decoded = Address::decode(&encoded.unwrap());
assert!(decoded.is_ok());
assert_eq!(addr, decoded.unwrap());
}
#[test]
fn test_url_codec() {
let addr = Address::Url(String::from("https://upendproject.net"));
let encoded = addr.encode();
assert!(encoded.is_ok());
let decoded = Address::decode(&encoded.unwrap());
assert!(decoded.is_ok());
assert_eq!(addr, decoded.unwrap());
}
}