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 #[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 #[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}