2023-06-28 21:20:42 +02:00
|
|
|
use std::fmt;
|
|
|
|
|
2023-06-27 21:11:10 +02:00
|
|
|
use crate::{addressing::Address, error::UpEndError};
|
2022-09-12 23:30:43 +02:00
|
|
|
use multihash::Hasher;
|
2023-06-28 21:20:42 +02:00
|
|
|
use serde::{
|
|
|
|
de::{self, Visitor},
|
|
|
|
ser, Deserialize, Deserializer, Serialize, Serializer,
|
|
|
|
};
|
2020-09-07 21:21:54 +02:00
|
|
|
|
2023-06-25 15:29:52 +02:00
|
|
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
|
|
|
#[cfg_attr(feature = "diesel", derive(diesel::FromSqlRow))]
|
2020-09-07 21:21:54 +02:00
|
|
|
|
2023-06-25 15:29:52 +02:00
|
|
|
pub struct Digest(pub Vec<u8>);
|
|
|
|
|
|
|
|
impl AsRef<[u8]> for Digest {
|
2021-02-19 20:27:30 +01:00
|
|
|
fn as_ref(&self) -> &[u8] {
|
|
|
|
self.0.as_ref()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-25 15:29:52 +02:00
|
|
|
impl Serialize for Digest {
|
2021-02-21 19:51:23 +01:00
|
|
|
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
|
|
|
|
where
|
|
|
|
S: Serializer,
|
|
|
|
{
|
|
|
|
serializer.serialize_str(
|
2022-01-26 16:55:23 +01:00
|
|
|
b58_encode(
|
2021-02-21 19:51:23 +01:00
|
|
|
Address::Hash(self.clone())
|
|
|
|
.encode()
|
|
|
|
.map_err(ser::Error::custom)?,
|
|
|
|
)
|
|
|
|
.as_str(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-28 21:20:42 +02:00
|
|
|
struct DigestVisitor;
|
|
|
|
|
|
|
|
impl<'de> Visitor<'de> for DigestVisitor {
|
|
|
|
type Value = Digest;
|
|
|
|
|
|
|
|
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,
|
|
|
|
{
|
|
|
|
b58_decode(str)
|
|
|
|
.map(|v| Digest(v))
|
|
|
|
.map_err(|e| de::Error::custom(format!("Error deserializing digest: {}", e)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'de> Deserialize<'de> for Digest {
|
|
|
|
fn deserialize<D>(deserializer: D) -> Result<Digest, D::Error>
|
|
|
|
where
|
|
|
|
D: Deserializer<'de>,
|
|
|
|
{
|
|
|
|
deserializer.deserialize_str(DigestVisitor)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-25 15:29:52 +02:00
|
|
|
#[cfg(feature = "diesel")]
|
|
|
|
impl diesel::types::FromSql<diesel::sql_types::Binary, diesel::sqlite::Sqlite> for Digest {
|
|
|
|
fn from_sql(
|
|
|
|
bytes: Option<&<diesel::sqlite::Sqlite as diesel::backend::Backend>::RawValue>,
|
|
|
|
) -> diesel::deserialize::Result<Self> {
|
|
|
|
Ok(Digest(Vec::from(diesel::not_none!(bytes).read_blob())))
|
2020-09-07 21:21:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-25 15:29:52 +02:00
|
|
|
pub fn sha256hash<T: AsRef<[u8]>>(input: T) -> Digest {
|
2022-09-12 23:30:43 +02:00
|
|
|
let mut hasher = multihash::Sha2_256::default();
|
|
|
|
hasher.update(input.as_ref());
|
2023-06-25 15:29:52 +02:00
|
|
|
Digest(Vec::from(hasher.finalize()))
|
2020-09-07 21:21:54 +02:00
|
|
|
}
|
|
|
|
|
2022-01-26 16:55:23 +01:00
|
|
|
pub fn b58_encode<T: AsRef<[u8]>>(vec: T) -> String {
|
2022-09-12 23:30:43 +02:00
|
|
|
multibase::encode(multibase::Base::Base58Btc, vec.as_ref())
|
2020-09-07 21:21:54 +02:00
|
|
|
}
|
|
|
|
|
2023-06-27 21:11:10 +02:00
|
|
|
pub fn b58_decode<T: AsRef<str>>(input: T) -> Result<Vec<u8>, UpEndError> {
|
2022-09-12 23:30:43 +02:00
|
|
|
let input = input.as_ref();
|
2023-06-27 21:11:10 +02:00
|
|
|
let (_base, data) =
|
|
|
|
multibase::decode(input).map_err(|err| UpEndError::HashDecodeError(err.to_string()))?;
|
2022-09-12 23:30:43 +02:00
|
|
|
Ok(data)
|
2020-09-07 21:21:54 +02:00
|
|
|
}
|
2021-02-19 22:27:54 +01:00
|
|
|
|
2023-06-25 15:29:52 +02:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct AsDigestError(pub String);
|
|
|
|
|
|
|
|
impl std::fmt::Display for AsDigestError {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
write!(f, "{}", self.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::error::Error for AsDigestError {}
|
|
|
|
|
|
|
|
impl From<std::io::Error> for AsDigestError {
|
|
|
|
fn from(err: std::io::Error) -> Self {
|
|
|
|
AsDigestError(err.to_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait AsDigest {
|
|
|
|
fn as_digest(&self) -> Result<Digest, AsDigestError>;
|
|
|
|
}
|
|
|
|
|
2021-02-19 22:27:54 +01:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2023-06-25 15:29:52 +02:00
|
|
|
use crate::hash::{b58_decode, b58_encode};
|
2021-02-19 22:27:54 +01:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_encode_decode() {
|
|
|
|
let content = "Hello, World!".as_bytes();
|
|
|
|
|
2022-01-26 16:55:23 +01:00
|
|
|
let encoded = b58_encode(content);
|
|
|
|
let decoded = b58_decode(encoded);
|
2021-02-19 22:27:54 +01:00
|
|
|
|
|
|
|
assert!(decoded.is_ok());
|
|
|
|
|
|
|
|
assert_eq!(content, decoded.unwrap());
|
|
|
|
}
|
|
|
|
}
|