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#[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 pub blackhole_addr: Option<HashBytes>,
21 pub fee_burn_num: u32,
23 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 pub fn is_valid(&self) -> bool {
41 self.fee_burn_num <= self.fee_burn_denom.get()
42 }
43
44 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#[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 pub mint_at: u32,
74 pub delta: CurrencyCollection,
76}
77
78#[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 pub normal_params: Lazy<ConfigProposalSetup>,
85 pub critical_params: Lazy<ConfigProposalSetup>,
87}
88
89#[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 pub min_total_rounds: u8,
96 pub max_total_rounds: u8,
98 pub min_wins: u8,
100 pub max_losses: u8,
102 pub min_store_sec: u32,
104 pub max_store_sec: u32,
106 pub bit_price: u32,
108 pub cell_price: u32,
110}
111
112#[derive(Debug, Clone, Eq, PartialEq)]
114#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
115pub struct WorkchainDescription {
116 pub enabled_since: u32,
118 pub actual_min_split: u8,
120 pub min_split: u8,
122 pub max_split: u8,
124 pub active: bool,
126 pub accept_msgs: bool,
128 pub zerostate_root_hash: HashBytes,
130 pub zerostate_file_hash: HashBytes,
132 pub version: u32,
134 pub format: WorkchainFormat,
136}
137
138impl WorkchainDescription {
139 const TAG: u8 = 0xa6;
140
141 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#[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(WorkchainFormatBasic),
222 Extended(WorkchainFormatExtended),
224}
225
226impl WorkchainFormat {
227 pub fn is_valid(&self) -> bool {
229 match self {
230 Self::Basic(_) => true,
231 Self::Extended(format) => format.is_valid(),
232 }
233 }
234
235 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#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
274#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
275pub struct WorkchainFormatBasic {
276 pub vm_version: i32,
278 pub vm_mode: u64,
280}
281
282#[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 pub min_addr_len: Uint12,
289 pub max_addr_len: Uint12,
291 pub addr_len_step: Uint12,
293 pub workchain_type_id: NonZeroU32,
295}
296
297impl WorkchainFormatExtended {
298 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 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#[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 pub masterchain_block_fee: Tokens,
333 pub basechain_block_fee: Tokens,
335}
336
337#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
339#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
340pub struct ElectionTimings {
341 pub validators_elected_for: u32,
343 pub elections_start_before: u32,
345 pub elections_end_before: u32,
347 pub stake_held_for: u32,
349}
350
351#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
353#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
354pub struct ValidatorCountParams {
355 pub max_validators: u16,
357 pub max_main_validators: u16,
359 pub min_validators: u16,
361}
362
363#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
365#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
366pub struct ValidatorStakeParams {
367 pub min_stake: Tokens,
369 pub max_stake: Tokens,
371 pub min_total_stake: Tokens,
373 pub max_stake_factor: u32,
375}
376
377#[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 pub utime_since: u32,
384 pub bit_price_ps: u64,
386 pub cell_price_ps: u64,
388 pub mc_bit_price_ps: u64,
390 pub mc_cell_price_ps: u64,
392}
393
394impl StoragePrices {
395 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#[derive(Default, Debug, Clone, Eq, PartialEq)]
416#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
417pub struct GasLimitsPrices {
418 pub gas_price: u64,
420 pub gas_limit: u64,
422 pub special_gas_limit: u64,
424 pub gas_credit: u64,
426 pub block_gas_limit: u64,
428 pub freeze_due_limit: u64,
430 pub delete_due_limit: u64,
432 pub flat_gas_limit: u64,
434 pub flat_gas_price: u64,
438}
439
440impl GasLimitsPrices {
441 pub const MAX_GAS: u64 = i64::MAX as u64;
446
447 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 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 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 gas_max
493 } else {
494 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 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#[derive(Debug, Clone, Copy)]
518pub struct ComputeGasParams<'a> {
519 pub account_balance: &'a Tokens,
521 pub message_balance: &'a Tokens,
523 pub is_special: bool,
525 pub is_tx_ordinary: bool,
527 pub is_in_msg_external: bool,
529}
530
531#[derive(Debug, Clone, Copy)]
533pub struct ComputedGasParams {
534 pub max: u64,
536 pub limit: u64,
538 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#[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 pub underload: u32,
606 pub soft_limit: u32,
608 pub hard_limit: u32,
610}
611
612impl BlockParamLimits {
613 pub fn is_valid(&self) -> bool {
615 self.underload <= self.soft_limit && self.soft_limit <= self.hard_limit
616 }
617}
618
619#[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 pub bytes: BlockParamLimits,
626 pub gas: BlockParamLimits,
628 pub lt_delta: BlockParamLimits,
630}
631
632#[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 pub lump_price: u64,
639 pub bit_price: u64,
641 pub cell_price: u64,
643 pub ihr_price_factor: u32,
645 pub first_frac: u16,
647 pub next_frac: u16,
649}
650
651impl MsgForwardPrices {
652 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 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 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#[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 pub isolate_mc_validators: bool,
680 pub shuffle_mc_validators: bool,
682 pub mc_catchain_lifetime: u32,
684 pub shard_catchain_lifetime: u32,
686 pub shard_validators_lifetime: u32,
688 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#[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 pub shuffle_mc_validators: bool,
765
766 pub mc_block_min_interval_ms: u32,
768
769 #[tlb(since_tag = 1)]
771 pub mc_block_max_interval_ms: u32,
772
773 pub empty_sc_block_interval_ms: u32,
775
776 pub max_uncommitted_chain_length: u8,
778 pub wu_used_to_import_next_anchor: u64,
780
781 pub msgs_exec_params: MsgsExecutionParams,
783
784 pub work_units_params: WorkUnitsParams,
786}
787
788#[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 pub buffer_limit: u32,
809
810 pub group_limit: u16,
815 pub group_vert_size: u16,
818
819 pub externals_expire_timeout: u16,
821
822 pub open_ranges_limit: u16,
825
826 pub par_0_int_msgs_count_limit: u32,
830
831 pub par_0_ext_msgs_count_limit: u32,
835
836 pub group_slots_fractions: Dict<u16, u8>,
839
840 #[tlb(since_tag = 1)]
842 pub range_messages_limit: u32,
843}
844
845#[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 pub prepare: WorkUnitsParamsPrepare,
861 pub execute: WorkUnitsParamsExecute,
863 pub finalize: WorkUnitsParamsFinalize,
865}
866
867#[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 pub fixed_part: u32,
887 pub msgs_stats: u16,
889 pub remaning_msgs_stats: u16,
891 pub read_ext_msgs: u16,
893 pub read_int_msgs: u16,
895 pub read_new_msgs: u16,
897 pub add_to_msg_groups: u16,
899}
900
901#[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 pub prepare: u32,
922 pub execute: u16,
924 pub execute_err: u16,
926 pub execute_delimiter: u32,
928 pub serialize_enqueue: u16,
930 pub serialize_dequeue: u16,
932 pub insert_new_msgs: u16,
934 pub subgroup_size: u16,
936}
937
938#[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 pub build_transactions: u16,
965 pub build_accounts: u16,
967 pub build_in_msg: u16,
969 pub build_out_msg: u16,
971 pub serialize_min: u32,
973 pub serialize_accounts: u16,
975 pub serialize_msg: u16,
977 pub state_update_min: u32,
979 pub state_update_accounts: u16,
981 pub state_update_msg: u16,
983 pub create_diff: u16,
985 pub serialize_diff: u16,
987 pub apply_diff: u16,
989 pub diff_tail_len: u16,
991}
992
993#[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 pub clock_skew_millis: NonZeroU16,
1026
1027 pub payload_batch_bytes: NonZeroU32,
1033
1034 #[cfg_attr(feature = "serde", serde(default))]
1036 pub _unused: u8,
1037
1038 pub commit_history_rounds: NonZeroU8,
1044
1045 pub deduplicate_rounds: u16,
1051
1052 pub max_consensus_lag_rounds: NonZeroU16,
1071
1072 pub payload_buffer_bytes: NonZeroU32,
1077
1078 pub broadcast_retry_millis: NonZeroU16,
1084
1085 pub download_retry_millis: NonZeroU16,
1091
1092 pub download_peers: NonZeroU8,
1098
1099 pub min_sign_attempts: NonZeroU8,
1104
1105 pub download_peer_queries: NonZeroU8,
1111
1112 pub sync_support_rounds: NonZeroU16,
1119}
1120
1121#[cfg(not(feature = "tycho"))]
1123#[derive(Debug, Clone, Eq, PartialEq)]
1124#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1125pub struct ConsensusConfig {
1126 pub new_catchain_ids: bool,
1128 pub round_candidates: NonZeroU32,
1130 pub next_candidate_delay_ms: u32,
1132 pub consensus_timeout_ms: u32,
1134 pub fast_attempts: u32,
1136 pub attempt_duration: u32,
1138 pub catchain_max_deps: u32,
1140 pub max_block_bytes: u32,
1142 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#[derive(Debug, Clone, Eq, PartialEq)]
1203#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1204pub struct ValidatorSet {
1205 pub utime_since: u32,
1207 pub utime_until: u32,
1209 pub main: NonZeroU16,
1211 pub total_weight: u64,
1213 pub list: Vec<ValidatorDescription>,
1215}
1216
1217impl ValidatorSet {
1218 const TAG_V1: u8 = 0x11;
1219 const TAG_V2: u8 = 0x12;
1220
1221 #[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 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 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; 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 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; 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 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#[derive(Debug, Clone, Eq, PartialEq)]
1572#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1573pub struct ValidatorDescription {
1574 pub public_key: HashBytes, pub weight: u64,
1578 #[cfg_attr(feature = "serde", serde(default))]
1580 pub adnl_addr: Option<HashBytes>,
1581 #[cfg_attr(feature = "serde", serde(default))]
1583 pub mc_seqno_since: u32,
1584
1585 #[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 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#[derive(Debug, Clone, Eq, PartialEq)]
1678#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1679pub struct IndexedValidatorDescription {
1680 pub desc: ValidatorDescription,
1682 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
1716pub struct ValidatorSetPRNG {
1718 context: [u8; 48],
1719 bag: [u64; 8],
1720}
1721
1722impl ValidatorSetPRNG {
1723 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 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 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 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 unsafe {
1776 std::ptr::copy_nonoverlapping(hash.as_ptr(), self.bag.as_mut_ptr() as *mut u8, 64);
1777 }
1778
1779 #[cfg(target_endian = "little")]
1781 self.bag
1782 .iter_mut()
1783 .for_each(|item| *item = item.swap_bytes());
1784
1785 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#[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 pub max_msg_bits: u32,
1812 pub max_msg_cells: u32,
1814 pub max_library_cells: u32,
1816 pub max_vm_data_depth: u16,
1818 pub max_ext_msg_size: u32,
1820 pub max_ext_msg_depth: u16,
1822 pub max_acc_state_cells: u32,
1824 pub max_acc_state_bits: u32,
1826 pub max_acc_public_libraries: u32,
1828 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#[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 pub authority_addresses: Dict<HashBytes, ()>,
1844 pub black_mark_id: u32,
1846 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}