cosmwasm_std/
hex_binary.rs

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