Skip to main content

tycho_types/models/config/
params.rs

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