tycho_types/models/config/
params.rs

1use std::num::{NonZeroU16, NonZeroU32};
2
3use tycho_crypto::ed25519;
4
5use crate::cell::*;
6use crate::dict::Dict;
7use crate::error::Error;
8use crate::models::block::ShardIdent;
9use crate::models::{CurrencyCollection, Signature};
10use crate::num::{Tokens, Uint12, VarUint248};
11
12/// Value flow burning config.
13#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15#[tlb(tag = "#01", validate_with = "Self::is_valid")]
16pub struct BurningConfig {
17    /// Address of the masterchain account which will burn all inbound message balance.
18    pub blackhole_addr: Option<HashBytes>,
19    /// Numerator of the potion of burned fees.
20    pub fee_burn_num: u32,
21    /// Denominator of the potion of burned fees.
22    pub fee_burn_denom: NonZeroU32,
23}
24
25impl Default for BurningConfig {
26    #[inline]
27    fn default() -> Self {
28        Self {
29            blackhole_addr: None,
30            fee_burn_num: 0,
31            fee_burn_denom: NonZeroU32::MIN,
32        }
33    }
34}
35
36impl BurningConfig {
37    /// Returns whether the config is well-formed.
38    pub fn is_valid(&self) -> bool {
39        self.fee_burn_num <= self.fee_burn_denom.get()
40    }
41
42    /// Computes how much fees to burn.
43    ///
44    /// NOTE: For a well-formed [`BurningConfig`] it never fails
45    ///       and returns a value not greater than `tokens`.
46    pub fn compute_burned_fees(&self, tokens: Tokens) -> Result<Tokens, Error> {
47        if self.fee_burn_num == 0 {
48            return Ok(Tokens::ZERO);
49        } else if !self.is_valid() {
50            return Err(Error::InvalidData);
51        }
52
53        let mut tokens = VarUint248::new(tokens.into_inner());
54        tokens *= self.fee_burn_num as u128;
55        tokens /= self.fee_burn_denom.get() as u128;
56        let (hi, lo) = tokens.into_words();
57        debug_assert_eq!(
58            hi, 0,
59            "burned fees must never be greater than original fees"
60        );
61        Ok(Tokens::new(lo))
62    }
63}
64
65/// One-time minting config (can be used by L2 to mint native currency).
66#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
67#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
68#[tlb(tag = "#01")]
69pub struct MintOnceConfig {
70    /// Exact masterchain block seqno.
71    pub mint_at: u32,
72    /// Native and extra currencies to mint.
73    pub delta: CurrencyCollection,
74}
75
76/// Config voting setup params.
77#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
78#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
79#[tlb(tag = "#91")]
80pub struct ConfigVotingSetup {
81    /// Proposal configuration for non-critical params.
82    pub normal_params: Lazy<ConfigProposalSetup>,
83    /// Proposal configuration for critical params.
84    pub critical_params: Lazy<ConfigProposalSetup>,
85}
86
87/// Config proposal setup params.
88#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
89#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
90#[tlb(tag = "#36")]
91pub struct ConfigProposalSetup {
92    /// The minimal number of voting rounds for the proposal.
93    pub min_total_rounds: u8,
94    /// The maximum number of voting rounds for the proposal.
95    pub max_total_rounds: u8,
96    /// The minimum number of winned voting rounds.
97    pub min_wins: u8,
98    /// The maximum number of lost voting rounds.
99    pub max_losses: u8,
100    /// The minimal proposal lifetime duration in seconds.
101    pub min_store_sec: u32,
102    /// The maximum proposal lifetime duration in seconds.
103    pub max_store_sec: u32,
104    /// Bit price for storage price computation.
105    pub bit_price: u32,
106    /// Cell price for storage price computation.
107    pub cell_price: u32,
108}
109
110/// Workchain description.
111#[derive(Debug, Clone, Eq, PartialEq)]
112#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
113pub struct WorkchainDescription {
114    /// Unix timestamp from which blocks can be produced.
115    pub enabled_since: u32,
116    /// Unused stub.
117    pub actual_min_split: u8,
118    /// The minimal shards split depths.
119    pub min_split: u8,
120    /// The maximum shards split depths.
121    pub max_split: u8,
122    /// Whether the workchain is enabled.
123    pub active: bool,
124    /// Whether the workchain accepts messages.
125    pub accept_msgs: bool,
126    /// A hash of the zerostate root cell.
127    pub zerostate_root_hash: HashBytes,
128    /// A hash of the zerostate file.
129    pub zerostate_file_hash: HashBytes,
130    /// Workchain version.
131    pub version: u32,
132    /// Workchain format description.
133    pub format: WorkchainFormat,
134}
135
136impl WorkchainDescription {
137    const TAG: u8 = 0xa6;
138
139    /// Returns `true` if the workchain description is valid.
140    pub fn is_valid(&self) -> bool {
141        self.min_split <= self.max_split
142            && self.max_split <= ShardIdent::MAX_SPLIT_DEPTH
143            && self.format.is_valid()
144    }
145}
146
147impl Store for WorkchainDescription {
148    fn store_into(
149        &self,
150        builder: &mut CellBuilder,
151        context: &dyn CellContext,
152    ) -> Result<(), Error> {
153        if !self.is_valid() {
154            return Err(Error::InvalidData);
155        }
156
157        let flags: u16 = ((self.format.is_basic() as u16) << 15)
158            | ((self.active as u16) << 14)
159            | ((self.accept_msgs as u16) << 13);
160
161        ok!(builder.store_u8(Self::TAG));
162        ok!(builder.store_u32(self.enabled_since));
163        ok!(builder.store_u8(self.actual_min_split));
164        ok!(builder.store_u8(self.min_split));
165        ok!(builder.store_u8(self.max_split));
166        ok!(builder.store_u16(flags));
167        ok!(builder.store_u256(&self.zerostate_root_hash));
168        ok!(builder.store_u256(&self.zerostate_file_hash));
169        ok!(builder.store_u32(self.version));
170        self.format.store_into(builder, context)
171    }
172}
173
174impl<'a> Load<'a> for WorkchainDescription {
175    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
176        match slice.load_u8() {
177            Ok(Self::TAG) => {}
178            Ok(_) => return Err(Error::InvalidTag),
179            Err(e) => return Err(e),
180        }
181
182        let enabled_since = ok!(slice.load_u32());
183        let actual_min_split = ok!(slice.load_u8());
184        let min_split = ok!(slice.load_u8());
185        let max_split = ok!(slice.load_u8());
186        let flags = ok!(slice.load_u16());
187        if flags << 3 != 0 {
188            return Err(Error::InvalidData);
189        }
190
191        let result = Self {
192            enabled_since,
193            actual_min_split,
194            min_split,
195            max_split,
196            active: flags & 0b0100_0000_0000_0000 != 0,
197            accept_msgs: flags & 0b0010_0000_0000_0000 != 0,
198            zerostate_root_hash: ok!(slice.load_u256()),
199            zerostate_file_hash: ok!(slice.load_u256()),
200            version: ok!(slice.load_u32()),
201            format: ok!(WorkchainFormat::load_from(slice)),
202        };
203
204        let basic = flags & 0b1000_0000_0000_0000 != 0;
205        if basic != result.format.is_basic() {
206            return Err(Error::InvalidData);
207        }
208
209        Ok(result)
210    }
211}
212
213/// Workchain format description.
214#[derive(Debug, Copy, Clone, Eq, PartialEq)]
215#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
216#[cfg_attr(feature = "serde", serde(tag = "ty"))]
217pub enum WorkchainFormat {
218    /// Basic workchain format.
219    Basic(WorkchainFormatBasic),
220    /// Extended workchain format.
221    Extended(WorkchainFormatExtended),
222}
223
224impl WorkchainFormat {
225    /// Returns `true` if the workchain format is valid.
226    pub fn is_valid(&self) -> bool {
227        match self {
228            Self::Basic(_) => true,
229            Self::Extended(format) => format.is_valid(),
230        }
231    }
232
233    /// Returns `true` if the workchain format is [`Basic`].
234    ///
235    /// [`Basic`]: WorkchainFormatBasic
236    pub fn is_basic(&self) -> bool {
237        matches!(self, Self::Basic(_))
238    }
239}
240
241impl Store for WorkchainFormat {
242    fn store_into(
243        &self,
244        builder: &mut CellBuilder,
245        context: &dyn CellContext,
246    ) -> Result<(), Error> {
247        match self {
248            Self::Basic(value) => {
249                ok!(builder.store_small_uint(0x1, 4));
250                value.store_into(builder, context)
251            }
252            Self::Extended(value) => {
253                ok!(builder.store_small_uint(0x0, 4));
254                value.store_into(builder, context)
255            }
256        }
257    }
258}
259
260impl<'a> Load<'a> for WorkchainFormat {
261    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
262        Ok(match ok!(slice.load_small_uint(4)) {
263            0x1 => Self::Basic(ok!(WorkchainFormatBasic::load_from(slice))),
264            0x0 => Self::Extended(ok!(WorkchainFormatExtended::load_from(slice))),
265            _ => return Err(Error::InvalidTag),
266        })
267    }
268}
269
270/// Basic workchain format description.
271#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
272#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
273pub struct WorkchainFormatBasic {
274    /// VM version.
275    pub vm_version: i32,
276    /// VM mode.
277    pub vm_mode: u64,
278}
279
280/// Extended workchain format description.
281#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
282#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
283#[tlb(validate_with = "Self::is_valid")]
284pub struct WorkchainFormatExtended {
285    /// The minimal address length in bits.
286    pub min_addr_len: Uint12,
287    /// The maximal address length in bits.
288    pub max_addr_len: Uint12,
289    /// Address length step in bits.
290    pub addr_len_step: Uint12,
291    /// Extended workchain type id.
292    pub workchain_type_id: NonZeroU32,
293}
294
295impl WorkchainFormatExtended {
296    /// Returns `true` if the workchain format is valid.
297    pub fn is_valid(&self) -> bool {
298        self.min_addr_len >= Uint12::new(64)
299            && self.min_addr_len <= self.max_addr_len
300            && self.max_addr_len <= Uint12::new(1023)
301            && self.addr_len_step <= Uint12::new(1023)
302    }
303
304    /// Checks that address length is in a valid range and is aligned to the len step.
305    pub fn check_addr_len(&self, addr_len: u16) -> bool {
306        let addr_len = Uint12::new(addr_len);
307
308        let is_aligned = || {
309            if self.addr_len_step.is_zero() {
310                return false;
311            }
312
313            let var_part = addr_len - self.min_addr_len;
314            let step_rem = var_part.into_inner() % self.addr_len_step.into_inner();
315            step_rem == 0
316        };
317
318        addr_len >= self.min_addr_len
319            && addr_len <= self.max_addr_len
320            && (addr_len == self.min_addr_len || addr_len == self.max_addr_len || is_aligned())
321    }
322}
323
324/// Block creation reward.
325#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
326#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
327#[tlb(tag = "#6b")]
328pub struct BlockCreationRewards {
329    /// Reward for each created masterchain block.
330    pub masterchain_block_fee: Tokens,
331    /// Base reward for basechain blocks.
332    pub basechain_block_fee: Tokens,
333}
334
335/// Validators election timings.
336#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
337#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
338pub struct ElectionTimings {
339    /// Validation round length in seconds.
340    pub validators_elected_for: u32,
341    /// Duration in seconds until the end of the validation round when the election starts.
342    pub elections_start_before: u32,
343    /// Duration in seconds until the end of the validation round when the election ends.
344    pub elections_end_before: u32,
345    /// How long validator stake will be frozen after the validation round end.
346    pub stake_held_for: u32,
347}
348
349/// Range of number of validators.
350#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
351#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
352pub struct ValidatorCountParams {
353    /// The maximum number of validators.
354    pub max_validators: u16,
355    /// The maximum number of masterchain validators.
356    pub max_main_validators: u16,
357    /// The minimum number of validators.
358    pub min_validators: u16,
359}
360
361/// Validator stake range and factor.
362#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
363#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
364pub struct ValidatorStakeParams {
365    /// The minimum validator stake.
366    pub min_stake: Tokens,
367    /// The maximum validator stake.
368    pub max_stake: Tokens,
369    /// The minimum required total stake for elections to be successful.
370    pub min_total_stake: Tokens,
371    /// Stake constraint (shifted by 16 bits).
372    pub max_stake_factor: u32,
373}
374
375/// Storage prices for some interval.
376#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
377#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
378#[tlb(tag = "#cc")]
379pub struct StoragePrices {
380    /// Unix timestamp since which this prices are used.
381    pub utime_since: u32,
382    /// Bit price in base workchain.
383    pub bit_price_ps: u64,
384    /// Cell price in base workchain.
385    pub cell_price_ps: u64,
386    /// Bit price in masterchain.
387    pub mc_bit_price_ps: u64,
388    /// Cell price in masterchain.
389    pub mc_cell_price_ps: u64,
390}
391
392impl StoragePrices {
393    /// Computes the amount of fees for storing `stats` data for `delta` seconds.
394    pub fn compute_storage_fee(
395        &self,
396        is_masterchain: bool,
397        delta: u64,
398        stats: CellTreeStats,
399    ) -> Tokens {
400        let mut res = if is_masterchain {
401            (stats.cell_count as u128 * self.mc_cell_price_ps as u128)
402                .saturating_add(stats.bit_count as u128 * self.mc_bit_price_ps as u128)
403        } else {
404            (stats.cell_count as u128 * self.cell_price_ps as u128)
405                .saturating_add(stats.bit_count as u128 * self.bit_price_ps as u128)
406        };
407        res = res.saturating_mul(delta as u128);
408        Tokens::new(shift_ceil_price(res))
409    }
410}
411
412/// Gas limits and prices.
413#[derive(Default, Debug, Clone, Eq, PartialEq)]
414#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
415pub struct GasLimitsPrices {
416    /// The price of gas unit.
417    pub gas_price: u64,
418    /// The maximum amount of gas available for a compute phase of an ordinary transaction.
419    pub gas_limit: u64,
420    /// The maximum amount of gas available for a compute phase of a special transaction.
421    pub special_gas_limit: u64,
422    /// The maximum amount of gas available before `ACCEPT`.
423    pub gas_credit: u64,
424    /// The maximum amount of gas units per block.
425    pub block_gas_limit: u64,
426    /// Amount of debt (in tokens) after which the account will be frozen.
427    pub freeze_due_limit: u64,
428    /// Amount of debt (in tokens) after which the contract will be deleted.
429    pub delete_due_limit: u64,
430    /// Size of the first portion of gas with different price.
431    pub flat_gas_limit: u64,
432    /// The gas price for the first portion determinted by [`flat_gas_limit`].
433    ///
434    /// [`flat_gas_limit`]: GasLimitsPrices::flat_gas_limit
435    pub flat_gas_price: u64,
436}
437
438impl GasLimitsPrices {
439    /// Converts gas units into tokens.
440    pub fn compute_gas_fee(&self, gas_used: u64) -> Tokens {
441        let mut res = self.flat_gas_price as u128;
442        if let Some(extra_gas) = gas_used.checked_sub(self.flat_gas_limit) {
443            res = res.saturating_add(shift_ceil_price(self.gas_price as u128 * extra_gas as u128));
444        }
445        Tokens::new(res)
446    }
447}
448
449impl GasLimitsPrices {
450    const TAG_BASE: u8 = 0xdd;
451    const TAG_EXT: u8 = 0xde;
452    const TAG_FLAT_PFX: u8 = 0xd1;
453}
454
455impl Store for GasLimitsPrices {
456    fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
457        ok!(builder.store_u8(Self::TAG_FLAT_PFX));
458        ok!(builder.store_u64(self.flat_gas_limit));
459        ok!(builder.store_u64(self.flat_gas_price));
460        ok!(builder.store_u8(Self::TAG_EXT));
461        ok!(builder.store_u64(self.gas_price));
462        ok!(builder.store_u64(self.gas_limit));
463        ok!(builder.store_u64(self.special_gas_limit));
464        ok!(builder.store_u64(self.gas_credit));
465        ok!(builder.store_u64(self.block_gas_limit));
466        ok!(builder.store_u64(self.freeze_due_limit));
467        builder.store_u64(self.delete_due_limit)
468    }
469}
470
471impl<'a> Load<'a> for GasLimitsPrices {
472    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
473        let mut result = Self::default();
474        loop {
475            match slice.load_u8() {
476                Ok(Self::TAG_FLAT_PFX) => {
477                    result.flat_gas_limit = ok!(slice.load_u64());
478                    result.flat_gas_price = ok!(slice.load_u64());
479                }
480                Ok(Self::TAG_EXT) => {
481                    result.gas_price = ok!(slice.load_u64());
482                    result.gas_limit = ok!(slice.load_u64());
483                    result.special_gas_limit = ok!(slice.load_u64());
484                    result.gas_credit = ok!(slice.load_u64());
485                    result.block_gas_limit = ok!(slice.load_u64());
486                    result.freeze_due_limit = ok!(slice.load_u64());
487                    result.delete_due_limit = ok!(slice.load_u64());
488                    return Ok(result);
489                }
490                Ok(Self::TAG_BASE) => {
491                    result.gas_price = ok!(slice.load_u64());
492                    result.gas_limit = ok!(slice.load_u64());
493                    result.gas_credit = ok!(slice.load_u64());
494                    result.block_gas_limit = ok!(slice.load_u64());
495                    result.freeze_due_limit = ok!(slice.load_u64());
496                    result.delete_due_limit = ok!(slice.load_u64());
497                    return Ok(result);
498                }
499                Ok(_) => return Err(Error::InvalidTag),
500                Err(e) => return Err(e),
501            }
502        }
503    }
504}
505
506/// Block limits parameter.
507#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
508#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
509#[tlb(tag = "#c3", validate_with = "Self::is_valid")]
510pub struct BlockParamLimits {
511    /// Value below which the parameter is considered underloaded.
512    pub underload: u32,
513    /// Soft limit.
514    pub soft_limit: u32,
515    /// Hard limit.
516    pub hard_limit: u32,
517}
518
519impl BlockParamLimits {
520    /// Returns `true` if parameter limits are valid.
521    pub fn is_valid(&self) -> bool {
522        self.underload <= self.soft_limit && self.soft_limit <= self.hard_limit
523    }
524}
525
526/// Block limits.
527#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
528#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
529#[tlb(tag = "#5d")]
530pub struct BlockLimits {
531    /// Block size limits in bytes.
532    pub bytes: BlockParamLimits,
533    /// Gas limits.
534    pub gas: BlockParamLimits,
535    /// Logical time delta limits.
536    pub lt_delta: BlockParamLimits,
537}
538
539/// Message forwarding prices.
540#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
541#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
542#[tlb(tag = "#ea")]
543pub struct MsgForwardPrices {
544    /// Fixed price in addition to the dynamic part.
545    pub lump_price: u64,
546    /// The price of bits in the message (bits in the root cell are not included).
547    pub bit_price: u64,
548    /// The price of cells in the message.
549    pub cell_price: u64,
550    /// TODO: add docs
551    pub ihr_price_factor: u32,
552    /// Part of fees that is included to the first block.
553    pub first_frac: u16,
554    /// Part of fees that goes to transit blocks.
555    pub next_frac: u16,
556}
557
558impl MsgForwardPrices {
559    /// Computes fees for forwarding the specified amount of data.
560    pub fn compute_fwd_fee(&self, stats: CellTreeStats) -> Tokens {
561        let lump = self.lump_price as u128;
562        let extra = shift_ceil_price(
563            (stats.cell_count as u128 * self.cell_price as u128)
564                .saturating_add(stats.bit_count as u128 * self.bit_price as u128),
565        );
566        Tokens::new(lump.saturating_add(extra))
567    }
568
569    /// Computes the part of the fees that is included to the total fees of the current block.
570    pub fn get_first_part(&self, total: Tokens) -> Tokens {
571        Tokens::new(total.into_inner().saturating_mul(self.first_frac as _) >> 16)
572    }
573
574    /// Computes the part of the fees that is included to the total fees of the transit block.
575    pub fn get_next_part(&self, total: Tokens) -> Tokens {
576        Tokens::new(total.into_inner().saturating_mul(self.next_frac as _) >> 16)
577    }
578}
579
580/// Catchain configuration params.
581#[cfg(not(feature = "tycho"))]
582#[derive(Debug, Copy, Clone, Eq, PartialEq)]
583#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
584pub struct CatchainConfig {
585    /// Exclude masterchain validators from a validators list for a base workchain.
586    pub isolate_mc_validators: bool,
587    /// Change the order of validators in the masterchain validators list.
588    pub shuffle_mc_validators: bool,
589    /// Masterchain catchain session lifetime in seconds.
590    pub mc_catchain_lifetime: u32,
591    /// Catchain session lifetime for shards in seconds.
592    pub shard_catchain_lifetime: u32,
593    /// Period in seconds for which the subset of validators is selected for each shard.
594    pub shard_validators_lifetime: u32,
595    /// The number of validators per shard.
596    pub shard_validators_num: u32,
597}
598
599#[cfg(not(feature = "tycho"))]
600impl CatchainConfig {
601    const TAG_V1: u8 = 0xc1;
602    const TAG_V2: u8 = 0xc2;
603}
604
605#[cfg(not(feature = "tycho"))]
606impl Store for CatchainConfig {
607    fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
608        let flags = ((self.isolate_mc_validators as u8) << 1) | (self.shuffle_mc_validators as u8);
609        ok!(builder.store_u8(Self::TAG_V2));
610        ok!(builder.store_u8(flags));
611        ok!(builder.store_u32(self.mc_catchain_lifetime));
612        ok!(builder.store_u32(self.shard_catchain_lifetime));
613        ok!(builder.store_u32(self.shard_validators_lifetime));
614        builder.store_u32(self.shard_validators_num)
615    }
616}
617
618#[cfg(not(feature = "tycho"))]
619impl<'a> Load<'a> for CatchainConfig {
620    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
621        let flags = match slice.load_u8() {
622            Ok(Self::TAG_V1) => 0,
623            Ok(Self::TAG_V2) => ok!(slice.load_u8()),
624            Ok(_) => return Err(Error::InvalidTag),
625            Err(e) => return Err(e),
626        };
627        if flags >> 2 != 0 {
628            return Err(Error::InvalidData);
629        }
630        Ok(Self {
631            isolate_mc_validators: flags & 0b10 != 0,
632            shuffle_mc_validators: flags & 0b01 != 0,
633            mc_catchain_lifetime: ok!(slice.load_u32()),
634            shard_catchain_lifetime: ok!(slice.load_u32()),
635            shard_validators_lifetime: ok!(slice.load_u32()),
636            shard_validators_num: ok!(slice.load_u32()),
637        })
638    }
639}
640
641/// Collation configuration params.
642///
643/// ```text
644/// collation_config_tycho#a6
645///     shuffle_mc_validators:Bool
646///     mc_block_min_interval_ms:uint32
647///     max_uncommitted_chain_length:uint8
648///     wu_used_to_import_next_anchor:uint64
649///     msgs_exec_params:MsgsExecutionParams
650///     work_units_params:WorkUnitsParams
651///     = CollationConfig;
652///
653/// collation_config_tycho#a7
654///     shuffle_mc_validators:Bool
655///     mc_block_min_interval_ms:uint32
656///     empty_sc_block_interval_ms:uint32
657///     max_uncommitted_chain_length:uint8
658///     wu_used_to_import_next_anchor:uint64
659///     msgs_exec_params:MsgsExecutionParams
660///     work_units_params:WorkUnitsParams
661///     = CollationConfig;
662/// ```
663#[cfg(feature = "tycho")]
664#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
665#[tlb(tag = "#a6")]
666#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
667pub struct CollationConfig {
668    /// Change the order of validators in the masterchain validators list.
669    pub shuffle_mc_validators: bool,
670
671    /// Minimum interval between master blocks.
672    pub mc_block_min_interval_ms: u32,
673
674    /// Time to wait before collating an empty shard block.
675    pub empty_sc_block_interval_ms: u32,
676
677    /// Maximum length on shard blocks chain after previous master block.
678    pub max_uncommitted_chain_length: u8,
679    /// Force import next anchor when wu used exceed limit.
680    pub wu_used_to_import_next_anchor: u64,
681
682    /// Messages execution params.
683    pub msgs_exec_params: MsgsExecutionParams,
684
685    /// Params to calculate the collation work in wu.
686    pub work_units_params: WorkUnitsParams,
687}
688
689/// Messages execution params.
690///
691/// ```text
692/// msgs_execution_params_tycho#00
693///     buffer_limit:uint32
694///     group_limit:uint16
695///     group_vert_size:uint16
696///     externals_expire_timeout:uint16
697///     open_ranges_limit:uint16
698///     par_0_int_msgs_count_limit:uint32
699///     par_0_ext_msgs_count_limit:uint32
700///     group_slots_fractions:(HashmapE 16 uint8)
701///     = MsgsExecutionParams;
702/// ```
703#[cfg(feature = "tycho")]
704#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
705#[tlb(tag = ["#00", "#01"])]
706#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
707pub struct MsgsExecutionParams {
708    /// Maximum limit of messages buffer.
709    pub buffer_limit: u32,
710
711    /// The horizontal limit of one message group.
712    /// Shows how many slots can be.
713    /// One slot may contain messages for some accounts.
714    /// One account can exist only in one slot.
715    pub group_limit: u16,
716    /// The vertical limit of one message group.
717    /// Shows how many messages can be per one slot in the group.
718    pub group_vert_size: u16,
719
720    /// The timeout for externals in seconds.
721    pub externals_expire_timeout: u16,
722
723    /// The maximum number of ranges
724    /// that we should store in ProcessedUptoInfo maps
725    pub open_ranges_limit: u16,
726
727    /// The maximum number of incoming internal messages on account.
728    /// When the internal messages queue on the account exceeds the limit
729    /// then all messages on this account  will be processed in other partition.
730    pub par_0_int_msgs_count_limit: u32,
731
732    /// The maximum number of incoming externals messages on account.
733    /// When the external messages queue on the account exceeds the limit
734    /// then all messages on this account  will be processed in other partition.
735    pub par_0_ext_msgs_count_limit: u32,
736
737    /// The fractions of message group slots
738    /// for messages subgroups
739    pub group_slots_fractions: Dict<u16, u8>,
740
741    /// The maximum number of blocks messages in one range.
742    #[tlb(since_tag = 1)]
743    pub range_messages_limit: u32,
744}
745
746/// Params to calculate the collation work in wu.
747///
748/// ```text
749/// work_units_params_tycho#00
750///     prepare:WorkUnitParamsPrepare
751///     execute:WorkUnitParamsExecute
752///     finalize:WorkUnitParamsFinalize
753///     = WorkUnitsParams;
754/// ```
755#[cfg(feature = "tycho")]
756#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
757#[tlb(tag = "#00")]
758#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
759pub struct WorkUnitsParams {
760    /// Params to calculate messages groups prepare work in wu.
761    pub prepare: WorkUnitsParamsPrepare,
762    /// Params to calculate messages execution work in wu.
763    pub execute: WorkUnitsParamsExecute,
764    /// Params to calculate block finalization work in wu.
765    pub finalize: WorkUnitsParamsFinalize,
766}
767
768/// Params to calculate messages groups prepare work in wu.
769///
770/// ```text
771/// work_units_params_prepare_tycho#00
772///     fixed:uint32
773///     msgs_stats:uint16
774///     remaning_msgs_stats:uint16
775///     read_ext_msgs:uint16
776///     read_int_msgs:uint16
777///     read_new_msgs:uint16
778///     add_to_msg_groups:uint16
779///     = WorkUnitsParamsPrepare;
780/// ```
781#[cfg(feature = "tycho")]
782#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
783#[tlb(tag = "#00")]
784#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
785pub struct WorkUnitsParamsPrepare {
786    /// TODO: Add docs.
787    pub fixed_part: u32,
788    /// TODO: Add docs.
789    pub msgs_stats: u16,
790    /// TODO: Add docs.
791    pub remaning_msgs_stats: u16,
792    /// TODO: Add docs.
793    pub read_ext_msgs: u16,
794    /// TODO: Add docs.
795    pub read_int_msgs: u16,
796    /// TODO: Add docs.
797    pub read_new_msgs: u16,
798    /// TODO: Add docs.
799    pub add_to_msg_groups: u16,
800}
801
802/// Params to calculate messages execution work in wu.
803///
804/// ```text
805/// work_units_params_execute_tycho#00
806///     prepare:uint32
807///     execute:uint16
808///     execute_err:uint16
809///     execute_delimiter:uint32
810///     serialize_enqueue:uint16
811///     serialize_dequeue:uint16
812///     insert_new_msgs:uint16
813///     subgroup_size:uint16
814///     = WorkUnitsParamsExecute;
815/// ```
816#[cfg(feature = "tycho")]
817#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
818#[tlb(tag = "#00")]
819#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
820pub struct WorkUnitsParamsExecute {
821    /// TODO: Add docs.
822    pub prepare: u32,
823    /// TODO: Add docs.
824    pub execute: u16,
825    /// TODO: Add docs.
826    pub execute_err: u16,
827    /// TODO: Add docs.
828    pub execute_delimiter: u32,
829    /// TODO: Add docs.
830    pub serialize_enqueue: u16,
831    /// TODO: Add docs.
832    pub serialize_dequeue: u16,
833    /// TODO: Add docs.
834    pub insert_new_msgs: u16,
835    /// TODO: Add docs.
836    pub subgroup_size: u16,
837}
838
839/// Params to calculate block finalization work in wu.
840///
841/// ```text
842/// work_units_params_finalize_tycho#00
843///     build_transactions:uint16
844///     build_accounts:uint16
845///     build_in_msg:uint16
846///     build_out_msg:uint16
847///     serialize_min:uint32
848///     serialize_accounts:uint16
849///     serialize_msg:uint16
850///     state_update_min:uint32
851///     state_update_accounts:uint16
852///     state_update_msg:uint16
853///     create_diff:uint16
854///     serialize_diff:uint16
855///     apply_diff:uint16
856///     diff_tail_len:uint16
857///     = WorkUnitsParamsFinalize;
858/// ```
859#[cfg(feature = "tycho")]
860#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
861#[tlb(tag = "#00")]
862#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
863pub struct WorkUnitsParamsFinalize {
864    /// TODO: Add docs.
865    pub build_transactions: u16,
866    /// TODO: Add docs.
867    pub build_accounts: u16,
868    /// TODO: Add docs.
869    pub build_in_msg: u16,
870    /// TODO: Add docs.
871    pub build_out_msg: u16,
872    /// TODO: Add docs.
873    pub serialize_min: u32,
874    /// TODO: Add docs.
875    pub serialize_accounts: u16,
876    /// TODO: Add docs.
877    pub serialize_msg: u16,
878    /// TODO: Add docs.
879    pub state_update_min: u32,
880    /// TODO: Add docs.
881    pub state_update_accounts: u16,
882    /// TODO: Add docs.
883    pub state_update_msg: u16,
884    /// TODO: Add docs.
885    pub create_diff: u16,
886    /// TODO: Add docs.
887    pub serialize_diff: u16,
888    /// TODO: Add docs.
889    pub apply_diff: u16,
890    /// TODO: Add docs.
891    pub diff_tail_len: u16,
892}
893
894/// DAG Consensus configuration params
895///
896/// ```text
897/// consensus_config_tycho#d8
898///     clock_skew_millis:uint16
899///     payload_batch_bytes:uint32
900///     commit_history_rounds:uint8
901///     deduplicate_rounds:uint16
902///     max_consensus_lag_rounds:uint16
903///     payload_buffer_bytes:uint32
904///     broadcast_retry_millis:uint8
905///     download_retry_millis:uint8
906///     download_peers:uint8
907///     download_tasks:uint16
908///     sync_support_rounds:uint16
909///     = ConsensusConfig;
910/// ```
911#[cfg(feature = "tycho")]
912#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
913#[tlb(tag = "#d8")]
914#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
915pub struct ConsensusConfig {
916    /// How far a ready-to-be-signed point (with time in its body)
917    /// may be in the future compared with signer's local (wall) time.
918    /// Lower bound is defined by genesis, and then advanced by leaders with every anchor.
919    /// Anchor time is strictly increasing as it is inherited from anchor candidate in every point.
920    ///
921    /// **NOTE: Affects overlay id.**
922    pub clock_skew_millis: u16,
923
924    /// Hard limit on point payload. Excessive messages will be postponed.
925    ///
926    /// **NOTE: Affects overlay id.**
927    pub payload_batch_bytes: u32,
928
929    /// Limits amount of rounds included in anchor history (points that appears in commit).
930    ///
931    /// **NOTE: Affects overlay id.**
932    pub commit_history_rounds: u16,
933
934    /// Size (amount of rounds) of a sliding window to deduplicate external messages across anchors.
935    ///
936    /// **NOTE: Affects overlay id.**
937    pub deduplicate_rounds: u16,
938
939    /// The max expected distance (amount of rounds) between two sequential top known anchors (TKA),
940    /// i.e. anchors from two sequential top known blocks (TKB, signed master chain blocks,
941    /// available to local node, and which state is not necessarily applied by local node). For
942    /// example, the last TKA=`7` and the config value is `210`, so a newer TKA is expected in
943    /// range `(8..=217).len() == 210`, i.e. some leader successfully completes its 3 rounds
944    /// in a row (collects 2F+1 signatures for its anchor trigger), and there are one or
945    /// two additional mempool rounds for the anchor trigger to be delivered to all nodes,
946    /// and every collator is expected to create and sign a block containing that new TKA and time.
947    /// Until a new TKA in range `11..=211'('7+4..<217-3-2`) is received by the local mempool,
948    /// it will not repeat its per-round routine at round `216` and keeps waiting in a "pause mode".
949    /// DAG will contain `217` round as it always does for the next round after current.
950    /// Switch of validator set may be scheduled for `218` round, as its round is not created.
951    ///
952    /// Effectively defines feedback from block validation consensus to mempool consensus.
953    ///
954    /// **NOTE: Affects overlay id.**
955    pub max_consensus_lag_rounds: u16,
956
957    /// Hard limit on ring buffer size to cache external messages before they are taken into
958    /// point payload. Newer messages may push older ones out of the buffer when limit is reached.
959    pub payload_buffer_bytes: u32,
960
961    /// Every round an instance tries to gather as many points and signatures as it can
962    /// within some time frame. It is a tradeoff between breaking current round
963    /// on exactly 2F+1 items (points and/or signatures) and waiting for slow nodes.
964    pub broadcast_retry_millis: u16,
965
966    /// Every missed dependency (point) is downloaded with a group of simultaneous requests to
967    /// neighbour peers. Every new group of requests is spawned after previous group completed
968    /// or this interval elapsed (in order not to wait for some slow responding peer).
969    pub download_retry_millis: u16,
970
971    /// Amount of peers to request at first download attempt. Amount will increase
972    /// respectively at each attempt, until 2F peers successfully responded `None`
973    /// or a verifiable point is found (incorrectly signed points do not count).
974    pub download_peers: u8,
975
976    /// Limits amount of unique points being simultaneously downloaded (except the first one).
977    pub download_tasks: u16,
978
979    /// Max duration (amount of rounds) at which local mempool is supposed to keep its history
980    /// for neighbours to sync. Also limits DAG growth when it syncs, as sync takes time.
981    pub sync_support_rounds: u16,
982}
983
984/// Consensus configuration params.
985#[cfg(not(feature = "tycho"))]
986#[derive(Debug, Clone, Eq, PartialEq)]
987#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
988pub struct ConsensusConfig {
989    /// Allow new catchain ids.
990    pub new_catchain_ids: bool,
991    /// Number of block candidates per round.
992    pub round_candidates: NonZeroU32,
993    /// Delay in seconds before proposing a new candidate.
994    pub next_candidate_delay_ms: u32,
995    /// Catchain processing timeout in seconds.
996    pub consensus_timeout_ms: u32,
997    /// Maximum number of attempts per round.
998    pub fast_attempts: u32,
999    /// Duration of a round attempt in seconds.
1000    pub attempt_duration: u32,
1001    /// The maximum number of dependencies to merge.
1002    pub catchain_max_deps: u32,
1003    /// The maximum block size in bytes.
1004    pub max_block_bytes: u32,
1005    /// THe maximum size of a collated data in bytes.
1006    pub max_collated_bytes: u32,
1007}
1008
1009#[cfg(not(feature = "tycho"))]
1010impl ConsensusConfig {
1011    const TAG_V1: u8 = 0xd6;
1012    const TAG_V2: u8 = 0xd7;
1013}
1014
1015#[cfg(not(feature = "tycho"))]
1016impl Store for ConsensusConfig {
1017    fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
1018        let flags = self.new_catchain_ids as u8;
1019
1020        ok!(builder.store_u8(Self::TAG_V2));
1021        ok!(builder.store_u8(flags));
1022        ok!(builder.store_u8(self.round_candidates.get() as u8));
1023        ok!(builder.store_u32(self.next_candidate_delay_ms));
1024        ok!(builder.store_u32(self.consensus_timeout_ms));
1025        ok!(builder.store_u32(self.fast_attempts));
1026        ok!(builder.store_u32(self.attempt_duration));
1027        ok!(builder.store_u32(self.catchain_max_deps));
1028        ok!(builder.store_u32(self.max_block_bytes));
1029        builder.store_u32(self.max_collated_bytes)
1030    }
1031}
1032
1033#[cfg(not(feature = "tycho"))]
1034impl<'a> Load<'a> for ConsensusConfig {
1035    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
1036        use std::num::NonZeroU8;
1037
1038        let (flags, round_candidates) = match slice.load_u8() {
1039            Ok(Self::TAG_V1) => (0, ok!(NonZeroU32::load_from(slice))),
1040            Ok(Self::TAG_V2) => {
1041                let flags = ok!(slice.load_u8());
1042                if flags >> 1 != 0 {
1043                    return Err(Error::InvalidData);
1044                }
1045                (flags, ok!(NonZeroU8::load_from(slice)).into())
1046            }
1047            Ok(_) => return Err(Error::InvalidTag),
1048            Err(e) => return Err(e),
1049        };
1050        Ok(Self {
1051            new_catchain_ids: flags & 0b1 != 0,
1052            round_candidates,
1053            next_candidate_delay_ms: ok!(slice.load_u32()),
1054            consensus_timeout_ms: ok!(slice.load_u32()),
1055            fast_attempts: ok!(slice.load_u32()),
1056            attempt_duration: ok!(slice.load_u32()),
1057            catchain_max_deps: ok!(slice.load_u32()),
1058            max_block_bytes: ok!(slice.load_u32()),
1059            max_collated_bytes: ok!(slice.load_u32()),
1060        })
1061    }
1062}
1063
1064/// Validator set.
1065#[derive(Debug, Clone, Eq, PartialEq)]
1066#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1067pub struct ValidatorSet {
1068    /// Unix timestamp from which this set will be active.
1069    pub utime_since: u32,
1070    /// Unix timestamp until which this set will be active.
1071    pub utime_until: u32,
1072    /// The number of masterchain validators.
1073    pub main: NonZeroU16,
1074    /// Total validators weight.
1075    pub total_weight: u64,
1076    /// Validators.
1077    pub list: Vec<ValidatorDescription>,
1078}
1079
1080impl ValidatorSet {
1081    const TAG_V1: u8 = 0x11;
1082    const TAG_V2: u8 = 0x12;
1083
1084    /// Computes a validator subset using a zero seed.
1085    #[cfg(not(feature = "tycho"))]
1086    pub fn compute_subset(
1087        &self,
1088        shard_ident: ShardIdent,
1089        cc_config: &CatchainConfig,
1090        cc_seqno: u32,
1091    ) -> Option<(Vec<ValidatorDescription>, u32)> {
1092        if shard_ident.is_masterchain() {
1093            return self.compute_mc_subset(cc_seqno, cc_config.shuffle_mc_validators);
1094        }
1095
1096        let total = self.list.len();
1097        let main = self.main.get() as usize;
1098
1099        let mut prng = ValidatorSetPRNG::new(shard_ident, cc_seqno);
1100
1101        let vset = if cc_config.isolate_mc_validators {
1102            if total <= main {
1103                return None;
1104            }
1105
1106            let mut list = self.list[main..].to_vec();
1107
1108            let mut total_weight = 0u64;
1109            for descr in &mut list {
1110                descr.prev_total_weight = total_weight;
1111                total_weight += descr.weight;
1112            }
1113
1114            std::borrow::Cow::Owned(Self {
1115                utime_since: self.utime_since,
1116                utime_until: self.utime_until,
1117                main: self.main,
1118                total_weight,
1119                list,
1120            })
1121        } else {
1122            std::borrow::Cow::Borrowed(self)
1123        };
1124
1125        let count = std::cmp::min(vset.list.len(), cc_config.shard_validators_num as usize);
1126
1127        let mut nodes = Vec::with_capacity(count);
1128        let mut holes = Vec::<(u64, u64)>::with_capacity(count);
1129        let mut total_wt = vset.total_weight;
1130
1131        for _ in 0..count {
1132            debug_assert!(total_wt > 0);
1133
1134            // Generate a pseudo-random number 0..total_wt-1
1135            let mut p = prng.next_ranged(total_wt);
1136
1137            for (prev_total_weight, weight) in &holes {
1138                if p < *prev_total_weight {
1139                    break;
1140                }
1141                p += weight;
1142            }
1143
1144            let entry = vset.at_weight(p);
1145
1146            nodes.push(ValidatorDescription {
1147                public_key: entry.public_key,
1148                weight: 1,
1149                adnl_addr: entry.adnl_addr,
1150                mc_seqno_since: 0,
1151                prev_total_weight: 0,
1152            });
1153            debug_assert!(total_wt >= entry.weight);
1154            total_wt -= entry.weight;
1155
1156            let new_hole = (entry.prev_total_weight, entry.weight);
1157            let i = holes.partition_point(|item| item <= &new_hole);
1158            debug_assert!(i == 0 || holes[i - 1] < new_hole);
1159
1160            holes.insert(i, new_hole);
1161        }
1162
1163        let hash_short = Self::compute_subset_hash_short(&nodes, cc_seqno);
1164
1165        Some((nodes, hash_short))
1166    }
1167
1168    /// Computes a masterchain validator subset using a zero seed.
1169    ///
1170    /// NOTE: In most cases you should use the more generic [`ValidatorSet::compute_subset`].
1171    pub fn compute_mc_subset(
1172        &self,
1173        cc_seqno: u32,
1174        shuffle: bool,
1175    ) -> Option<(Vec<ValidatorDescription>, u32)> {
1176        let total = self.list.len();
1177        let main = self.main.get() as usize;
1178
1179        let count = std::cmp::min(total, main);
1180        let subset = if !shuffle {
1181            self.list[0..count].to_vec()
1182        } else {
1183            let mut prng = ValidatorSetPRNG::new(ShardIdent::MASTERCHAIN, cc_seqno);
1184
1185            let mut indices = vec![0; count];
1186            for i in 0..count {
1187                let j = prng.next_ranged(i as u64 + 1) as usize; // number 0 .. i
1188                debug_assert!(j <= i);
1189                indices[i] = indices[j];
1190                indices[j] = i;
1191            }
1192
1193            let mut subset = Vec::with_capacity(count);
1194            for index in indices.into_iter().take(count) {
1195                subset.push(self.list[index].clone());
1196            }
1197            subset
1198        };
1199
1200        let hash_short = Self::compute_subset_hash_short(&subset, cc_seqno);
1201        Some((subset, hash_short))
1202    }
1203
1204    /// Compoutes a validator subset short hash.
1205    pub fn compute_subset_hash_short(subset: &[ValidatorDescription], cc_seqno: u32) -> u32 {
1206        const HASH_SHORT_MAGIC: u32 = 0x901660ED;
1207
1208        let mut hash = crc32c::crc32c(&HASH_SHORT_MAGIC.to_le_bytes());
1209        hash = crc32c::crc32c_append(hash, &cc_seqno.to_le_bytes());
1210        hash = crc32c::crc32c_append(hash, &(subset.len() as u32).to_le_bytes());
1211
1212        for node in subset {
1213            hash = crc32c::crc32c_append(hash, node.public_key.as_slice());
1214            hash = crc32c::crc32c_append(hash, &node.weight.to_le_bytes());
1215            hash = crc32c::crc32c_append(
1216                hash,
1217                node.adnl_addr
1218                    .as_ref()
1219                    .unwrap_or(HashBytes::wrap(&[0u8; 32]))
1220                    .as_ref(),
1221            );
1222        }
1223
1224        hash
1225    }
1226
1227    #[cfg(not(feature = "tycho"))]
1228    fn at_weight(&self, weight_pos: u64) -> &ValidatorDescription {
1229        debug_assert!(weight_pos < self.total_weight);
1230        debug_assert!(!self.list.is_empty());
1231        let i = self
1232            .list
1233            .partition_point(|item| item.prev_total_weight <= weight_pos);
1234        debug_assert!(i != 0);
1235        &self.list[i - 1]
1236    }
1237}
1238
1239impl Store for ValidatorSet {
1240    fn store_into(
1241        &self,
1242        builder: &mut CellBuilder,
1243        context: &dyn CellContext,
1244    ) -> Result<(), Error> {
1245        let Ok(total) = u16::try_from(self.list.len()) else {
1246            return Err(Error::IntOverflow);
1247        };
1248
1249        // TODO: optimize
1250        let mut validators = Dict::<u16, ValidatorDescription>::new();
1251        for (i, item) in self.list.iter().enumerate() {
1252            ok!(validators.set_ext(i as u16, item, context));
1253        }
1254
1255        ok!(builder.store_u8(Self::TAG_V2));
1256        ok!(builder.store_u32(self.utime_since));
1257        ok!(builder.store_u32(self.utime_until));
1258        ok!(builder.store_u16(total));
1259        ok!(builder.store_u16(self.main.get()));
1260        ok!(builder.store_u64(self.total_weight));
1261        validators.store_into(builder, context)
1262    }
1263}
1264
1265impl<'a> Load<'a> for ValidatorSet {
1266    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
1267        let with_total_weight = match slice.load_u8() {
1268            Ok(Self::TAG_V1) => false,
1269            Ok(Self::TAG_V2) => true,
1270            Ok(_) => return Err(Error::InvalidTag),
1271            Err(e) => return Err(e),
1272        };
1273
1274        let utime_since = ok!(slice.load_u32());
1275        let utime_until = ok!(slice.load_u32());
1276        let total = ok!(slice.load_u16()) as usize;
1277        let main = ok!(NonZeroU16::load_from(slice));
1278        if main.get() as usize > total {
1279            return Err(Error::InvalidData);
1280        }
1281
1282        let context = Cell::empty_context();
1283
1284        let (mut total_weight, validators) = if with_total_weight {
1285            let total_weight = ok!(slice.load_u64());
1286            let dict = ok!(Dict::<u16, ValidatorDescription>::load_from(slice));
1287            (total_weight, dict)
1288        } else {
1289            let dict = ok!(Dict::<u16, ValidatorDescription>::load_from_root_ext(
1290                slice, context
1291            ));
1292            (0, dict)
1293        };
1294
1295        let mut computed_total_weight = 0u64;
1296        let mut list = Vec::with_capacity(std::cmp::min(total, 512));
1297        for (i, entry) in validators.iter().enumerate().take(total) {
1298            let mut descr = match entry {
1299                Ok((idx, descr)) if idx as usize == i => descr,
1300                Ok(_) => return Err(Error::InvalidData),
1301                Err(e) => return Err(e),
1302            };
1303
1304            descr.prev_total_weight = computed_total_weight;
1305            computed_total_weight = match computed_total_weight.checked_add(descr.weight) {
1306                Some(weight) => weight,
1307                None => return Err(Error::InvalidData),
1308            };
1309            list.push(descr);
1310        }
1311
1312        if list.is_empty() {
1313            return Err(Error::InvalidData);
1314        }
1315
1316        if with_total_weight {
1317            if total_weight != computed_total_weight {
1318                return Err(Error::InvalidData);
1319            }
1320        } else {
1321            total_weight = computed_total_weight;
1322        }
1323
1324        Ok(Self {
1325            utime_since,
1326            utime_until,
1327            main,
1328            total_weight,
1329            list,
1330        })
1331    }
1332}
1333
1334#[cfg(feature = "serde")]
1335impl<'de> serde::Deserialize<'de> for ValidatorSet {
1336    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1337        use serde::de::Error;
1338
1339        #[derive(serde::Deserialize)]
1340        struct ValidatorSetHelper {
1341            utime_since: u32,
1342            utime_until: u32,
1343            main: NonZeroU16,
1344            #[serde(default)]
1345            total_weight: u64,
1346            list: Vec<ValidatorDescription>,
1347        }
1348
1349        let parsed = ValidatorSetHelper::deserialize(deserializer)?;
1350        if parsed.list.is_empty() {
1351            return Err(Error::custom("empty validators list"));
1352        }
1353
1354        let mut result = Self {
1355            utime_since: parsed.utime_since,
1356            utime_until: parsed.utime_until,
1357            main: parsed.main,
1358            total_weight: 0,
1359            list: parsed.list,
1360        };
1361
1362        for descr in &mut result.list {
1363            descr.prev_total_weight = result.total_weight;
1364            let Some(new_total_weight) = result.total_weight.checked_add(descr.weight) else {
1365                return Err(Error::custom("total weight overflow"));
1366            };
1367            result.total_weight = new_total_weight;
1368        }
1369
1370        if parsed.total_weight > 0 && parsed.total_weight != result.total_weight {
1371            return Err(Error::custom("total weight mismatch"));
1372        }
1373
1374        Ok(result)
1375    }
1376}
1377
1378/// Validator description.
1379#[derive(Debug, Clone, Eq, PartialEq)]
1380#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1381pub struct ValidatorDescription {
1382    /// Validator public key.
1383    pub public_key: HashBytes, // TODO: replace with tycho_crypto::ed25519::PublicKey ?
1384    /// Validator weight in some units.
1385    pub weight: u64,
1386    /// Optional validator ADNL address.
1387    #[cfg_attr(feature = "serde", serde(default))]
1388    pub adnl_addr: Option<HashBytes>,
1389    /// Since which seqno this validator will be active.
1390    #[cfg_attr(feature = "serde", serde(default))]
1391    pub mc_seqno_since: u32,
1392
1393    /// Total weight of the previous validators in the list.
1394    /// The field is not serialized.
1395    #[cfg_attr(feature = "serde", serde(skip))]
1396    pub prev_total_weight: u64,
1397}
1398
1399impl ValidatorDescription {
1400    const TAG_BASIC: u8 = 0x53;
1401    const TAG_WITH_ADNL: u8 = 0x73;
1402    const TAG_WITH_MC_SEQNO: u8 = 0x93;
1403
1404    const PUBKEY_TAG: u32 = 0x8e81278a;
1405
1406    /// Verifies message signature and current public key.
1407    pub fn verify_signature(&self, data: &[u8], signature: &Signature) -> bool {
1408        if let Some(public_key) = ed25519::PublicKey::from_bytes(self.public_key.0) {
1409            public_key.verify_raw(data, signature.as_ref())
1410        } else {
1411            false
1412        }
1413    }
1414}
1415
1416impl Store for ValidatorDescription {
1417    fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
1418        let with_mc_seqno = self.mc_seqno_since != 0;
1419
1420        let tag = if with_mc_seqno {
1421            Self::TAG_WITH_MC_SEQNO
1422        } else if self.adnl_addr.is_some() {
1423            Self::TAG_WITH_ADNL
1424        } else {
1425            Self::TAG_BASIC
1426        };
1427
1428        ok!(builder.store_u8(tag));
1429        ok!(builder.store_u32(Self::PUBKEY_TAG));
1430        ok!(builder.store_u256(&self.public_key));
1431        ok!(builder.store_u64(self.weight));
1432
1433        let mut adnl = self.adnl_addr.as_ref();
1434        if with_mc_seqno {
1435            adnl = Some(HashBytes::wrap(&[0; 32]));
1436        }
1437
1438        if let Some(adnl) = adnl {
1439            ok!(builder.store_u256(adnl));
1440        }
1441
1442        if with_mc_seqno {
1443            builder.store_u32(self.mc_seqno_since)
1444        } else {
1445            Ok(())
1446        }
1447    }
1448}
1449
1450impl<'a> Load<'a> for ValidatorDescription {
1451    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
1452        let (with_adnl, with_mc_seqno) = match slice.load_u8() {
1453            Ok(Self::TAG_BASIC) => (false, false),
1454            Ok(Self::TAG_WITH_ADNL) => (true, false),
1455            Ok(Self::TAG_WITH_MC_SEQNO) => (true, true),
1456            Ok(_) => return Err(Error::InvalidTag),
1457            Err(e) => return Err(e),
1458        };
1459
1460        Ok(Self {
1461            public_key: {
1462                match slice.load_u32() {
1463                    Ok(Self::PUBKEY_TAG) => ok!(slice.load_u256()),
1464                    Ok(_) => return Err(Error::InvalidTag),
1465                    Err(e) => return Err(e),
1466                }
1467            },
1468            weight: ok!(slice.load_u64()),
1469            adnl_addr: if with_adnl {
1470                Some(ok!(slice.load_u256()))
1471            } else {
1472                None
1473            },
1474            mc_seqno_since: if with_mc_seqno {
1475                ok!(slice.load_u32())
1476            } else {
1477                0
1478            },
1479            prev_total_weight: 0,
1480        })
1481    }
1482}
1483
1484/// Random generator used for validator subset calculation.
1485pub struct ValidatorSetPRNG {
1486    context: [u8; 48],
1487    bag: [u64; 8],
1488}
1489
1490impl ValidatorSetPRNG {
1491    /// Creates a new generator with zero seed.
1492    pub fn new(shard_ident: ShardIdent, cc_seqno: u32) -> Self {
1493        let seed = [0; 32];
1494        Self::with_seed(shard_ident, cc_seqno, &seed)
1495    }
1496
1497    /// Creates a new generator with the specified seed.
1498    pub fn with_seed(shard_ident: ShardIdent, cc_seqno: u32, seed: &[u8; 32]) -> Self {
1499        let mut context = [0u8; 48];
1500        context[..32].copy_from_slice(seed);
1501        context[32..40].copy_from_slice(&shard_ident.prefix().to_be_bytes());
1502        context[40..44].copy_from_slice(&shard_ident.workchain().to_be_bytes());
1503        context[44..48].copy_from_slice(&cc_seqno.to_be_bytes());
1504
1505        let mut res = ValidatorSetPRNG {
1506            context,
1507            bag: [0; 8],
1508        };
1509        res.bag[0] = 8;
1510        res
1511    }
1512
1513    /// Generates next `u64`.
1514    pub fn next_u64(&mut self) -> u64 {
1515        if self.cursor() < 7 {
1516            let next = self.bag[1 + self.cursor() as usize];
1517            self.bag[0] += 1;
1518            next
1519        } else {
1520            self.reset()
1521        }
1522    }
1523
1524    /// Generates next `u64` multiplied by the specified range.
1525    pub fn next_ranged(&mut self, range: u64) -> u64 {
1526        let val = self.next_u64();
1527        ((range as u128 * val as u128) >> 64) as u64
1528    }
1529
1530    fn reset(&mut self) -> u64 {
1531        use sha2::digest::Digest;
1532
1533        let hash: [u8; 64] = sha2::Sha512::digest(self.context).into();
1534
1535        for ctx in self.context[..32].iter_mut().rev() {
1536            *ctx = ctx.wrapping_add(1);
1537            if *ctx != 0 {
1538                break;
1539            }
1540        }
1541
1542        // SAFETY: `std::mem::size_of::<[u64; 8]>() == 64` and src alignment is 1
1543        unsafe {
1544            std::ptr::copy_nonoverlapping(hash.as_ptr(), self.bag.as_mut_ptr() as *mut u8, 64);
1545        }
1546
1547        // Swap bytes for little endian
1548        #[cfg(target_endian = "little")]
1549        self.bag
1550            .iter_mut()
1551            .for_each(|item| *item = item.swap_bytes());
1552
1553        // Reset and use bag[0] as counter
1554        std::mem::take(&mut self.bag[0])
1555    }
1556
1557    #[inline]
1558    const fn cursor(&self) -> u64 {
1559        self.bag[0]
1560    }
1561}
1562
1563/// size_limits_config_v2#02
1564///     max_msg_bits:uint32
1565///     max_msg_cells:uint32
1566///     max_library_cells:uint32
1567///     max_vm_data_depth:uint16
1568///     max_ext_msg_size:uint32
1569///     max_ext_msg_depth:uint16
1570///     max_acc_state_cells:uint32
1571///     max_acc_state_bits:uint32
1572///     max_acc_public_libraries:uint32
1573///     defer_out_queue_size_limit:uint32 = SizeLimitsConfig;
1574#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
1575#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1576#[tlb(tag = "#02")]
1577pub struct SizeLimitsConfig {
1578    /// Max number of bits in message.
1579    pub max_msg_bits: u32,
1580    /// Max number of cells in message.
1581    pub max_msg_cells: u32,
1582    /// Max number of cells in library.
1583    pub max_library_cells: u32,
1584    /// Max cell tree depth for VM data.
1585    pub max_vm_data_depth: u16,
1586    /// Max number of bytes of a BOC-encoded external message.
1587    pub max_ext_msg_size: u32,
1588    /// Max cell tree depth of an external message.
1589    pub max_ext_msg_depth: u16,
1590    /// Max number of cells per account.
1591    pub max_acc_state_cells: u32,
1592    /// Max number of bits per account.
1593    pub max_acc_state_bits: u32,
1594    /// Max number of public libraries per account.
1595    pub max_acc_public_libraries: u32,
1596    /// Size limit of a deferred out messages queue.
1597    pub defer_out_queue_size_limit: u32,
1598}
1599
1600const fn shift_ceil_price(value: u128) -> u128 {
1601    let r = value & 0xffff != 0;
1602    (value >> 16) + r as u128
1603}
1604
1605#[cfg(test)]
1606mod tests {
1607    use super::*;
1608
1609    #[test]
1610    fn validator_set_prng() {
1611        fn make_indices(cc_seqno: u32) -> Vec<usize> {
1612            let mut prng = ValidatorSetPRNG::new(ShardIdent::BASECHAIN, cc_seqno);
1613
1614            let count = 10;
1615            let mut indices = vec![0; count];
1616            for i in 0..count {
1617                let j = prng.next_ranged(i as u64 + 1) as usize;
1618                debug_assert!(j <= i);
1619                indices[i] = indices[j];
1620                indices[j] = i;
1621            }
1622
1623            indices
1624        }
1625
1626        let vs10_first = make_indices(10);
1627        let vs10_second = make_indices(10);
1628        assert_eq!(vs10_first, vs10_second);
1629
1630        let vs11_first = make_indices(11);
1631        let vs11_second = make_indices(11);
1632        assert_eq!(vs11_first, vs11_second);
1633        assert_ne!(vs10_first, vs11_second);
1634    }
1635}