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