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