chia_protocol/
bytes.rs

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