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