everscale_types/models/shard/
mod.rs

1//! Shard state models.
2
3#[cfg(feature = "sync")]
4use std::sync::OnceLock;
5
6use crate::cell::*;
7use crate::dict::Dict;
8use crate::error::*;
9
10use crate::models::block::{BlockRef, ShardIdent};
11use crate::models::currency::CurrencyCollection;
12use crate::models::Lazy;
13
14#[cfg(feature = "tycho")]
15use crate::models::ShardIdentFull;
16
17pub use self::shard_accounts::*;
18pub use self::shard_extra::*;
19
20#[cfg(feature = "venom")]
21use super::ShardBlockRefs;
22
23mod shard_accounts;
24mod shard_extra;
25
26#[cfg(test)]
27mod tests;
28
29/// Applied shard state.
30#[allow(clippy::large_enum_variant)]
31#[derive(Debug, Clone, Eq, PartialEq)]
32pub enum ShardState {
33    /// The next indivisible state in the shard.
34    Unsplit(ShardStateUnsplit),
35    /// Next indivisible states after shard split.
36    Split(ShardStateSplit),
37}
38
39impl Store for ShardState {
40    fn store_into(
41        &self,
42        builder: &mut CellBuilder,
43        context: &mut dyn CellContext,
44    ) -> Result<(), Error> {
45        match self {
46            Self::Unsplit(state) => state.store_into(builder, context),
47            Self::Split(state) => state.store_into(builder, context),
48        }
49    }
50}
51
52impl<'a> Load<'a> for ShardState {
53    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
54        Ok(if ok!(slice.get_bit(0)) {
55            match ShardStateUnsplit::load_from(slice) {
56                Ok(state) => Self::Unsplit(state),
57                Err(e) => return Err(e),
58            }
59        } else {
60            match ShardStateSplit::load_from(slice) {
61                Ok(state) => Self::Split(state),
62                Err(e) => return Err(e),
63            }
64        })
65    }
66}
67
68/// State of the single shard.
69#[derive(Debug, Clone, Eq, PartialEq)]
70pub struct ShardStateUnsplit {
71    /// Global network id.
72    pub global_id: i32,
73    /// Id of the shard.
74    pub shard_ident: ShardIdent,
75    /// Sequence number of the corresponding block.
76    pub seqno: u32,
77    /// Vertical sequcent number of the corresponding block.
78    pub vert_seqno: u32,
79    /// Unix timestamp when the block was created.
80    pub gen_utime: u32,
81    /// Milliseconds part of the timestamp when the block was created.
82    #[cfg(any(feature = "venom", feature = "tycho"))]
83    pub gen_utime_ms: u16,
84    /// Logical time when the state was created.
85    pub gen_lt: u64,
86    /// Minimal referenced seqno of the masterchain block.
87    pub min_ref_mc_seqno: u32,
88
89    /// Output messages queue info (stub).
90    #[cfg(not(feature = "tycho"))]
91    pub out_msg_queue_info: Cell,
92
93    /// Processed up to info for externals and internals.
94    #[cfg(feature = "tycho")]
95    pub processed_upto: Lazy<ProcessedUptoInfo>,
96
97    /// Whether this state was produced before the shards split.
98    pub before_split: bool,
99    /// Reference to the dictionary with shard accounts.
100    pub accounts: Lazy<ShardAccounts>,
101    /// Mask for the overloaded blocks.
102    pub overload_history: u64,
103    /// Mask for the underloaded blocks.
104    pub underload_history: u64,
105    /// Total balance for all currencies.
106    pub total_balance: CurrencyCollection,
107    /// Total pending validator fees.
108    pub total_validator_fees: CurrencyCollection,
109    /// Dictionary with all libraries and its providers.
110    pub libraries: Dict<HashBytes, LibDescr>,
111    /// Optional reference to the masterchain block.
112    pub master_ref: Option<BlockRef>,
113    /// Shard state additional info.
114    pub custom: Option<Lazy<McStateExtra>>,
115    /// References to the latest known blocks from all shards.
116    #[cfg(feature = "venom")]
117    pub shard_block_refs: Option<ShardBlockRefs>,
118}
119
120#[cfg(feature = "sync")]
121impl Default for ShardStateUnsplit {
122    fn default() -> Self {
123        Self {
124            global_id: 0,
125            shard_ident: ShardIdent::MASTERCHAIN,
126            seqno: 0,
127            vert_seqno: 0,
128            gen_utime: 0,
129            #[cfg(any(feature = "venom", feature = "tycho"))]
130            gen_utime_ms: 0,
131            gen_lt: 0,
132            min_ref_mc_seqno: 0,
133            #[cfg(not(feature = "tycho"))]
134            out_msg_queue_info: Cell::default(),
135            #[cfg(feature = "tycho")]
136            processed_upto: Self::empty_processed_upto_info().clone(),
137            before_split: false,
138            accounts: Self::empty_shard_accounts().clone(),
139            overload_history: 0,
140            underload_history: 0,
141            total_balance: CurrencyCollection::ZERO,
142            total_validator_fees: CurrencyCollection::ZERO,
143            libraries: Dict::new(),
144            master_ref: None,
145            custom: None,
146            #[cfg(feature = "venom")]
147            shard_block_refs: None,
148        }
149    }
150}
151
152impl ShardStateUnsplit {
153    const TAG_V1: u32 = 0x9023afe2;
154    #[cfg(any(feature = "venom", feature = "tycho"))]
155    const TAG_V2: u32 = 0x9023aeee;
156
157    /// Returns a static reference to the empty processed up to info.
158    #[cfg(all(feature = "sync", feature = "tycho"))]
159    pub fn empty_processed_upto_info() -> &'static Lazy<ProcessedUptoInfo> {
160        static PROCESSED_UPTO_INFO: OnceLock<Lazy<ProcessedUptoInfo>> = OnceLock::new();
161        PROCESSED_UPTO_INFO.get_or_init(|| Lazy::new(&ProcessedUptoInfo::default()).unwrap())
162    }
163
164    /// Returns a static reference to the empty shard accounts.
165    #[cfg(feature = "sync")]
166    pub fn empty_shard_accounts() -> &'static Lazy<ShardAccounts> {
167        static SHARD_ACCOUNTS: OnceLock<Lazy<ShardAccounts>> = OnceLock::new();
168        SHARD_ACCOUNTS.get_or_init(|| Lazy::new(&ShardAccounts::new()).unwrap())
169    }
170
171    /// Tries to load shard accounts dictionary.
172    pub fn load_accounts(&self) -> Result<ShardAccounts, Error> {
173        self.accounts.load()
174    }
175
176    /// Tries to load additional masterchain data.
177    pub fn load_custom(&self) -> Result<Option<McStateExtra>, Error> {
178        match &self.custom {
179            Some(custom) => match custom.load() {
180                Ok(custom) => Ok(Some(custom)),
181                Err(e) => Err(e),
182            },
183            None => Ok(None),
184        }
185    }
186
187    /// Tries to set additional masterchain data.
188    pub fn set_custom(&mut self, value: Option<&McStateExtra>) -> Result<(), Error> {
189        match (&mut self.custom, value) {
190            (None, None) => Ok(()),
191            (None, Some(value)) => {
192                self.custom = Some(ok!(Lazy::new(value)));
193                Ok(())
194            }
195            (Some(_), None) => {
196                self.custom = None;
197                Ok(())
198            }
199            (Some(custom), Some(value)) => custom.set(value),
200        }
201    }
202}
203
204impl Store for ShardStateUnsplit {
205    fn store_into(
206        &self,
207        builder: &mut CellBuilder,
208        context: &mut dyn CellContext,
209    ) -> Result<(), Error> {
210        let child_cell = {
211            let mut builder = CellBuilder::new();
212            ok!(builder.store_u64(self.overload_history));
213            ok!(builder.store_u64(self.underload_history));
214            ok!(self.total_balance.store_into(&mut builder, context));
215            ok!(self.total_validator_fees.store_into(&mut builder, context));
216            ok!(self.libraries.store_into(&mut builder, context));
217            ok!(self.master_ref.store_into(&mut builder, context));
218            ok!(builder.build_ext(context))
219        };
220
221        #[cfg(not(any(feature = "venom", feature = "tycho")))]
222        ok!(builder.store_u32(Self::TAG_V1));
223        #[cfg(any(feature = "venom", feature = "tycho"))]
224        ok!(builder.store_u32(Self::TAG_V2));
225
226        ok!(builder.store_u32(self.global_id as u32));
227        ok!(self.shard_ident.store_into(builder, context));
228        ok!(builder.store_u32(self.seqno));
229        ok!(builder.store_u32(self.vert_seqno));
230        ok!(builder.store_u32(self.gen_utime));
231
232        #[cfg(any(feature = "venom", feature = "tycho"))]
233        ok!(builder.store_u16(self.gen_utime_ms));
234
235        ok!(builder.store_u64(self.gen_lt));
236        ok!(builder.store_u32(self.min_ref_mc_seqno));
237        #[cfg(not(feature = "tycho"))]
238        ok!(self.out_msg_queue_info.store_into(builder, context));
239        #[cfg(feature = "tycho")]
240        ok!(self.processed_upto.store_into(builder, context));
241        ok!(builder.store_bit(self.before_split));
242        ok!(builder.store_reference(self.accounts.cell.clone()));
243        ok!(builder.store_reference(child_cell));
244
245        #[cfg(not(feature = "venom"))]
246        ok!(self.custom.store_into(builder, context));
247
248        #[cfg(feature = "venom")]
249        if self.custom.is_some() && self.shard_block_refs.is_some() {
250            return Err(Error::InvalidData);
251        } else if let Some(refs) = &self.shard_block_refs {
252            ok!(refs.store_into(builder, context));
253        } else {
254            ok!(self.custom.store_into(builder, context));
255        }
256
257        Ok(())
258    }
259}
260
261impl<'a> Load<'a> for ShardStateUnsplit {
262    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
263        let fast_finality = match slice.load_u32() {
264            Ok(Self::TAG_V1) => false,
265            #[cfg(any(feature = "venom", feature = "tycho"))]
266            Ok(Self::TAG_V2) => true,
267            Ok(_) => return Err(Error::InvalidTag),
268            Err(e) => return Err(e),
269        };
270
271        #[cfg(not(any(feature = "venom", feature = "tycho")))]
272        let _ = fast_finality;
273
274        #[cfg(not(feature = "tycho"))]
275        let out_msg_queue_info = ok!(<_>::load_from(slice));
276
277        #[cfg(feature = "tycho")]
278        let processed_upto = ok!(Lazy::load_from(slice));
279
280        let accounts = ok!(Lazy::load_from(slice));
281
282        let child_slice = &mut ok!(slice.load_reference_as_slice());
283
284        let global_id = ok!(slice.load_u32()) as i32;
285        let shard_ident = ok!(ShardIdent::load_from(slice));
286
287        Ok(Self {
288            global_id,
289            shard_ident,
290            seqno: ok!(slice.load_u32()),
291            vert_seqno: ok!(slice.load_u32()),
292            gen_utime: ok!(slice.load_u32()),
293            #[cfg(any(feature = "venom", feature = "tycho"))]
294            gen_utime_ms: if fast_finality {
295                ok!(slice.load_u16())
296            } else {
297                0
298            },
299            gen_lt: ok!(slice.load_u64()),
300            min_ref_mc_seqno: ok!(slice.load_u32()),
301            before_split: ok!(slice.load_bit()),
302            accounts,
303            overload_history: ok!(child_slice.load_u64()),
304            underload_history: ok!(child_slice.load_u64()),
305            total_balance: ok!(CurrencyCollection::load_from(child_slice)),
306            total_validator_fees: ok!(CurrencyCollection::load_from(child_slice)),
307            libraries: ok!(Dict::load_from(child_slice)),
308            master_ref: ok!(Option::<BlockRef>::load_from(child_slice)),
309            #[cfg(not(feature = "tycho"))]
310            out_msg_queue_info,
311            #[cfg(feature = "tycho")]
312            processed_upto,
313            #[allow(unused_labels)]
314            custom: 'custom: {
315                #[cfg(feature = "venom")]
316                if !shard_ident.is_masterchain() {
317                    break 'custom None;
318                }
319                ok!(Option::<Lazy<McStateExtra>>::load_from(slice))
320            },
321            #[cfg(feature = "venom")]
322            shard_block_refs: if shard_ident.is_masterchain() {
323                None
324            } else {
325                Some(ok!(ShardBlockRefs::load_from(slice)))
326            },
327        })
328    }
329}
330
331/// Next indivisible states after shard split.
332#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
333#[tlb(tag = "#5f327da5")]
334pub struct ShardStateSplit {
335    /// Reference to the state of the left shard.
336    pub left: Lazy<ShardStateUnsplit>,
337    /// Reference to the state of the right shard.
338    pub right: Lazy<ShardStateUnsplit>,
339}
340
341/// Shared libraries currently can be present only in masterchain blocks.
342#[derive(Debug, Clone, Eq, PartialEq)]
343pub struct LibDescr {
344    /// Library code.
345    pub lib: Cell,
346    /// Accounts in the masterchain that store this library.
347    pub publishers: Dict<HashBytes, ()>,
348}
349
350impl Store for LibDescr {
351    fn store_into(&self, builder: &mut CellBuilder, _: &mut dyn CellContext) -> Result<(), Error> {
352        ok!(builder.store_small_uint(0, 2));
353        ok!(builder.store_reference(self.lib.clone()));
354        match self.publishers.root() {
355            Some(root) => builder.store_reference(root.clone()),
356            None => Err(Error::InvalidData),
357        }
358    }
359}
360
361impl<'a> Load<'a> for LibDescr {
362    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
363        if ok!(slice.load_small_uint(2)) != 0 {
364            return Err(Error::InvalidTag);
365        }
366        Ok(Self {
367            lib: ok!(slice.load_reference_cloned()),
368            publishers: ok!(Dict::load_from_root_ext(slice, &mut Cell::empty_context())),
369        })
370    }
371}
372
373/// Processed up to info for externals and internals.
374#[cfg(feature = "tycho")]
375#[derive(Debug, Default, Clone, Store, Load)]
376pub struct ProcessedUptoInfo {
377    /// Externals processed up to point and range
378    /// to reproduce last messages buffer
379    /// (if it was not fully processed in prev block collation).
380    pub externals: Option<ExternalsProcessedUpto>,
381    /// Internals processed up to points and ranges by shards
382    /// to reproduce last messages buffer
383    /// (if it was not fully processed in prev block collation).
384    pub internals: Dict<ShardIdentFull, InternalsProcessedUpto>,
385    /// Offset of processed messages from buffer.
386    /// Will be `!=0` if the buffer was not fully processed in prev block collation.
387    pub processed_offset: u32,
388}
389
390/// Describes the processed up to point and range of externals
391/// that we should read to reproduce the same messages buffer
392/// on which the previous block collation stopped:
393/// from message in FROM achor up to message in TO anchor: (FROM..TO].
394///
395/// If last buffer was fully processed then
396/// will be `processed_to == read_to`.
397/// So we do not need to reproduce the last buffer
398/// and we can continue to read externals from `read_to`.
399#[cfg(feature = "tycho")]
400#[derive(Debug, Clone, Store, Load)]
401pub struct ExternalsProcessedUpto {
402    /// Externals processed to (anchor, len).
403    /// All externals to this point
404    /// already processed during previous blocks collations.
405    ///
406    /// Needs to read next externals after this point to reproduce messages buffer for collation.
407    pub processed_to: (u32, u64),
408    /// Needs to read externals up to this point to reproduce messages buffer for collation.
409    pub read_to: (u32, u64),
410}
411
412/// Describes the processed up to point and range of internals
413/// that we should read from shard to reproduce the same messages buffer
414/// on which the previous block collation stopped:
415/// from message LT_HASH to message LT_HASH.
416///
417/// If last read messages set was fully processed then
418/// will be
419/// ```
420/// processed_to_msg == read_to_msg
421/// ```
422///
423/// So we do not need to reproduce the last messages buffer
424/// and we can continue to read internals after `read_to_msg`.
425#[cfg(feature = "tycho")]
426#[derive(Debug, Clone, Store, Load)]
427pub struct InternalsProcessedUpto {
428    /// Internals processed up to message (LT, Hash).
429    /// All internals upto this point already processed
430    /// during previous blocks collations.
431    ///
432    /// Needs to read next internals after this point to reproduce messages buffer for collation.
433    pub processed_to_msg: (u64, HashBytes),
434    /// Needs to read internals up to this point to reproduce messages buffer for collation.
435    pub read_to_msg: (u64, HashBytes),
436}