Skip to main content

monero_rpc/
util.rs

1// Copyright 2019-2023 Artem Vorotnikov and Monero Rust Contributors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//   http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use serde::{Deserialize, Deserializer, Serialize};
16use std::fmt::{self, Display};
17
18/// Get bytes and parse from `str` interface.
19pub trait HashType: Sized {
20    /// Get bytes representation.
21    fn bytes(&self) -> &[u8]
22    where
23        Self: AsRef<[u8]>,
24    {
25        self.as_ref()
26    }
27    /// Parse from `str`.
28    fn from_str(v: &str) -> anyhow::Result<Self>;
29}
30
31macro_rules! hash_type_impl {
32    ($name:ty) => {
33        impl HashType for $name {
34            fn from_str(v: &str) -> anyhow::Result<Self> {
35                Ok(v.parse()?)
36            }
37        }
38    };
39}
40
41hash_type_impl!(monero::util::address::PaymentId);
42hash_type_impl!(monero::cryptonote::hash::Hash);
43
44impl HashType for Vec<u8> {
45    fn from_str(v: &str) -> anyhow::Result<Self> {
46        let v = v.strip_prefix("0x").unwrap_or(v);
47        Ok(hex::decode(v)?)
48    }
49}
50
51/// Wrapper type to help serializating types through string.
52#[derive(Clone, Debug, Eq, PartialEq)]
53pub struct HashString<T>(pub T);
54
55impl<T> Display for HashString<T>
56where
57    T: HashType + AsRef<[u8]>,
58{
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        write!(f, "{}", hex::encode(self.0.bytes()))
61    }
62}
63
64impl<T> Serialize for HashString<T>
65where
66    T: HashType + AsRef<[u8]>,
67{
68    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
69    where
70        S: serde::ser::Serializer,
71    {
72        serializer.serialize_str(&self.to_string())
73    }
74}
75
76impl<'de, T> Deserialize<'de> for HashString<T>
77where
78    T: HashType,
79{
80    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
81    where
82        D: Deserializer<'de>,
83    {
84        let s = String::deserialize(deserializer)?;
85        Ok(Self(T::from_str(&s).map_err(serde::de::Error::custom)?))
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92    use serde_test::{Token, assert_tokens};
93
94    #[test]
95    fn trait_hash_type_for_payment_id() {
96        use monero::util::address::PaymentId;
97
98        let payment_id = PaymentId([0, 1, 2, 3, 4, 5, 6, 7]);
99
100        assert_eq!(payment_id.bytes(), &[0, 1, 2, 3, 4, 5, 6, 7]);
101
102        assert!(
103            <PaymentId as HashType>::from_str("")
104                .unwrap_err()
105                .is::<rustc_hex::FromHexError>()
106        );
107        assert!(
108            <PaymentId as HashType>::from_str("0x01234567")
109                .unwrap_err()
110                .is::<rustc_hex::FromHexError>()
111        );
112        assert!(
113            <PaymentId as HashType>::from_str("0xgg")
114                .unwrap_err()
115                .is::<rustc_hex::FromHexError>()
116        );
117
118        assert_eq!(
119            <PaymentId as HashType>::from_str("0x0001020304050607").unwrap(),
120            payment_id
121        );
122        assert_eq!(
123            <PaymentId as HashType>::from_str("0001020304050607").unwrap(),
124            payment_id
125        );
126    }
127
128    #[test]
129    fn trait_hash_type_for_cryptonote_hash() {
130        use monero::cryptonote::hash::Hash;
131
132        let hash = Hash([250; 32]);
133
134        assert_eq!(hash.bytes(), [250; 32].as_slice());
135
136        assert!(
137            <Hash as HashType>::from_str("")
138                .unwrap_err()
139                .is::<rustc_hex::FromHexError>()
140        );
141        assert!(
142            <Hash as HashType>::from_str("0x01234567")
143                .unwrap_err()
144                .is::<rustc_hex::FromHexError>()
145        );
146        assert!(
147            <Hash as HashType>::from_str("0xgg")
148                .unwrap_err()
149                .is::<rustc_hex::FromHexError>()
150        );
151
152        let hash_str = "fa".repeat(32);
153        assert_eq!(<Hash as HashType>::from_str(&hash_str).unwrap(), hash);
154
155        let hash_str = format!("0x{}", hash_str);
156        assert_eq!(<Hash as HashType>::from_str(&hash_str).unwrap(), hash);
157    }
158
159    #[test]
160    fn trait_hash_type_for_vec_u8() {
161        let vec_non_empty = vec![0, 1, 2, 3, 4];
162
163        assert_eq!(vec_non_empty.bytes(), &[0, 1, 2, 3, 4]);
164
165        assert_eq!(
166            <Vec<u8> as HashType>::from_str("").unwrap(),
167            Vec::<u8>::new()
168        );
169        assert!(
170            <Vec<u8> as HashType>::from_str("0xgg")
171                .unwrap_err()
172                .is::<hex::FromHexError>()
173        );
174
175        assert_eq!(
176            <Vec<u8> as HashType>::from_str("0x0001020304").unwrap(),
177            vec_non_empty
178        );
179        assert_eq!(
180            <Vec<u8> as HashType>::from_str("0001020304").unwrap(),
181            vec_non_empty
182        );
183    }
184
185    #[test]
186    fn display_for_hash_string() {
187        let vec = vec![0, 1, 2, 3, 4];
188        let hash_string = HashString(vec);
189        assert_eq!(hash_string.to_string(), "0001020304");
190    }
191
192    #[test]
193    fn se_de_for_hash_string() {
194        let vec = vec![0, 1, 2, 3, 4];
195        let hash_string = HashString(vec);
196
197        assert_tokens(&hash_string, &[Token::Str("0001020304")]);
198    }
199}