1use core::fmt;
2use core::ops::Deref;
3
4use schemars::JsonSchema;
5use serde::{de, ser, Deserialize, Deserializer, Serialize};
6
7use crate::{Binary, StdError, StdResult};
8
9#[derive(Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord, JsonSchema)]
15pub struct HexBinary(#[schemars(with = "String")] Vec<u8>);
16
17impl HexBinary {
18 pub fn from_hex(input: &str) -> StdResult<Self> {
19 let vec = hex::decode(input).map_err(StdError::invalid_hex)?;
20 Ok(Self(vec))
21 }
22
23 pub fn to_hex(&self) -> String {
24 hex::encode(&self.0)
25 }
26
27 pub fn as_slice(&self) -> &[u8] {
28 self.0.as_slice()
29 }
30
31 pub fn to_array<const LENGTH: usize>(&self) -> StdResult<[u8; LENGTH]> {
53 if self.len() != LENGTH {
54 return Err(StdError::invalid_data_size(LENGTH, self.len()));
55 }
56
57 let mut out: [u8; LENGTH] = [0; LENGTH];
58 out.copy_from_slice(&self.0);
59 Ok(out)
60 }
61}
62
63impl fmt::Display for HexBinary {
64 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65 write!(f, "{}", self.to_hex())
66 }
67}
68
69impl fmt::Debug for HexBinary {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 write!(f, "HexBinary(")?;
74 for byte in self.0.iter() {
75 write!(f, "{byte:02x}")?;
76 }
77 write!(f, ")")?;
78 Ok(())
79 }
80}
81
82impl Deref for HexBinary {
88 type Target = [u8];
89
90 fn deref(&self) -> &Self::Target {
91 self.as_slice()
92 }
93}
94
95impl AsRef<[u8]> for HexBinary {
96 fn as_ref(&self) -> &[u8] {
97 self.as_slice()
98 }
99}
100
101impl From<&[u8]> for HexBinary {
103 fn from(binary: &[u8]) -> Self {
104 Self(binary.to_vec())
105 }
106}
107
108impl<const LENGTH: usize> From<&[u8; LENGTH]> for HexBinary {
110 fn from(source: &[u8; LENGTH]) -> Self {
111 Self(source.to_vec())
112 }
113}
114
115impl<const LENGTH: usize> From<[u8; LENGTH]> for HexBinary {
117 fn from(source: [u8; LENGTH]) -> Self {
118 Self(source.into())
119 }
120}
121
122impl From<Vec<u8>> for HexBinary {
123 fn from(vec: Vec<u8>) -> Self {
124 Self(vec)
125 }
126}
127
128impl From<HexBinary> for Vec<u8> {
129 fn from(original: HexBinary) -> Vec<u8> {
130 original.0
131 }
132}
133
134impl From<Binary> for HexBinary {
135 fn from(original: Binary) -> Self {
136 Self(original.into())
137 }
138}
139
140impl From<HexBinary> for Binary {
141 fn from(original: HexBinary) -> Binary {
142 Binary::from(original.0)
143 }
144}
145
146impl PartialEq<Vec<u8>> for HexBinary {
148 fn eq(&self, rhs: &Vec<u8>) -> bool {
149 self.0 == *rhs
151 }
152}
153
154impl PartialEq<HexBinary> for Vec<u8> {
156 fn eq(&self, rhs: &HexBinary) -> bool {
157 *self == rhs.0
159 }
160}
161
162impl PartialEq<&[u8]> for HexBinary {
164 fn eq(&self, rhs: &&[u8]) -> bool {
165 self.as_slice() == *rhs
167 }
168}
169
170impl PartialEq<HexBinary> for &[u8] {
172 fn eq(&self, rhs: &HexBinary) -> bool {
173 *self == rhs.as_slice()
175 }
176}
177
178impl<const LENGTH: usize> PartialEq<[u8; LENGTH]> for HexBinary {
180 fn eq(&self, rhs: &[u8; LENGTH]) -> bool {
181 self.as_slice() == rhs.as_slice()
182 }
183}
184
185impl<const LENGTH: usize> PartialEq<HexBinary> for [u8; LENGTH] {
187 fn eq(&self, rhs: &HexBinary) -> bool {
188 self.as_slice() == rhs.as_slice()
189 }
190}
191
192impl<const LENGTH: usize> PartialEq<&[u8; LENGTH]> for HexBinary {
194 fn eq(&self, rhs: &&[u8; LENGTH]) -> bool {
195 self.as_slice() == rhs.as_slice()
196 }
197}
198
199impl<const LENGTH: usize> PartialEq<HexBinary> for &[u8; LENGTH] {
201 fn eq(&self, rhs: &HexBinary) -> bool {
202 self.as_slice() == rhs.as_slice()
203 }
204}
205
206impl Serialize for HexBinary {
208 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
209 where
210 S: ser::Serializer,
211 {
212 if serializer.is_human_readable() {
213 serializer.serialize_str(&self.to_hex())
214 } else {
215 panic!("HexBinary 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.")
216 }
217 }
218}
219
220impl<'de> Deserialize<'de> for HexBinary {
222 fn deserialize<D>(deserializer: D) -> Result<HexBinary, D::Error>
223 where
224 D: Deserializer<'de>,
225 {
226 if deserializer.is_human_readable() {
227 deserializer.deserialize_str(HexVisitor)
228 } else {
229 panic!("HexBinary 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.")
230 }
231 }
232}
233
234struct HexVisitor;
235
236impl<'de> de::Visitor<'de> for HexVisitor {
237 type Value = HexBinary;
238
239 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
240 formatter.write_str("valid hex encoded string")
241 }
242
243 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
244 where
245 E: de::Error,
246 {
247 match HexBinary::from_hex(v) {
248 Ok(data) => Ok(data),
249 Err(_) => Err(E::custom(format!("invalid hex: {v}"))),
250 }
251 }
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257
258 use crate::{assert_hash_works, from_json, to_json_vec, StdError};
259
260 #[test]
261 fn from_hex_works() {
262 let data = HexBinary::from_hex("").unwrap();
263 assert_eq!(data, b"");
264 let data = HexBinary::from_hex("61").unwrap();
265 assert_eq!(data, b"a");
266 let data = HexBinary::from_hex("00").unwrap();
267 assert_eq!(data, b"\0");
268
269 let data = HexBinary::from_hex("68656c6c6f").unwrap();
270 assert_eq!(data, b"hello");
271 let data = HexBinary::from_hex("68656C6C6F").unwrap();
272 assert_eq!(data, b"hello");
273 let data = HexBinary::from_hex("72616e646f6d695a").unwrap();
274 assert_eq!(data.as_slice(), b"randomiZ");
275
276 match HexBinary::from_hex("123").unwrap_err() {
278 StdError::InvalidHex { msg, .. } => {
279 assert_eq!(msg, "Odd number of digits")
280 }
281 _ => panic!("Unexpected error type"),
282 }
283 match HexBinary::from_hex("efgh").unwrap_err() {
285 StdError::InvalidHex { msg, .. } => {
286 assert_eq!(msg, "Invalid character 'g' at position 2")
287 }
288 _ => panic!("Unexpected error type"),
289 }
290 match HexBinary::from_hex("0xaa").unwrap_err() {
292 StdError::InvalidHex { msg, .. } => {
293 assert_eq!(msg, "Invalid character 'x' at position 1")
294 }
295 _ => panic!("Unexpected error type"),
296 }
297 assert!(matches!(
299 HexBinary::from_hex("aa ").unwrap_err(),
300 StdError::InvalidHex { .. }
301 ));
302 assert!(matches!(
303 HexBinary::from_hex(" aa").unwrap_err(),
304 StdError::InvalidHex { .. }
305 ));
306 assert!(matches!(
307 HexBinary::from_hex("a a").unwrap_err(),
308 StdError::InvalidHex { .. }
309 ));
310 assert!(matches!(
311 HexBinary::from_hex(" aa ").unwrap_err(),
312 StdError::InvalidHex { .. }
313 ));
314 }
315
316 #[test]
317 fn to_hex_works() {
318 let binary: &[u8] = b"";
319 let encoded = HexBinary::from(binary).to_hex();
320 assert_eq!(encoded, "");
321
322 let binary: &[u8] = b"hello";
323 let encoded = HexBinary::from(binary).to_hex();
324 assert_eq!(encoded, "68656c6c6f");
325
326 let binary = vec![12u8, 187, 0, 17, 250, 1];
327 let encoded = HexBinary(binary).to_hex();
328 assert_eq!(encoded, "0cbb0011fa01");
329 }
330
331 #[test]
332 fn to_array_works() {
333 let binary = HexBinary::from(&[1, 2, 3]);
335 let array: [u8; 3] = binary.to_array().unwrap();
336 assert_eq!(array, [1, 2, 3]);
337
338 let binary = HexBinary::from(&[]);
340 let array: [u8; 0] = binary.to_array().unwrap();
341 assert_eq!(array, [] as [u8; 0]);
342
343 let binary = HexBinary::from(&[1, 2, 3]);
345 let error = binary.to_array::<8>().unwrap_err();
346 match error {
347 StdError::InvalidDataSize {
348 expected, actual, ..
349 } => {
350 assert_eq!(expected, 8);
351 assert_eq!(actual, 3);
352 }
353 err => panic!("Unexpected error: {err:?}"),
354 }
355
356 let binary =
358 HexBinary::from_hex("b75d7d24e428c7859440498efe7caa3997cefb08c99bdd581b6b1f9f866096f0")
359 .unwrap();
360 let array: [u8; 32] = binary.to_array().unwrap();
361 assert_eq!(
362 array,
363 [
364 0xb7, 0x5d, 0x7d, 0x24, 0xe4, 0x28, 0xc7, 0x85, 0x94, 0x40, 0x49, 0x8e, 0xfe, 0x7c,
365 0xaa, 0x39, 0x97, 0xce, 0xfb, 0x08, 0xc9, 0x9b, 0xdd, 0x58, 0x1b, 0x6b, 0x1f, 0x9f,
366 0x86, 0x60, 0x96, 0xf0,
367 ]
368 );
369
370 let binary = HexBinary::from_hex(
372 "b75d7d24e428c7859440498efe7caa3997cefb08c99bdd581b6b1f9f866096f073c8c3b0316abe",
373 )
374 .unwrap();
375 let array: [u8; 39] = binary.to_array().unwrap();
376 assert_eq!(
377 array,
378 [
379 0xb7, 0x5d, 0x7d, 0x24, 0xe4, 0x28, 0xc7, 0x85, 0x94, 0x40, 0x49, 0x8e, 0xfe, 0x7c,
380 0xaa, 0x39, 0x97, 0xce, 0xfb, 0x08, 0xc9, 0x9b, 0xdd, 0x58, 0x1b, 0x6b, 0x1f, 0x9f,
381 0x86, 0x60, 0x96, 0xf0, 0x73, 0xc8, 0xc3, 0xb0, 0x31, 0x6a, 0xbe,
382 ]
383 );
384 }
385
386 #[test]
387 fn from_json_works() {
388 let original: &[u8] = &[0u8, 187, 61, 11, 250, 0];
389 let binary: HexBinary = original.into();
390 assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
391 }
392
393 #[test]
394 fn from_fixed_length_array_works() {
395 let original = &[];
396 let binary: HexBinary = original.into();
397 assert_eq!(binary.len(), 0);
398
399 let original = &[0u8];
400 let binary: HexBinary = original.into();
401 assert_eq!(binary.as_slice(), [0u8]);
402
403 let original = &[0u8, 187, 61, 11, 250, 0];
404 let binary: HexBinary = original.into();
405 assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
406
407 let original = &[
408 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,
409 1, 1, 1,
410 ];
411 let binary: HexBinary = original.into();
412 assert_eq!(
413 binary.as_slice(),
414 [
415 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,
416 1, 1, 1, 1,
417 ]
418 );
419 }
420
421 #[test]
422 fn from_owned_fixed_length_array_works() {
423 let original = [];
424 let binary: HexBinary = original.into();
425 assert_eq!(binary.len(), 0);
426
427 let original = [0u8];
428 let binary: HexBinary = original.into();
429 assert_eq!(binary.as_slice(), [0u8]);
430
431 let original = [0u8, 187, 61, 11, 250, 0];
432 let binary: HexBinary = original.into();
433 assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
434
435 let original = [
436 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,
437 1, 1, 1,
438 ];
439 let binary: HexBinary = original.into();
440 assert_eq!(
441 binary.as_slice(),
442 [
443 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,
444 1, 1, 1, 1,
445 ]
446 );
447 }
448
449 #[test]
450 fn from_literal_works() {
451 let a: HexBinary = b"".into();
452 assert_eq!(a.len(), 0);
453
454 let a: HexBinary = b".".into();
455 assert_eq!(a.len(), 1);
456
457 let a: HexBinary = b"...".into();
458 assert_eq!(a.len(), 3);
459
460 let a: HexBinary = b"...............................".into();
461 assert_eq!(a.len(), 31);
462
463 let a: HexBinary = b"................................".into();
464 assert_eq!(a.len(), 32);
465
466 let a: HexBinary = (b".................................").into();
467 assert_eq!(a.len(), 33);
468 }
469
470 #[test]
471 fn from_vec_works() {
472 let original = vec![0u8, 187, 61, 11, 250, 0];
473 let original_ptr = original.as_ptr();
474 let binary: HexBinary = original.into();
475 assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
476 assert_eq!(binary.0.as_ptr(), original_ptr, "vector must not be copied");
477 }
478
479 #[test]
480 fn into_vec_works() {
481 let original = HexBinary(vec![0u8, 187, 61, 11, 250, 0]);
483 let original_ptr = original.0.as_ptr();
484 let vec: Vec<u8> = original.into();
485 assert_eq!(vec.as_slice(), [0u8, 187, 61, 11, 250, 0]);
486 assert_eq!(vec.as_ptr(), original_ptr, "vector must not be copied");
487
488 let original = HexBinary(vec![7u8, 35, 49, 101, 0, 255]);
490 let original_ptr = original.0.as_ptr();
491 let vec = Vec::<u8>::from(original);
492 assert_eq!(vec.as_slice(), [7u8, 35, 49, 101, 0, 255]);
493 assert_eq!(vec.as_ptr(), original_ptr, "vector must not be copied");
494 }
495
496 #[test]
497 fn from_binary_works() {
498 let original = Binary::from([0u8, 187, 61, 11, 250, 0]);
499 let original_ptr = original.as_ptr();
500 let binary: HexBinary = original.into();
501 assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
502 assert_eq!(binary.0.as_ptr(), original_ptr, "vector must not be copied");
503 }
504
505 #[test]
506 fn into_binary_works() {
507 let original = HexBinary(vec![0u8, 187, 61, 11, 250, 0]);
509 let original_ptr = original.0.as_ptr();
510 let bin: Binary = original.into();
511 assert_eq!(bin.as_slice(), [0u8, 187, 61, 11, 250, 0]);
512 assert_eq!(bin.as_ptr(), original_ptr, "vector must not be copied");
513
514 let original = HexBinary(vec![7u8, 35, 49, 101, 0, 255]);
516 let original_ptr = original.0.as_ptr();
517 let bin = Binary::from(original);
518 assert_eq!(bin.as_slice(), [7u8, 35, 49, 101, 0, 255]);
519 assert_eq!(bin.as_ptr(), original_ptr, "vector must not be copied");
520 }
521
522 #[test]
523 fn serialization_works() {
524 let binary = HexBinary(vec![0u8, 187, 61, 11, 250, 0]);
525
526 let json = to_json_vec(&binary).unwrap();
527 let deserialized: HexBinary = from_json(json).unwrap();
528
529 assert_eq!(binary, deserialized);
530 }
531
532 #[test]
533 fn deserialize_from_valid_string() {
534 let hex = "00bb3d0bfa00";
535 let expected = vec![0u8, 187, 61, 11, 250, 0];
537
538 let serialized = to_json_vec(&hex).unwrap();
539 let deserialized: HexBinary = from_json(serialized).unwrap();
540 assert_eq!(expected, deserialized.as_slice());
541 }
542
543 #[test]
544 fn deserialize_from_invalid_string() {
545 let invalid_str = "**BAD!**";
546 let serialized = to_json_vec(&invalid_str).unwrap();
547 let res = from_json::<HexBinary>(&serialized);
548 assert!(res.is_err());
549 }
550
551 #[test]
552 fn hex_binary_implements_debug() {
553 let data = HexBinary(vec![0x07, 0x35, 0xAA, 0xcb, 0x00, 0xff]);
555 assert_eq!(format!("{data:?}"), "HexBinary(0735aacb00ff)",);
556
557 let data = HexBinary(vec![]);
559 assert_eq!(format!("{data:?}"), "HexBinary()",);
560 }
561
562 #[test]
563 fn hex_binary_implements_deref() {
564 let data = HexBinary(vec![7u8, 35, 49, 101, 0, 255]);
566 assert_eq!(*data, [7u8, 35, 49, 101, 0, 255]);
567
568 let data = HexBinary(vec![7u8, 35, 49, 101, 0, 255]);
570 assert_eq!(data.len(), 6);
571 let data_slice: &[u8] = &data;
572 assert_eq!(data_slice, &[7u8, 35, 49, 101, 0, 255]);
573 }
574
575 #[test]
576 fn hex_binary_implements_as_ref() {
577 let want = &[7u8, 35, 49, 101, 0, 255];
578 let data = HexBinary(want.to_vec());
579 assert_eq!(want, AsRef::<[u8]>::as_ref(&data));
580 assert_eq!(want, AsRef::<[u8]>::as_ref(&&data));
581 }
582
583 #[test]
586 fn hex_binary_implements_hash_eq() {
587 let a = HexBinary::from([0, 187, 61, 11, 250, 0]);
588 let b = HexBinary::from([16, 21, 33, 0, 255, 9]);
589 assert_hash_works!(a, b);
590 }
591
592 #[test]
593 fn hex_binary_implements_partial_eq_with_vector() {
594 let a = HexBinary(vec![5u8; 3]);
595 let b = vec![5u8; 3];
596 let c = vec![9u8; 3];
597 assert_eq!(a, b);
598 assert_eq!(b, a);
599 assert_ne!(a, c);
600 assert_ne!(c, a);
601 }
602
603 #[test]
604 fn hex_binary_implements_partial_eq_with_slice_and_array() {
605 let a = HexBinary(vec![0xAA, 0xBB]);
606
607 assert_eq!(a, b"\xAA\xBB" as &[u8]);
609 assert_eq!(b"\xAA\xBB" as &[u8], a);
610 assert_ne!(a, b"\x11\x22" as &[u8]);
611 assert_ne!(b"\x11\x22" as &[u8], a);
612
613 assert_eq!(a, b"\xAA\xBB");
615 assert_eq!(b"\xAA\xBB", a);
616 assert_ne!(a, b"\x11\x22");
617 assert_ne!(b"\x11\x22", a);
618
619 assert_eq!(a, [0xAA, 0xBB]);
621 assert_eq!([0xAA, 0xBB], a);
622 assert_ne!(a, [0x11, 0x22]);
623 assert_ne!([0x11, 0x22], a);
624 }
625}