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            panic!("Binary 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.")
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            panic!("Binary 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.")
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
253#[cfg(test)]
254mod tests {
255    use super::*;
256    use crate::assert_hash_works;
257    use crate::errors::StdError;
258
259    #[test]
260    fn to_array_works() {
261        // simple
262        let binary = Binary::from(&[1, 2, 3]);
263        let array: [u8; 3] = binary.to_array().unwrap();
264        assert_eq!(array, [1, 2, 3]);
265
266        // empty
267        let binary = Binary::from(&[]);
268        let array: [u8; 0] = binary.to_array().unwrap();
269        assert_eq!(array, [] as [u8; 0]);
270
271        // invalid size
272        let binary = Binary::from(&[1, 2, 3]);
273        let error = binary.to_array::<8>().unwrap_err();
274        match error {
275            StdError::InvalidDataSize {
276                expected, actual, ..
277            } => {
278                assert_eq!(expected, 8);
279                assert_eq!(actual, 3);
280            }
281            err => panic!("Unexpected error: {err:?}"),
282        }
283
284        // long array (32 bytes)
285        let binary = Binary::from_base64("t119JOQox4WUQEmO/nyqOZfO+wjJm91YG2sfn4ZglvA=").unwrap();
286        let array: [u8; 32] = binary.to_array().unwrap();
287        assert_eq!(
288            array,
289            [
290                0xb7, 0x5d, 0x7d, 0x24, 0xe4, 0x28, 0xc7, 0x85, 0x94, 0x40, 0x49, 0x8e, 0xfe, 0x7c,
291                0xaa, 0x39, 0x97, 0xce, 0xfb, 0x08, 0xc9, 0x9b, 0xdd, 0x58, 0x1b, 0x6b, 0x1f, 0x9f,
292                0x86, 0x60, 0x96, 0xf0,
293            ]
294        );
295
296        // very long array > 32 bytes (requires Rust 1.47+)
297        let binary =
298            Binary::from_base64("t119JOQox4WUQEmO/nyqOZfO+wjJm91YG2sfn4ZglvBzyMOwMWq+").unwrap();
299        let array: [u8; 39] = binary.to_array().unwrap();
300        assert_eq!(
301            array,
302            [
303                0xb7, 0x5d, 0x7d, 0x24, 0xe4, 0x28, 0xc7, 0x85, 0x94, 0x40, 0x49, 0x8e, 0xfe, 0x7c,
304                0xaa, 0x39, 0x97, 0xce, 0xfb, 0x08, 0xc9, 0x9b, 0xdd, 0x58, 0x1b, 0x6b, 0x1f, 0x9f,
305                0x86, 0x60, 0x96, 0xf0, 0x73, 0xc8, 0xc3, 0xb0, 0x31, 0x6a, 0xbe,
306            ]
307        );
308    }
309
310    #[test]
311    fn test_base64_encoding_success() {
312        for (value, encoded, encoded_no_pad) in [
313            (&b""[..], "", ""),
314            (&b"hello"[..], "aGVsbG8=", "aGVsbG8"),
315            (&b"\x0C\xBB\x00\x11\xFA\x01"[..], "DLsAEfoB", "DLsAEfoB"),
316            (&b"rand"[..], "cmFuZA==", "cmFuZA"),
317            (&b"rand"[..], "cmFuZA==", "cmFuZA="),
318            (&b"randomiZ"[..], "cmFuZG9taVo=", "cmFuZG9taVo"),
319        ] {
320            let value = Binary::from(value);
321            assert_eq!(encoded, value.to_base64());
322            assert_eq!(Ok(value.clone()), Binary::from_base64(encoded));
323            assert_eq!(Ok(value.clone()), Binary::from_base64(encoded_no_pad));
324        }
325    }
326
327    #[test]
328    fn test_base64_encoding_error() {
329        for (invalid_base64, want) in [
330            ("cm%uZG9taVo", "Invalid symbol 37, offset 2."),
331            ("cmFuZ", "Invalid input length: 5"),
332        ] {
333            match Binary::from_base64(invalid_base64) {
334                Err(StdError::InvalidBase64 { msg, .. }) => assert_eq!(want, msg),
335                result => panic!("Unexpected result: {result:?}"),
336            }
337        }
338    }
339
340    #[test]
341    fn from_slice_works() {
342        let original: &[u8] = &[0u8, 187, 61, 11, 250, 0];
343        let binary: Binary = original.into();
344        assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
345    }
346
347    #[test]
348    fn from_fixed_length_array_works() {
349        let original = &[];
350        let binary: Binary = original.into();
351        assert_eq!(binary.len(), 0);
352
353        let original = &[0u8];
354        let binary: Binary = original.into();
355        assert_eq!(binary.as_slice(), [0u8]);
356
357        let original = &[0u8, 187, 61, 11, 250, 0];
358        let binary: Binary = original.into();
359        assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
360
361        let original = &[
362            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,
363            1, 1, 1,
364        ];
365        let binary: Binary = original.into();
366        assert_eq!(
367            binary.as_slice(),
368            [
369                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,
370                1, 1, 1, 1,
371            ]
372        );
373    }
374
375    #[test]
376    fn from_owned_fixed_length_array_works() {
377        let original = [];
378        let binary: Binary = original.into();
379        assert_eq!(binary.len(), 0);
380
381        let original = [0u8];
382        let binary: Binary = original.into();
383        assert_eq!(binary.as_slice(), [0u8]);
384
385        let original = [0u8, 187, 61, 11, 250, 0];
386        let binary: Binary = original.into();
387        assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
388
389        let original = [
390            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,
391            1, 1, 1,
392        ];
393        let binary: Binary = original.into();
394        assert_eq!(
395            binary.as_slice(),
396            [
397                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,
398                1, 1, 1, 1,
399            ]
400        );
401    }
402
403    #[test]
404    fn from_literal_works() {
405        let a: Binary = b"".into();
406        assert_eq!(a.len(), 0);
407
408        let a: Binary = b".".into();
409        assert_eq!(a.len(), 1);
410
411        let a: Binary = b"...".into();
412        assert_eq!(a.len(), 3);
413
414        let a: Binary = b"...............................".into();
415        assert_eq!(a.len(), 31);
416
417        let a: Binary = b"................................".into();
418        assert_eq!(a.len(), 32);
419
420        let a: Binary = b".................................".into();
421        assert_eq!(a.len(), 33);
422    }
423
424    #[test]
425    fn from_vec_works() {
426        let original = vec![0u8, 187, 61, 11, 250, 0];
427        let original_ptr = original.as_ptr();
428        let binary: Binary = original.into();
429        assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
430        assert_eq!(binary.0.as_ptr(), original_ptr, "vector must not be copied");
431    }
432
433    #[test]
434    fn into_vec_works() {
435        // Into<Vec<u8>> for Binary
436        let original = Binary(vec![0u8, 187, 61, 11, 250, 0]);
437        let original_ptr = original.0.as_ptr();
438        let vec: Vec<u8> = original.into();
439        assert_eq!(vec.as_slice(), [0u8, 187, 61, 11, 250, 0]);
440        assert_eq!(vec.as_ptr(), original_ptr, "vector must not be copied");
441
442        // From<Binary> for Vec<u8>
443        let original = Binary(vec![7u8, 35, 49, 101, 0, 255]);
444        let original_ptr = original.0.as_ptr();
445        let vec = Vec::<u8>::from(original);
446        assert_eq!(vec.as_slice(), [7u8, 35, 49, 101, 0, 255]);
447        assert_eq!(vec.as_ptr(), original_ptr, "vector must not be copied");
448    }
449
450    #[test]
451    fn serialization_works() {
452        let binary = Binary(vec![0u8, 187, 61, 11, 250, 0]);
453
454        let json = serde_json::to_vec(&binary).unwrap();
455        let deserialized: Binary = serde_json::from_slice(&json).unwrap();
456
457        assert_eq!(binary, deserialized);
458    }
459
460    #[test]
461    fn deserialize_from_valid_string() {
462        let b64_str = "ALs9C/oA";
463        // this is the binary behind above string
464        let expected = vec![0u8, 187, 61, 11, 250, 0];
465
466        let serialized = serde_json::to_vec(&b64_str).unwrap();
467        let deserialized: Binary = serde_json::from_slice(&serialized).unwrap();
468        assert_eq!(expected, deserialized.as_slice());
469    }
470
471    #[test]
472    fn deserialize_from_invalid_string() {
473        let invalid_str = "**BAD!**";
474        let serialized = serde_json::to_vec(&invalid_str).unwrap();
475        let res = serde_json::from_slice::<Binary>(&serialized);
476        assert!(res.is_err());
477    }
478
479    #[test]
480    fn binary_implements_debug() {
481        // Some data
482        let binary = Binary(vec![0x07, 0x35, 0xAA, 0xcb, 0x00, 0xff]);
483        assert_eq!(format!("{binary:?}"), "Binary(0735aacb00ff)",);
484
485        // Empty
486        let binary = Binary(vec![]);
487        assert_eq!(format!("{binary:?}"), "Binary()",);
488    }
489
490    #[test]
491    fn binary_implements_deref() {
492        // Dereference to [u8]
493        let binary = Binary(vec![7u8, 35, 49, 101, 0, 255]);
494        assert_eq!(*binary, [7u8, 35, 49, 101, 0, 255]);
495
496        // This checks deref coercions from &Binary to &[u8] works
497        let binary = Binary(vec![7u8, 35, 49, 101, 0, 255]);
498        assert_eq!(binary.len(), 6);
499        let binary_slice: &[u8] = &binary;
500        assert_eq!(binary_slice, &[7u8, 35, 49, 101, 0, 255]);
501    }
502
503    #[test]
504    fn binary_implements_as_ref() {
505        let want = &[7u8, 35, 49, 101, 0, 255];
506        let data = Binary(want.to_vec());
507        assert_eq!(want, AsRef::<[u8]>::as_ref(&data));
508        assert_eq!(want, AsRef::<[u8]>::as_ref(&&data));
509    }
510
511    /// Tests that `Binary` implements `EQ` and `Hash` correctly and thus can be
512    /// used with hash maps and sets.
513    #[test]
514    fn binary_implements_hash_eq() {
515        let a = Binary::from([0, 187, 61, 11, 250, 0]);
516        let b = Binary::from([16, 21, 33, 0, 255, 9]);
517        assert_hash_works!(a, b);
518    }
519
520    #[test]
521    fn binary_implements_partial_eq_with_vector() {
522        let a = Binary(vec![5u8; 3]);
523        let b = vec![5u8; 3];
524        let c = vec![9u8; 3];
525        assert_eq!(a, b);
526        assert_eq!(b, a);
527        assert_ne!(a, c);
528        assert_ne!(c, a);
529    }
530
531    #[test]
532    fn binary_implements_partial_eq_with_slice_and_array() {
533        let a = Binary(vec![0xAA, 0xBB]);
534
535        // Slice: &[u8]
536        assert_eq!(a, b"\xAA\xBB" as &[u8]);
537        assert_eq!(b"\xAA\xBB" as &[u8], a);
538        assert_ne!(a, b"\x11\x22" as &[u8]);
539        assert_ne!(b"\x11\x22" as &[u8], a);
540
541        // Array reference: &[u8; 2]
542        assert_eq!(a, b"\xAA\xBB");
543        assert_eq!(b"\xAA\xBB", a);
544        assert_ne!(a, b"\x11\x22");
545        assert_ne!(b"\x11\x22", a);
546
547        // Array: [u8; 2]
548        assert_eq!(a, [0xAA, 0xBB]);
549        assert_eq!([0xAA, 0xBB], a);
550        assert_ne!(a, [0x11, 0x22]);
551        assert_ne!([0x11, 0x22], a);
552    }
553}