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