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