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