1use core::fmt;
2use core::ops::Deref;
3
4use base64::engine::{Engine, GeneralPurpose};
5use schemars::JsonSchema;
6use serde::{de, ser, Deserialize, Deserializer, Serialize};
7
8use crate::errors::{StdError, StdResult};
9
10#[derive(Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord, JsonSchema)]
16pub struct Binary(#[schemars(with = "String")] pub Vec<u8>);
17
18impl Binary {
19 const B64_ENGINE: GeneralPurpose = GeneralPurpose::new(
24 &base64::alphabet::STANDARD,
25 base64::engine::GeneralPurposeConfig::new()
26 .with_decode_padding_mode(base64::engine::DecodePaddingMode::Indifferent),
27 );
28
29 pub fn from_base64(encoded: &str) -> StdResult<Self> {
32 Self::B64_ENGINE
33 .decode(encoded.as_bytes())
34 .map(Binary::from)
35 .map_err(StdError::invalid_base64)
36 }
37
38 pub fn to_base64(&self) -> String {
41 Self::B64_ENGINE.encode(self.0.as_slice())
42 }
43
44 pub fn as_slice(&self) -> &[u8] {
45 self.0.as_slice()
46 }
47
48 pub fn to_array<const LENGTH: usize>(&self) -> StdResult<[u8; LENGTH]> {
70 if self.len() != LENGTH {
71 return Err(StdError::invalid_data_size(LENGTH, self.len()));
72 }
73
74 let mut out: [u8; LENGTH] = [0; LENGTH];
75 out.copy_from_slice(&self.0);
76 Ok(out)
77 }
78}
79
80impl fmt::Display for Binary {
81 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
82 write!(f, "{}", self.to_base64())
83 }
84}
85
86impl fmt::Debug for Binary {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 write!(f, "Binary(")?;
91 for byte in self.0.iter() {
92 write!(f, "{byte:02x}")?;
93 }
94 write!(f, ")")?;
95 Ok(())
96 }
97}
98
99impl Deref for Binary {
105 type Target = [u8];
106
107 fn deref(&self) -> &Self::Target {
108 self.as_slice()
109 }
110}
111
112impl AsRef<[u8]> for Binary {
113 fn as_ref(&self) -> &[u8] {
114 self.as_slice()
115 }
116}
117
118impl From<&[u8]> for Binary {
120 fn from(binary: &[u8]) -> Self {
121 Self(binary.to_vec())
122 }
123}
124
125impl<const LENGTH: usize> From<&[u8; LENGTH]> for Binary {
127 fn from(source: &[u8; LENGTH]) -> Self {
128 Self(source.to_vec())
129 }
130}
131
132impl<const LENGTH: usize> From<[u8; LENGTH]> for Binary {
134 fn from(source: [u8; LENGTH]) -> Self {
135 Self(source.into())
136 }
137}
138
139impl From<Vec<u8>> for Binary {
140 fn from(vec: Vec<u8>) -> Self {
141 Self(vec)
142 }
143}
144
145impl From<Binary> for Vec<u8> {
146 fn from(original: Binary) -> Vec<u8> {
147 original.0
148 }
149}
150
151impl PartialEq<Vec<u8>> for Binary {
153 fn eq(&self, rhs: &Vec<u8>) -> bool {
154 self.0 == *rhs
156 }
157}
158
159impl PartialEq<Binary> for Vec<u8> {
161 fn eq(&self, rhs: &Binary) -> bool {
162 *self == rhs.0
164 }
165}
166
167impl PartialEq<&[u8]> for Binary {
169 fn eq(&self, rhs: &&[u8]) -> bool {
170 self.as_slice() == *rhs
172 }
173}
174
175impl PartialEq<Binary> for &[u8] {
177 fn eq(&self, rhs: &Binary) -> bool {
178 *self == rhs.as_slice()
180 }
181}
182
183impl<const LENGTH: usize> PartialEq<&[u8; LENGTH]> for Binary {
185 fn eq(&self, rhs: &&[u8; LENGTH]) -> bool {
186 self.as_slice() == rhs.as_slice()
187 }
188}
189
190impl<const LENGTH: usize> PartialEq<Binary> for &[u8; LENGTH] {
192 fn eq(&self, rhs: &Binary) -> bool {
193 self.as_slice() == rhs.as_slice()
194 }
195}
196
197impl<const LENGTH: usize> PartialEq<[u8; LENGTH]> for Binary {
199 fn eq(&self, rhs: &[u8; LENGTH]) -> bool {
200 self.as_slice() == rhs.as_slice()
201 }
202}
203
204impl<const LENGTH: usize> PartialEq<Binary> for [u8; LENGTH] {
206 fn eq(&self, rhs: &Binary) -> bool {
207 self.as_slice() == rhs.as_slice()
208 }
209}
210
211impl Serialize for Binary {
213 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
214 where
215 S: ser::Serializer,
216 {
217 if serializer.is_human_readable() {
218 serializer.serialize_str(&self.to_base64())
219 } else {
220 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.")
221 }
222 }
223}
224
225impl<'de> Deserialize<'de> for Binary {
227 fn deserialize<D>(deserializer: D) -> Result<Binary, D::Error>
228 where
229 D: Deserializer<'de>,
230 {
231 if deserializer.is_human_readable() {
232 deserializer.deserialize_str(Base64Visitor)
233 } else {
234 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.")
235 }
236 }
237}
238
239struct Base64Visitor;
240
241impl<'de> de::Visitor<'de> for Base64Visitor {
242 type Value = Binary;
243
244 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
245 formatter.write_str("valid base64 encoded string")
246 }
247
248 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
249 where
250 E: de::Error,
251 {
252 match Binary::from_base64(v) {
253 Ok(binary) => Ok(binary),
254 Err(_) => Err(E::custom(format!("invalid base64: {v}"))),
255 }
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262 use crate::assert_hash_works;
263 use crate::errors::StdError;
264 use crate::serde::{from_json, to_json_vec};
265
266 #[test]
267 fn to_array_works() {
268 let binary = Binary::from(&[1, 2, 3]);
270 let array: [u8; 3] = binary.to_array().unwrap();
271 assert_eq!(array, [1, 2, 3]);
272
273 let binary = Binary::from(&[]);
275 let array: [u8; 0] = binary.to_array().unwrap();
276 assert_eq!(array, [] as [u8; 0]);
277
278 let binary = Binary::from(&[1, 2, 3]);
280 let error = binary.to_array::<8>().unwrap_err();
281 match error {
282 StdError::InvalidDataSize {
283 expected, actual, ..
284 } => {
285 assert_eq!(expected, 8);
286 assert_eq!(actual, 3);
287 }
288 err => panic!("Unexpected error: {err:?}"),
289 }
290
291 let binary = Binary::from_base64("t119JOQox4WUQEmO/nyqOZfO+wjJm91YG2sfn4ZglvA=").unwrap();
293 let array: [u8; 32] = binary.to_array().unwrap();
294 assert_eq!(
295 array,
296 [
297 0xb7, 0x5d, 0x7d, 0x24, 0xe4, 0x28, 0xc7, 0x85, 0x94, 0x40, 0x49, 0x8e, 0xfe, 0x7c,
298 0xaa, 0x39, 0x97, 0xce, 0xfb, 0x08, 0xc9, 0x9b, 0xdd, 0x58, 0x1b, 0x6b, 0x1f, 0x9f,
299 0x86, 0x60, 0x96, 0xf0,
300 ]
301 );
302
303 let binary =
305 Binary::from_base64("t119JOQox4WUQEmO/nyqOZfO+wjJm91YG2sfn4ZglvBzyMOwMWq+").unwrap();
306 let array: [u8; 39] = binary.to_array().unwrap();
307 assert_eq!(
308 array,
309 [
310 0xb7, 0x5d, 0x7d, 0x24, 0xe4, 0x28, 0xc7, 0x85, 0x94, 0x40, 0x49, 0x8e, 0xfe, 0x7c,
311 0xaa, 0x39, 0x97, 0xce, 0xfb, 0x08, 0xc9, 0x9b, 0xdd, 0x58, 0x1b, 0x6b, 0x1f, 0x9f,
312 0x86, 0x60, 0x96, 0xf0, 0x73, 0xc8, 0xc3, 0xb0, 0x31, 0x6a, 0xbe,
313 ]
314 );
315 }
316
317 #[test]
318 fn test_base64_encoding_success() {
319 for (value, encoded, encoded_no_pad) in [
320 (&b""[..], "", ""),
321 (&b"hello"[..], "aGVsbG8=", "aGVsbG8"),
322 (&b"\x0C\xBB\x00\x11\xFA\x01"[..], "DLsAEfoB", "DLsAEfoB"),
323 (&b"rand"[..], "cmFuZA==", "cmFuZA"),
324 (&b"rand"[..], "cmFuZA==", "cmFuZA="),
325 (&b"randomiZ"[..], "cmFuZG9taVo=", "cmFuZG9taVo"),
326 ] {
327 let value = Binary::from(value);
328 assert_eq!(encoded, value.to_base64());
329 assert_eq!(Ok(value.clone()), Binary::from_base64(encoded));
330 assert_eq!(Ok(value.clone()), Binary::from_base64(encoded_no_pad));
331 }
332 }
333
334 #[test]
335 fn test_base64_encoding_error() {
336 for (invalid_base64, want) in [
337 ("cm%uZG9taVo", "Invalid byte 37, offset 2."),
338 ("cmFuZ", "Encoded text cannot have a 6-bit remainder."),
339 ] {
340 match Binary::from_base64(invalid_base64) {
341 Err(StdError::InvalidBase64 { msg }) => assert_eq!(want, msg),
342 result => panic!("Unexpected result: {result:?}"),
343 }
344 }
345 }
346
347 #[test]
348 fn from_slice_works() {
349 let original: &[u8] = &[0u8, 187, 61, 11, 250, 0];
350 let binary: Binary = original.into();
351 assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
352 }
353
354 #[test]
355 fn from_fixed_length_array_works() {
356 let original = &[];
357 let binary: Binary = original.into();
358 assert_eq!(binary.len(), 0);
359
360 let original = &[0u8];
361 let binary: Binary = original.into();
362 assert_eq!(binary.as_slice(), [0u8]);
363
364 let original = &[0u8, 187, 61, 11, 250, 0];
365 let binary: Binary = original.into();
366 assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
367
368 let original = &[
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, 1,
370 1, 1, 1,
371 ];
372 let binary: Binary = original.into();
373 assert_eq!(
374 binary.as_slice(),
375 [
376 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,
377 1, 1, 1, 1,
378 ]
379 );
380 }
381
382 #[test]
383 fn from_owned_fixed_length_array_works() {
384 let original = [];
385 let binary: Binary = original.into();
386 assert_eq!(binary.len(), 0);
387
388 let original = [0u8];
389 let binary: Binary = original.into();
390 assert_eq!(binary.as_slice(), [0u8]);
391
392 let original = [0u8, 187, 61, 11, 250, 0];
393 let binary: Binary = original.into();
394 assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
395
396 let original = [
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, 1,
398 1, 1, 1,
399 ];
400 let binary: Binary = original.into();
401 assert_eq!(
402 binary.as_slice(),
403 [
404 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,
405 1, 1, 1, 1,
406 ]
407 );
408 }
409
410 #[test]
411 fn from_literal_works() {
412 let a: Binary = b"".into();
413 assert_eq!(a.len(), 0);
414
415 let a: Binary = b".".into();
416 assert_eq!(a.len(), 1);
417
418 let a: Binary = b"...".into();
419 assert_eq!(a.len(), 3);
420
421 let a: Binary = b"...............................".into();
422 assert_eq!(a.len(), 31);
423
424 let a: Binary = b"................................".into();
425 assert_eq!(a.len(), 32);
426
427 let a: Binary = b".................................".into();
428 assert_eq!(a.len(), 33);
429 }
430
431 #[test]
432 fn from_vec_works() {
433 let original = vec![0u8, 187, 61, 11, 250, 0];
434 let original_ptr = original.as_ptr();
435 let binary: Binary = original.into();
436 assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
437 assert_eq!(binary.0.as_ptr(), original_ptr, "vector must not be copied");
438 }
439
440 #[test]
441 fn into_vec_works() {
442 let original = Binary(vec![0u8, 187, 61, 11, 250, 0]);
444 let original_ptr = original.0.as_ptr();
445 let vec: Vec<u8> = original.into();
446 assert_eq!(vec.as_slice(), [0u8, 187, 61, 11, 250, 0]);
447 assert_eq!(vec.as_ptr(), original_ptr, "vector must not be copied");
448
449 let original = Binary(vec![7u8, 35, 49, 101, 0, 255]);
451 let original_ptr = original.0.as_ptr();
452 let vec = Vec::<u8>::from(original);
453 assert_eq!(vec.as_slice(), [7u8, 35, 49, 101, 0, 255]);
454 assert_eq!(vec.as_ptr(), original_ptr, "vector must not be copied");
455 }
456
457 #[test]
458 fn serialization_works() {
459 let binary = Binary(vec![0u8, 187, 61, 11, 250, 0]);
460
461 let json = to_json_vec(&binary).unwrap();
462 let deserialized: Binary = from_json(json).unwrap();
463
464 assert_eq!(binary, deserialized);
465 }
466
467 #[test]
468 fn deserialize_from_valid_string() {
469 let b64_str = "ALs9C/oA";
470 let expected = vec![0u8, 187, 61, 11, 250, 0];
472
473 let serialized = to_json_vec(&b64_str).unwrap();
474 let deserialized: Binary = from_json(serialized).unwrap();
475 assert_eq!(expected, deserialized.as_slice());
476 }
477
478 #[test]
479 fn deserialize_from_invalid_string() {
480 let invalid_str = "**BAD!**";
481 let serialized = to_json_vec(&invalid_str).unwrap();
482 let res = from_json::<Binary>(&serialized);
483 assert!(res.is_err());
484 }
485
486 #[test]
487 fn binary_implements_debug() {
488 let binary = Binary(vec![0x07, 0x35, 0xAA, 0xcb, 0x00, 0xff]);
490 assert_eq!(format!("{binary:?}"), "Binary(0735aacb00ff)",);
491
492 let binary = Binary(vec![]);
494 assert_eq!(format!("{binary:?}"), "Binary()",);
495 }
496
497 #[test]
498 fn binary_implements_deref() {
499 let binary = Binary(vec![7u8, 35, 49, 101, 0, 255]);
501 assert_eq!(*binary, [7u8, 35, 49, 101, 0, 255]);
502
503 let binary = Binary(vec![7u8, 35, 49, 101, 0, 255]);
505 assert_eq!(binary.len(), 6);
506 let binary_slice: &[u8] = &binary;
507 assert_eq!(binary_slice, &[7u8, 35, 49, 101, 0, 255]);
508 }
509
510 #[test]
511 fn binary_implements_as_ref() {
512 let want = &[7u8, 35, 49, 101, 0, 255];
513 let data = Binary(want.to_vec());
514 assert_eq!(want, AsRef::<[u8]>::as_ref(&data));
515 assert_eq!(want, AsRef::<[u8]>::as_ref(&&data));
516 }
517
518 #[test]
521 fn binary_implements_hash_eq() {
522 let a = Binary::from([0, 187, 61, 11, 250, 0]);
523 let b = Binary::from([16, 21, 33, 0, 255, 9]);
524 assert_hash_works!(a, b);
525 }
526
527 #[test]
528 fn binary_implements_partial_eq_with_vector() {
529 let a = Binary(vec![5u8; 3]);
530 let b = vec![5u8; 3];
531 let c = vec![9u8; 3];
532 assert_eq!(a, b);
533 assert_eq!(b, a);
534 assert_ne!(a, c);
535 assert_ne!(c, a);
536 }
537
538 #[test]
539 fn binary_implements_partial_eq_with_slice_and_array() {
540 let a = Binary(vec![0xAA, 0xBB]);
541
542 assert_eq!(a, b"\xAA\xBB" as &[u8]);
544 assert_eq!(b"\xAA\xBB" as &[u8], a);
545 assert_ne!(a, b"\x11\x22" as &[u8]);
546 assert_ne!(b"\x11\x22" as &[u8], a);
547
548 assert_eq!(a, b"\xAA\xBB");
550 assert_eq!(b"\xAA\xBB", a);
551 assert_ne!(a, b"\x11\x22");
552 assert_ne!(b"\x11\x22", a);
553
554 assert_eq!(a, [0xAA, 0xBB]);
556 assert_eq!([0xAA, 0xBB], a);
557 assert_ne!(a, [0x11, 0x22]);
558 assert_ne!([0x11, 0x22], a);
559 }
560}