secret_cosmwasm_std/
binary.rs

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