tycho_types/models/block/
mod.rs

1//! Block models.
2
3#[cfg(feature = "sync")]
4use std::sync::OnceLock;
5
6pub use self::block_extra::*;
7pub use self::block_id::*;
8pub use self::block_proof::*;
9pub use self::shard_hashes::*;
10use crate::cell::*;
11#[allow(unused)]
12use crate::dict::Dict;
13use crate::error::Error;
14use crate::merkle::MerkleUpdate;
15use crate::models::currency::CurrencyCollection;
16use crate::models::global_version::GlobalVersion;
17use crate::util::*;
18
19mod block_extra;
20mod block_id;
21mod block_proof;
22mod shard_hashes;
23
24#[cfg(test)]
25mod tests;
26
27/// Shard block.
28#[derive(Debug, Clone, Eq, PartialEq)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize))]
30pub struct Block {
31    /// Global network id.
32    pub global_id: i32,
33    /// Block info.
34    pub info: Lazy<BlockInfo>,
35    /// Tokens flow info.
36    pub value_flow: Lazy<ValueFlow>,
37    /// Merkle update for the shard state.
38    pub state_update: LazyExotic<MerkleUpdate>,
39    /// Merkle updates for the outgoing messages queue.
40    #[cfg(not(feature = "tycho"))]
41    pub out_msg_queue_updates: Option<Dict<u32, LazyExotic<MerkleUpdate>>>,
42    /// Out messages queue info.
43    #[cfg(feature = "tycho")]
44    pub out_msg_queue_updates: OutMsgQueueUpdates,
45    /// Block content.
46    pub extra: Lazy<BlockExtra>,
47}
48
49impl Block {
50    #[cfg(not(feature = "tycho"))]
51    const TAG_V1: u32 = 0x11ef55aa;
52    const TAG_V2: u32 = 0x11ef55bb;
53
54    const DATA_FOR_SIGN_SIZE: usize = 4 + 32 + 32;
55    const DATA_FOR_SIGN_TAG: [u8; 4] = [0x70, 0x6e, 0x0b, 0xc5];
56
57    /// Tries to load block info.
58    pub fn load_info(&self) -> Result<BlockInfo, Error> {
59        self.info.load()
60    }
61
62    /// Tries to load tokens flow info.
63    pub fn load_value_flow(&self) -> Result<ValueFlow, Error> {
64        self.value_flow.load()
65    }
66
67    /// Tries to load state update.
68    pub fn load_state_update(&self) -> Result<MerkleUpdate, Error> {
69        self.state_update.load()
70    }
71
72    /// Tries to load block content.
73    pub fn load_extra(&self) -> Result<BlockExtra, Error> {
74        self.extra.load()
75    }
76
77    /// Builds a data for validators to sign.
78    pub fn build_data_for_sign(block_id: &BlockId) -> [u8; Self::DATA_FOR_SIGN_SIZE] {
79        let mut data = [0u8; Self::DATA_FOR_SIGN_SIZE];
80        data[0..4].copy_from_slice(&Self::DATA_FOR_SIGN_TAG);
81        data[4..36].copy_from_slice(block_id.root_hash.as_ref());
82        data[36..68].copy_from_slice(block_id.file_hash.as_ref());
83        data
84    }
85}
86
87impl Store for Block {
88    fn store_into(
89        &self,
90        builder: &mut CellBuilder,
91        context: &dyn CellContext,
92    ) -> Result<(), Error> {
93        #[cfg(not(feature = "tycho"))]
94        let tag = if self.out_msg_queue_updates.is_none() {
95            Self::TAG_V1
96        } else {
97            Self::TAG_V2
98        };
99        #[cfg(feature = "tycho")]
100        let tag = Self::TAG_V2;
101
102        ok!(builder.store_u32(tag));
103        ok!(builder.store_u32(self.global_id as u32));
104        ok!(builder.store_reference(self.info.inner().clone()));
105        ok!(builder.store_reference(self.value_flow.inner().clone()));
106
107        #[cfg(not(feature = "tycho"))]
108        let out_msg_queue_updates = self.out_msg_queue_updates.as_ref();
109        #[cfg(feature = "tycho")]
110        let out_msg_queue_updates = Some(&self.out_msg_queue_updates);
111
112        ok!(if let Some(out_msg_queue_updates) = out_msg_queue_updates {
113            let cell = {
114                let mut builder = CellBuilder::new();
115                ok!(self.state_update.store_into(&mut builder, context));
116                ok!(out_msg_queue_updates.store_into(&mut builder, context));
117                ok!(builder.build_ext(context))
118            };
119            builder.store_reference(cell)
120        } else {
121            self.state_update.store_into(builder, context)
122        });
123
124        self.extra.store_into(builder, context)
125    }
126}
127
128impl<'a> Load<'a> for Block {
129    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
130        #[cfg(not(feature = "tycho"))]
131        let with_out_msg_queue_updates = match ok!(slice.load_u32()) {
132            Self::TAG_V1 => false,
133            Self::TAG_V2 => true,
134            _ => return Err(Error::InvalidTag),
135        };
136
137        #[cfg(feature = "tycho")]
138        if ok!(slice.load_u32()) != Self::TAG_V2 {
139            return Err(Error::InvalidTag);
140        }
141
142        let global_id = ok!(slice.load_u32()) as i32;
143        let info = ok!(Lazy::load_from(slice));
144        let value_flow = ok!(Lazy::load_from(slice));
145
146        #[cfg(not(feature = "tycho"))]
147        let (state_update, out_msg_queue_updates) = if with_out_msg_queue_updates {
148            let slice = &mut ok!(slice.load_reference_as_slice());
149            (
150                ok!(Lazy::load_from(slice)),
151                Some(ok!(Dict::load_from(slice))),
152            )
153        } else {
154            (ok!(Lazy::load_from(slice)), None)
155        };
156
157        #[cfg(feature = "tycho")]
158        let (state_update, out_msg_queue_updates) = {
159            let slice = &mut ok!(slice.load_reference_as_slice());
160            (ok!(<_>::load_from(slice)), ok!(<_>::load_from(slice)))
161        };
162
163        Ok(Self {
164            global_id,
165            info,
166            value_flow,
167            state_update,
168            out_msg_queue_updates,
169            extra: ok!(<_>::load_from(slice)),
170        })
171    }
172}
173
174/// Block info.
175#[derive(Debug, Clone, Eq, PartialEq)]
176#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
177pub struct BlockInfo {
178    /// Block model version.
179    pub version: u32,
180    /// Whether this block was produced after the shards were merged.
181    pub after_merge: bool,
182    /// Whether this block was produced before the shards split.
183    pub before_split: bool,
184    /// Whether this block was produced after the shards split.
185    pub after_split: bool,
186    /// Hint that the shard with this block should split.
187    pub want_split: bool,
188    /// Hint that the shard with this block should merge.
189    pub want_merge: bool,
190    /// Whether this block is a key block.
191    pub key_block: bool,
192
193    /// Block flags (currently only bit 1 is used, for [`gen_software`])
194    ///
195    /// [`gen_software`]: Self::gen_software
196    pub flags: u8,
197    /// Block sequence number.
198    pub seqno: u32,
199    /// Block vertical sequence number.
200    pub vert_seqno: u32,
201
202    /// Shard id where this block was produced.
203    pub shard: ShardIdent,
204    /// Unix timestamp when the block was created.
205    pub gen_utime: u32,
206    /// Milliseconds part of the timestamp when the block was created.
207    #[cfg(feature = "tycho")]
208    pub gen_utime_ms: u16,
209    /// Logical time range start.
210    pub start_lt: u64,
211    /// Logical time range end.
212    pub end_lt: u64,
213    /// Last 4 bytes of the hash of the validator list.
214    pub gen_validator_list_hash_short: u32,
215    /// Seqno of the catchain session where this block was produced.
216    pub gen_catchain_seqno: u32,
217    /// Minimal referenced seqno of the masterchain block.
218    pub min_ref_mc_seqno: u32,
219    /// Previous key block seqno.
220    pub prev_key_block_seqno: u32,
221    /// The version and capabilities of the software that created this block.
222    pub gen_software: GlobalVersion,
223
224    /// Reference to the masterchain block which was used during the creation of this block.
225    pub master_ref: Option<Lazy<BlockRef>>,
226    /// Reference to the previous block (or blocks).
227    #[cfg_attr(feature = "serde", serde(with = "serde_prev_block_ref"))]
228    pub prev_ref: Cell,
229    /// Optional reference to the previous vertical block.
230    pub prev_vert_ref: Option<Lazy<BlockRef>>,
231}
232
233#[cfg(feature = "sync")]
234impl Default for BlockInfo {
235    fn default() -> Self {
236        Self {
237            version: 0,
238            after_merge: false,
239            before_split: false,
240            after_split: false,
241            want_split: false,
242            want_merge: false,
243            key_block: false,
244            flags: 0,
245            seqno: 0,
246            vert_seqno: 0,
247            shard: ShardIdent::MASTERCHAIN,
248            gen_utime: 0,
249            #[cfg(feature = "tycho")]
250            gen_utime_ms: 0,
251            start_lt: 0,
252            end_lt: 0,
253            gen_validator_list_hash_short: 0,
254            gen_catchain_seqno: 0,
255            min_ref_mc_seqno: 0,
256            prev_key_block_seqno: 0,
257            gen_software: Default::default(),
258            master_ref: None,
259            prev_ref: PrevBlockRef::empty_single_ref().clone(),
260            prev_vert_ref: None,
261        }
262    }
263}
264
265impl BlockInfo {
266    const TAG_V1: u32 = 0x9bc7a987;
267    #[cfg(feature = "tycho")]
268    const TAG_V2: u32 = 0x9bc7a988;
269    const FLAG_WITH_GEN_SOFTWARE: u8 = 0x1;
270
271    /// Set the version and capabilities of the software that created this block.
272    pub fn set_gen_software(&mut self, gen_software: Option<GlobalVersion>) {
273        if let Some(gen_software) = gen_software {
274            self.gen_software = gen_software;
275            self.flags |= BlockInfo::FLAG_WITH_GEN_SOFTWARE;
276        } else {
277            self.gen_software = Default::default();
278            self.flags &= !BlockInfo::FLAG_WITH_GEN_SOFTWARE;
279        }
280    }
281
282    /// Tries to load a reference to the masterchain block.
283    pub fn load_master_ref(&self) -> Result<Option<BlockRef>, Error> {
284        match &self.master_ref {
285            Some(master_ref) => master_ref.load().map(Some),
286            None => Ok(None),
287        }
288    }
289
290    /// Tries to load a reference to the previous block (or blocks).
291    pub fn load_prev_ref(&self) -> Result<PrevBlockRef, Error> {
292        PrevBlockRef::load_from_cell(&self.prev_ref, self.after_merge)
293    }
294
295    /// Set previous block reference.
296    pub fn set_prev_ref(&mut self, prev_ref: &PrevBlockRef) {
297        match prev_ref {
298            PrevBlockRef::Single(prev_ref) => self.set_prev_ref_single(prev_ref),
299            PrevBlockRef::AfterMerge { left, right } => self.set_prev_ref_after_merge(left, right),
300        }
301    }
302
303    /// Set previous block reference (direct).
304    pub fn set_prev_ref_single(&mut self, prev_ref: &BlockRef) {
305        // NOTE: Unwrap is ok because we control the input.
306        self.prev_ref = CellBuilder::build_from(prev_ref).unwrap();
307    }
308
309    /// Set previous block reference (split).
310    pub fn set_prev_ref_after_merge(&mut self, left: &BlockRef, right: &BlockRef) {
311        fn store_split_ref(left: &BlockRef, right: &BlockRef) -> Result<Cell, Error> {
312            let cx = Cell::empty_context();
313            let mut builder = CellBuilder::new();
314            ok!(builder.store_reference(ok!(CellBuilder::build_from_ext(left, cx))));
315            ok!(builder.store_reference(ok!(CellBuilder::build_from_ext(right, cx))));
316            builder.build_ext(cx)
317        }
318
319        // NOTE: Unwrap is ok because we control the input.
320        self.prev_ref = store_split_ref(left, right).unwrap();
321    }
322}
323
324impl Store for BlockInfo {
325    fn store_into(
326        &self,
327        builder: &mut CellBuilder,
328        context: &dyn CellContext,
329    ) -> Result<(), Error> {
330        let packed_flags = ((self.master_ref.is_some() as u8) << 7)
331            | ((self.after_merge as u8) << 6)
332            | ((self.before_split as u8) << 5)
333            | ((self.after_split as u8) << 4)
334            | ((self.want_split as u8) << 3)
335            | ((self.want_merge as u8) << 2)
336            | ((self.key_block as u8) << 1)
337            | (self.prev_vert_ref.is_some() as u8);
338
339        #[cfg(not(feature = "tycho"))]
340        ok!(builder.store_u32(Self::TAG_V1));
341        #[cfg(feature = "tycho")]
342        ok!(builder.store_u32(Self::TAG_V2));
343
344        ok!(builder.store_u32(self.version));
345        ok!(builder.store_u16(u16::from_be_bytes([packed_flags, self.flags])));
346        ok!(builder.store_u32(self.seqno));
347        ok!(builder.store_u32(self.vert_seqno));
348        ok!(self.shard.store_into(builder, context));
349        ok!(builder.store_u32(self.gen_utime));
350        #[cfg(feature = "tycho")]
351        ok!(builder.store_u16(self.gen_utime_ms));
352        ok!(builder.store_u64(self.start_lt));
353        ok!(builder.store_u64(self.end_lt));
354        ok!(builder.store_u32(self.gen_validator_list_hash_short));
355        ok!(builder.store_u32(self.gen_catchain_seqno));
356        ok!(builder.store_u32(self.min_ref_mc_seqno));
357        ok!(builder.store_u32(self.prev_key_block_seqno));
358
359        if self.flags & Self::FLAG_WITH_GEN_SOFTWARE != 0 {
360            ok!(self.gen_software.store_into(builder, context));
361        }
362
363        if let Some(master_ref) = &self.master_ref {
364            ok!(builder.store_reference(master_ref.inner().clone()));
365        }
366
367        ok!(builder.store_reference(self.prev_ref.clone()));
368
369        if let Some(prev_vert_ref) = &self.prev_vert_ref {
370            builder.store_reference(prev_vert_ref.inner().clone())
371        } else {
372            Ok(())
373        }
374    }
375}
376
377impl<'a> Load<'a> for BlockInfo {
378    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
379        let with_ms = match slice.load_u32() {
380            Ok(Self::TAG_V1) => false,
381            #[cfg(feature = "tycho")]
382            Ok(Self::TAG_V2) => true,
383            Ok(_) => return Err(Error::InvalidTag),
384            Err(e) => return Err(e),
385        };
386
387        #[cfg(not(feature = "tycho"))]
388        let _ = with_ms;
389
390        let version = ok!(slice.load_u32());
391        let [packed_flags, flags] = ok!(slice.load_u16()).to_be_bytes();
392        let seqno = ok!(slice.load_u32());
393        if seqno == 0 {
394            return Err(Error::InvalidData);
395        }
396        let vert_seqno = ok!(slice.load_u32());
397        let shard = ok!(ShardIdent::load_from(slice));
398        let gen_utime = ok!(slice.load_u32());
399        #[cfg(feature = "tycho")]
400        let gen_utime_ms = if with_ms { ok!(slice.load_u16()) } else { 0 };
401        let start_lt = ok!(slice.load_u64());
402        let end_lt = ok!(slice.load_u64());
403        let gen_validator_list_hash_short = ok!(slice.load_u32());
404        let gen_catchain_seqno = ok!(slice.load_u32());
405        let min_ref_mc_seqno = ok!(slice.load_u32());
406        let prev_key_block_seqno = ok!(slice.load_u32());
407
408        let gen_software = if flags & Self::FLAG_WITH_GEN_SOFTWARE != 0 {
409            ok!(GlobalVersion::load_from(slice))
410        } else {
411            GlobalVersion::default()
412        };
413
414        let master_ref = if packed_flags & 0b10000000 != 0 {
415            Some(ok!(Lazy::<BlockRef>::load_from(slice)))
416        } else {
417            None
418        };
419
420        let prev_ref = ok!(slice.load_reference_cloned());
421
422        let prev_vert_ref = if packed_flags & 0b00000001 != 0 {
423            Some(ok!(Lazy::<BlockRef>::load_from(slice)))
424        } else {
425            None
426        };
427
428        if vert_seqno < prev_vert_ref.is_some() as u32 {
429            return Err(Error::InvalidData);
430        }
431
432        Ok(Self {
433            version,
434            after_merge: packed_flags & 0b01000000 != 0,
435            before_split: packed_flags & 0b00100000 != 0,
436            after_split: packed_flags & 0b00010000 != 0,
437            want_split: packed_flags & 0b00001000 != 0,
438            want_merge: packed_flags & 0b00000100 != 0,
439            key_block: packed_flags & 0b00000010 != 0,
440            flags,
441            seqno,
442            vert_seqno,
443            shard,
444            gen_utime,
445            #[cfg(feature = "tycho")]
446            gen_utime_ms,
447            start_lt,
448            end_lt,
449            gen_validator_list_hash_short,
450            gen_catchain_seqno,
451            min_ref_mc_seqno,
452            prev_key_block_seqno,
453            gen_software,
454            master_ref,
455            prev_ref,
456            prev_vert_ref,
457        })
458    }
459}
460
461#[cfg(feature = "serde")]
462mod serde_prev_block_ref {
463    use super::*;
464
465    pub fn serialize<S: serde::Serializer>(value: &Cell, serializer: S) -> Result<S::Ok, S::Error> {
466        use serde::ser::{Error, Serialize};
467
468        if serializer.is_human_readable() {
469            let prev_block_ref = ok!(PrevBlockRef::load_from_cell(
470                value,
471                value.reference_count() > 0
472            )
473            .map_err(Error::custom));
474            prev_block_ref.serialize(serializer)
475        } else {
476            crate::boc::Boc::serialize(value, serializer)
477        }
478    }
479
480    pub fn deserialize<'de, D>(deserializer: D) -> Result<Cell, D::Error>
481    where
482        D: serde::Deserializer<'de>,
483    {
484        use serde::de::{Deserialize, Error};
485
486        if deserializer.is_human_readable() {
487            PrevBlockRef::deserialize(deserializer).and_then(|prev_block_ref| {
488                CellBuilder::build_from(prev_block_ref).map_err(Error::custom)
489            })
490        } else {
491            crate::boc::Boc::deserialize(deserializer)
492        }
493    }
494}
495
496/// Reference to the previous block.
497#[derive(Debug, Clone, Eq, PartialEq)]
498#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
499#[cfg_attr(feature = "serde", serde(tag = "type", content = "id"))]
500pub enum PrevBlockRef {
501    /// Reference to the parent block (simple case).
502    Single(BlockRef),
503    /// Reference to both parent blocks (case after merge).
504    AfterMerge {
505        /// Block id from the "left" shard.
506        ///
507        /// See [`is_left_child`].
508        ///
509        /// [`is_left_child`]: ShardIdent::is_left_child
510        left: BlockRef,
511        /// Block id from the "right" shard.
512        ///
513        /// See [`is_right_child`].
514        ///
515        /// [`is_right_child`]: ShardIdent::is_right_child
516        right: BlockRef,
517    },
518}
519
520impl PrevBlockRef {
521    /// Returns a static reference to an empty single reference.
522    #[cfg(feature = "sync")]
523    pub fn empty_single_ref() -> &'static Cell {
524        static CELL: OnceLock<Cell> = OnceLock::new();
525        CELL.get_or_init(|| {
526            CellBuilder::build_from(&BlockRef {
527                end_lt: 0,
528                seqno: 0,
529                root_hash: HashBytes::ZERO,
530                file_hash: HashBytes::ZERO,
531            })
532            .unwrap()
533        })
534    }
535
536    fn load_from_cell(value: &Cell, after_merge: bool) -> Result<Self, Error> {
537        let mut s = ok!(value.as_slice());
538        Ok(if unlikely(after_merge) {
539            let left = ok!(BlockRef::load_from(&mut ok!(s.load_reference_as_slice())));
540            let right = ok!(BlockRef::load_from(&mut ok!(s.load_reference_as_slice())));
541            PrevBlockRef::AfterMerge { left, right }
542        } else {
543            PrevBlockRef::Single(ok!(BlockRef::load_from(&mut s)))
544        })
545    }
546}
547
548impl Store for PrevBlockRef {
549    fn store_into(&self, builder: &mut CellBuilder, cx: &dyn CellContext) -> Result<(), Error> {
550        match self {
551            PrevBlockRef::Single(block_ref) => block_ref.store_into(builder, cx),
552            PrevBlockRef::AfterMerge { left, right } => {
553                ok!(builder.store_reference(ok!(CellBuilder::build_from_ext(left, cx))));
554                builder.store_reference(ok!(CellBuilder::build_from_ext(right, cx)))
555            }
556        }
557    }
558}
559
560/// Reference to the external block.
561#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
562#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
563pub struct BlockRef {
564    /// The end of the logical time of the referenced block.
565    pub end_lt: u64,
566    /// Sequence number of the referenced block.
567    pub seqno: u32,
568    /// Representation hash of the root cell of the referenced block.
569    pub root_hash: HashBytes,
570    /// Hash of the BOC encoded root cell of the referenced block.
571    pub file_hash: HashBytes,
572}
573
574impl BlockRef {
575    /// Converts a `BlockRef` to a `BlockId` given a shard identifier.
576    pub fn as_block_id(&self, shard: ShardIdent) -> BlockId {
577        BlockId {
578            shard,
579            seqno: self.seqno,
580            root_hash: self.root_hash,
581            file_hash: self.file_hash,
582        }
583    }
584}
585
586/// Tokens flow info.
587#[derive(Debug, Clone, Eq, PartialEq, Default)]
588#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
589pub struct ValueFlow {
590    /// Total amount transferred from the previous block.
591    pub from_prev_block: CurrencyCollection,
592    /// Total amount transferred to the next block.
593    pub to_next_block: CurrencyCollection,
594    /// Sum of all imported amounts from messages.
595    pub imported: CurrencyCollection,
596    /// Sum of all exported amounts of messages.
597    pub exported: CurrencyCollection,
598
599    /// Total fees collected in this block.
600    pub fees_collected: CurrencyCollection,
601
602    /// Burned native tokens and extra currencies.
603    pub burned: CurrencyCollection,
604
605    /// Shard fees imported to this block.
606    pub fees_imported: CurrencyCollection,
607    /// Fee recovery (?)
608    pub recovered: CurrencyCollection,
609    /// Block creation fees.
610    pub created: CurrencyCollection,
611    /// Minted native tokens and extra currencies.
612    pub minted: CurrencyCollection,
613}
614
615impl ValueFlow {
616    const TAG_V1: u32 = 0xb8e48dfb;
617    const TAG_V2: u32 = 0x3ebf98b7;
618
619    /// Returns `true` if an inbound value flow is in sync with an outbound.
620    pub fn validate(&self) -> Result<bool, Error> {
621        let in_val = self
622            .from_prev_block
623            .checked_add(&self.imported)
624            .and_then(|val| val.checked_add(&self.fees_imported))
625            .and_then(|val| val.checked_add(&self.created))
626            .and_then(|val| val.checked_add(&self.minted))
627            .and_then(|val| val.checked_add(&self.recovered))?;
628
629        let out_val = self
630            .to_next_block
631            .checked_add(&self.exported)
632            .and_then(|val| val.checked_add(&self.fees_collected))
633            .and_then(|val| val.checked_add(&self.burned))?;
634
635        Ok(in_val == out_val)
636    }
637}
638
639impl Store for ValueFlow {
640    fn store_into(
641        &self,
642        builder: &mut CellBuilder,
643        context: &dyn CellContext,
644    ) -> Result<(), Error> {
645        let (tag, store_burned) = if self.burned.is_zero() {
646            (Self::TAG_V1, false)
647        } else {
648            (Self::TAG_V2, true)
649        };
650
651        let cell1 = {
652            let mut builder = CellBuilder::new();
653            ok!(self.from_prev_block.store_into(&mut builder, context));
654            ok!(self.to_next_block.store_into(&mut builder, context));
655            ok!(self.imported.store_into(&mut builder, context));
656            ok!(self.exported.store_into(&mut builder, context));
657            ok!(builder.build_ext(context))
658        };
659
660        ok!(builder.store_u32(tag));
661        ok!(builder.store_reference(cell1));
662
663        ok!(self.fees_collected.store_into(builder, context));
664        if store_burned {
665            ok!(self.burned.store_into(builder, context));
666        }
667
668        let cell2 = {
669            let mut builder = CellBuilder::new();
670            ok!(self.fees_imported.store_into(&mut builder, context));
671            ok!(self.recovered.store_into(&mut builder, context));
672            ok!(self.created.store_into(&mut builder, context));
673            ok!(self.minted.store_into(&mut builder, context));
674            ok!(builder.build_ext(context))
675        };
676        builder.store_reference(cell2)
677    }
678}
679
680impl<'a> Load<'a> for ValueFlow {
681    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
682        let with_burned = match ok!(slice.load_u32()) {
683            Self::TAG_V1 => false,
684            Self::TAG_V2 => true,
685            _ => return Err(Error::InvalidTag),
686        };
687
688        let fees_collected = ok!(CurrencyCollection::load_from(slice));
689        let burned = if with_burned {
690            ok!(CurrencyCollection::load_from(slice))
691        } else {
692            CurrencyCollection::ZERO
693        };
694
695        let slice1 = &mut ok!(slice.load_reference_as_slice());
696        let slice2 = &mut ok!(slice.load_reference_as_slice());
697
698        Ok(Self {
699            from_prev_block: ok!(CurrencyCollection::load_from(slice1)),
700            to_next_block: ok!(CurrencyCollection::load_from(slice1)),
701            imported: ok!(CurrencyCollection::load_from(slice1)),
702            exported: ok!(CurrencyCollection::load_from(slice1)),
703            fees_collected,
704            burned,
705            fees_imported: ok!(CurrencyCollection::load_from(slice2)),
706            recovered: ok!(CurrencyCollection::load_from(slice2)),
707            created: ok!(CurrencyCollection::load_from(slice2)),
708            minted: ok!(CurrencyCollection::load_from(slice2)),
709        })
710    }
711}
712
713/// Outgoing message queue updates.
714#[cfg(feature = "tycho")]
715#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
716#[cfg_attr(feature = "serde", derive(serde::Serialize))]
717#[tlb(tag = "#1")]
718pub struct OutMsgQueueUpdates {
719    /// Hash of the serialized queue diff.
720    pub diff_hash: HashBytes,
721    /// The number of additional queue diffs, excluding the current one,
722    /// that may still be required by other shards.
723    pub tail_len: u32,
724}