everscale_types/boc/
mod.rs

1//! BOC (Bag Of Cells) implementation.
2
3use crate::cell::{Cell, CellBuilder, CellContext, CellFamily, DynCell, HashBytes, Load, Store};
4
5#[cfg(feature = "serde")]
6pub use self::serde::SerdeBoc;
7
8/// BOC decoder implementation.
9pub mod de;
10/// BOC encoder implementation.
11pub mod ser;
12
13#[cfg(feature = "serde")]
14mod serde;
15
16#[cfg(test)]
17mod tests;
18
19/// BOC file magic number.
20#[derive(Default, Copy, Clone, Eq, PartialEq)]
21pub enum BocTag {
22    /// Single root, cells index, no CRC32.
23    Indexed,
24    /// Single root, cells index, with CRC32.
25    IndexedCrc32,
26    /// Multiple roots, optional cells index, optional CRC32.
27    #[default]
28    Generic,
29}
30
31impl BocTag {
32    const INDEXED: [u8; 4] = [0x68, 0xff, 0x65, 0xf3];
33    const INDEXED_CRC32: [u8; 4] = [0xac, 0xc3, 0xa7, 0x28];
34    const GENERIC: [u8; 4] = [0xb5, 0xee, 0x9c, 0x72];
35
36    /// Tries to match bytes with BOC tag.
37    pub const fn from_bytes(data: [u8; 4]) -> Option<Self> {
38        match data {
39            Self::GENERIC => Some(Self::Generic),
40            Self::INDEXED_CRC32 => Some(Self::IndexedCrc32),
41            Self::INDEXED => Some(Self::Indexed),
42            _ => None,
43        }
44    }
45
46    /// Converts BOC tag to bytes.
47    pub const fn to_bytes(self) -> [u8; 4] {
48        match self {
49            Self::Indexed => Self::INDEXED,
50            Self::IndexedCrc32 => Self::INDEXED_CRC32,
51            Self::Generic => Self::GENERIC,
52        }
53    }
54}
55
56/// BOC (Bag Of Cells) helper.
57pub struct Boc;
58
59impl Boc {
60    /// Computes a simple SHA256 hash of the data.
61    #[inline]
62    pub fn file_hash(data: impl AsRef<[u8]>) -> HashBytes {
63        use sha2::Digest;
64
65        sha2::Sha256::digest(data).into()
66    }
67
68    /// Computes a Blake3 hash of the data.
69    #[cfg(feature = "blake3")]
70    #[inline]
71    pub fn file_hash_blake(data: impl AsRef<[u8]>) -> HashBytes {
72        #[cfg(not(feature = "rayon"))]
73        {
74            blake3::hash(data.as_ref()).into()
75        }
76
77        #[cfg(feature = "rayon")]
78        {
79            // Use Rayon for parallel hashing if data is larger than 256 KB.
80            const RAYON_THRESHOLD: usize = 256 * 1024;
81
82            let data = data.as_ref();
83            if data.len() < RAYON_THRESHOLD {
84                blake3::hash(data)
85            } else {
86                blake3::Hasher::new().update_rayon(data).finalize()
87            }
88            .into()
89        }
90    }
91
92    /// Encodes the specified cell tree as BOC and
93    /// returns the `hex` encoded bytes as a string.
94    pub fn encode_hex<T>(cell: T) -> String
95    where
96        T: AsRef<DynCell>,
97    {
98        hex::encode(Self::encode(cell))
99    }
100
101    /// Encodes the specified cell tree as BOC and
102    /// returns the `base64` encoded bytes as a string.
103    #[cfg(any(feature = "base64", test))]
104    pub fn encode_base64<T>(cell: T) -> String
105    where
106        T: AsRef<DynCell>,
107    {
108        crate::util::encode_base64(Self::encode(cell))
109    }
110
111    /// Encodes the specified cell tree as BOC and
112    /// returns the `hex` encoded bytes as a string.
113    ///
114    /// Uses `rayon` under the hood to parallelize encoding.
115    #[cfg(feature = "rayon")]
116    pub fn encode_hex_rayon<T>(cell: T) -> String
117    where
118        T: AsRef<DynCell>,
119    {
120        hex::encode(Self::encode_rayon(cell))
121    }
122
123    /// Encodes the specified cell tree as BOC and
124    /// returns the `base64` encoded bytes as a string.
125    ///
126    /// Uses `rayon` under the hood to parallelize encoding.
127    #[cfg(all(any(feature = "base64", test), feature = "rayon"))]
128    pub fn encode_base64_rayon<T>(cell: T) -> String
129    where
130        T: AsRef<DynCell>,
131    {
132        crate::util::encode_base64(Self::encode_rayon(cell))
133    }
134
135    /// Encodes the specified cell tree as BOC.
136    pub fn encode<T>(cell: T) -> Vec<u8>
137    where
138        T: AsRef<DynCell>,
139    {
140        fn encode_impl(cell: &DynCell) -> Vec<u8> {
141            let mut result = Vec::new();
142            ser::BocHeader::<ahash::RandomState>::with_root(cell).encode(&mut result);
143            result
144        }
145        encode_impl(cell.as_ref())
146    }
147
148    /// Encodes the specified cell tree as BOC.
149    ///
150    /// Uses `rayon` under the hood to parallelize encoding.
151    #[cfg(feature = "rayon")]
152    pub fn encode_rayon<T>(cell: T) -> Vec<u8>
153    where
154        T: AsRef<DynCell>,
155    {
156        fn encode_impl(cell: &DynCell) -> Vec<u8> {
157            let mut result = Vec::new();
158            ser::BocHeader::<ahash::RandomState>::with_root(cell).encode_rayon(&mut result);
159            result
160        }
161        encode_impl(cell.as_ref())
162    }
163
164    /// Encodes a pair of cell trees as BOC.
165    pub fn encode_pair<T1, T2>((cell1, cell2): (T1, T2)) -> Vec<u8>
166    where
167        T1: AsRef<DynCell>,
168        T2: AsRef<DynCell>,
169    {
170        fn encode_pair_impl(cell1: &DynCell, cell2: &DynCell) -> Vec<u8> {
171            let mut result = Vec::new();
172            let mut encoder = ser::BocHeader::<ahash::RandomState>::with_root(cell1);
173            encoder.add_root(cell2);
174            encoder.encode(&mut result);
175            result
176        }
177        encode_pair_impl(cell1.as_ref(), cell2.as_ref())
178    }
179
180    /// Decodes a `hex` encoded BOC into a cell tree
181    /// using an empty cell context.
182    pub fn decode_hex<T: AsRef<[u8]>>(data: T) -> Result<Cell, de::Error> {
183        fn decode_hex_impl(data: &[u8]) -> Result<Cell, de::Error> {
184            match hex::decode(data) {
185                Ok(data) => Boc::decode_ext(data.as_slice(), &mut Cell::empty_context()),
186                Err(_) => Err(de::Error::UnknownBocTag),
187            }
188        }
189        decode_hex_impl(data.as_ref())
190    }
191
192    /// Decodes a `base64` encoded BOC into a cell tree
193    /// using an empty cell context.
194    #[cfg(any(feature = "base64", test))]
195    #[inline]
196    pub fn decode_base64<T: AsRef<[u8]>>(data: T) -> Result<Cell, de::Error> {
197        fn decode_base64_impl(data: &[u8]) -> Result<Cell, de::Error> {
198            match crate::util::decode_base64(data) {
199                Ok(data) => Boc::decode_ext(data.as_slice(), &mut Cell::empty_context()),
200                Err(_) => Err(de::Error::UnknownBocTag),
201            }
202        }
203        decode_base64_impl(data.as_ref())
204    }
205
206    /// Decodes a cell tree using an empty cell context.
207    #[inline]
208    pub fn decode<T>(data: T) -> Result<Cell, de::Error>
209    where
210        T: AsRef<[u8]>,
211    {
212        fn decode_impl(data: &[u8]) -> Result<Cell, de::Error> {
213            Boc::decode_ext(data, &mut Cell::empty_context())
214        }
215        decode_impl(data.as_ref())
216    }
217
218    /// Decodes a pair of cell trees using an empty cell context.
219    #[inline]
220    pub fn decode_pair<T>(data: T) -> Result<(Cell, Cell), de::Error>
221    where
222        T: AsRef<[u8]>,
223    {
224        fn decode_pair_impl(data: &[u8]) -> Result<(Cell, Cell), de::Error> {
225            Boc::decode_pair_ext(data, &mut Cell::empty_context())
226        }
227        decode_pair_impl(data.as_ref())
228    }
229
230    /// Decodes a cell tree using the specified cell context.
231    pub fn decode_ext(data: &[u8], context: &mut dyn CellContext) -> Result<Cell, de::Error> {
232        use self::de::*;
233
234        let header = ok!(de::BocHeader::decode(
235            data,
236            &Options {
237                max_roots: Some(1),
238                min_roots: Some(1),
239            },
240        ));
241
242        if let Some(&root) = header.roots().first() {
243            let cells = ok!(header.finalize(context));
244            if let Some(root) = cells.get(root) {
245                return Ok(root);
246            }
247        }
248
249        Err(de::Error::RootCellNotFound)
250    }
251
252    /// Decodes a pair of cell trees using the specified cell context.
253    pub fn decode_pair_ext(
254        data: &[u8],
255        context: &mut dyn CellContext,
256    ) -> Result<(Cell, Cell), de::Error> {
257        use self::de::*;
258
259        let header = ok!(de::BocHeader::decode(
260            data,
261            &Options {
262                max_roots: Some(2),
263                min_roots: Some(2),
264            },
265        ));
266
267        let mut roots = header.roots().iter();
268        if let (Some(&root1), Some(&root2)) = (roots.next(), roots.next()) {
269            let cells = ok!(header.finalize(context));
270            if let (Some(root1), Some(root2)) = (cells.get(root1), cells.get(root2)) {
271                return Ok((root1, root2));
272            }
273        }
274
275        Err(de::Error::RootCellNotFound)
276    }
277
278    /// Serializes cell into an encoded BOC (as base64 for human readable serializers).
279    #[cfg(feature = "serde")]
280    pub fn serialize<T, S>(value: T, serializer: S) -> Result<S::Ok, S::Error>
281    where
282        SerdeBoc<T>: ::serde::Serialize,
283        S: ::serde::Serializer,
284    {
285        use ::serde::Serialize;
286
287        SerdeBoc::from(value).serialize(serializer)
288    }
289
290    /// Deserializes cell from an encoded BOC (from base64 for human readable deserializers).
291    #[cfg(feature = "serde")]
292    pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
293    where
294        SerdeBoc<T>: ::serde::Deserialize<'de>,
295        D: ::serde::Deserializer<'de>,
296    {
297        use ::serde::Deserialize;
298
299        SerdeBoc::<T>::deserialize(deserializer).map(SerdeBoc::into_inner)
300    }
301}
302
303/// BOC representation helper.
304pub struct BocRepr;
305
306impl BocRepr {
307    /// Encodes the specified cell tree as BOC using an empty cell context and
308    /// returns the `hex` encoded bytes as a string.
309    pub fn encode_hex<T>(data: T) -> Result<String, crate::error::Error>
310    where
311        T: Store,
312    {
313        let boc = ok!(Self::encode_ext(data, &mut Cell::empty_context()));
314        Ok(hex::encode(boc))
315    }
316
317    /// Encodes the specified cell tree as BOC using an empty cell context and
318    /// returns the `base64` encoded bytes as a string.
319    #[cfg(any(feature = "base64", test))]
320    pub fn encode_base64<T>(data: T) -> Result<String, crate::error::Error>
321    where
322        T: Store,
323    {
324        let boc = ok!(Self::encode_ext(data, &mut Cell::empty_context()));
325        Ok(crate::util::encode_base64(boc))
326    }
327
328    /// Encodes the specified cell tree as BOC using an empty cell context and
329    /// returns the `hex` encoded bytes as a string.
330    ///
331    /// Uses `rayon` under the hood to parallelize encoding.
332    #[cfg(feature = "rayon")]
333    pub fn encode_hex_rayon<T>(data: T) -> Result<String, crate::error::Error>
334    where
335        T: Store,
336    {
337        let boc = ok!(Self::encode_rayon_ext(data, &mut Cell::empty_context()));
338        Ok(hex::encode(boc))
339    }
340
341    /// Encodes the specified cell tree as BOC using an empty cell context and
342    /// returns the `base64` encoded bytes as a string.
343    ///
344    /// Uses `rayon` under the hood to parallelize encoding.
345    #[cfg(all(any(feature = "base64", test), feature = "rayon"))]
346    pub fn encode_base64_rayon<T>(data: T) -> Result<String, crate::error::Error>
347    where
348        T: Store,
349    {
350        let boc = ok!(Self::encode_rayon_ext(data, &mut Cell::empty_context()));
351        Ok(crate::util::encode_base64(boc))
352    }
353
354    /// Encodes the specified cell tree as BOC using an empty cell context.
355    pub fn encode<T>(data: T) -> Result<Vec<u8>, crate::error::Error>
356    where
357        T: Store,
358    {
359        Self::encode_ext(data, &mut Cell::empty_context())
360    }
361
362    /// Encodes the specified cell tree as BOC using an empty cell context.
363    ///
364    /// Uses `rayon` under the hood to parallelize encoding.
365    #[cfg(feature = "rayon")]
366    pub fn encode_rayon<T>(data: T) -> Result<Vec<u8>, crate::error::Error>
367    where
368        T: Store,
369    {
370        Self::encode_rayon_ext(data, &mut Cell::empty_context())
371    }
372
373    /// Decodes a `hex` encoded BOC into an object
374    /// using an empty cell context.
375    #[inline]
376    pub fn decode_hex<T, D>(data: D) -> Result<T, BocReprError>
377    where
378        for<'a> T: Load<'a>,
379        D: AsRef<[u8]>,
380    {
381        fn decode_hex_impl<T>(data: &[u8]) -> Result<T, BocReprError>
382        where
383            for<'a> T: Load<'a>,
384        {
385            match hex::decode(data) {
386                Ok(data) => BocRepr::decode_ext(data.as_slice(), &mut Cell::empty_context()),
387                Err(_) => Err(BocReprError::InvalidBoc(de::Error::UnknownBocTag)),
388            }
389        }
390        decode_hex_impl::<T>(data.as_ref())
391    }
392
393    /// Decodes a `base64` encoded BOC into an object
394    /// using an empty cell context.
395    #[cfg(any(feature = "base64", test))]
396    #[inline]
397    pub fn decode_base64<T, D>(data: D) -> Result<T, BocReprError>
398    where
399        for<'a> T: Load<'a>,
400        D: AsRef<[u8]>,
401    {
402        fn decode_base64_impl<T>(data: &[u8]) -> Result<T, BocReprError>
403        where
404            for<'a> T: Load<'a>,
405        {
406            match crate::util::decode_base64(data) {
407                Ok(data) => BocRepr::decode_ext(data.as_slice(), &mut Cell::empty_context()),
408                Err(_) => Err(BocReprError::InvalidBoc(de::Error::UnknownBocTag)),
409            }
410        }
411        decode_base64_impl::<T>(data.as_ref())
412    }
413
414    /// Decodes an object using an empty cell context.
415    #[inline]
416    pub fn decode<T, D>(data: D) -> Result<T, BocReprError>
417    where
418        for<'a> T: Load<'a>,
419        D: AsRef<[u8]>,
420    {
421        fn decode_impl<T>(data: &[u8]) -> Result<T, BocReprError>
422        where
423            for<'a> T: Load<'a>,
424        {
425            BocRepr::decode_ext(data, &mut Cell::empty_context())
426        }
427        decode_impl::<T>(data.as_ref())
428    }
429
430    /// Encodes the specified object as BOC.
431    pub fn encode_ext<T>(
432        data: T,
433        context: &mut dyn CellContext,
434    ) -> Result<Vec<u8>, crate::error::Error>
435    where
436        T: Store,
437    {
438        fn encode_ext_impl(
439            data: &dyn Store,
440            context: &mut dyn CellContext,
441        ) -> Result<Vec<u8>, crate::error::Error> {
442            let mut builder = CellBuilder::new();
443            ok!(data.store_into(&mut builder, context));
444            let cell = ok!(builder.build_ext(context));
445            Ok(Boc::encode(cell))
446        }
447        encode_ext_impl(&data, context)
448    }
449
450    /// Encodes the specified object as BOC.
451    ///
452    /// Uses `rayon` under the hood to parallelize encoding.
453    #[cfg(feature = "rayon")]
454    pub fn encode_rayon_ext<T>(
455        data: T,
456        context: &mut dyn CellContext,
457    ) -> Result<Vec<u8>, crate::error::Error>
458    where
459        T: Store,
460    {
461        fn encode_ext_impl(
462            data: &dyn Store,
463            context: &mut dyn CellContext,
464        ) -> Result<Vec<u8>, crate::error::Error> {
465            let mut builder = CellBuilder::new();
466            ok!(data.store_into(&mut builder, context));
467            let cell = ok!(builder.build_ext(context));
468            Ok(Boc::encode_rayon(cell))
469        }
470        encode_ext_impl(&data, context)
471    }
472
473    /// Decodes object from BOC using the specified cell context.
474    pub fn decode_ext<T>(data: &[u8], context: &mut dyn CellContext) -> Result<T, BocReprError>
475    where
476        for<'a> T: Load<'a>,
477    {
478        let cell = match Boc::decode_ext(data, context) {
479            Ok(cell) => cell,
480            Err(e) => return Err(BocReprError::InvalidBoc(e)),
481        };
482
483        match cell.as_ref().parse::<T>() {
484            Ok(data) => Ok(data),
485            Err(e) => Err(BocReprError::InvalidData(e)),
486        }
487    }
488
489    /// Serializes the type into an encoded BOC using an empty cell context
490    /// (as base64 for human readable serializers).
491    #[cfg(feature = "serde")]
492    pub fn serialize<S, T>(data: &T, serializer: S) -> Result<S::Ok, S::Error>
493    where
494        S: ::serde::Serializer,
495        T: Store,
496    {
497        use ::serde::ser::{Error, Serialize};
498
499        let context = &mut Cell::empty_context();
500
501        let mut builder = CellBuilder::new();
502        if data.store_into(&mut builder, context).is_err() {
503            return Err(Error::custom("cell overflow"));
504        }
505
506        let cell = match builder.build_ext(context) {
507            Ok(cell) => cell,
508            Err(_) => return Err(Error::custom("failed to store into builder")),
509        };
510
511        cell.as_ref().serialize(serializer)
512    }
513
514    /// Deserializes the type from an encoded BOC using an empty cell context
515    /// (from base64 for human readable serializers).
516    #[cfg(feature = "serde")]
517    pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
518    where
519        D: ::serde::Deserializer<'de>,
520        for<'a> T: Load<'a>,
521    {
522        use ::serde::de::Error;
523
524        let cell = ok!(Boc::deserialize::<Cell, _>(deserializer));
525        match cell.as_ref().parse::<T>() {
526            Ok(data) => Ok(data),
527            Err(_) => Err(Error::custom("failed to decode object from cells")),
528        }
529    }
530}
531
532/// Error type for BOC repr decoding related errors.
533#[derive(Debug, thiserror::Error)]
534pub enum BocReprError {
535    /// Failed to decode BOC.
536    #[error("invalid BOC")]
537    InvalidBoc(#[source] de::Error),
538    /// Failed to decode data from cells.
539    #[error("failed to decode object from cells")]
540    InvalidData(#[source] crate::error::Error),
541}