everscale_types/models/config/
params.rs

1use std::num::{NonZeroU16, NonZeroU32};
2
3use everscale_crypto::ed25519;
4
5use crate::cell::*;
6use crate::dict::Dict;
7use crate::error::Error;
8use crate::num::{Tokens, Uint12};
9
10use crate::models::block::ShardIdent;
11use crate::models::{Lazy, Signature};
12
13/// Config voting setup params.
14#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16#[tlb(tag = "#91")]
17pub struct ConfigVotingSetup {
18    /// Proposal configuration for non-critical params.
19    pub normal_params: Lazy<ConfigProposalSetup>,
20    /// Proposal configuration for critical params.
21    pub critical_params: Lazy<ConfigProposalSetup>,
22}
23
24/// Config proposal setup params.
25#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27#[tlb(tag = "#36")]
28pub struct ConfigProposalSetup {
29    /// The minimal number of voting rounds for the proposal.
30    pub min_total_rounds: u8,
31    /// The maximum number of voting rounds for the proposal.
32    pub max_total_rounds: u8,
33    /// The minimum number of winned voting rounds.
34    pub min_wins: u8,
35    /// The maximum number of lost voting rounds.
36    pub max_losses: u8,
37    /// The minimal proposal lifetime duration in seconds.
38    pub min_store_sec: u32,
39    /// The maximum proposal lifetime duration in seconds.
40    pub max_store_sec: u32,
41    /// Bit price for storage price computation.
42    pub bit_price: u32,
43    /// Cell price for storage price computation.
44    pub cell_price: u32,
45}
46
47/// Workchain description.
48#[derive(Debug, Clone, Eq, PartialEq)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
50pub struct WorkchainDescription {
51    /// Unix timestamp from which blocks can be produced.
52    pub enabled_since: u32,
53    /// Unused stub.
54    pub actual_min_split: u8,
55    /// The minimal shards split depths.
56    pub min_split: u8,
57    /// The maximum shards split depths.
58    pub max_split: u8,
59    /// Whether the workchain is enabled.
60    pub active: bool,
61    /// Whether the workchain accepts messages.
62    pub accept_msgs: bool,
63    /// A hash of the zerostate root cell.
64    pub zerostate_root_hash: HashBytes,
65    /// A hash of the zerostate file.
66    pub zerostate_file_hash: HashBytes,
67    /// Workchain version.
68    pub version: u32,
69    /// Workchain format description.
70    pub format: WorkchainFormat,
71}
72
73impl WorkchainDescription {
74    const TAG: u8 = 0xa6;
75
76    /// Returns `true` if the workchain description is valid.
77    pub fn is_valid(&self) -> bool {
78        self.min_split <= self.max_split
79            && self.max_split <= ShardIdent::MAX_SPLIT_DEPTH
80            && self.format.is_valid()
81    }
82}
83
84impl Store for WorkchainDescription {
85    fn store_into(
86        &self,
87        builder: &mut CellBuilder,
88        context: &mut dyn CellContext,
89    ) -> Result<(), Error> {
90        if !self.is_valid() {
91            return Err(Error::InvalidData);
92        }
93
94        let flags: u16 = ((self.format.is_basic() as u16) << 15)
95            | ((self.active as u16) << 14)
96            | ((self.accept_msgs as u16) << 13);
97
98        ok!(builder.store_u8(Self::TAG));
99        ok!(builder.store_u32(self.enabled_since));
100        ok!(builder.store_u8(self.actual_min_split));
101        ok!(builder.store_u8(self.min_split));
102        ok!(builder.store_u8(self.max_split));
103        ok!(builder.store_u16(flags));
104        ok!(builder.store_u256(&self.zerostate_root_hash));
105        ok!(builder.store_u256(&self.zerostate_file_hash));
106        ok!(builder.store_u32(self.version));
107        self.format.store_into(builder, context)
108    }
109}
110
111impl<'a> Load<'a> for WorkchainDescription {
112    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
113        match slice.load_u8() {
114            Ok(Self::TAG) => {}
115            Ok(_) => return Err(Error::InvalidTag),
116            Err(e) => return Err(e),
117        }
118
119        let enabled_since = ok!(slice.load_u32());
120        let actual_min_split = ok!(slice.load_u8());
121        let min_split = ok!(slice.load_u8());
122        let max_split = ok!(slice.load_u8());
123        let flags = ok!(slice.load_u16());
124        if flags << 3 != 0 {
125            return Err(Error::InvalidData);
126        }
127
128        let result = Self {
129            enabled_since,
130            actual_min_split,
131            min_split,
132            max_split,
133            active: flags & 0b0100_0000_0000_0000 != 0,
134            accept_msgs: flags & 0b0010_0000_0000_0000 != 0,
135            zerostate_root_hash: ok!(slice.load_u256()),
136            zerostate_file_hash: ok!(slice.load_u256()),
137            version: ok!(slice.load_u32()),
138            format: ok!(WorkchainFormat::load_from(slice)),
139        };
140
141        let basic = flags & 0b1000_0000_0000_0000 != 0;
142        if basic != result.format.is_basic() {
143            return Err(Error::InvalidData);
144        }
145
146        Ok(result)
147    }
148}
149
150/// Workchain format description.
151#[derive(Debug, Copy, Clone, Eq, PartialEq)]
152#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
153#[cfg_attr(feature = "serde", serde(tag = "ty"))]
154pub enum WorkchainFormat {
155    /// Basic workchain format.
156    Basic(WorkchainFormatBasic),
157    /// Extended workchain format.
158    Extended(WorkchainFormatExtended),
159}
160
161impl WorkchainFormat {
162    /// Returns `true` if the workchain format is valid.
163    pub fn is_valid(&self) -> bool {
164        match self {
165            Self::Basic(_) => true,
166            Self::Extended(format) => format.is_valid(),
167        }
168    }
169
170    /// Returns `true` if the workchain format is [`Basic`].
171    ///
172    /// [`Basic`]: WorkchainFormatBasic
173    pub fn is_basic(&self) -> bool {
174        matches!(self, Self::Basic(_))
175    }
176}
177
178impl Store for WorkchainFormat {
179    fn store_into(
180        &self,
181        builder: &mut CellBuilder,
182        context: &mut dyn CellContext,
183    ) -> Result<(), Error> {
184        match self {
185            Self::Basic(value) => {
186                ok!(builder.store_small_uint(0x1, 4));
187                value.store_into(builder, context)
188            }
189            Self::Extended(value) => {
190                ok!(builder.store_small_uint(0x0, 4));
191                value.store_into(builder, context)
192            }
193        }
194    }
195}
196
197impl<'a> Load<'a> for WorkchainFormat {
198    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
199        Ok(match ok!(slice.load_small_uint(4)) {
200            0x1 => Self::Basic(ok!(WorkchainFormatBasic::load_from(slice))),
201            0x0 => Self::Extended(ok!(WorkchainFormatExtended::load_from(slice))),
202            _ => return Err(Error::InvalidTag),
203        })
204    }
205}
206
207/// Basic workchain format description.
208#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
209#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
210pub struct WorkchainFormatBasic {
211    /// VM version.
212    pub vm_version: i32,
213    /// VM mode.
214    pub vm_mode: u64,
215}
216
217/// Extended workchain format description.
218#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
219#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
220#[tlb(validate_with = "Self::is_valid")]
221pub struct WorkchainFormatExtended {
222    /// The minimal address length in bits.
223    pub min_addr_len: Uint12,
224    /// The maximal address length in bits.
225    pub max_addr_len: Uint12,
226    /// Address length step in bits.
227    pub addr_len_step: Uint12,
228    /// Extended workchain type id.
229    pub workchain_type_id: NonZeroU32,
230}
231
232impl WorkchainFormatExtended {
233    /// Returns `true` if the workchain format is valid.
234    pub fn is_valid(&self) -> bool {
235        self.min_addr_len >= Uint12::new(64)
236            && self.min_addr_len <= self.max_addr_len
237            && self.max_addr_len <= Uint12::new(1023)
238            && self.addr_len_step <= Uint12::new(1023)
239    }
240}
241
242/// Block creation reward.
243#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
244#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
245#[tlb(tag = "#6b")]
246pub struct BlockCreationRewards {
247    /// Reward for each created masterchain block.
248    pub masterchain_block_fee: Tokens,
249    /// Base reward for basechain blocks.
250    pub basechain_block_fee: Tokens,
251}
252
253/// Validators election timings.
254#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
255#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
256pub struct ElectionTimings {
257    /// Validation round length in seconds.
258    pub validators_elected_for: u32,
259    /// Duration in seconds until the end of the validation round when the election starts.
260    pub elections_start_before: u32,
261    /// Duration in seconds until the end of the validation round when the election ends.
262    pub elections_end_before: u32,
263    /// How long validator stake will be frozen after the validation round end.
264    pub stake_held_for: u32,
265}
266
267/// Range of number of validators.
268#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
269#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
270pub struct ValidatorCountParams {
271    /// The maximum number of validators.
272    pub max_validators: u16,
273    /// The maximum number of masterchain validators.
274    pub max_main_validators: u16,
275    /// The minimum number of validators.
276    pub min_validators: u16,
277}
278
279/// Validator stake range and factor.
280#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
281#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
282pub struct ValidatorStakeParams {
283    /// The minimum validator stake.
284    pub min_stake: Tokens,
285    /// The maximum validator stake.
286    pub max_stake: Tokens,
287    /// The minimum required total stake for elections to be successful.
288    pub min_total_stake: Tokens,
289    /// Stake constraint (shifted by 16 bits).
290    pub max_stake_factor: u32,
291}
292
293/// Storage prices for some interval.
294#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
295#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
296#[tlb(tag = "#cc")]
297pub struct StoragePrices {
298    /// Unix timestamp since which this prices are used.
299    pub utime_since: u32,
300    /// Bit price in base workchain.
301    pub bit_price_ps: u64,
302    /// Cell price in base workchain.
303    pub cell_price_ps: u64,
304    /// Bit price in masterchain.
305    pub mc_bit_price_ps: u64,
306    /// Cell price in masterchain.
307    pub mc_cell_price_ps: u64,
308}
309
310/// Gas limits and prices.
311#[derive(Default, Debug, Clone, Eq, PartialEq)]
312#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
313pub struct GasLimitsPrices {
314    /// The price of gas unit.
315    pub gas_price: u64,
316    /// The maximum amount of gas available for a compute phase of an ordinary transaction.
317    pub gas_limit: u64,
318    /// The maximum amount of gas available for a compute phase of a special transaction.
319    pub special_gas_limit: u64,
320    /// The maximum amount of gas available before `ACCEPT`.
321    pub gas_credit: u64,
322    /// The maximum amount of gas units per block.
323    pub block_gas_limit: u64,
324    /// Amount of debt (in tokens) after which the account will be frozen.
325    pub freeze_due_limit: u64,
326    /// Amount of debt (in tokens) after which the contract will be deleted.
327    pub delete_due_limit: u64,
328    /// Size of the first portion of gas with different price.
329    pub flat_gas_limit: u64,
330    /// The gas price for the first portion determinted by [`flat_gas_limit`].
331    ///
332    /// [`flat_gas_limit`]: GasLimitsPrices::flat_gas_limit
333    pub flat_gas_price: u64,
334}
335
336impl GasLimitsPrices {
337    const TAG_BASE: u8 = 0xdd;
338    const TAG_EXT: u8 = 0xde;
339    const TAG_FLAT_PFX: u8 = 0xd1;
340}
341
342impl Store for GasLimitsPrices {
343    fn store_into(&self, builder: &mut CellBuilder, _: &mut dyn CellContext) -> Result<(), Error> {
344        ok!(builder.store_u8(Self::TAG_FLAT_PFX));
345        ok!(builder.store_u64(self.flat_gas_limit));
346        ok!(builder.store_u64(self.flat_gas_price));
347        ok!(builder.store_u8(Self::TAG_EXT));
348        ok!(builder.store_u64(self.gas_price));
349        ok!(builder.store_u64(self.gas_limit));
350        ok!(builder.store_u64(self.special_gas_limit));
351        ok!(builder.store_u64(self.gas_credit));
352        ok!(builder.store_u64(self.block_gas_limit));
353        ok!(builder.store_u64(self.freeze_due_limit));
354        builder.store_u64(self.delete_due_limit)
355    }
356}
357
358impl<'a> Load<'a> for GasLimitsPrices {
359    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
360        let mut result = Self::default();
361        loop {
362            match slice.load_u8() {
363                Ok(Self::TAG_FLAT_PFX) => {
364                    result.flat_gas_limit = ok!(slice.load_u64());
365                    result.flat_gas_price = ok!(slice.load_u64());
366                }
367                Ok(Self::TAG_EXT) => {
368                    result.gas_price = ok!(slice.load_u64());
369                    result.gas_limit = ok!(slice.load_u64());
370                    result.special_gas_limit = ok!(slice.load_u64());
371                    result.gas_credit = ok!(slice.load_u64());
372                    result.block_gas_limit = ok!(slice.load_u64());
373                    result.freeze_due_limit = ok!(slice.load_u64());
374                    result.delete_due_limit = ok!(slice.load_u64());
375                    return Ok(result);
376                }
377                Ok(Self::TAG_BASE) => {
378                    result.gas_price = ok!(slice.load_u64());
379                    result.gas_limit = ok!(slice.load_u64());
380                    result.gas_credit = ok!(slice.load_u64());
381                    result.block_gas_limit = ok!(slice.load_u64());
382                    result.freeze_due_limit = ok!(slice.load_u64());
383                    result.delete_due_limit = ok!(slice.load_u64());
384                    return Ok(result);
385                }
386                Ok(_) => return Err(Error::InvalidTag),
387                Err(e) => return Err(e),
388            }
389        }
390    }
391}
392
393/// Block limits parameter.
394#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
395#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
396#[tlb(tag = "#c3", validate_with = "Self::is_valid")]
397pub struct BlockParamLimits {
398    /// Value below which the parameter is considered underloaded.
399    pub underload: u32,
400    /// Soft limit.
401    pub soft_limit: u32,
402    /// Hard limit.
403    pub hard_limit: u32,
404}
405
406impl BlockParamLimits {
407    /// Returns `true` if parameter limits are valid.
408    pub fn is_valid(&self) -> bool {
409        self.underload <= self.soft_limit && self.soft_limit <= self.hard_limit
410    }
411}
412
413/// Block limits.
414#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
415#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
416#[tlb(tag = "#5d")]
417pub struct BlockLimits {
418    /// Block size limits in bytes.
419    pub bytes: BlockParamLimits,
420    /// Gas limits.
421    pub gas: BlockParamLimits,
422    /// Logical time delta limits.
423    pub lt_delta: BlockParamLimits,
424}
425
426/// Message forwarding prices.
427#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
428#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
429#[tlb(tag = "#ea")]
430pub struct MsgForwardPrices {
431    /// Fixed price in addition to the dynamic part.
432    pub lump_price: u64,
433    /// The price of bits in the message (bits in the root cell are not included).
434    pub bit_price: u64,
435    /// The price of cells in the message.
436    pub cell_price: u64,
437    /// TODO: add docs
438    pub ihr_price_factor: u32,
439    /// TODO: add docs
440    pub first_frac: u16,
441    /// TODO: add docs
442    pub next_frac: u16,
443}
444
445/// Catchain configuration params.
446#[cfg(not(feature = "tycho"))]
447#[derive(Debug, Copy, Clone, Eq, PartialEq)]
448#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
449pub struct CatchainConfig {
450    /// Exclude masterchain validators from a validators list for a base workchain.
451    pub isolate_mc_validators: bool,
452    /// Change the order of validators in the masterchain validators list.
453    pub shuffle_mc_validators: bool,
454    /// Masterchain catchain session lifetime in seconds.
455    pub mc_catchain_lifetime: u32,
456    /// Catchain session lifetime for shards in seconds.
457    pub shard_catchain_lifetime: u32,
458    /// Period in seconds for which the subset of validators is selected for each shard.
459    pub shard_validators_lifetime: u32,
460    /// The number of validators per shard.
461    pub shard_validators_num: u32,
462}
463
464#[cfg(not(feature = "tycho"))]
465impl CatchainConfig {
466    const TAG_V1: u8 = 0xc1;
467    const TAG_V2: u8 = 0xc2;
468}
469
470#[cfg(not(feature = "tycho"))]
471impl Store for CatchainConfig {
472    fn store_into(&self, builder: &mut CellBuilder, _: &mut dyn CellContext) -> Result<(), Error> {
473        let flags = ((self.isolate_mc_validators as u8) << 1) | (self.shuffle_mc_validators as u8);
474        ok!(builder.store_u8(Self::TAG_V2));
475        ok!(builder.store_u8(flags));
476        ok!(builder.store_u32(self.mc_catchain_lifetime));
477        ok!(builder.store_u32(self.shard_catchain_lifetime));
478        ok!(builder.store_u32(self.shard_validators_lifetime));
479        builder.store_u32(self.shard_validators_num)
480    }
481}
482
483#[cfg(not(feature = "tycho"))]
484impl<'a> Load<'a> for CatchainConfig {
485    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
486        let flags = match slice.load_u8() {
487            Ok(Self::TAG_V1) => 0,
488            Ok(Self::TAG_V2) => ok!(slice.load_u8()),
489            Ok(_) => return Err(Error::InvalidTag),
490            Err(e) => return Err(e),
491        };
492        if flags >> 2 != 0 {
493            return Err(Error::InvalidData);
494        }
495        Ok(Self {
496            isolate_mc_validators: flags & 0b10 != 0,
497            shuffle_mc_validators: flags & 0b01 != 0,
498            mc_catchain_lifetime: ok!(slice.load_u32()),
499            shard_catchain_lifetime: ok!(slice.load_u32()),
500            shard_validators_lifetime: ok!(slice.load_u32()),
501            shard_validators_num: ok!(slice.load_u32()),
502        })
503    }
504}
505
506/// Collation configuration params.
507///
508/// ```text
509/// collation_config_tycho#a6
510///     shuffle_mc_validators:Bool
511///     mc_block_min_interval_ms:uint32
512///     max_uncommitted_chain_length:uint8
513///     wu_used_to_import_next_anchor:uint64
514///     msgs_exec_params:MsgsExecutionParams
515///     work_units_params:WorkUnitsParams
516///     = CollationConfig;
517///
518/// collation_config_tycho#a7
519///     shuffle_mc_validators:Bool
520///     mc_block_min_interval_ms:uint32
521///     empty_sc_block_interval_ms:uint32
522///     max_uncommitted_chain_length:uint8
523///     wu_used_to_import_next_anchor:uint64
524///     msgs_exec_params:MsgsExecutionParams
525///     work_units_params:WorkUnitsParams
526///     = CollationConfig;
527/// ```
528#[cfg(feature = "tycho")]
529#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
530#[tlb(tag = ["#a6", "#a7"])]
531#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
532pub struct CollationConfig {
533    /// Change the order of validators in the masterchain validators list.
534    pub shuffle_mc_validators: bool,
535
536    /// Minimum interval between master blocks.
537    pub mc_block_min_interval_ms: u32,
538
539    /// Time to wait before collating an empty shard block.
540    #[tlb(since_tag = 1)]
541    pub empty_sc_block_interval_ms: u32,
542
543    /// Maximum length on shard blocks chain after previous master block.
544    pub max_uncommitted_chain_length: u8,
545    /// Force import next anchor when wu used exceed limit.
546    pub wu_used_to_import_next_anchor: u64,
547
548    /// Messages execution params.
549    pub msgs_exec_params: MsgsExecutionParams,
550
551    /// Params to calculate the collation work in wu.
552    pub work_units_params: WorkUnitsParams,
553}
554
555/// Messages execution params.
556///
557/// ```text
558/// msgs_execution_params_tycho#00
559///     buffer_limit:uint32
560///     group_limit:uint16
561///     group_vert_size:uint16
562///     = MsgsExecutionParams;
563/// ```
564#[cfg(feature = "tycho")]
565#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
566#[tlb(tag = "#00")]
567#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
568pub struct MsgsExecutionParams {
569    /// Maximum limit of messages buffer.
570    pub buffer_limit: u32,
571    /// The horizontal limit of one message group.
572    /// Shows how many unique destination accounts can be.
573    pub group_limit: u16,
574    /// The vertical limit of one message group.
575    /// Shows how many messages can be per one account in the group.
576    pub group_vert_size: u16,
577}
578
579/// Params to calculate the collation work in wu.
580///
581/// ```text
582/// work_units_params_tycho#00
583///     prepare:WorkUnitParamsPrepare
584///     execute:WorkUnitParamsExecute
585///     finalize:WorkUnitParamsFinalize
586///     = WorkUnitsParams;
587/// ```
588#[cfg(feature = "tycho")]
589#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
590#[tlb(tag = "#00")]
591#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
592pub struct WorkUnitsParams {
593    /// Params to calculate messages groups prepare work in wu.
594    pub prepare: WorkUnitsParamsPrepare,
595    /// Params to calculate messages execution work in wu.
596    pub execute: WorkUnitsParamsExecute,
597    /// Params to calculate block finalization work in wu.
598    pub finalize: WorkUnitsParamsFinalize,
599}
600
601/// Params to calculate messages groups prepare work in wu.
602///
603/// ```text
604/// work_units_params_prepare_tycho#00
605///     fixed:uint32
606///     read_ext_msgs:uint16
607///     read_int_msgs:uint16
608///     read_new_msgs:uint32
609///     = WorkUnitsParamsPrepare;
610/// ```
611#[cfg(feature = "tycho")]
612#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
613#[tlb(tag = "#00")]
614#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
615pub struct WorkUnitsParamsPrepare {
616    /// TODO: Add docs.
617    pub fixed_part: u32,
618    /// TODO: Add docs.
619    pub read_ext_msgs: u16,
620    /// TODO: Add docs.
621    pub read_int_msgs: u16,
622    /// TODO: Add docs.
623    pub read_new_msgs: u32,
624}
625
626/// Params to calculate messages execution work in wu.
627///
628/// ```text
629/// work_units_params_execute_tycho#00
630///     prepare:uint32
631///     execute:uint16
632///     execute_err:uint16
633///     execute_delimiter:uint32
634///     serialize_enqueue:uint16
635///     serialize_dequeue:uint16
636///     insert_new_msgs_to_iterator:uint16
637///     subgroup_size:uint16
638///     = WorkUnitsParamsExecute;
639/// ```
640#[cfg(feature = "tycho")]
641#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
642#[tlb(tag = "#00")]
643#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
644pub struct WorkUnitsParamsExecute {
645    /// TODO: Add docs.
646    pub prepare: u32,
647    /// TODO: Add docs.
648    pub execute: u16,
649    /// TODO: Add docs.
650    pub execute_err: u16,
651    /// TODO: Add docs.
652    pub execute_delimiter: u32,
653    /// TODO: Add docs.
654    pub serialize_enqueue: u16,
655    /// TODO: Add docs.
656    pub serialize_dequeue: u16,
657    /// TODO: Add docs.
658    pub insert_new_msgs_to_iterator: u16,
659    /// TODO: Add docs.
660    pub subgroup_size: u16,
661}
662
663/// Params to calculate block finalization work in wu.
664///
665/// ```text
666/// work_units_params_finalize_tycho#00
667///     build_transactions:uint16
668///     build_accounts:uint16
669///     build_in_msg:uint16
670///     build_out_msg:uint16
671///     serialize_min:uint32
672///     serialize_accounts:uint16
673///     serialize_msg:uint16
674///     state_update_min:uint32
675///     state_update_accounts:uint32
676///     state_update_msg:uint16
677///     = WorkUnitsParamsFinalize;
678/// ```
679#[cfg(feature = "tycho")]
680#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
681#[tlb(tag = "#00")]
682#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
683pub struct WorkUnitsParamsFinalize {
684    /// TODO: Add docs.
685    pub build_transactions: u16,
686    /// TODO: Add docs.
687    pub build_accounts: u16,
688    /// TODO: Add docs.
689    pub build_in_msg: u16,
690    /// TODO: Add docs.
691    pub build_out_msg: u16,
692    /// TODO: Add docs.
693    pub serialize_min: u32,
694    /// TODO: Add docs.
695    pub serialize_accounts: u16,
696    /// TODO: Add docs.
697    pub serialize_msg: u16,
698    /// TODO: Add docs.
699    pub state_update_min: u32,
700    /// TODO: Add docs.
701    pub state_update_accounts: u32,
702    /// TODO: Add docs.
703    pub state_update_msg: u16,
704}
705
706/// DAG Consensus configuration params
707///
708/// ```text
709/// consensus_config_tycho#d8
710///     clock_skew_millis:uint16
711///     payload_batch_bytes:uint32
712///     commit_history_rounds:uint8
713///     deduplicate_rounds:uint16
714///     max_consensus_lag_rounds:uint16
715///     payload_buffer_bytes:uint32
716///     broadcast_retry_millis:uint8
717///     download_retry_millis:uint8
718///     download_peers:uint8
719///     download_tasks:uint16
720///     sync_support_rounds:uint16
721///     = ConsensusConfig;
722/// ```
723#[cfg(feature = "tycho")]
724#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
725#[tlb(tag = "#d8")]
726#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
727pub struct ConsensusConfig {
728    /// TODO: Add docs.
729    ///
730    /// **NOTE: Affects overlay id.**
731    pub clock_skew_millis: u16,
732
733    /// TODO: Add docs.
734    ///
735    /// **NOTE: Affects overlay id.**
736    pub payload_batch_bytes: u32,
737
738    /// TODO: Add docs.
739    ///
740    /// **NOTE: Affects overlay id.**
741    pub commit_history_rounds: u16,
742
743    /// TODO: Add docs.
744    ///
745    /// **NOTE: Affects overlay id.**
746    pub deduplicate_rounds: u16,
747
748    /// TODO: Add docs.
749    ///
750    /// **NOTE: Affects overlay id.**
751    pub max_consensus_lag_rounds: u16,
752
753    /// TODO: Add docs.
754    pub payload_buffer_bytes: u32,
755
756    /// TODO: Add docs.
757    pub broadcast_retry_millis: u16,
758
759    /// TODO: Add docs.
760    pub download_retry_millis: u16,
761
762    /// TODO: Add docs.
763    pub download_peers: u8,
764
765    /// TODO: Add docs.
766    pub download_tasks: u16,
767
768    /// TODO: Add docs.
769    pub sync_support_rounds: u16,
770}
771
772/// Consensus configuration params.
773#[cfg(not(feature = "tycho"))]
774#[derive(Debug, Clone, Eq, PartialEq)]
775#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
776pub struct ConsensusConfig {
777    /// Allow new catchain ids.
778    pub new_catchain_ids: bool,
779    /// Number of block candidates per round.
780    pub round_candidates: NonZeroU32,
781    /// Delay in seconds before proposing a new candidate.
782    pub next_candidate_delay_ms: u32,
783    /// Catchain processing timeout in seconds.
784    pub consensus_timeout_ms: u32,
785    /// Maximum number of attempts per round.
786    pub fast_attempts: u32,
787    /// Duration of a round attempt in seconds.
788    pub attempt_duration: u32,
789    /// The maximum number of dependencies to merge.
790    pub catchain_max_deps: u32,
791    /// The maximum block size in bytes.
792    pub max_block_bytes: u32,
793    /// THe maximum size of a collated data in bytes.
794    pub max_collated_bytes: u32,
795}
796
797#[cfg(not(feature = "tycho"))]
798impl ConsensusConfig {
799    const TAG_V1: u8 = 0xd6;
800    const TAG_V2: u8 = 0xd7;
801}
802
803#[cfg(not(feature = "tycho"))]
804impl Store for ConsensusConfig {
805    fn store_into(&self, builder: &mut CellBuilder, _: &mut dyn CellContext) -> Result<(), Error> {
806        let flags = self.new_catchain_ids as u8;
807
808        ok!(builder.store_u8(Self::TAG_V2));
809        ok!(builder.store_u8(flags));
810        ok!(builder.store_u8(self.round_candidates.get() as u8));
811        ok!(builder.store_u32(self.next_candidate_delay_ms));
812        ok!(builder.store_u32(self.consensus_timeout_ms));
813        ok!(builder.store_u32(self.fast_attempts));
814        ok!(builder.store_u32(self.attempt_duration));
815        ok!(builder.store_u32(self.catchain_max_deps));
816        ok!(builder.store_u32(self.max_block_bytes));
817        builder.store_u32(self.max_collated_bytes)
818    }
819}
820
821#[cfg(not(feature = "tycho"))]
822impl<'a> Load<'a> for ConsensusConfig {
823    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
824        use std::num::NonZeroU8;
825
826        let (flags, round_candidates) = match slice.load_u8() {
827            Ok(Self::TAG_V1) => (0, ok!(NonZeroU32::load_from(slice))),
828            Ok(Self::TAG_V2) => {
829                let flags = ok!(slice.load_u8());
830                if flags >> 1 != 0 {
831                    return Err(Error::InvalidData);
832                }
833                (flags, ok!(NonZeroU8::load_from(slice)).into())
834            }
835            Ok(_) => return Err(Error::InvalidTag),
836            Err(e) => return Err(e),
837        };
838        Ok(Self {
839            new_catchain_ids: flags & 0b1 != 0,
840            round_candidates,
841            next_candidate_delay_ms: ok!(slice.load_u32()),
842            consensus_timeout_ms: ok!(slice.load_u32()),
843            fast_attempts: ok!(slice.load_u32()),
844            attempt_duration: ok!(slice.load_u32()),
845            catchain_max_deps: ok!(slice.load_u32()),
846            max_block_bytes: ok!(slice.load_u32()),
847            max_collated_bytes: ok!(slice.load_u32()),
848        })
849    }
850}
851
852/// Validator set.
853#[derive(Debug, Clone, Eq, PartialEq)]
854#[cfg_attr(feature = "serde", derive(serde::Serialize))]
855pub struct ValidatorSet {
856    /// Unix timestamp from which this set will be active.
857    pub utime_since: u32,
858    /// Unix timestamp until which this set will be active.
859    pub utime_until: u32,
860    /// The number of masterchain validators.
861    pub main: NonZeroU16,
862    /// Total validators weight.
863    pub total_weight: u64,
864    /// Validators.
865    pub list: Vec<ValidatorDescription>,
866}
867
868impl ValidatorSet {
869    const TAG_V1: u8 = 0x11;
870    const TAG_V2: u8 = 0x12;
871
872    /// Computes a validator subset using a zero seed.
873    #[cfg(not(feature = "tycho"))]
874    pub fn compute_subset(
875        &self,
876        shard_ident: ShardIdent,
877        cc_config: &CatchainConfig,
878        cc_seqno: u32,
879    ) -> Option<(Vec<ValidatorDescription>, u32)> {
880        if shard_ident.is_masterchain() {
881            return self.compute_mc_subset(cc_seqno, cc_config.shuffle_mc_validators);
882        }
883
884        let total = self.list.len();
885        let main = self.main.get() as usize;
886
887        let mut prng = ValidatorSetPRNG::new(shard_ident, cc_seqno);
888
889        let vset = if cc_config.isolate_mc_validators {
890            if total <= main {
891                return None;
892            }
893
894            let mut list = self.list[main..].to_vec();
895
896            let mut total_weight = 0u64;
897            for descr in &mut list {
898                descr.prev_total_weight = total_weight;
899                total_weight += descr.weight;
900            }
901
902            std::borrow::Cow::Owned(Self {
903                utime_since: self.utime_since,
904                utime_until: self.utime_until,
905                main: self.main,
906                total_weight,
907                list,
908            })
909        } else {
910            std::borrow::Cow::Borrowed(self)
911        };
912
913        let count = std::cmp::min(vset.list.len(), cc_config.shard_validators_num as usize);
914
915        let mut nodes = Vec::with_capacity(count);
916        let mut holes = Vec::<(u64, u64)>::with_capacity(count);
917        let mut total_wt = vset.total_weight;
918
919        for _ in 0..count {
920            debug_assert!(total_wt > 0);
921
922            // Generate a pseudo-random number 0..total_wt-1
923            let mut p = prng.next_ranged(total_wt);
924
925            for (prev_total_weight, weight) in &holes {
926                if p < *prev_total_weight {
927                    break;
928                }
929                p += weight;
930            }
931
932            let entry = vset.at_weight(p);
933
934            nodes.push(ValidatorDescription {
935                public_key: entry.public_key,
936                weight: 1,
937                adnl_addr: entry.adnl_addr,
938                mc_seqno_since: 0,
939                prev_total_weight: 0,
940            });
941            debug_assert!(total_wt >= entry.weight);
942            total_wt -= entry.weight;
943
944            let new_hole = (entry.prev_total_weight, entry.weight);
945            let i = holes.partition_point(|item| item <= &new_hole);
946            debug_assert!(i == 0 || holes[i - 1] < new_hole);
947
948            holes.insert(i, new_hole);
949        }
950
951        let hash_short = Self::compute_subset_hash_short(&nodes, cc_seqno);
952
953        Some((nodes, hash_short))
954    }
955
956    /// Computes a masterchain validator subset using a zero seed.
957    ///
958    /// NOTE: In most cases you should use the more generic [`ValidatorSet::compute_subset`].
959    pub fn compute_mc_subset(
960        &self,
961        cc_seqno: u32,
962        shuffle: bool,
963    ) -> Option<(Vec<ValidatorDescription>, u32)> {
964        let total = self.list.len();
965        let main = self.main.get() as usize;
966
967        let count = std::cmp::min(total, main);
968        let subset = if !shuffle {
969            self.list[0..count].to_vec()
970        } else {
971            let mut prng = ValidatorSetPRNG::new(ShardIdent::MASTERCHAIN, cc_seqno);
972
973            let mut indices = vec![0; count];
974            for i in 0..count {
975                let j = prng.next_ranged(i as u64 + 1) as usize; // number 0 .. i
976                debug_assert!(j <= i);
977                indices[i] = indices[j];
978                indices[j] = i;
979            }
980
981            let mut subset = Vec::with_capacity(count);
982            for index in indices.into_iter().take(count) {
983                subset.push(self.list[index].clone());
984            }
985            subset
986        };
987
988        let hash_short = Self::compute_subset_hash_short(&subset, cc_seqno);
989        Some((subset, hash_short))
990    }
991
992    /// Compoutes a validator subset short hash.
993    pub fn compute_subset_hash_short(subset: &[ValidatorDescription], cc_seqno: u32) -> u32 {
994        const HASH_SHORT_MAGIC: u32 = 0x901660ED;
995
996        let mut hash = crc32c::crc32c(&HASH_SHORT_MAGIC.to_le_bytes());
997        hash = crc32c::crc32c_append(hash, &cc_seqno.to_le_bytes());
998        hash = crc32c::crc32c_append(hash, &(subset.len() as u32).to_le_bytes());
999
1000        for node in subset {
1001            hash = crc32c::crc32c_append(hash, node.public_key.as_slice());
1002            hash = crc32c::crc32c_append(hash, &node.weight.to_le_bytes());
1003            hash = crc32c::crc32c_append(
1004                hash,
1005                node.adnl_addr
1006                    .as_ref()
1007                    .unwrap_or(HashBytes::wrap(&[0u8; 32]))
1008                    .as_ref(),
1009            );
1010        }
1011
1012        hash
1013    }
1014
1015    #[cfg(not(feature = "tycho"))]
1016    fn at_weight(&self, weight_pos: u64) -> &ValidatorDescription {
1017        debug_assert!(weight_pos < self.total_weight);
1018        debug_assert!(!self.list.is_empty());
1019        let i = self
1020            .list
1021            .partition_point(|item| item.prev_total_weight <= weight_pos);
1022        debug_assert!(i != 0);
1023        &self.list[i - 1]
1024    }
1025}
1026
1027impl Store for ValidatorSet {
1028    fn store_into(
1029        &self,
1030        builder: &mut CellBuilder,
1031        context: &mut dyn CellContext,
1032    ) -> Result<(), Error> {
1033        let Ok(total) = u16::try_from(self.list.len()) else {
1034            return Err(Error::IntOverflow);
1035        };
1036
1037        // TODO: optimize
1038        let mut validators = Dict::<u16, ValidatorDescription>::new();
1039        for (i, item) in self.list.iter().enumerate() {
1040            ok!(validators.set_ext(i as u16, item, context));
1041        }
1042
1043        ok!(builder.store_u8(Self::TAG_V2));
1044        ok!(builder.store_u32(self.utime_since));
1045        ok!(builder.store_u32(self.utime_until));
1046        ok!(builder.store_u16(total));
1047        ok!(builder.store_u16(self.main.get()));
1048        ok!(builder.store_u64(self.total_weight));
1049        validators.store_into(builder, context)
1050    }
1051}
1052
1053impl<'a> Load<'a> for ValidatorSet {
1054    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
1055        let with_total_weight = match slice.load_u8() {
1056            Ok(Self::TAG_V1) => false,
1057            Ok(Self::TAG_V2) => true,
1058            Ok(_) => return Err(Error::InvalidTag),
1059            Err(e) => return Err(e),
1060        };
1061
1062        let utime_since = ok!(slice.load_u32());
1063        let utime_until = ok!(slice.load_u32());
1064        let total = ok!(slice.load_u16()) as usize;
1065        let main = ok!(NonZeroU16::load_from(slice));
1066        if main.get() as usize > total {
1067            return Err(Error::InvalidData);
1068        }
1069
1070        let context = &mut Cell::empty_context();
1071
1072        let (mut total_weight, validators) = if with_total_weight {
1073            let total_weight = ok!(slice.load_u64());
1074            let dict = ok!(Dict::<u16, ValidatorDescription>::load_from(slice));
1075            (total_weight, dict)
1076        } else {
1077            let dict = ok!(Dict::<u16, ValidatorDescription>::load_from_root_ext(
1078                slice, context
1079            ));
1080            (0, dict)
1081        };
1082
1083        let mut computed_total_weight = 0u64;
1084        let mut list = Vec::with_capacity(std::cmp::min(total, 512));
1085        for (i, entry) in validators.iter().enumerate().take(total) {
1086            let mut descr = match entry {
1087                Ok((idx, descr)) if idx as usize == i => descr,
1088                Ok(_) => return Err(Error::InvalidData),
1089                Err(e) => return Err(e),
1090            };
1091
1092            descr.prev_total_weight = computed_total_weight;
1093            computed_total_weight = match computed_total_weight.checked_add(descr.weight) {
1094                Some(weight) => weight,
1095                None => return Err(Error::InvalidData),
1096            };
1097            list.push(descr);
1098        }
1099
1100        if list.is_empty() {
1101            return Err(Error::InvalidData);
1102        }
1103
1104        if with_total_weight {
1105            if total_weight != computed_total_weight {
1106                return Err(Error::InvalidData);
1107            }
1108        } else {
1109            total_weight = computed_total_weight;
1110        }
1111
1112        Ok(Self {
1113            utime_since,
1114            utime_until,
1115            main,
1116            total_weight,
1117            list,
1118        })
1119    }
1120}
1121
1122#[cfg(feature = "serde")]
1123impl<'de> serde::Deserialize<'de> for ValidatorSet {
1124    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1125        use serde::de::Error;
1126
1127        #[derive(serde::Deserialize)]
1128        struct ValidatorSetHelper {
1129            utime_since: u32,
1130            utime_until: u32,
1131            main: NonZeroU16,
1132            #[serde(default)]
1133            total_weight: u64,
1134            list: Vec<ValidatorDescription>,
1135        }
1136
1137        let parsed = ValidatorSetHelper::deserialize(deserializer)?;
1138        if parsed.list.is_empty() {
1139            return Err(Error::custom("empty validators list"));
1140        }
1141
1142        let mut result = Self {
1143            utime_since: parsed.utime_since,
1144            utime_until: parsed.utime_until,
1145            main: parsed.main,
1146            total_weight: 0,
1147            list: parsed.list,
1148        };
1149
1150        for descr in &mut result.list {
1151            descr.prev_total_weight = result.total_weight;
1152            let Some(new_total_weight) = result.total_weight.checked_add(descr.weight) else {
1153                return Err(Error::custom("total weight overflow"));
1154            };
1155            result.total_weight = new_total_weight;
1156        }
1157
1158        if parsed.total_weight > 0 && parsed.total_weight != result.total_weight {
1159            return Err(Error::custom("total weight mismatch"));
1160        }
1161
1162        Ok(result)
1163    }
1164}
1165
1166/// Validator description.
1167#[derive(Debug, Clone, Eq, PartialEq)]
1168#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1169pub struct ValidatorDescription {
1170    /// Validator public key.
1171    pub public_key: HashBytes, // TODO: replace with everscale_crypto::ed25519::PublicKey ?
1172    /// Validator weight in some units.
1173    pub weight: u64,
1174    /// Optional validator ADNL address.
1175    #[cfg_attr(feature = "serde", serde(default))]
1176    pub adnl_addr: Option<HashBytes>,
1177    /// Since which seqno this validator will be active.
1178    #[cfg_attr(feature = "serde", serde(default))]
1179    pub mc_seqno_since: u32,
1180
1181    /// Total weight of the previous validators in the list.
1182    /// The field is not serialized.
1183    #[cfg_attr(feature = "serde", serde(skip))]
1184    pub prev_total_weight: u64,
1185}
1186
1187impl ValidatorDescription {
1188    const TAG_BASIC: u8 = 0x53;
1189    const TAG_WITH_ADNL: u8 = 0x73;
1190    const TAG_WITH_MC_SEQNO: u8 = 0x93;
1191
1192    const PUBKEY_TAG: u32 = 0x8e81278a;
1193
1194    /// Verifies message signature and current public key.
1195    pub fn verify_signature(&self, data: &[u8], signature: &Signature) -> bool {
1196        if let Some(public_key) = ed25519::PublicKey::from_bytes(self.public_key.0) {
1197            public_key.verify_raw(data, signature.as_ref())
1198        } else {
1199            false
1200        }
1201    }
1202}
1203
1204impl Store for ValidatorDescription {
1205    fn store_into(&self, builder: &mut CellBuilder, _: &mut dyn CellContext) -> Result<(), Error> {
1206        let with_mc_seqno = self.mc_seqno_since != 0;
1207
1208        let tag = if with_mc_seqno {
1209            Self::TAG_WITH_MC_SEQNO
1210        } else if self.adnl_addr.is_some() {
1211            Self::TAG_WITH_ADNL
1212        } else {
1213            Self::TAG_BASIC
1214        };
1215
1216        ok!(builder.store_u8(tag));
1217        ok!(builder.store_u32(Self::PUBKEY_TAG));
1218        ok!(builder.store_u256(&self.public_key));
1219        ok!(builder.store_u64(self.weight));
1220
1221        let mut adnl = self.adnl_addr.as_ref();
1222        if with_mc_seqno {
1223            adnl = Some(HashBytes::wrap(&[0; 32]));
1224        }
1225
1226        if let Some(adnl) = adnl {
1227            ok!(builder.store_u256(adnl));
1228        }
1229
1230        if with_mc_seqno {
1231            builder.store_u32(self.mc_seqno_since)
1232        } else {
1233            Ok(())
1234        }
1235    }
1236}
1237
1238impl<'a> Load<'a> for ValidatorDescription {
1239    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
1240        let (with_adnl, with_mc_seqno) = match slice.load_u8() {
1241            Ok(Self::TAG_BASIC) => (false, false),
1242            Ok(Self::TAG_WITH_ADNL) => (true, false),
1243            Ok(Self::TAG_WITH_MC_SEQNO) => (true, true),
1244            Ok(_) => return Err(Error::InvalidTag),
1245            Err(e) => return Err(e),
1246        };
1247
1248        Ok(Self {
1249            public_key: {
1250                match slice.load_u32() {
1251                    Ok(Self::PUBKEY_TAG) => ok!(slice.load_u256()),
1252                    Ok(_) => return Err(Error::InvalidTag),
1253                    Err(e) => return Err(e),
1254                }
1255            },
1256            weight: ok!(slice.load_u64()),
1257            adnl_addr: if with_adnl {
1258                Some(ok!(slice.load_u256()))
1259            } else {
1260                None
1261            },
1262            mc_seqno_since: if with_mc_seqno {
1263                ok!(slice.load_u32())
1264            } else {
1265                0
1266            },
1267            prev_total_weight: 0,
1268        })
1269    }
1270}
1271
1272/// Random generator used for validator subset calculation.
1273pub struct ValidatorSetPRNG {
1274    context: [u8; 48],
1275    bag: [u64; 8],
1276}
1277
1278impl ValidatorSetPRNG {
1279    /// Creates a new generator with zero seed.
1280    pub fn new(shard_ident: ShardIdent, cc_seqno: u32) -> Self {
1281        let seed = [0; 32];
1282        Self::with_seed(shard_ident, cc_seqno, &seed)
1283    }
1284
1285    /// Creates a new generator with the specified seed.
1286    pub fn with_seed(shard_ident: ShardIdent, cc_seqno: u32, seed: &[u8; 32]) -> Self {
1287        let mut context = [0u8; 48];
1288        context[..32].copy_from_slice(seed);
1289        context[32..40].copy_from_slice(&shard_ident.prefix().to_be_bytes());
1290        context[40..44].copy_from_slice(&shard_ident.workchain().to_be_bytes());
1291        context[44..48].copy_from_slice(&cc_seqno.to_be_bytes());
1292
1293        let mut res = ValidatorSetPRNG {
1294            context,
1295            bag: [0; 8],
1296        };
1297        res.bag[0] = 8;
1298        res
1299    }
1300
1301    /// Generates next `u64`.
1302    pub fn next_u64(&mut self) -> u64 {
1303        if self.cursor() < 7 {
1304            let next = self.bag[1 + self.cursor() as usize];
1305            self.bag[0] += 1;
1306            next
1307        } else {
1308            self.reset()
1309        }
1310    }
1311
1312    /// Generates next `u64` multiplied by the specified range.
1313    pub fn next_ranged(&mut self, range: u64) -> u64 {
1314        let val = self.next_u64();
1315        ((range as u128 * val as u128) >> 64) as u64
1316    }
1317
1318    fn reset(&mut self) -> u64 {
1319        use sha2::digest::Digest;
1320
1321        let hash: [u8; 64] = sha2::Sha512::digest(self.context).into();
1322
1323        for ctx in self.context[..32].iter_mut().rev() {
1324            *ctx = ctx.wrapping_add(1);
1325            if *ctx != 0 {
1326                break;
1327            }
1328        }
1329
1330        // SAFETY: `std::mem::size_of::<[u64; 8]>() == 64` and src alignment is 1
1331        unsafe {
1332            std::ptr::copy_nonoverlapping(hash.as_ptr(), self.bag.as_mut_ptr() as *mut u8, 64);
1333        }
1334
1335        // Swap bytes for little endian
1336        #[cfg(target_endian = "little")]
1337        self.bag
1338            .iter_mut()
1339            .for_each(|item| *item = item.swap_bytes());
1340
1341        // Reset and use bag[0] as counter
1342        std::mem::take(&mut self.bag[0])
1343    }
1344
1345    #[inline]
1346    const fn cursor(&self) -> u64 {
1347        self.bag[0]
1348    }
1349}
1350
1351#[cfg(test)]
1352mod tests {
1353    use super::*;
1354
1355    #[test]
1356    fn validator_set_prng() {
1357        fn make_indices(cc_seqno: u32) -> Vec<usize> {
1358            let mut prng = ValidatorSetPRNG::new(ShardIdent::BASECHAIN, cc_seqno);
1359
1360            let count = 10;
1361            let mut indices = vec![0; count];
1362            for i in 0..count {
1363                let j = prng.next_ranged(i as u64 + 1) as usize;
1364                debug_assert!(j <= i);
1365                indices[i] = indices[j];
1366                indices[j] = i;
1367            }
1368
1369            indices
1370        }
1371
1372        let vs10_first = make_indices(10);
1373        let vs10_second = make_indices(10);
1374        assert_eq!(vs10_first, vs10_second);
1375
1376        let vs11_first = make_indices(11);
1377        let vs11_second = make_indices(11);
1378        assert_eq!(vs11_first, vs11_second);
1379        assert_ne!(vs10_first, vs11_second);
1380    }
1381}