iota_graphql_rpc/types/
digest.rs1use std::{fmt, str::FromStr};
6
7use async_graphql::*;
8use fastcrypto::encoding::{Base58, Encoding};
9use iota_types::digests::{ObjectDigest, TransactionDigest};
10
11use crate::types::string_input::impl_string_input;
12
13pub(crate) const BASE58_DIGEST_LENGTH: usize = 32;
14
15#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
16pub(crate) struct Digest([u8; BASE58_DIGEST_LENGTH]);
17
18#[derive(thiserror::Error, Debug)]
19pub(crate) enum Error {
20 #[error("Invalid Base58: {0}")]
21 InvalidBase58(String),
22
23 #[error("Expected digest to be {expect}B, but got {actual}B")]
24 BadDigestLength { expect: usize, actual: usize },
25}
26
27impl Digest {
28 pub(crate) fn to_vec(self) -> Vec<u8> {
29 self.0.to_vec()
30 }
31
32 pub(crate) fn as_slice(&self) -> &[u8] {
33 &self.0
34 }
35}
36
37impl_string_input!(Digest);
38
39impl FromStr for Digest {
40 type Err = Error;
41
42 fn from_str(s: &str) -> Result<Self, Self::Err> {
43 let buffer = Base58::decode(s).map_err(|_| Error::InvalidBase58(s.to_string()))?;
44 Digest::try_from(&buffer[..])
45 }
46}
47
48impl TryFrom<&[u8]> for Digest {
49 type Error = Error;
50
51 fn try_from(value: &[u8]) -> Result<Self, Error> {
52 let mut result = [0u8; BASE58_DIGEST_LENGTH];
53
54 if value.len() != BASE58_DIGEST_LENGTH {
55 return Err(Error::BadDigestLength {
56 expect: BASE58_DIGEST_LENGTH,
57 actual: value.len(),
58 });
59 }
60
61 result.copy_from_slice(value);
62 Ok(Digest(result))
63 }
64}
65
66impl From<Digest> for ObjectDigest {
67 fn from(digest: Digest) -> Self {
68 ObjectDigest::new(digest.0)
69 }
70}
71
72impl From<TransactionDigest> for Digest {
73 fn from(digest: TransactionDigest) -> Self {
74 Digest(digest.into_inner())
75 }
76}
77
78impl fmt::Display for Digest {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 write!(f, "{}", Base58::encode(self.0))
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::{Error, *};
87
88 #[test]
89 fn test_base58_digest() {
90 let digest = [
91 183u8, 119, 223, 39, 204, 68, 220, 4, 126, 234, 232, 146, 106, 249, 98, 12, 170, 209,
92 98, 203, 243, 77, 154, 225, 177, 216, 169, 101, 51, 116, 79, 223,
93 ];
94
95 assert_eq!(
96 Digest::from_str("DMBdBZnpYR4EeTXzXL8A6BtVafqGjAWGsFZhP2zJYmXU").unwrap(),
97 Digest(digest)
98 );
99
100 assert!(matches!(
101 Digest::from_str("ILoveBase58").unwrap_err(),
102 Error::InvalidBase58(_),
103 ));
104
105 let long_digest = {
106 let mut bytes = vec![];
107 bytes.extend(digest);
108 bytes.extend(digest);
109 Base58::encode(bytes)
110 };
111
112 assert!(matches!(
113 Digest::from_str(&long_digest).unwrap_err(),
114 Error::BadDigestLength { .. },
115 ))
116 }
117}