chia_protocol/
bytes.rs

1use chia_sha2::Sha256;
2use chia_traits::{Streamable, chia_error, read_bytes};
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<Py<PyAny>> {
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<Py<PyAny>> {
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<'a, 'py, const N: usize> FromPyObject<'a, 'py> for BytesImpl<N> {
463    type Error = PyErr;
464
465    fn extract(obj: pyo3::Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
466        let b = obj.cast::<PyBytes>()?;
467        let slice: &[u8] = b.as_bytes();
468        let buf: [u8; N] = slice.try_into()?;
469        Ok(BytesImpl::<N>(buf))
470    }
471}
472
473#[cfg(feature = "py-bindings")]
474impl<'py> IntoPyObject<'py> for Bytes {
475    type Target = PyAny;
476    type Output = Bound<'py, Self::Target>;
477    type Error = std::convert::Infallible;
478
479    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
480        Ok(PyBytes::new(py, &self.0).into_any())
481    }
482}
483
484#[cfg(feature = "py-bindings")]
485impl ChiaToPython for Bytes {
486    fn to_python<'a>(&self, py: Python<'a>) -> PyResult<Bound<'a, PyAny>> {
487        Ok(PyBytes::new(py, &self.0).into_any())
488    }
489}
490
491#[cfg(feature = "py-bindings")]
492impl<'a, 'py> FromPyObject<'a, 'py> for Bytes {
493    type Error = PyErr;
494
495    fn extract(obj: pyo3::Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
496        let b = obj.cast::<PyBytes>().map_err(pyo3::PyErr::from)?;
497        Ok(Bytes(b.as_bytes().to_vec()))
498    }
499}
500
501#[cfg(test)]
502#[allow(clippy::needless_pass_by_value)]
503mod tests {
504    use super::*;
505
506    use clvmr::{
507        Allocator,
508        serde::{node_from_bytes, node_to_bytes},
509    };
510    use rstest::rstest;
511
512    #[rstest]
513    // Bytess32
514    #[case(
515        "0000000000000000000000000000000000000000000000000000000000000000",
516        "0000000000000000000000000000000000000000000000000000000000000000",
517        true
518    )]
519    #[case(
520        "0000000000000000000000000000000000000000000000000000000000000000",
521        "0000000000000000000000000000000000000000000000000000000000000100",
522        false
523    )]
524    #[case(
525        "fff0000000000000000000000000000000000000000000000000000000000100",
526        "fff0000000000000000000000000000000000000000000000000000000000100",
527        true
528    )]
529    // Bytes
530    #[case("000000", "000000", true)]
531    #[case("123456", "125456", false)]
532    #[case("000001", "00000001", false)]
533    #[case("00000001", "000001", false)]
534    #[case("ffff01", "ffff01", true)]
535    #[case("", "", true)]
536    fn test_bytes_comparisons(#[case] lhs: &str, #[case] rhs: &str, #[case] expect_equal: bool) {
537        let lhs_vec: Vec<u8> = hex::decode(lhs).expect("hex::decode");
538        let rhs_vec: Vec<u8> = hex::decode(rhs).expect("hex::decode");
539
540        if lhs_vec.len() == 32 && rhs_vec.len() == 32 {
541            let lhs = Bytes32::try_from(&lhs_vec).unwrap();
542            let rhs = Bytes32::try_from(&rhs_vec).unwrap();
543
544            assert_eq!(lhs.len(), 32);
545            assert_eq!(rhs.len(), 32);
546
547            assert_eq!(lhs.is_empty(), lhs_vec.is_empty());
548            assert_eq!(rhs.is_empty(), rhs_vec.is_empty());
549
550            if expect_equal {
551                assert_eq!(lhs, rhs);
552                assert_eq!(rhs, lhs);
553            } else {
554                assert!(lhs != rhs);
555                assert!(rhs != lhs);
556            }
557        } else {
558            let lhs = Bytes::from(lhs_vec.clone());
559            let rhs = Bytes::from(rhs_vec.clone());
560
561            assert_eq!(lhs.len(), lhs_vec.len());
562            assert_eq!(rhs.len(), rhs_vec.len());
563
564            assert_eq!(lhs.is_empty(), lhs_vec.is_empty());
565            assert_eq!(rhs.is_empty(), rhs_vec.is_empty());
566
567            if expect_equal {
568                assert_eq!(lhs, rhs);
569                assert_eq!(rhs, lhs);
570            } else {
571                assert!(lhs != rhs);
572                assert!(rhs != lhs);
573            }
574        }
575    }
576
577    fn from_bytes<T: Streamable + fmt::Debug + PartialEq>(buf: &[u8], expected: T) {
578        let mut input = Cursor::<&[u8]>::new(buf);
579        assert_eq!(T::parse::<false>(&mut input).unwrap(), expected);
580    }
581
582    fn from_bytes_fail<T: Streamable + fmt::Debug + PartialEq>(
583        buf: &[u8],
584        expected: chia_error::Error,
585    ) {
586        let mut input = Cursor::<&[u8]>::new(buf);
587        assert_eq!(T::parse::<false>(&mut input).unwrap_err(), expected);
588    }
589
590    fn stream<T: Streamable>(v: &T) -> Vec<u8> {
591        let mut buf = Vec::<u8>::new();
592        v.stream(&mut buf).unwrap();
593        let mut ctx1 = Sha256::new();
594        let mut ctx2 = Sha256::new();
595        v.update_digest(&mut ctx1);
596        ctx2.update(&buf);
597        assert_eq!(&ctx1.finalize(), &ctx2.finalize());
598        buf
599    }
600
601    #[test]
602    fn test_stream_bytes32() {
603        let buf = [
604            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
605            25, 26, 27, 28, 29, 30, 31, 32,
606        ];
607        let out = stream(&Bytes32::from(buf));
608        assert_eq!(buf.as_slice(), &out);
609    }
610
611    #[test]
612    fn test_stream_bytes() {
613        let val: Bytes = vec![
614            1_u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
615            24, 25, 26, 27, 28, 29, 30, 31, 32,
616        ]
617        .into();
618        println!("{val:?}");
619        let buf = stream(&val);
620        println!("buf: {buf:?}");
621        from_bytes(&buf, val);
622    }
623
624    #[test]
625    fn test_parse_bytes_empty() {
626        let buf: &[u8] = &[0, 0, 0, 0];
627        from_bytes::<Bytes>(buf, [].to_vec().into());
628    }
629
630    #[test]
631    fn test_parse_bytes() {
632        let buf: &[u8] = &[0, 0, 0, 3, 1, 2, 3];
633        from_bytes::<Bytes>(buf, [1_u8, 2, 3].to_vec().into());
634    }
635
636    #[test]
637    fn test_parse_truncated_len() {
638        let buf: &[u8] = &[0, 0, 1];
639        from_bytes_fail::<Bytes>(buf, chia_error::Error::EndOfBuffer);
640    }
641
642    #[test]
643    fn test_parse_truncated() {
644        let buf: &[u8] = &[0, 0, 0, 4, 1, 2, 3];
645        from_bytes_fail::<Bytes>(buf, chia_error::Error::EndOfBuffer);
646    }
647
648    #[test]
649    fn test_parse_bytes32() {
650        let buf = [
651            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
652            25, 26, 27, 28, 29, 30, 31, 32,
653        ];
654        from_bytes::<Bytes32>(&buf, Bytes32::from(buf));
655        from_bytes_fail::<Bytes32>(&buf[0..30], chia_error::Error::EndOfBuffer);
656    }
657
658    #[test]
659    fn test_parse_bytes48() {
660        let buf = [
661            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
662            25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
663            47, 48,
664        ];
665        from_bytes::<Bytes48>(&buf, Bytes48::from(buf));
666        from_bytes_fail::<Bytes48>(&buf[0..47], chia_error::Error::EndOfBuffer);
667    }
668
669    #[test]
670    fn bytes_roundtrip() {
671        let a = &mut Allocator::new();
672        let expected = "84facef00d";
673        let expected_bytes = hex::decode(expected).unwrap();
674
675        let ptr = node_from_bytes(a, &expected_bytes).unwrap();
676        let bytes = Bytes::from_clvm(a, ptr).unwrap();
677
678        let round_trip = bytes.to_clvm(a).unwrap();
679        assert_eq!(expected, hex::encode(node_to_bytes(a, round_trip).unwrap()));
680    }
681
682    #[test]
683    fn bytes32_roundtrip() {
684        let a = &mut Allocator::new();
685        let expected = "a0eff07522495060c066f66f32acc2a77e3a3e737aca8baea4d1a64ea4cdc13da9";
686        let expected_bytes = hex::decode(expected).unwrap();
687
688        let ptr = node_from_bytes(a, &expected_bytes).unwrap();
689        let bytes32 = Bytes32::from_clvm(a, ptr).unwrap();
690
691        let round_trip = bytes32.to_clvm(a).unwrap();
692        assert_eq!(expected, hex::encode(node_to_bytes(a, round_trip).unwrap()));
693    }
694
695    #[test]
696    fn bytes32_failure() {
697        let a = &mut Allocator::new();
698        let bytes =
699            hex::decode("f07522495060c066f66f32acc2a77e3a3e737aca8baea4d1a64ea4cdc13da9").unwrap();
700        let ptr = a.new_atom(&bytes).unwrap();
701        assert!(Bytes32::from_clvm(a, ptr).is_err());
702
703        let ptr = a.new_pair(a.one(), a.one()).unwrap();
704        assert_eq!(
705            Bytes32::from_clvm(a, ptr).unwrap_err(),
706            FromClvmError::ExpectedAtom
707        );
708    }
709}