cosmwasm_std/
hex_binary.rs

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