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