secret_cosmwasm_std/
hex_binary.rs

1use std::fmt;
2use std::ops::Deref;
3
4use schemars::JsonSchema;
5use serde::{de, ser, Deserialize, Deserializer, Serialize};
6
7use crate::{Binary, StdError, StdResult};
8
9/// This is a wrapper around Vec<u8> to add hex de/serialization
10/// with serde. It also adds some helper methods to help encode inline.
11///
12/// This is similar to `cosmwasm_std::Binary` but uses hex.
13/// See also <https://github.com/CosmWasm/cosmwasm/blob/main/docs/MESSAGE_TYPES.md>.
14#[derive(Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord, JsonSchema)]
15pub struct HexBinary(#[schemars(with = "String")] Vec<u8>);
16
17impl HexBinary {
18    pub fn from_hex(input: &str) -> StdResult<Self> {
19        let vec = hex::decode(input).map_err(StdError::invalid_hex)?;
20        Ok(Self(vec))
21    }
22
23    pub fn to_hex(&self) -> String {
24        hex::encode(&self.0)
25    }
26
27    pub fn as_slice(&self) -> &[u8] {
28        self.0.as_slice()
29    }
30
31    /// Copies content into fixed-sized array.
32    ///
33    /// # Examples
34    ///
35    /// Copy to array of explicit length
36    ///
37    /// ```
38    /// # use secret_cosmwasm_std::HexBinary;
39    /// let data = HexBinary::from(&[0xfb, 0x1f, 0x37]);
40    /// let array: [u8; 3] = data.to_array().unwrap();
41    /// assert_eq!(array, [0xfb, 0x1f, 0x37]);
42    /// ```
43    ///
44    /// Copy to integer
45    ///
46    /// ```
47    /// # use secret_cosmwasm_std::HexBinary;
48    /// let data = HexBinary::from(&[0x8b, 0x67, 0x64, 0x84, 0xb5, 0xfb, 0x1f, 0x37]);
49    /// let num = u64::from_be_bytes(data.to_array().unwrap());
50    /// assert_eq!(num, 10045108015024774967);
51    /// ```
52    pub fn to_array<const LENGTH: usize>(&self) -> StdResult<[u8; LENGTH]> {
53        if self.len() != LENGTH {
54            return Err(StdError::invalid_data_size(LENGTH, self.len()));
55        }
56
57        let mut out: [u8; LENGTH] = [0; LENGTH];
58        out.copy_from_slice(&self.0);
59        Ok(out)
60    }
61}
62
63impl fmt::Display for HexBinary {
64    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65        write!(f, "{}", self.to_hex())
66    }
67}
68
69impl fmt::Debug for HexBinary {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        // Use an output inspired by tuples (https://doc.rust-lang.org/std/fmt/struct.Formatter.html#method.debug_tuple)
72        // but with a custom implementation to avoid the need for an intemediate hex string.
73        write!(f, "HexBinary(")?;
74        for byte in self.0.iter() {
75            write!(f, "{:02x}", byte)?;
76        }
77        write!(f, ")")?;
78        Ok(())
79    }
80}
81
82impl From<&[u8]> for HexBinary {
83    fn from(binary: &[u8]) -> Self {
84        Self(binary.to_vec())
85    }
86}
87
88/// Just like Vec<u8>, HexBinary is a smart pointer to [u8].
89/// This implements `*data` for us and allows us to
90/// do `&*data`, returning a `&[u8]` from a `&HexBinary`.
91/// With [deref coercions](https://doc.rust-lang.org/1.22.1/book/first-edition/deref-coercions.html#deref-coercions),
92/// this allows us to use `&data` whenever a `&[u8]` is required.
93impl Deref for HexBinary {
94    type Target = [u8];
95
96    fn deref(&self) -> &Self::Target {
97        self.as_slice()
98    }
99}
100
101// Reference
102impl<const LENGTH: usize> From<&[u8; LENGTH]> for HexBinary {
103    fn from(source: &[u8; LENGTH]) -> Self {
104        Self(source.to_vec())
105    }
106}
107
108// Owned
109impl<const LENGTH: usize> From<[u8; LENGTH]> for HexBinary {
110    fn from(source: [u8; LENGTH]) -> Self {
111        Self(source.into())
112    }
113}
114
115impl From<Vec<u8>> for HexBinary {
116    fn from(vec: Vec<u8>) -> Self {
117        Self(vec)
118    }
119}
120
121impl From<HexBinary> for Vec<u8> {
122    fn from(original: HexBinary) -> Vec<u8> {
123        original.0
124    }
125}
126
127impl From<Binary> for HexBinary {
128    fn from(original: Binary) -> Self {
129        Self(original.into())
130    }
131}
132
133impl From<HexBinary> for Binary {
134    fn from(original: HexBinary) -> Binary {
135        Binary::from(original.0)
136    }
137}
138
139/// Implement `HexBinary == std::vec::Vec<u8>`
140impl PartialEq<Vec<u8>> for HexBinary {
141    fn eq(&self, rhs: &Vec<u8>) -> bool {
142        // Use Vec<u8> == Vec<u8>
143        self.0 == *rhs
144    }
145}
146
147/// Implement `std::vec::Vec<u8> == HexBinary`
148impl PartialEq<HexBinary> for Vec<u8> {
149    fn eq(&self, rhs: &HexBinary) -> bool {
150        // Use Vec<u8> == Vec<u8>
151        *self == rhs.0
152    }
153}
154
155/// Implement `HexBinary == &[u8]`
156impl PartialEq<&[u8]> for HexBinary {
157    fn eq(&self, rhs: &&[u8]) -> bool {
158        // Use &[u8] == &[u8]
159        self.as_slice() == *rhs
160    }
161}
162
163/// Implement `&[u8] == HexBinary`
164impl PartialEq<HexBinary> for &[u8] {
165    fn eq(&self, rhs: &HexBinary) -> bool {
166        // Use &[u8] == &[u8]
167        *self == rhs.as_slice()
168    }
169}
170
171/// Implement `HexBinary == [u8; LENGTH]`
172impl<const LENGTH: usize> PartialEq<[u8; LENGTH]> for HexBinary {
173    fn eq(&self, rhs: &[u8; LENGTH]) -> bool {
174        self.as_slice() == rhs.as_slice()
175    }
176}
177
178/// Implement `[u8; LENGTH] == HexBinary`
179impl<const LENGTH: usize> PartialEq<HexBinary> for [u8; LENGTH] {
180    fn eq(&self, rhs: &HexBinary) -> bool {
181        self.as_slice() == rhs.as_slice()
182    }
183}
184
185/// Implement `HexBinary == &[u8; LENGTH]`
186impl<const LENGTH: usize> PartialEq<&[u8; LENGTH]> for HexBinary {
187    fn eq(&self, rhs: &&[u8; LENGTH]) -> bool {
188        self.as_slice() == rhs.as_slice()
189    }
190}
191
192/// Implement `&[u8; LENGTH] == HexBinary`
193impl<const LENGTH: usize> PartialEq<HexBinary> for &[u8; LENGTH] {
194    fn eq(&self, rhs: &HexBinary) -> bool {
195        self.as_slice() == rhs.as_slice()
196    }
197}
198
199/// Serializes as a hex string
200impl Serialize for HexBinary {
201    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
202    where
203        S: ser::Serializer,
204    {
205        serializer.serialize_str(&self.to_hex())
206    }
207}
208
209/// Deserializes as a hex string
210impl<'de> Deserialize<'de> for HexBinary {
211    fn deserialize<D>(deserializer: D) -> Result<HexBinary, D::Error>
212    where
213        D: Deserializer<'de>,
214    {
215        deserializer.deserialize_str(HexVisitor)
216    }
217}
218
219struct HexVisitor;
220
221impl<'de> de::Visitor<'de> for HexVisitor {
222    type Value = HexBinary;
223
224    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
225        formatter.write_str("valid hex encoded string")
226    }
227
228    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
229    where
230        E: de::Error,
231    {
232        match HexBinary::from_hex(v) {
233            Ok(data) => Ok(data),
234            Err(_) => Err(E::custom(format!("invalid hex: {}", v))),
235        }
236    }
237}
238
239#[cfg(test)]
240mod tests {
241    use super::*;
242
243    use crate::{from_slice, to_vec, StdError};
244    use std::collections::hash_map::DefaultHasher;
245    use std::collections::HashSet;
246    use std::hash::{Hash, Hasher};
247
248    #[test]
249    fn from_hex_works() {
250        let data = HexBinary::from_hex("").unwrap();
251        assert_eq!(data, b"");
252        let data = HexBinary::from_hex("61").unwrap();
253        assert_eq!(data, b"a");
254        let data = HexBinary::from_hex("00").unwrap();
255        assert_eq!(data, b"\0");
256
257        let data = HexBinary::from_hex("68656c6c6f").unwrap();
258        assert_eq!(data, b"hello");
259        let data = HexBinary::from_hex("68656C6C6F").unwrap();
260        assert_eq!(data, b"hello");
261        let data = HexBinary::from_hex("72616e646f6d695a").unwrap();
262        assert_eq!(data.as_slice(), b"randomiZ");
263
264        // odd
265        match HexBinary::from_hex("123").unwrap_err() {
266            StdError::InvalidHex { msg, .. } => {
267                assert_eq!(msg, "Odd number of digits")
268            }
269            _ => panic!("Unexpected error type"),
270        }
271        // non-hex
272        match HexBinary::from_hex("efgh").unwrap_err() {
273            StdError::InvalidHex { msg, .. } => {
274                assert_eq!(msg, "Invalid character 'g' at position 2")
275            }
276            _ => panic!("Unexpected error type"),
277        }
278        // 0x prefixed
279        match HexBinary::from_hex("0xaa").unwrap_err() {
280            StdError::InvalidHex { msg, .. } => {
281                assert_eq!(msg, "Invalid character 'x' at position 1")
282            }
283            _ => panic!("Unexpected error type"),
284        }
285        // spaces
286        assert!(matches!(
287            HexBinary::from_hex("aa ").unwrap_err(),
288            StdError::InvalidHex { .. }
289        ));
290        assert!(matches!(
291            HexBinary::from_hex(" aa").unwrap_err(),
292            StdError::InvalidHex { .. }
293        ));
294        assert!(matches!(
295            HexBinary::from_hex("a a").unwrap_err(),
296            StdError::InvalidHex { .. }
297        ));
298        assert!(matches!(
299            HexBinary::from_hex(" aa ").unwrap_err(),
300            StdError::InvalidHex { .. }
301        ));
302    }
303
304    #[test]
305    fn to_hex_works() {
306        let binary: &[u8] = b"";
307        let encoded = HexBinary::from(binary).to_hex();
308        assert_eq!(encoded, "");
309
310        let binary: &[u8] = b"hello";
311        let encoded = HexBinary::from(binary).to_hex();
312        assert_eq!(encoded, "68656c6c6f");
313
314        let binary = vec![12u8, 187, 0, 17, 250, 1];
315        let encoded = HexBinary(binary).to_hex();
316        assert_eq!(encoded, "0cbb0011fa01");
317    }
318
319    #[test]
320    fn to_array_works() {
321        // simple
322        let binary = HexBinary::from(&[1, 2, 3]);
323        let array: [u8; 3] = binary.to_array().unwrap();
324        assert_eq!(array, [1, 2, 3]);
325
326        // empty
327        let binary = HexBinary::from(&[]);
328        let array: [u8; 0] = binary.to_array().unwrap();
329        assert_eq!(array, [] as [u8; 0]);
330
331        // invalid size
332        let binary = HexBinary::from(&[1, 2, 3]);
333        let error = binary.to_array::<8>().unwrap_err();
334        match error {
335            StdError::InvalidDataSize {
336                expected, actual, ..
337            } => {
338                assert_eq!(expected, 8);
339                assert_eq!(actual, 3);
340            }
341            err => panic!("Unexpected error: {:?}", err),
342        }
343
344        // long array (32 bytes)
345        let binary =
346            HexBinary::from_hex("b75d7d24e428c7859440498efe7caa3997cefb08c99bdd581b6b1f9f866096f0")
347                .unwrap();
348        let array: [u8; 32] = binary.to_array().unwrap();
349        assert_eq!(
350            array,
351            [
352                0xb7, 0x5d, 0x7d, 0x24, 0xe4, 0x28, 0xc7, 0x85, 0x94, 0x40, 0x49, 0x8e, 0xfe, 0x7c,
353                0xaa, 0x39, 0x97, 0xce, 0xfb, 0x08, 0xc9, 0x9b, 0xdd, 0x58, 0x1b, 0x6b, 0x1f, 0x9f,
354                0x86, 0x60, 0x96, 0xf0,
355            ]
356        );
357
358        // very long array > 32 bytes (requires Rust 1.47+)
359        let binary = HexBinary::from_hex(
360            "b75d7d24e428c7859440498efe7caa3997cefb08c99bdd581b6b1f9f866096f073c8c3b0316abe",
361        )
362        .unwrap();
363        let array: [u8; 39] = binary.to_array().unwrap();
364        assert_eq!(
365            array,
366            [
367                0xb7, 0x5d, 0x7d, 0x24, 0xe4, 0x28, 0xc7, 0x85, 0x94, 0x40, 0x49, 0x8e, 0xfe, 0x7c,
368                0xaa, 0x39, 0x97, 0xce, 0xfb, 0x08, 0xc9, 0x9b, 0xdd, 0x58, 0x1b, 0x6b, 0x1f, 0x9f,
369                0x86, 0x60, 0x96, 0xf0, 0x73, 0xc8, 0xc3, 0xb0, 0x31, 0x6a, 0xbe,
370            ]
371        );
372    }
373
374    #[test]
375    fn from_slice_works() {
376        let original: &[u8] = &[0u8, 187, 61, 11, 250, 0];
377        let binary: HexBinary = original.into();
378        assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
379    }
380
381    #[test]
382    fn from_fixed_length_array_works() {
383        let original = &[];
384        let binary: HexBinary = original.into();
385        assert_eq!(binary.len(), 0);
386
387        let original = &[0u8];
388        let binary: HexBinary = original.into();
389        assert_eq!(binary.as_slice(), [0u8]);
390
391        let original = &[0u8, 187, 61, 11, 250, 0];
392        let binary: HexBinary = original.into();
393        assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
394
395        let original = &[
396            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
397            1, 1, 1,
398        ];
399        let binary: HexBinary = original.into();
400        assert_eq!(
401            binary.as_slice(),
402            [
403                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
404                1, 1, 1, 1,
405            ]
406        );
407    }
408
409    #[test]
410    fn from_owned_fixed_length_array_works() {
411        let original = [];
412        let binary: HexBinary = original.into();
413        assert_eq!(binary.len(), 0);
414
415        let original = [0u8];
416        let binary: HexBinary = original.into();
417        assert_eq!(binary.as_slice(), [0u8]);
418
419        let original = [0u8, 187, 61, 11, 250, 0];
420        let binary: HexBinary = original.into();
421        assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
422
423        let original = [
424            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
425            1, 1, 1,
426        ];
427        let binary: HexBinary = original.into();
428        assert_eq!(
429            binary.as_slice(),
430            [
431                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
432                1, 1, 1, 1,
433            ]
434        );
435    }
436
437    #[test]
438    fn from_literal_works() {
439        let a: HexBinary = b"".into();
440        assert_eq!(a.len(), 0);
441
442        let a: HexBinary = b".".into();
443        assert_eq!(a.len(), 1);
444
445        let a: HexBinary = b"...".into();
446        assert_eq!(a.len(), 3);
447
448        let a: HexBinary = b"...............................".into();
449        assert_eq!(a.len(), 31);
450
451        let a: HexBinary = b"................................".into();
452        assert_eq!(a.len(), 32);
453
454        let a: HexBinary = (b".................................").into();
455        assert_eq!(a.len(), 33);
456    }
457
458    #[test]
459    fn from_vec_works() {
460        let original = vec![0u8, 187, 61, 11, 250, 0];
461        let original_ptr = original.as_ptr();
462        let binary: HexBinary = original.into();
463        assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
464        assert_eq!(binary.0.as_ptr(), original_ptr, "vector must not be copied");
465    }
466
467    #[test]
468    fn into_vec_works() {
469        // Into<Vec<u8>> for HexBinary
470        let original = HexBinary(vec![0u8, 187, 61, 11, 250, 0]);
471        let original_ptr = original.0.as_ptr();
472        let vec: Vec<u8> = original.into();
473        assert_eq!(vec.as_slice(), [0u8, 187, 61, 11, 250, 0]);
474        assert_eq!(vec.as_ptr(), original_ptr, "vector must not be copied");
475
476        // From<HexBinary> for Vec<u8>
477        let original = HexBinary(vec![7u8, 35, 49, 101, 0, 255]);
478        let original_ptr = original.0.as_ptr();
479        let vec = Vec::<u8>::from(original);
480        assert_eq!(vec.as_slice(), [7u8, 35, 49, 101, 0, 255]);
481        assert_eq!(vec.as_ptr(), original_ptr, "vector must not be copied");
482    }
483
484    #[test]
485    fn from_binary_works() {
486        let original = Binary::from([0u8, 187, 61, 11, 250, 0]);
487        let original_ptr = original.as_ptr();
488        let binary: HexBinary = original.into();
489        assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
490        assert_eq!(binary.0.as_ptr(), original_ptr, "vector must not be copied");
491    }
492
493    #[test]
494    fn into_binary_works() {
495        // Into<Binary> for HexBinary
496        let original = HexBinary(vec![0u8, 187, 61, 11, 250, 0]);
497        let original_ptr = original.0.as_ptr();
498        let bin: Binary = original.into();
499        assert_eq!(bin.as_slice(), [0u8, 187, 61, 11, 250, 0]);
500        assert_eq!(bin.as_ptr(), original_ptr, "vector must not be copied");
501
502        // From<HexBinary> for Binary
503        let original = HexBinary(vec![7u8, 35, 49, 101, 0, 255]);
504        let original_ptr = original.0.as_ptr();
505        let bin = Binary::from(original);
506        assert_eq!(bin.as_slice(), [7u8, 35, 49, 101, 0, 255]);
507        assert_eq!(bin.as_ptr(), original_ptr, "vector must not be copied");
508    }
509
510    #[test]
511    fn serialization_works() {
512        let binary = HexBinary(vec![0u8, 187, 61, 11, 250, 0]);
513
514        let json = to_vec(&binary).unwrap();
515        let deserialized: HexBinary = from_slice(&json).unwrap();
516
517        assert_eq!(binary, deserialized);
518    }
519
520    #[test]
521    fn deserialize_from_valid_string() {
522        let hex = "00bb3d0bfa00";
523        // this is the binary behind above string
524        let expected = vec![0u8, 187, 61, 11, 250, 0];
525
526        let serialized = to_vec(&hex).unwrap();
527        let deserialized: HexBinary = from_slice(&serialized).unwrap();
528        assert_eq!(expected, deserialized.as_slice());
529    }
530
531    #[test]
532    fn deserialize_from_invalid_string() {
533        let invalid_str = "**BAD!**";
534        let serialized = to_vec(&invalid_str).unwrap();
535        let res = from_slice::<HexBinary>(&serialized);
536        assert!(res.is_err());
537    }
538
539    #[test]
540    fn hex_binary_implements_debug() {
541        // Some data
542        let data = HexBinary(vec![0x07, 0x35, 0xAA, 0xcb, 0x00, 0xff]);
543        assert_eq!(format!("{:?}", data), "HexBinary(0735aacb00ff)",);
544
545        // Empty
546        let data = HexBinary(vec![]);
547        assert_eq!(format!("{:?}", data), "HexBinary()",);
548    }
549
550    #[test]
551    fn hex_binary_implements_deref() {
552        // Dereference to [u8]
553        let data = HexBinary(vec![7u8, 35, 49, 101, 0, 255]);
554        assert_eq!(*data, [7u8, 35, 49, 101, 0, 255]);
555
556        // This checks deref coercions from &Binary to &[u8] works
557        let data = HexBinary(vec![7u8, 35, 49, 101, 0, 255]);
558        assert_eq!(data.len(), 6);
559        let data_slice: &[u8] = &data;
560        assert_eq!(data_slice, &[7u8, 35, 49, 101, 0, 255]);
561    }
562
563    #[test]
564    fn hex_binary_implements_hash() {
565        let a1 = HexBinary::from([0, 187, 61, 11, 250, 0]);
566        let mut hasher = DefaultHasher::new();
567        a1.hash(&mut hasher);
568        let a1_hash = hasher.finish();
569
570        let a2 = HexBinary::from([0, 187, 61, 11, 250, 0]);
571        let mut hasher = DefaultHasher::new();
572        a2.hash(&mut hasher);
573        let a2_hash = hasher.finish();
574
575        let b = HexBinary::from([16, 21, 33, 0, 255, 9]);
576        let mut hasher = DefaultHasher::new();
577        b.hash(&mut hasher);
578        let b_hash = hasher.finish();
579
580        assert_eq!(a1_hash, a2_hash);
581        assert_ne!(a1_hash, b_hash);
582    }
583
584    /// This requires Hash and Eq to be implemented
585    #[test]
586    fn hex_binary_can_be_used_in_hash_set() {
587        let a1 = HexBinary::from([0, 187, 61, 11, 250, 0]);
588        let a2 = HexBinary::from([0, 187, 61, 11, 250, 0]);
589        let b = HexBinary::from([16, 21, 33, 0, 255, 9]);
590
591        let mut set = HashSet::new();
592        set.insert(a1.clone());
593        set.insert(a2.clone());
594        set.insert(b.clone());
595        assert_eq!(set.len(), 2);
596
597        let set1 = HashSet::<HexBinary>::from_iter(vec![b.clone(), a1.clone()]);
598        let set2 = HashSet::from_iter(vec![a1, a2, b]);
599        assert_eq!(set1, set2);
600    }
601
602    #[test]
603    fn hex_binary_implements_partial_eq_with_vector() {
604        let a = HexBinary(vec![5u8; 3]);
605        let b = vec![5u8; 3];
606        let c = vec![9u8; 3];
607        assert_eq!(a, b);
608        assert_eq!(b, a);
609        assert_ne!(a, c);
610        assert_ne!(c, a);
611    }
612
613    #[test]
614    fn hex_binary_implements_partial_eq_with_slice_and_array() {
615        let a = HexBinary(vec![0xAA, 0xBB]);
616
617        // Slice: &[u8]
618        assert_eq!(a, b"\xAA\xBB" as &[u8]);
619        assert_eq!(b"\xAA\xBB" as &[u8], a);
620        assert_ne!(a, b"\x11\x22" as &[u8]);
621        assert_ne!(b"\x11\x22" as &[u8], a);
622
623        // Array reference: &[u8; 2]
624        assert_eq!(a, b"\xAA\xBB");
625        assert_eq!(b"\xAA\xBB", a);
626        assert_ne!(a, b"\x11\x22");
627        assert_ne!(b"\x11\x22", a);
628
629        // Array: [u8; 2]
630        assert_eq!(a, [0xAA, 0xBB]);
631        assert_eq!([0xAA, 0xBB], a);
632        assert_ne!(a, [0x11, 0x22]);
633        assert_ne!([0x11, 0x22], a);
634    }
635}