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