1use chia_sha2::Sha256;
2use chia_traits::{chia_error, read_bytes, Streamable};
3use clvm_traits::{ClvmDecoder, ClvmEncoder, FromClvm, FromClvmError, ToClvm, ToClvmError};
4use clvm_utils::TreeHash;
5use clvmr::Atom;
6use std::array::TryFromSliceError;
7use std::fmt;
8use std::io::Cursor;
9use std::ops::Deref;
10
11#[cfg(feature = "py-bindings")]
12use chia_traits::{ChiaToPython, FromJsonDict, ToJsonDict};
13#[cfg(feature = "py-bindings")]
14use hex::FromHex;
15#[cfg(feature = "py-bindings")]
16use pyo3::exceptions::PyValueError;
17#[cfg(feature = "py-bindings")]
18use pyo3::prelude::*;
19#[cfg(feature = "py-bindings")]
20use pyo3::types::PyBytes;
21
22#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
23#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
24pub struct Bytes(Vec<u8>);
25
26impl Bytes {
27 pub fn new(bytes: Vec<u8>) -> Self {
28 Self(bytes)
29 }
30
31 pub fn len(&self) -> usize {
32 self.0.len()
33 }
34
35 pub fn is_empty(&self) -> bool {
36 self.0.is_empty()
37 }
38
39 pub fn as_slice(&self) -> &[u8] {
40 self.0.as_slice()
41 }
42
43 pub fn to_vec(&self) -> Vec<u8> {
44 self.0.clone()
45 }
46
47 pub fn into_inner(self) -> Vec<u8> {
48 self.0
49 }
50}
51
52impl fmt::Debug for Bytes {
53 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
54 formatter.write_str(&hex::encode(self))
55 }
56}
57
58impl fmt::Display for Bytes {
59 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
60 formatter.write_str(&hex::encode(self))
61 }
62}
63
64#[cfg(feature = "serde")]
65impl serde::Serialize for Bytes {
66 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
67 where
68 S: serde::Serializer,
69 {
70 chia_serde::ser_bytes(self, serializer, false)
71 }
72}
73
74#[cfg(feature = "serde")]
75impl<'de> serde::Deserialize<'de> for Bytes {
76 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
77 where
78 D: serde::Deserializer<'de>,
79 {
80 chia_serde::de_bytes(deserializer)
81 }
82}
83
84impl Streamable for Bytes {
85 fn update_digest(&self, digest: &mut Sha256) {
86 (self.0.len() as u32).update_digest(digest);
87 digest.update(&self.0);
88 }
89
90 fn stream(&self, out: &mut Vec<u8>) -> chia_error::Result<()> {
91 if self.0.len() > u32::MAX as usize {
92 Err(chia_error::Error::SequenceTooLarge)
93 } else {
94 (self.0.len() as u32).stream(out)?;
95 out.extend_from_slice(&self.0);
96 Ok(())
97 }
98 }
99
100 fn parse<const TRUSTED: bool>(input: &mut Cursor<&[u8]>) -> chia_error::Result<Self> {
101 let len = u32::parse::<TRUSTED>(input)?;
102 Ok(Bytes(read_bytes(input, len as usize)?.to_vec()))
103 }
104}
105
106#[cfg(feature = "py-bindings")]
107impl ToJsonDict for Bytes {
108 fn to_json_dict(&self, py: Python<'_>) -> PyResult<PyObject> {
109 let prefix = if self.0.is_empty() { "" } else { "0x" };
110 Ok(format!("{prefix}{self}")
111 .into_pyobject(py)?
112 .into_any()
113 .unbind())
114 }
115}
116
117#[cfg(feature = "py-bindings")]
118impl FromJsonDict for Bytes {
119 fn from_json_dict(o: &Bound<'_, PyAny>) -> PyResult<Self> {
120 let s: String = o.extract()?;
121 if s.is_empty() {
122 return Ok(Bytes::default());
123 }
124 if !s.starts_with("0x") {
125 return Err(PyValueError::new_err(
126 "bytes object is expected to start with 0x",
127 ));
128 }
129 let s = &s[2..];
130 let buf = match Vec::from_hex(s) {
131 Err(_) => {
132 return Err(PyValueError::new_err("invalid hex"));
133 }
134 Ok(v) => v,
135 };
136 Ok(buf.into())
137 }
138}
139
140impl<N, E: ClvmEncoder<Node = N>> ToClvm<E> for Bytes {
141 fn to_clvm(&self, encoder: &mut E) -> Result<N, ToClvmError> {
142 encoder.encode_atom(Atom::Borrowed(self.0.as_slice()))
143 }
144}
145
146impl<N, D: ClvmDecoder<Node = N>> FromClvm<D> for Bytes {
147 fn from_clvm(decoder: &D, node: N) -> Result<Self, FromClvmError> {
148 let bytes = decoder.decode_atom(&node)?;
149 Ok(Self(bytes.as_ref().to_vec()))
150 }
151}
152
153impl From<&[u8]> for Bytes {
154 fn from(value: &[u8]) -> Self {
155 Self(value.to_vec())
156 }
157}
158
159impl<const N: usize> From<BytesImpl<N>> for Bytes {
160 fn from(value: BytesImpl<N>) -> Self {
161 Self(value.0.to_vec())
162 }
163}
164
165impl From<Vec<u8>> for Bytes {
166 fn from(value: Vec<u8>) -> Self {
167 Self(value)
168 }
169}
170
171impl From<Bytes> for Vec<u8> {
172 fn from(value: Bytes) -> Self {
173 value.0
174 }
175}
176
177impl AsRef<[u8]> for Bytes {
178 fn as_ref(&self) -> &[u8] {
179 &self.0
180 }
181}
182
183impl Deref for Bytes {
184 type Target = [u8];
185
186 fn deref(&self) -> &[u8] {
187 &self.0
188 }
189}
190
191#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
192#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
193pub struct BytesImpl<const N: usize>([u8; N]);
194
195impl<const N: usize> BytesImpl<N> {
196 pub const fn new(bytes: [u8; N]) -> Self {
197 Self(bytes)
198 }
199
200 pub const fn len(&self) -> usize {
201 N
202 }
203
204 pub const fn is_empty(&self) -> bool {
205 N == 0
206 }
207
208 pub fn as_slice(&self) -> &[u8] {
209 &self.0
210 }
211
212 pub fn to_bytes(self) -> [u8; N] {
213 self.0
214 }
215
216 pub fn to_vec(&self) -> Vec<u8> {
217 self.0.to_vec()
218 }
219}
220
221impl<const N: usize> Default for BytesImpl<N> {
222 fn default() -> Self {
223 Self([0; N])
224 }
225}
226
227impl<const N: usize> fmt::Debug for BytesImpl<N> {
228 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
229 formatter.write_str(&hex::encode(self))
230 }
231}
232
233impl<const N: usize> fmt::Display for BytesImpl<N> {
234 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
235 formatter.write_str(&hex::encode(self))
236 }
237}
238
239#[cfg(feature = "serde")]
240impl<const N: usize> serde::Serialize for BytesImpl<N> {
241 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
242 where
243 S: serde::Serializer,
244 {
245 chia_serde::ser_bytes(self, serializer, true)
246 }
247}
248
249#[cfg(feature = "serde")]
250impl<'de, const N: usize> serde::Deserialize<'de> for BytesImpl<N> {
251 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
252 where
253 D: serde::Deserializer<'de>,
254 {
255 chia_serde::de_bytes(deserializer)
256 }
257}
258
259impl<const N: usize> Streamable for BytesImpl<N> {
260 fn update_digest(&self, digest: &mut Sha256) {
261 digest.update(self.0);
262 }
263 fn stream(&self, out: &mut Vec<u8>) -> chia_error::Result<()> {
264 out.extend_from_slice(&self.0);
265 Ok(())
266 }
267
268 fn parse<const TRUSTED: bool>(input: &mut Cursor<&[u8]>) -> chia_error::Result<Self> {
269 Ok(BytesImpl(read_bytes(input, N)?.try_into().unwrap()))
270 }
271}
272
273#[cfg(feature = "py-bindings")]
274impl<const N: usize> ToJsonDict for BytesImpl<N> {
275 fn to_json_dict(&self, py: Python<'_>) -> PyResult<PyObject> {
276 Ok(format!("0x{self}").into_pyobject(py)?.into_any().unbind())
277 }
278}
279
280#[cfg(feature = "py-bindings")]
281impl<const N: usize> FromJsonDict for BytesImpl<N> {
282 fn from_json_dict(o: &Bound<'_, PyAny>) -> PyResult<Self> {
283 let s: String = o.extract()?;
284 if !s.starts_with("0x") {
285 return Err(PyValueError::new_err(
286 "bytes object is expected to start with 0x",
287 ));
288 }
289 let s = &s[2..];
290 let buf = match Vec::from_hex(s) {
291 Err(_) => {
292 return Err(PyValueError::new_err("invalid hex"));
293 }
294 Ok(v) => v,
295 };
296 if buf.len() != N {
297 return Err(PyValueError::new_err(format!(
298 "invalid length {} expected {}",
299 buf.len(),
300 N
301 )));
302 }
303 Ok(buf.try_into().unwrap())
304 }
305}
306
307impl<N, E: ClvmEncoder<Node = N>, const LEN: usize> ToClvm<E> for BytesImpl<LEN> {
308 fn to_clvm(&self, encoder: &mut E) -> Result<N, ToClvmError> {
309 encoder.encode_atom(Atom::Borrowed(self.0.as_slice()))
310 }
311}
312
313impl<N, D: ClvmDecoder<Node = N>, const LEN: usize> FromClvm<D> for BytesImpl<LEN> {
314 fn from_clvm(decoder: &D, node: N) -> Result<Self, FromClvmError> {
315 let bytes = decoder.decode_atom(&node)?;
316 if bytes.as_ref().len() != LEN {
317 return Err(FromClvmError::WrongAtomLength {
318 expected: LEN,
319 found: bytes.as_ref().len(),
320 });
321 }
322 Ok(Self::try_from(bytes.as_ref()).unwrap())
323 }
324}
325
326impl<const N: usize> TryFrom<&[u8]> for BytesImpl<N> {
327 type Error = TryFromSliceError;
328
329 fn try_from(value: &[u8]) -> Result<Self, TryFromSliceError> {
330 Ok(Self(value.try_into()?))
331 }
332}
333
334impl<const N: usize> TryFrom<Vec<u8>> for BytesImpl<N> {
335 type Error = TryFromSliceError;
336
337 fn try_from(value: Vec<u8>) -> Result<Self, TryFromSliceError> {
338 value.as_slice().try_into()
339 }
340}
341
342impl<const N: usize> TryFrom<&Vec<u8>> for BytesImpl<N> {
343 type Error = TryFromSliceError;
344
345 fn try_from(value: &Vec<u8>) -> Result<Self, TryFromSliceError> {
346 value.as_slice().try_into()
347 }
348}
349
350impl<const N: usize> TryFrom<Bytes> for BytesImpl<N> {
351 type Error = TryFromSliceError;
352
353 fn try_from(value: Bytes) -> Result<Self, TryFromSliceError> {
354 value.0.as_slice().try_into()
355 }
356}
357
358impl<const N: usize> TryFrom<&Bytes> for BytesImpl<N> {
359 type Error = TryFromSliceError;
360
361 fn try_from(value: &Bytes) -> Result<Self, TryFromSliceError> {
362 value.0.as_slice().try_into()
363 }
364}
365
366impl<const N: usize> From<BytesImpl<N>> for Vec<u8> {
367 fn from(value: BytesImpl<N>) -> Self {
368 value.to_vec()
369 }
370}
371
372impl<const N: usize> From<[u8; N]> for BytesImpl<N> {
373 fn from(value: [u8; N]) -> Self {
374 Self(value)
375 }
376}
377
378impl<const N: usize> From<&[u8; N]> for BytesImpl<N> {
379 fn from(value: &[u8; N]) -> Self {
380 Self(*value)
381 }
382}
383
384impl<const N: usize> From<BytesImpl<N>> for [u8; N] {
385 fn from(value: BytesImpl<N>) -> Self {
386 value.0
387 }
388}
389
390impl<'a, const N: usize> From<&'a BytesImpl<N>> for &'a [u8; N] {
391 fn from(value: &'a BytesImpl<N>) -> &'a [u8; N] {
392 &value.0
393 }
394}
395
396impl<const N: usize> From<&BytesImpl<N>> for [u8; N] {
397 fn from(value: &BytesImpl<N>) -> [u8; N] {
398 value.0
399 }
400}
401
402impl<'a, const N: usize> From<&'a BytesImpl<N>> for &'a [u8] {
403 fn from(value: &'a BytesImpl<N>) -> &'a [u8] {
404 &value.0
405 }
406}
407
408impl<const N: usize> AsRef<[u8]> for BytesImpl<N> {
409 fn as_ref(&self) -> &[u8] {
410 &self.0
411 }
412}
413
414impl<const N: usize> Deref for BytesImpl<N> {
415 type Target = [u8];
416
417 fn deref(&self) -> &[u8] {
418 &self.0
419 }
420}
421
422pub type Bytes32 = BytesImpl<32>;
423pub type Bytes48 = BytesImpl<48>;
424pub type Bytes96 = BytesImpl<96>;
425pub type Bytes100 = BytesImpl<100>;
426
427impl From<Bytes32> for TreeHash {
428 fn from(value: Bytes32) -> Self {
429 Self::new(value.0)
430 }
431}
432
433impl From<TreeHash> for Bytes32 {
434 fn from(value: TreeHash) -> Self {
435 Self(value.to_bytes())
436 }
437}
438
439#[cfg(feature = "py-bindings")]
440impl<'py, const N: usize> IntoPyObject<'py> for BytesImpl<N> {
441 type Target = PyAny;
442 type Output = Bound<'py, Self::Target>;
443 type Error = PyErr;
444
445 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
446 ChiaToPython::to_python(&self, py)
447 }
448}
449
450#[cfg(feature = "py-bindings")]
451impl<const N: usize> ChiaToPython for BytesImpl<N> {
452 fn to_python<'a>(&self, py: Python<'a>) -> PyResult<Bound<'a, PyAny>> {
453 if N == 32 {
454 let bytes_module = PyModule::import(py, "chia_rs.sized_bytes")?;
455 let ty = bytes_module.getattr("bytes32")?;
456 ty.call1((self.0.into_pyobject(py)?,))
457 } else if N == 48 {
458 let bytes_module = PyModule::import(py, "chia_rs.sized_bytes")?;
459 let ty = bytes_module.getattr("bytes48")?;
460 ty.call1((self.0.into_pyobject(py)?,))
461 } else {
462 Ok(PyBytes::new(py, &self.0).into_any())
463 }
464 }
465}
466
467#[cfg(feature = "py-bindings")]
468impl<'py, const N: usize> FromPyObject<'py> for BytesImpl<N> {
469 fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
470 let b = obj.downcast::<PyBytes>()?;
471 let slice: &[u8] = b.as_bytes();
472 let buf: [u8; N] = slice.try_into()?;
473 Ok(BytesImpl::<N>(buf))
474 }
475}
476
477#[cfg(feature = "py-bindings")]
478impl<'py> IntoPyObject<'py> for Bytes {
479 type Target = PyAny;
480 type Output = Bound<'py, Self::Target>;
481 type Error = std::convert::Infallible;
482
483 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
484 Ok(PyBytes::new(py, &self.0).into_any())
485 }
486}
487
488#[cfg(feature = "py-bindings")]
489impl ChiaToPython for Bytes {
490 fn to_python<'a>(&self, py: Python<'a>) -> PyResult<Bound<'a, PyAny>> {
491 Ok(PyBytes::new(py, &self.0).into_any())
492 }
493}
494
495#[cfg(feature = "py-bindings")]
496impl<'py> FromPyObject<'py> for Bytes {
497 fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
498 let b = obj.downcast::<PyBytes>()?;
499 Ok(Bytes(b.as_bytes().to_vec()))
500 }
501}
502
503#[cfg(test)]
504#[allow(clippy::needless_pass_by_value)]
505mod tests {
506 use super::*;
507
508 use clvmr::{
509 serde::{node_from_bytes, node_to_bytes},
510 Allocator,
511 };
512 use rstest::rstest;
513
514 #[rstest]
515 #[case(
517 "0000000000000000000000000000000000000000000000000000000000000000",
518 "0000000000000000000000000000000000000000000000000000000000000000",
519 true
520 )]
521 #[case(
522 "0000000000000000000000000000000000000000000000000000000000000000",
523 "0000000000000000000000000000000000000000000000000000000000000100",
524 false
525 )]
526 #[case(
527 "fff0000000000000000000000000000000000000000000000000000000000100",
528 "fff0000000000000000000000000000000000000000000000000000000000100",
529 true
530 )]
531 #[case("000000", "000000", true)]
533 #[case("123456", "125456", false)]
534 #[case("000001", "00000001", false)]
535 #[case("00000001", "000001", false)]
536 #[case("ffff01", "ffff01", true)]
537 #[case("", "", true)]
538 fn test_bytes_comparisons(#[case] lhs: &str, #[case] rhs: &str, #[case] expect_equal: bool) {
539 let lhs_vec: Vec<u8> = hex::decode(lhs).expect("hex::decode");
540 let rhs_vec: Vec<u8> = hex::decode(rhs).expect("hex::decode");
541
542 if lhs_vec.len() == 32 && rhs_vec.len() == 32 {
543 let lhs = Bytes32::try_from(&lhs_vec).unwrap();
544 let rhs = Bytes32::try_from(&rhs_vec).unwrap();
545
546 assert_eq!(lhs.len(), 32);
547 assert_eq!(rhs.len(), 32);
548
549 assert_eq!(lhs.is_empty(), lhs_vec.is_empty());
550 assert_eq!(rhs.is_empty(), rhs_vec.is_empty());
551
552 if expect_equal {
553 assert_eq!(lhs, rhs);
554 assert_eq!(rhs, lhs);
555 } else {
556 assert!(lhs != rhs);
557 assert!(rhs != lhs);
558 }
559 } else {
560 let lhs = Bytes::from(lhs_vec.clone());
561 let rhs = Bytes::from(rhs_vec.clone());
562
563 assert_eq!(lhs.len(), lhs_vec.len());
564 assert_eq!(rhs.len(), rhs_vec.len());
565
566 assert_eq!(lhs.is_empty(), lhs_vec.is_empty());
567 assert_eq!(rhs.is_empty(), rhs_vec.is_empty());
568
569 if expect_equal {
570 assert_eq!(lhs, rhs);
571 assert_eq!(rhs, lhs);
572 } else {
573 assert!(lhs != rhs);
574 assert!(rhs != lhs);
575 }
576 }
577 }
578
579 fn from_bytes<T: Streamable + fmt::Debug + PartialEq>(buf: &[u8], expected: T) {
580 let mut input = Cursor::<&[u8]>::new(buf);
581 assert_eq!(T::parse::<false>(&mut input).unwrap(), expected);
582 }
583
584 fn from_bytes_fail<T: Streamable + fmt::Debug + PartialEq>(
585 buf: &[u8],
586 expected: chia_error::Error,
587 ) {
588 let mut input = Cursor::<&[u8]>::new(buf);
589 assert_eq!(T::parse::<false>(&mut input).unwrap_err(), expected);
590 }
591
592 fn stream<T: Streamable>(v: &T) -> Vec<u8> {
593 let mut buf = Vec::<u8>::new();
594 v.stream(&mut buf).unwrap();
595 let mut ctx1 = Sha256::new();
596 let mut ctx2 = Sha256::new();
597 v.update_digest(&mut ctx1);
598 ctx2.update(&buf);
599 assert_eq!(&ctx1.finalize(), &ctx2.finalize());
600 buf
601 }
602
603 #[test]
604 fn test_stream_bytes32() {
605 let buf = [
606 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
607 25, 26, 27, 28, 29, 30, 31, 32,
608 ];
609 let out = stream(&Bytes32::from(buf));
610 assert_eq!(buf.as_slice(), &out);
611 }
612
613 #[test]
614 fn test_stream_bytes() {
615 let val: Bytes = vec![
616 1_u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
617 24, 25, 26, 27, 28, 29, 30, 31, 32,
618 ]
619 .into();
620 println!("{val:?}");
621 let buf = stream(&val);
622 println!("buf: {buf:?}");
623 from_bytes(&buf, val);
624 }
625
626 #[test]
627 fn test_parse_bytes_empty() {
628 let buf: &[u8] = &[0, 0, 0, 0];
629 from_bytes::<Bytes>(buf, [].to_vec().into());
630 }
631
632 #[test]
633 fn test_parse_bytes() {
634 let buf: &[u8] = &[0, 0, 0, 3, 1, 2, 3];
635 from_bytes::<Bytes>(buf, [1_u8, 2, 3].to_vec().into());
636 }
637
638 #[test]
639 fn test_parse_truncated_len() {
640 let buf: &[u8] = &[0, 0, 1];
641 from_bytes_fail::<Bytes>(buf, chia_error::Error::EndOfBuffer);
642 }
643
644 #[test]
645 fn test_parse_truncated() {
646 let buf: &[u8] = &[0, 0, 0, 4, 1, 2, 3];
647 from_bytes_fail::<Bytes>(buf, chia_error::Error::EndOfBuffer);
648 }
649
650 #[test]
651 fn test_parse_bytes32() {
652 let buf = [
653 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
654 25, 26, 27, 28, 29, 30, 31, 32,
655 ];
656 from_bytes::<Bytes32>(&buf, Bytes32::from(buf));
657 from_bytes_fail::<Bytes32>(&buf[0..30], chia_error::Error::EndOfBuffer);
658 }
659
660 #[test]
661 fn test_parse_bytes48() {
662 let buf = [
663 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
664 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
665 47, 48,
666 ];
667 from_bytes::<Bytes48>(&buf, Bytes48::from(buf));
668 from_bytes_fail::<Bytes48>(&buf[0..47], chia_error::Error::EndOfBuffer);
669 }
670
671 #[test]
672 fn bytes_roundtrip() {
673 let a = &mut Allocator::new();
674 let expected = "84facef00d";
675 let expected_bytes = hex::decode(expected).unwrap();
676
677 let ptr = node_from_bytes(a, &expected_bytes).unwrap();
678 let bytes = Bytes::from_clvm(a, ptr).unwrap();
679
680 let round_trip = bytes.to_clvm(a).unwrap();
681 assert_eq!(expected, hex::encode(node_to_bytes(a, round_trip).unwrap()));
682 }
683
684 #[test]
685 fn bytes32_roundtrip() {
686 let a = &mut Allocator::new();
687 let expected = "a0eff07522495060c066f66f32acc2a77e3a3e737aca8baea4d1a64ea4cdc13da9";
688 let expected_bytes = hex::decode(expected).unwrap();
689
690 let ptr = node_from_bytes(a, &expected_bytes).unwrap();
691 let bytes32 = Bytes32::from_clvm(a, ptr).unwrap();
692
693 let round_trip = bytes32.to_clvm(a).unwrap();
694 assert_eq!(expected, hex::encode(node_to_bytes(a, round_trip).unwrap()));
695 }
696
697 #[test]
698 fn bytes32_failure() {
699 let a = &mut Allocator::new();
700 let bytes =
701 hex::decode("f07522495060c066f66f32acc2a77e3a3e737aca8baea4d1a64ea4cdc13da9").unwrap();
702 let ptr = a.new_atom(&bytes).unwrap();
703 assert!(Bytes32::from_clvm(a, ptr).is_err());
704
705 let ptr = a.new_pair(a.one(), a.one()).unwrap();
706 assert_eq!(
707 Bytes32::from_clvm(a, ptr).unwrap_err(),
708 FromClvmError::ExpectedAtom
709 );
710 }
711}