tycho_types/models/shard/
mod.rs

1//! Shard state models.
2
3#[cfg(feature = "sync")]
4use std::sync::OnceLock;
5
6pub use self::shard_accounts::*;
7pub use self::shard_extra::*;
8#[cfg(feature = "tycho")]
9use super::MsgsExecutionParams;
10use crate::cell::*;
11use crate::dict::Dict;
12use crate::error::*;
13use crate::models::block::{BlockRef, ShardIdent};
14use crate::models::currency::CurrencyCollection;
15#[cfg(feature = "tycho")]
16use crate::models::ShardIdentFull;
17
18mod shard_accounts;
19mod shard_extra;
20
21#[cfg(test)]
22mod tests;
23
24/// Applied shard state.
25#[allow(clippy::large_enum_variant)]
26#[derive(Debug, Clone, Eq, PartialEq)]
27pub enum ShardState {
28    /// The next indivisible state in the shard.
29    Unsplit(ShardStateUnsplit),
30    /// Next indivisible states after shard split.
31    Split(ShardStateSplit),
32}
33
34impl Store for ShardState {
35    fn store_into(
36        &self,
37        builder: &mut CellBuilder,
38        context: &dyn CellContext,
39    ) -> Result<(), Error> {
40        match self {
41            Self::Unsplit(state) => state.store_into(builder, context),
42            Self::Split(state) => state.store_into(builder, context),
43        }
44    }
45}
46
47impl<'a> Load<'a> for ShardState {
48    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
49        Ok(if ok!(slice.get_bit(0)) {
50            match ShardStateUnsplit::load_from(slice) {
51                Ok(state) => Self::Unsplit(state),
52                Err(e) => return Err(e),
53            }
54        } else {
55            match ShardStateSplit::load_from(slice) {
56                Ok(state) => Self::Split(state),
57                Err(e) => return Err(e),
58            }
59        })
60    }
61}
62
63/// State of the single shard.
64///
65/// # TLB scheme
66///
67/// Old:
68/// ```text
69/// shard_state#9023afe2
70///     global_id:int32
71///     shard_id:ShardIdent
72///     seq_no:uint32 vert_seq_no:#
73///     gen_utime:uint32 gen_lt:uint64
74///     min_ref_mc_seqno:uint32
75///     out_msg_queue_info:^OutMsgQueueInfo
76///     before_split:(## 1)
77///     accounts:^ShardAccounts
78///     ^[
79///         overload_history:uint64
80///         underload_history:uint64
81///         total_balance:CurrencyCollection
82///         total_validator_fees:CurrencyCollection
83///         libraries:(HashmapE 256 LibDescr)
84///         master_ref:(Maybe BlkMasterInfo)
85///     ]
86///     custom:(Maybe ^McStateExtra)
87///     = ShardStateUnsplit;
88/// ```
89///
90/// New:
91/// ```text
92/// shard_state#9023aeee
93///     global_id:int32
94///     shard_id:ShardIdent
95///     seq_no:uint32 vert_seq_no:#
96///     gen_utime:uint32
97///     gen_utime_ms:uint16
98///     gen_lt:uint64
99///     min_ref_mc_seqno:uint32
100///     processed_upto:^ProcessedUptoInfo
101///     before_split:(## 1)
102///     accounts:^ShardAccounts
103///     ^[
104///         overload_history:uint64
105///         underload_history:uint64
106///         total_balance:CurrencyCollection
107///         total_validator_fees:CurrencyCollection
108///         libraries:(HashmapE 256 LibDescr)
109///         master_ref:(Maybe BlkMasterInfo)
110///     ]
111///     custom:(Maybe ^McStateExtra)
112///     = ShardStateUnsplit;
113/// ```
114#[derive(Debug, Clone, Eq, PartialEq)]
115pub struct ShardStateUnsplit {
116    /// Global network id.
117    pub global_id: i32,
118    /// Id of the shard.
119    pub shard_ident: ShardIdent,
120    /// Sequence number of the corresponding block.
121    pub seqno: u32,
122    /// Vertical sequcent number of the corresponding block.
123    pub vert_seqno: u32,
124    /// Unix timestamp when the block was created.
125    pub gen_utime: u32,
126    /// Milliseconds part of the timestamp when the block was created.
127    #[cfg(feature = "tycho")]
128    pub gen_utime_ms: u16,
129    /// Logical time when the state was created.
130    pub gen_lt: u64,
131    /// Minimal referenced seqno of the masterchain block.
132    pub min_ref_mc_seqno: u32,
133
134    /// Output messages queue info (stub).
135    #[cfg(not(feature = "tycho"))]
136    pub out_msg_queue_info: Cell,
137
138    /// Processed up to info for externals and internals.
139    #[cfg(feature = "tycho")]
140    pub processed_upto: Lazy<ProcessedUptoInfo>,
141
142    /// Whether this state was produced before the shards split.
143    pub before_split: bool,
144    /// Reference to the dictionary with shard accounts.
145    pub accounts: Lazy<ShardAccounts>,
146    /// Mask for the overloaded blocks.
147    pub overload_history: u64,
148    /// Mask for the underloaded blocks.
149    pub underload_history: u64,
150    /// Total balance for all currencies.
151    pub total_balance: CurrencyCollection,
152    /// Total pending validator fees.
153    pub total_validator_fees: CurrencyCollection,
154    /// Dictionary with all libraries and its providers.
155    pub libraries: Dict<HashBytes, LibDescr>,
156    /// Optional reference to the masterchain block.
157    pub master_ref: Option<BlockRef>,
158    /// Shard state additional info.
159    pub custom: Option<Lazy<McStateExtra>>,
160}
161
162#[cfg(feature = "sync")]
163impl Default for ShardStateUnsplit {
164    fn default() -> Self {
165        Self {
166            global_id: 0,
167            shard_ident: ShardIdent::MASTERCHAIN,
168            seqno: 0,
169            vert_seqno: 0,
170            gen_utime: 0,
171            #[cfg(feature = "tycho")]
172            gen_utime_ms: 0,
173            gen_lt: 0,
174            min_ref_mc_seqno: 0,
175            #[cfg(not(feature = "tycho"))]
176            out_msg_queue_info: Cell::default(),
177            #[cfg(feature = "tycho")]
178            processed_upto: Self::empty_processed_upto_info().clone(),
179            before_split: false,
180            accounts: Self::empty_shard_accounts().clone(),
181            overload_history: 0,
182            underload_history: 0,
183            total_balance: CurrencyCollection::ZERO,
184            total_validator_fees: CurrencyCollection::ZERO,
185            libraries: Dict::new(),
186            master_ref: None,
187            custom: None,
188        }
189    }
190}
191
192impl ShardStateUnsplit {
193    const TAG_V1: u32 = 0x9023afe2;
194    #[cfg(feature = "tycho")]
195    const TAG_V2: u32 = 0x9023aeee;
196
197    /// Returns a static reference to the empty processed up to info.
198    #[cfg(all(feature = "sync", feature = "tycho"))]
199    pub fn empty_processed_upto_info() -> &'static Lazy<ProcessedUptoInfo> {
200        static PROCESSED_UPTO_INFO: OnceLock<Lazy<ProcessedUptoInfo>> = OnceLock::new();
201        PROCESSED_UPTO_INFO.get_or_init(|| Lazy::new(&ProcessedUptoInfo::default()).unwrap())
202    }
203
204    /// Returns a static reference to the empty shard accounts.
205    #[cfg(feature = "sync")]
206    pub fn empty_shard_accounts() -> &'static Lazy<ShardAccounts> {
207        static SHARD_ACCOUNTS: OnceLock<Lazy<ShardAccounts>> = OnceLock::new();
208        SHARD_ACCOUNTS.get_or_init(|| Lazy::new(&ShardAccounts::new()).unwrap())
209    }
210
211    /// Tries to load shard accounts dictionary.
212    pub fn load_accounts(&self) -> Result<ShardAccounts, Error> {
213        self.accounts.load()
214    }
215
216    /// Tries to load additional masterchain data.
217    pub fn load_custom(&self) -> Result<Option<McStateExtra>, Error> {
218        match &self.custom {
219            Some(custom) => match custom.load() {
220                Ok(custom) => Ok(Some(custom)),
221                Err(e) => Err(e),
222            },
223            None => Ok(None),
224        }
225    }
226
227    /// Tries to set additional masterchain data.
228    pub fn set_custom(&mut self, value: Option<&McStateExtra>) -> Result<(), Error> {
229        match (&mut self.custom, value) {
230            (None, None) => Ok(()),
231            (None, Some(value)) => {
232                self.custom = Some(ok!(Lazy::new(value)));
233                Ok(())
234            }
235            (Some(_), None) => {
236                self.custom = None;
237                Ok(())
238            }
239            (Some(custom), Some(value)) => custom.set(value),
240        }
241    }
242}
243
244impl Store for ShardStateUnsplit {
245    fn store_into(
246        &self,
247        builder: &mut CellBuilder,
248        context: &dyn CellContext,
249    ) -> Result<(), Error> {
250        let child_cell = {
251            let mut builder = CellBuilder::new();
252            ok!(builder.store_u64(self.overload_history));
253            ok!(builder.store_u64(self.underload_history));
254            ok!(self.total_balance.store_into(&mut builder, context));
255            ok!(self.total_validator_fees.store_into(&mut builder, context));
256            ok!(self.libraries.store_into(&mut builder, context));
257            ok!(self.master_ref.store_into(&mut builder, context));
258            ok!(builder.build_ext(context))
259        };
260
261        #[cfg(not(feature = "tycho"))]
262        ok!(builder.store_u32(Self::TAG_V1));
263        #[cfg(feature = "tycho")]
264        ok!(builder.store_u32(Self::TAG_V2));
265
266        ok!(builder.store_u32(self.global_id as u32));
267        ok!(self.shard_ident.store_into(builder, context));
268        ok!(builder.store_u32(self.seqno));
269        ok!(builder.store_u32(self.vert_seqno));
270        ok!(builder.store_u32(self.gen_utime));
271
272        #[cfg(feature = "tycho")]
273        ok!(builder.store_u16(self.gen_utime_ms));
274
275        ok!(builder.store_u64(self.gen_lt));
276        ok!(builder.store_u32(self.min_ref_mc_seqno));
277        #[cfg(not(feature = "tycho"))]
278        ok!(self.out_msg_queue_info.store_into(builder, context));
279        #[cfg(feature = "tycho")]
280        ok!(self.processed_upto.store_into(builder, context));
281        ok!(builder.store_bit(self.before_split));
282        ok!(builder.store_reference(self.accounts.inner().clone()));
283        ok!(builder.store_reference(child_cell));
284
285        ok!(self.custom.store_into(builder, context));
286
287        Ok(())
288    }
289}
290
291impl<'a> Load<'a> for ShardStateUnsplit {
292    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
293        let fast_finality = match slice.load_u32() {
294            Ok(Self::TAG_V1) => false,
295            #[cfg(feature = "tycho")]
296            Ok(Self::TAG_V2) => true,
297            Ok(_) => return Err(Error::InvalidTag),
298            Err(e) => return Err(e),
299        };
300
301        #[cfg(not(feature = "tycho"))]
302        let _ = fast_finality;
303
304        #[cfg(not(feature = "tycho"))]
305        let out_msg_queue_info = ok!(<_>::load_from(slice));
306
307        #[cfg(feature = "tycho")]
308        let processed_upto = ok!(Lazy::load_from(slice));
309
310        let accounts = ok!(Lazy::load_from(slice));
311
312        let child_slice = &mut ok!(slice.load_reference_as_slice());
313
314        let global_id = ok!(slice.load_u32()) as i32;
315        let shard_ident = ok!(ShardIdent::load_from(slice));
316
317        Ok(Self {
318            global_id,
319            shard_ident,
320            seqno: ok!(slice.load_u32()),
321            vert_seqno: ok!(slice.load_u32()),
322            gen_utime: ok!(slice.load_u32()),
323            #[cfg(feature = "tycho")]
324            gen_utime_ms: if fast_finality {
325                ok!(slice.load_u16())
326            } else {
327                0
328            },
329            gen_lt: ok!(slice.load_u64()),
330            min_ref_mc_seqno: ok!(slice.load_u32()),
331            before_split: ok!(slice.load_bit()),
332            accounts,
333            overload_history: ok!(child_slice.load_u64()),
334            underload_history: ok!(child_slice.load_u64()),
335            total_balance: ok!(CurrencyCollection::load_from(child_slice)),
336            total_validator_fees: ok!(CurrencyCollection::load_from(child_slice)),
337            libraries: ok!(Dict::load_from(child_slice)),
338            master_ref: ok!(Option::<BlockRef>::load_from(child_slice)),
339            #[cfg(not(feature = "tycho"))]
340            out_msg_queue_info,
341            #[cfg(feature = "tycho")]
342            processed_upto,
343            #[allow(unused_labels)]
344            custom: ok!(Option::<Lazy<McStateExtra>>::load_from(slice)),
345        })
346    }
347}
348
349/// Next indivisible states after shard split.
350#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
351#[tlb(tag = "#5f327da5")]
352pub struct ShardStateSplit {
353    /// Reference to the state of the left shard.
354    pub left: Lazy<ShardStateUnsplit>,
355    /// Reference to the state of the right shard.
356    pub right: Lazy<ShardStateUnsplit>,
357}
358
359/// Shared libraries currently can be present only in masterchain blocks.
360#[derive(Debug, Clone, Eq, PartialEq)]
361pub struct LibDescr {
362    /// Library code.
363    pub lib: Cell,
364    /// Accounts in the masterchain that store this library.
365    pub publishers: Dict<HashBytes, ()>,
366}
367
368impl Store for LibDescr {
369    fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
370        ok!(builder.store_small_uint(0, 2));
371        ok!(builder.store_reference(self.lib.clone()));
372        match self.publishers.root() {
373            Some(root) => builder.store_slice(root.as_slice_allow_exotic()),
374            None => Err(Error::InvalidData),
375        }
376    }
377}
378
379impl<'a> Load<'a> for LibDescr {
380    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
381        if ok!(slice.load_small_uint(2)) != 0 {
382            return Err(Error::InvalidTag);
383        }
384        Ok(Self {
385            lib: ok!(slice.load_reference_cloned()),
386            publishers: ok!(Dict::load_from_root_ext(slice, Cell::empty_context())),
387        })
388    }
389}
390
391/// Processed upto info for externals/internals
392/// and messages execution params.
393///
394/// # TLB scheme
395///
396/// ```text
397/// processedUptoInfo#00
398///     partitions:(HashmapE 16 ProcessedUptoPartition)
399///     msgs_exec_params:(Maybe ^MsgsExecutionParams)
400///     = ProcessedUptoInfo;
401/// ```
402#[cfg(feature = "tycho")]
403#[derive(Debug, Default, Clone, Store, Load)]
404#[tlb(tag = "#00")]
405pub struct ProcessedUptoInfo {
406    /// We split messages by partitions.
407    /// Main partition 0 and others.
408    pub partitions: Dict<u16, ProcessedUptoPartition>,
409
410    /// Actual messages execution params used for collated block.
411    /// They help to refill messages buffers on sync/restart and
412    /// process remaning messages in queues with previous params
413    /// before switching to a new params version.
414    pub msgs_exec_params: Option<Lazy<MsgsExecutionParams>>,
415}
416
417/// Processed up to info for externals and internals in one partition.
418///
419/// # TLB scheme
420///
421/// ```text
422/// processedUptoPartition#00
423///     externals:ExternalsProcessedUpto
424///     internals:InternalsProcessedUpto
425///     = ProcessedUptoPartition
426/// ```
427#[cfg(feature = "tycho")]
428#[derive(Debug, Default, Clone, Store, Load)]
429#[tlb(tag = "#00")]
430pub struct ProcessedUptoPartition {
431    /// Externals read range and processed to info.
432    pub externals: ExternalsProcessedUpto,
433
434    /// Internals read ranges and processed to info.
435    pub internals: InternalsProcessedUpto,
436}
437
438/// Describes the processed to point and ranges of externals
439/// that we should read to reproduce the same messages buffer
440/// on which the previous block collation stopped.
441///
442/// # TLB scheme
443///
444/// ```text
445/// externalsProcessedUpto#00
446///     processed_to_anchor_id:uint32
447///     processed_to_msgs_offset:uint64
448///     ranges:(HashmapE 32 ExternalsRange)
449///     = ExternalsProcessedUpto;
450/// ```
451#[cfg(feature = "tycho")]
452#[derive(Debug, Default, Clone, Store, Load)]
453#[tlb(tag = "#00")]
454pub struct ExternalsProcessedUpto {
455    /// Externals processed to (anchor id, msgs offset).
456    /// All externals up to this point
457    /// already processed during previous blocks collations.
458    pub processed_to: (u32, u64),
459
460    /// Externals read ranges map by block seqno.
461    pub ranges: Dict<u32, ExternalsRange>,
462}
463
464/// Describes externals read range.
465///
466/// # TLB scheme
467///
468/// ```text
469/// externalsRange#00
470///     from_anchor_id:uint32
471///     from_msgs_offset:uint64
472///     to_anchor_id:uint32
473///     to_msgs_offset:uint64
474///     chain_time:uint64
475///     skip_offset:uint32
476///     processed_offset:uint32
477///     = ExternalsRange;
478/// ```
479#[cfg(feature = "tycho")]
480#[derive(Debug, Default, Clone, Store, Load)]
481#[tlb(tag = "#00")]
482pub struct ExternalsRange {
483    /// From mempool anchor id and msgs offset.
484    pub from: (u32, u64),
485    /// To mempool anchor id and msgs offset.
486    pub to: (u32, u64),
487
488    /// Chain time of the block when range was read.
489    pub chain_time: u64,
490
491    /// Skip offset before collecting messages from this range.
492    /// Because we should collect from others.
493    pub skip_offset: u32,
494    /// How many times externals messages were collected from all ranges.
495    /// Every range contains offset that was reached when range was the last.
496    /// So the current last range contains the actual offset.
497    pub processed_offset: u32,
498}
499
500/// Describes the processed to point and ranges of internals
501/// that we should read to reproduce the same messages buffer
502/// on which the previous block collation stopped.
503///
504/// # TLB scheme
505///
506/// ```text
507/// internalsProcessedUpto#00
508///     processed_to:(HashmapE 96 ProcessedUpto)
509///     ranges:(HashmapE 32 InternalsRange)
510///     = InternalsProcessedUpto;
511/// ```
512#[cfg(feature = "tycho")]
513#[derive(Debug, Default, Clone, Store, Load)]
514#[tlb(tag = "#00")]
515pub struct InternalsProcessedUpto {
516    /// Internals processed to (LT, HASH) by source shards.
517    /// All internals up to this point
518    /// already processed during previous blocks collations.
519    pub processed_to: Dict<ShardIdentFull, (u64, HashBytes)>,
520
521    /// Internals read ranges map by block seqno.
522    pub ranges: Dict<u32, InternalsRange>,
523}
524
525/// Describes internals read range.
526///
527/// # TLB scheme
528///
529/// ```text
530/// internalsRange#00
531///     skip_offset:uint32
532///     processed_offset:uint32
533///     shards:(HashmapE 96 ShardRange)
534///     = InternalsRange;
535/// ```
536#[cfg(feature = "tycho")]
537#[derive(Debug, Default, Clone, Store, Load)]
538#[tlb(tag = "#00")]
539pub struct InternalsRange {
540    /// Skip offset before collecting messages from this range.
541    /// Because we should collect from others.
542    pub skip_offset: u32,
543    /// How many times internal messages were collected from all ranges.
544    /// Every range contains offset that was reached when range was the last.
545    /// So the current last range contains the actual offset.
546    pub processed_offset: u32,
547
548    /// Internals read ranges by source shards.
549    pub shards: Dict<ShardIdentFull, ShardRange>,
550}
551
552/// Describes internals read range from one shard.
553///
554/// # TLB scheme
555///
556/// ```text
557/// shardRange#00
558///     from:uint64
559///     to:uint64
560///     = ShardRange;
561/// ```
562#[cfg(feature = "tycho")]
563#[derive(Debug, Default, Clone, Store, Load)]
564#[tlb(tag = "#00")]
565pub struct ShardRange {
566    /// From LT.
567    pub from: u64,
568    /// To LT.
569    pub to: u64,
570}