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;
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 fn compute_gas_fee(&self, gas_used: u64) -> Tokens {
443 let mut res = self.flat_gas_price as u128;
444 if let Some(extra_gas) = gas_used.checked_sub(self.flat_gas_limit) {
445 res = res.saturating_add(shift_ceil_price(self.gas_price as u128 * extra_gas as u128));
446 }
447 Tokens::new(res)
448 }
449}
450
451impl GasLimitsPrices {
452 const TAG_BASE: u8 = 0xdd;
453 const TAG_EXT: u8 = 0xde;
454 const TAG_FLAT_PFX: u8 = 0xd1;
455}
456
457impl Store for GasLimitsPrices {
458 fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
459 ok!(builder.store_u8(Self::TAG_FLAT_PFX));
460 ok!(builder.store_u64(self.flat_gas_limit));
461 ok!(builder.store_u64(self.flat_gas_price));
462 ok!(builder.store_u8(Self::TAG_EXT));
463 ok!(builder.store_u64(self.gas_price));
464 ok!(builder.store_u64(self.gas_limit));
465 ok!(builder.store_u64(self.special_gas_limit));
466 ok!(builder.store_u64(self.gas_credit));
467 ok!(builder.store_u64(self.block_gas_limit));
468 ok!(builder.store_u64(self.freeze_due_limit));
469 builder.store_u64(self.delete_due_limit)
470 }
471}
472
473impl<'a> Load<'a> for GasLimitsPrices {
474 fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
475 let mut result = Self::default();
476 loop {
477 match slice.load_u8() {
478 Ok(Self::TAG_FLAT_PFX) => {
479 result.flat_gas_limit = ok!(slice.load_u64());
480 result.flat_gas_price = ok!(slice.load_u64());
481 }
482 Ok(Self::TAG_EXT) => {
483 result.gas_price = ok!(slice.load_u64());
484 result.gas_limit = ok!(slice.load_u64());
485 result.special_gas_limit = ok!(slice.load_u64());
486 result.gas_credit = ok!(slice.load_u64());
487 result.block_gas_limit = ok!(slice.load_u64());
488 result.freeze_due_limit = ok!(slice.load_u64());
489 result.delete_due_limit = ok!(slice.load_u64());
490 return Ok(result);
491 }
492 Ok(Self::TAG_BASE) => {
493 result.gas_price = ok!(slice.load_u64());
494 result.gas_limit = ok!(slice.load_u64());
495 result.gas_credit = ok!(slice.load_u64());
496 result.block_gas_limit = ok!(slice.load_u64());
497 result.freeze_due_limit = ok!(slice.load_u64());
498 result.delete_due_limit = ok!(slice.load_u64());
499 return Ok(result);
500 }
501 Ok(_) => return Err(Error::InvalidTag),
502 Err(e) => return Err(e),
503 }
504 }
505 }
506}
507
508#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
510#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
511#[tlb(tag = "#c3", validate_with = "Self::is_valid")]
512pub struct BlockParamLimits {
513 pub underload: u32,
515 pub soft_limit: u32,
517 pub hard_limit: u32,
519}
520
521impl BlockParamLimits {
522 pub fn is_valid(&self) -> bool {
524 self.underload <= self.soft_limit && self.soft_limit <= self.hard_limit
525 }
526}
527
528#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
530#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
531#[tlb(tag = "#5d")]
532pub struct BlockLimits {
533 pub bytes: BlockParamLimits,
535 pub gas: BlockParamLimits,
537 pub lt_delta: BlockParamLimits,
539}
540
541#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
543#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
544#[tlb(tag = "#ea")]
545pub struct MsgForwardPrices {
546 pub lump_price: u64,
548 pub bit_price: u64,
550 pub cell_price: u64,
552 pub ihr_price_factor: u32,
554 pub first_frac: u16,
556 pub next_frac: u16,
558}
559
560impl MsgForwardPrices {
561 pub fn compute_fwd_fee(&self, stats: CellTreeStats) -> Tokens {
563 let lump = self.lump_price as u128;
564 let extra = shift_ceil_price(
565 (stats.cell_count as u128 * self.cell_price as u128)
566 .saturating_add(stats.bit_count as u128 * self.bit_price as u128),
567 );
568 Tokens::new(lump.saturating_add(extra))
569 }
570
571 pub fn get_first_part(&self, total: Tokens) -> Tokens {
573 Tokens::new(total.into_inner().saturating_mul(self.first_frac as _) >> 16)
574 }
575
576 pub fn get_next_part(&self, total: Tokens) -> Tokens {
578 Tokens::new(total.into_inner().saturating_mul(self.next_frac as _) >> 16)
579 }
580}
581
582#[cfg(not(feature = "tycho"))]
584#[derive(Debug, Copy, Clone, Eq, PartialEq)]
585#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
586pub struct CatchainConfig {
587 pub isolate_mc_validators: bool,
589 pub shuffle_mc_validators: bool,
591 pub mc_catchain_lifetime: u32,
593 pub shard_catchain_lifetime: u32,
595 pub shard_validators_lifetime: u32,
597 pub shard_validators_num: u32,
599}
600
601#[cfg(not(feature = "tycho"))]
602impl CatchainConfig {
603 const TAG_V1: u8 = 0xc1;
604 const TAG_V2: u8 = 0xc2;
605}
606
607#[cfg(not(feature = "tycho"))]
608impl Store for CatchainConfig {
609 fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
610 let flags = ((self.isolate_mc_validators as u8) << 1) | (self.shuffle_mc_validators as u8);
611 ok!(builder.store_u8(Self::TAG_V2));
612 ok!(builder.store_u8(flags));
613 ok!(builder.store_u32(self.mc_catchain_lifetime));
614 ok!(builder.store_u32(self.shard_catchain_lifetime));
615 ok!(builder.store_u32(self.shard_validators_lifetime));
616 builder.store_u32(self.shard_validators_num)
617 }
618}
619
620#[cfg(not(feature = "tycho"))]
621impl<'a> Load<'a> for CatchainConfig {
622 fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
623 let flags = match slice.load_u8() {
624 Ok(Self::TAG_V1) => 0,
625 Ok(Self::TAG_V2) => ok!(slice.load_u8()),
626 Ok(_) => return Err(Error::InvalidTag),
627 Err(e) => return Err(e),
628 };
629 if flags >> 2 != 0 {
630 return Err(Error::InvalidData);
631 }
632 Ok(Self {
633 isolate_mc_validators: flags & 0b10 != 0,
634 shuffle_mc_validators: flags & 0b01 != 0,
635 mc_catchain_lifetime: ok!(slice.load_u32()),
636 shard_catchain_lifetime: ok!(slice.load_u32()),
637 shard_validators_lifetime: ok!(slice.load_u32()),
638 shard_validators_num: ok!(slice.load_u32()),
639 })
640 }
641}
642
643#[cfg(feature = "tycho")]
668#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
669#[tlb(tag = ["#a6", "#a7"])]
670#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
671pub struct CollationConfig {
672 pub shuffle_mc_validators: bool,
674
675 pub mc_block_min_interval_ms: u32,
677
678 #[tlb(since_tag = 1)]
680 pub mc_block_max_interval_ms: u32,
681
682 pub empty_sc_block_interval_ms: u32,
684
685 pub max_uncommitted_chain_length: u8,
687 pub wu_used_to_import_next_anchor: u64,
689
690 pub msgs_exec_params: MsgsExecutionParams,
692
693 pub work_units_params: WorkUnitsParams,
695}
696
697#[cfg(feature = "tycho")]
712#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
713#[tlb(tag = ["#00", "#01"])]
714#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
715pub struct MsgsExecutionParams {
716 pub buffer_limit: u32,
718
719 pub group_limit: u16,
724 pub group_vert_size: u16,
727
728 pub externals_expire_timeout: u16,
730
731 pub open_ranges_limit: u16,
734
735 pub par_0_int_msgs_count_limit: u32,
739
740 pub par_0_ext_msgs_count_limit: u32,
744
745 pub group_slots_fractions: Dict<u16, u8>,
748
749 #[tlb(since_tag = 1)]
751 pub range_messages_limit: u32,
752}
753
754#[cfg(feature = "tycho")]
764#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
765#[tlb(tag = "#00")]
766#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
767pub struct WorkUnitsParams {
768 pub prepare: WorkUnitsParamsPrepare,
770 pub execute: WorkUnitsParamsExecute,
772 pub finalize: WorkUnitsParamsFinalize,
774}
775
776#[cfg(feature = "tycho")]
790#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
791#[tlb(tag = "#00")]
792#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
793pub struct WorkUnitsParamsPrepare {
794 pub fixed_part: u32,
796 pub msgs_stats: u16,
798 pub remaning_msgs_stats: u16,
800 pub read_ext_msgs: u16,
802 pub read_int_msgs: u16,
804 pub read_new_msgs: u16,
806 pub add_to_msg_groups: u16,
808}
809
810#[cfg(feature = "tycho")]
825#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
826#[tlb(tag = "#00")]
827#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
828pub struct WorkUnitsParamsExecute {
829 pub prepare: u32,
831 pub execute: u16,
833 pub execute_err: u16,
835 pub execute_delimiter: u32,
837 pub serialize_enqueue: u16,
839 pub serialize_dequeue: u16,
841 pub insert_new_msgs: u16,
843 pub subgroup_size: u16,
845}
846
847#[cfg(feature = "tycho")]
868#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
869#[tlb(tag = "#00")]
870#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
871pub struct WorkUnitsParamsFinalize {
872 pub build_transactions: u16,
874 pub build_accounts: u16,
876 pub build_in_msg: u16,
878 pub build_out_msg: u16,
880 pub serialize_min: u32,
882 pub serialize_accounts: u16,
884 pub serialize_msg: u16,
886 pub state_update_min: u32,
888 pub state_update_accounts: u16,
890 pub state_update_msg: u16,
892 pub create_diff: u16,
894 pub serialize_diff: u16,
896 pub apply_diff: u16,
898 pub diff_tail_len: u16,
900}
901
902#[cfg(feature = "tycho")]
922#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
923#[tlb(tag = "#d8")]
924#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
925pub struct ConsensusConfig {
926 pub clock_skew_millis: NonZeroU16,
935
936 pub payload_batch_bytes: NonZeroU32,
942
943 #[cfg_attr(feature = "serde", serde(default))]
945 pub _unused: u8,
946
947 pub commit_history_rounds: NonZeroU8,
953
954 pub deduplicate_rounds: u16,
960
961 pub max_consensus_lag_rounds: NonZeroU16,
980
981 pub payload_buffer_bytes: NonZeroU32,
986
987 pub broadcast_retry_millis: NonZeroU16,
993
994 pub download_retry_millis: NonZeroU16,
1000
1001 pub download_peers: NonZeroU8,
1007
1008 pub min_sign_attempts: NonZeroU8,
1013
1014 pub download_peer_queries: NonZeroU8,
1020
1021 pub sync_support_rounds: NonZeroU16,
1028}
1029
1030#[cfg(not(feature = "tycho"))]
1032#[derive(Debug, Clone, Eq, PartialEq)]
1033#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1034pub struct ConsensusConfig {
1035 pub new_catchain_ids: bool,
1037 pub round_candidates: NonZeroU32,
1039 pub next_candidate_delay_ms: u32,
1041 pub consensus_timeout_ms: u32,
1043 pub fast_attempts: u32,
1045 pub attempt_duration: u32,
1047 pub catchain_max_deps: u32,
1049 pub max_block_bytes: u32,
1051 pub max_collated_bytes: u32,
1053}
1054
1055#[cfg(not(feature = "tycho"))]
1056impl ConsensusConfig {
1057 const TAG_V1: u8 = 0xd6;
1058 const TAG_V2: u8 = 0xd7;
1059}
1060
1061#[cfg(not(feature = "tycho"))]
1062impl Store for ConsensusConfig {
1063 fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
1064 let flags = self.new_catchain_ids as u8;
1065
1066 ok!(builder.store_u8(Self::TAG_V2));
1067 ok!(builder.store_u8(flags));
1068 ok!(builder.store_u8(self.round_candidates.get() as u8));
1069 ok!(builder.store_u32(self.next_candidate_delay_ms));
1070 ok!(builder.store_u32(self.consensus_timeout_ms));
1071 ok!(builder.store_u32(self.fast_attempts));
1072 ok!(builder.store_u32(self.attempt_duration));
1073 ok!(builder.store_u32(self.catchain_max_deps));
1074 ok!(builder.store_u32(self.max_block_bytes));
1075 builder.store_u32(self.max_collated_bytes)
1076 }
1077}
1078
1079#[cfg(not(feature = "tycho"))]
1080impl<'a> Load<'a> for ConsensusConfig {
1081 fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
1082 use std::num::NonZeroU8;
1083
1084 let (flags, round_candidates) = match slice.load_u8() {
1085 Ok(Self::TAG_V1) => (0, ok!(NonZeroU32::load_from(slice))),
1086 Ok(Self::TAG_V2) => {
1087 let flags = ok!(slice.load_u8());
1088 if flags >> 1 != 0 {
1089 return Err(Error::InvalidData);
1090 }
1091 (flags, ok!(NonZeroU8::load_from(slice)).into())
1092 }
1093 Ok(_) => return Err(Error::InvalidTag),
1094 Err(e) => return Err(e),
1095 };
1096 Ok(Self {
1097 new_catchain_ids: flags & 0b1 != 0,
1098 round_candidates,
1099 next_candidate_delay_ms: ok!(slice.load_u32()),
1100 consensus_timeout_ms: ok!(slice.load_u32()),
1101 fast_attempts: ok!(slice.load_u32()),
1102 attempt_duration: ok!(slice.load_u32()),
1103 catchain_max_deps: ok!(slice.load_u32()),
1104 max_block_bytes: ok!(slice.load_u32()),
1105 max_collated_bytes: ok!(slice.load_u32()),
1106 })
1107 }
1108}
1109
1110#[derive(Debug, Clone, Eq, PartialEq)]
1112#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1113pub struct ValidatorSet {
1114 pub utime_since: u32,
1116 pub utime_until: u32,
1118 pub main: NonZeroU16,
1120 pub total_weight: u64,
1122 pub list: Vec<ValidatorDescription>,
1124}
1125
1126impl ValidatorSet {
1127 const TAG_V1: u8 = 0x11;
1128 const TAG_V2: u8 = 0x12;
1129
1130 #[cfg(not(feature = "tycho"))]
1132 pub fn compute_subset(
1133 &self,
1134 shard_ident: ShardIdent,
1135 cc_config: &CatchainConfig,
1136 cc_seqno: u32,
1137 ) -> Option<(Vec<ValidatorDescription>, u32)> {
1138 if shard_ident.is_masterchain() {
1139 return self.compute_mc_subset(cc_seqno, cc_config.shuffle_mc_validators);
1140 }
1141
1142 let total = self.list.len();
1143 let main = self.main.get() as usize;
1144
1145 let mut prng = ValidatorSetPRNG::new(shard_ident, cc_seqno);
1146
1147 let vset = if cc_config.isolate_mc_validators {
1148 if total <= main {
1149 return None;
1150 }
1151
1152 let mut list = self.list[main..].to_vec();
1153
1154 let mut total_weight = 0u64;
1155 for descr in &mut list {
1156 descr.prev_total_weight = total_weight;
1157 total_weight += descr.weight;
1158 }
1159
1160 std::borrow::Cow::Owned(Self {
1161 utime_since: self.utime_since,
1162 utime_until: self.utime_until,
1163 main: self.main,
1164 total_weight,
1165 list,
1166 })
1167 } else {
1168 std::borrow::Cow::Borrowed(self)
1169 };
1170
1171 let count = std::cmp::min(vset.list.len(), cc_config.shard_validators_num as usize);
1172
1173 let mut nodes = Vec::with_capacity(count);
1174 let mut holes = Vec::<(u64, u64)>::with_capacity(count);
1175 let mut total_wt = vset.total_weight;
1176
1177 for _ in 0..count {
1178 debug_assert!(total_wt > 0);
1179
1180 let mut p = prng.next_ranged(total_wt);
1182
1183 for (prev_total_weight, weight) in &holes {
1184 if p < *prev_total_weight {
1185 break;
1186 }
1187 p += weight;
1188 }
1189
1190 let entry = vset.at_weight(p);
1191
1192 nodes.push(ValidatorDescription {
1193 public_key: entry.public_key,
1194 weight: 1,
1195 adnl_addr: entry.adnl_addr,
1196 mc_seqno_since: 0,
1197 prev_total_weight: 0,
1198 });
1199 debug_assert!(total_wt >= entry.weight);
1200 total_wt -= entry.weight;
1201
1202 let new_hole = (entry.prev_total_weight, entry.weight);
1203 let i = holes.partition_point(|item| item <= &new_hole);
1204 debug_assert!(i == 0 || holes[i - 1] < new_hole);
1205
1206 holes.insert(i, new_hole);
1207 }
1208
1209 let hash_short = Self::compute_subset_hash_short(&nodes, cc_seqno);
1210
1211 Some((nodes, hash_short))
1212 }
1213
1214 pub fn compute_mc_subset(
1218 &self,
1219 cc_seqno: u32,
1220 shuffle: bool,
1221 ) -> Option<(Vec<ValidatorDescription>, u32)> {
1222 let total = self.list.len();
1223 let main = self.main.get() as usize;
1224
1225 let count = std::cmp::min(total, main);
1226 let subset = if !shuffle {
1227 self.list[0..count].to_vec()
1228 } else {
1229 let mut prng = ValidatorSetPRNG::new(ShardIdent::MASTERCHAIN, cc_seqno);
1230
1231 let mut indices = vec![0; count];
1232 for i in 0..count {
1233 let j = prng.next_ranged(i as u64 + 1) as usize; debug_assert!(j <= i);
1235 indices[i] = indices[j];
1236 indices[j] = i;
1237 }
1238
1239 let mut subset = Vec::with_capacity(count);
1240 for index in indices.into_iter().take(count) {
1241 subset.push(self.list[index].clone());
1242 }
1243 subset
1244 };
1245
1246 let hash_short = Self::compute_subset_hash_short(&subset, cc_seqno);
1247 Some((subset, hash_short))
1248 }
1249
1250 pub fn compute_mc_subset_indexed(
1255 &self,
1256 cc_seqno: u32,
1257 shuffle: bool,
1258 ) -> Option<(Vec<IndexedValidatorDescription>, u32)> {
1259 let total = self.list.len();
1260 let main = self.main.get() as usize;
1261
1262 let count = std::cmp::min(total, main);
1263 let subset = if !shuffle {
1264 self.list[0..count]
1265 .iter()
1266 .enumerate()
1267 .map(|(i, desc)| IndexedValidatorDescription {
1268 desc: desc.clone(),
1269 validator_idx: i as u16,
1270 })
1271 .collect::<Vec<_>>()
1272 } else {
1273 let mut prng = ValidatorSetPRNG::new(ShardIdent::MASTERCHAIN, cc_seqno);
1274
1275 let mut indices = vec![0; count];
1276 for i in 0..count {
1277 let j = prng.next_ranged(i as u64 + 1) as usize; debug_assert!(j <= i);
1279 indices[i] = indices[j];
1280 indices[j] = i;
1281 }
1282
1283 let mut subset = Vec::with_capacity(count);
1284 for index in indices.into_iter().take(count) {
1285 subset.push(IndexedValidatorDescription {
1286 desc: self.list[index].clone(),
1287 validator_idx: index as u16,
1288 });
1289 }
1290 subset
1291 };
1292
1293 let hash_short =
1294 Self::compute_subset_hash_short(subset.iter().map(AsRef::as_ref), cc_seqno);
1295 Some((subset, hash_short))
1296 }
1297
1298 pub fn compute_subset_hash_short<'a, I>(subset: I, cc_seqno: u32) -> u32
1300 where
1301 I: IntoIterator<Item = &'a ValidatorDescription, IntoIter: ExactSizeIterator>,
1302 {
1303 const HASH_SHORT_MAGIC: u32 = 0x901660ED;
1304
1305 let subset = subset.into_iter();
1306
1307 let mut hash = crc32c::crc32c(&HASH_SHORT_MAGIC.to_le_bytes());
1308 hash = crc32c::crc32c_append(hash, &cc_seqno.to_le_bytes());
1309 hash = crc32c::crc32c_append(hash, &(subset.len() as u32).to_le_bytes());
1310
1311 for node in subset {
1312 hash = crc32c::crc32c_append(hash, node.public_key.as_slice());
1313 hash = crc32c::crc32c_append(hash, &node.weight.to_le_bytes());
1314 hash = crc32c::crc32c_append(
1315 hash,
1316 node.adnl_addr
1317 .as_ref()
1318 .unwrap_or(HashBytes::wrap(&[0u8; 32]))
1319 .as_ref(),
1320 );
1321 }
1322
1323 hash
1324 }
1325
1326 #[cfg(not(feature = "tycho"))]
1327 fn at_weight(&self, weight_pos: u64) -> &ValidatorDescription {
1328 debug_assert!(weight_pos < self.total_weight);
1329 debug_assert!(!self.list.is_empty());
1330 let i = self
1331 .list
1332 .partition_point(|item| item.prev_total_weight <= weight_pos);
1333 debug_assert!(i != 0);
1334 &self.list[i - 1]
1335 }
1336}
1337
1338impl Store for ValidatorSet {
1339 fn store_into(
1340 &self,
1341 builder: &mut CellBuilder,
1342 context: &dyn CellContext,
1343 ) -> Result<(), Error> {
1344 let Ok(total) = u16::try_from(self.list.len()) else {
1345 return Err(Error::IntOverflow);
1346 };
1347
1348 let mut validators = Dict::<u16, ValidatorDescription>::new();
1350 for (i, item) in self.list.iter().enumerate() {
1351 ok!(validators.set_ext(i as u16, item, context));
1352 }
1353
1354 ok!(builder.store_u8(Self::TAG_V2));
1355 ok!(builder.store_u32(self.utime_since));
1356 ok!(builder.store_u32(self.utime_until));
1357 ok!(builder.store_u16(total));
1358 ok!(builder.store_u16(self.main.get()));
1359 ok!(builder.store_u64(self.total_weight));
1360 validators.store_into(builder, context)
1361 }
1362}
1363
1364impl<'a> Load<'a> for ValidatorSet {
1365 fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
1366 let with_total_weight = match slice.load_u8() {
1367 Ok(Self::TAG_V1) => false,
1368 Ok(Self::TAG_V2) => true,
1369 Ok(_) => return Err(Error::InvalidTag),
1370 Err(e) => return Err(e),
1371 };
1372
1373 let utime_since = ok!(slice.load_u32());
1374 let utime_until = ok!(slice.load_u32());
1375 let total = ok!(slice.load_u16()) as usize;
1376 let main = ok!(NonZeroU16::load_from(slice));
1377 if main.get() as usize > total {
1378 return Err(Error::InvalidData);
1379 }
1380
1381 let context = Cell::empty_context();
1382
1383 let (mut total_weight, validators) = if with_total_weight {
1384 let total_weight = ok!(slice.load_u64());
1385 let dict = ok!(Dict::<u16, ValidatorDescription>::load_from(slice));
1386 (total_weight, dict)
1387 } else {
1388 let dict = ok!(Dict::<u16, ValidatorDescription>::load_from_root_ext(
1389 slice, context
1390 ));
1391 (0, dict)
1392 };
1393
1394 let mut computed_total_weight = 0u64;
1395 let mut list = Vec::with_capacity(std::cmp::min(total, 512));
1396 for (i, entry) in validators.iter().enumerate().take(total) {
1397 let mut descr = match entry {
1398 Ok((idx, descr)) if idx as usize == i => descr,
1399 Ok(_) => return Err(Error::InvalidData),
1400 Err(e) => return Err(e),
1401 };
1402
1403 descr.prev_total_weight = computed_total_weight;
1404 computed_total_weight = match computed_total_weight.checked_add(descr.weight) {
1405 Some(weight) => weight,
1406 None => return Err(Error::InvalidData),
1407 };
1408 list.push(descr);
1409 }
1410
1411 if list.is_empty() {
1412 return Err(Error::InvalidData);
1413 }
1414
1415 if with_total_weight {
1416 if total_weight != computed_total_weight {
1417 return Err(Error::InvalidData);
1418 }
1419 } else {
1420 total_weight = computed_total_weight;
1421 }
1422
1423 Ok(Self {
1424 utime_since,
1425 utime_until,
1426 main,
1427 total_weight,
1428 list,
1429 })
1430 }
1431}
1432
1433#[cfg(feature = "serde")]
1434impl<'de> serde::Deserialize<'de> for ValidatorSet {
1435 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1436 use serde::de::Error;
1437
1438 #[derive(serde::Deserialize)]
1439 struct ValidatorSetHelper {
1440 utime_since: u32,
1441 utime_until: u32,
1442 main: NonZeroU16,
1443 #[serde(default)]
1444 total_weight: u64,
1445 list: Vec<ValidatorDescription>,
1446 }
1447
1448 let parsed = ValidatorSetHelper::deserialize(deserializer)?;
1449 if parsed.list.is_empty() {
1450 return Err(Error::custom("empty validators list"));
1451 }
1452
1453 let mut result = Self {
1454 utime_since: parsed.utime_since,
1455 utime_until: parsed.utime_until,
1456 main: parsed.main,
1457 total_weight: 0,
1458 list: parsed.list,
1459 };
1460
1461 for descr in &mut result.list {
1462 descr.prev_total_weight = result.total_weight;
1463 let Some(new_total_weight) = result.total_weight.checked_add(descr.weight) else {
1464 return Err(Error::custom("total weight overflow"));
1465 };
1466 result.total_weight = new_total_weight;
1467 }
1468
1469 if parsed.total_weight > 0 && parsed.total_weight != result.total_weight {
1470 return Err(Error::custom("total weight mismatch"));
1471 }
1472
1473 Ok(result)
1474 }
1475}
1476
1477#[derive(Debug, Clone, Eq, PartialEq)]
1479#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1480pub struct ValidatorDescription {
1481 pub public_key: HashBytes, pub weight: u64,
1485 #[cfg_attr(feature = "serde", serde(default))]
1487 pub adnl_addr: Option<HashBytes>,
1488 #[cfg_attr(feature = "serde", serde(default))]
1490 pub mc_seqno_since: u32,
1491
1492 #[cfg_attr(feature = "serde", serde(skip))]
1495 pub prev_total_weight: u64,
1496}
1497
1498impl ValidatorDescription {
1499 const TAG_BASIC: u8 = 0x53;
1500 const TAG_WITH_ADNL: u8 = 0x73;
1501 const TAG_WITH_MC_SEQNO: u8 = 0x93;
1502
1503 const PUBKEY_TAG: u32 = 0x8e81278a;
1504
1505 pub fn verify_signature(&self, data: &[u8], signature: &Signature) -> bool {
1507 if let Some(public_key) = ed25519::PublicKey::from_bytes(self.public_key.0) {
1508 public_key.verify_raw(data, signature.as_ref())
1509 } else {
1510 false
1511 }
1512 }
1513}
1514
1515impl Store for ValidatorDescription {
1516 fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
1517 let with_mc_seqno = self.mc_seqno_since != 0;
1518
1519 let tag = if with_mc_seqno {
1520 Self::TAG_WITH_MC_SEQNO
1521 } else if self.adnl_addr.is_some() {
1522 Self::TAG_WITH_ADNL
1523 } else {
1524 Self::TAG_BASIC
1525 };
1526
1527 ok!(builder.store_u8(tag));
1528 ok!(builder.store_u32(Self::PUBKEY_TAG));
1529 ok!(builder.store_u256(&self.public_key));
1530 ok!(builder.store_u64(self.weight));
1531
1532 let mut adnl = self.adnl_addr.as_ref();
1533 if with_mc_seqno {
1534 adnl = Some(HashBytes::wrap(&[0; 32]));
1535 }
1536
1537 if let Some(adnl) = adnl {
1538 ok!(builder.store_u256(adnl));
1539 }
1540
1541 if with_mc_seqno {
1542 builder.store_u32(self.mc_seqno_since)
1543 } else {
1544 Ok(())
1545 }
1546 }
1547}
1548
1549impl<'a> Load<'a> for ValidatorDescription {
1550 fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
1551 let (with_adnl, with_mc_seqno) = match slice.load_u8() {
1552 Ok(Self::TAG_BASIC) => (false, false),
1553 Ok(Self::TAG_WITH_ADNL) => (true, false),
1554 Ok(Self::TAG_WITH_MC_SEQNO) => (true, true),
1555 Ok(_) => return Err(Error::InvalidTag),
1556 Err(e) => return Err(e),
1557 };
1558
1559 Ok(Self {
1560 public_key: {
1561 match slice.load_u32() {
1562 Ok(Self::PUBKEY_TAG) => ok!(slice.load_u256()),
1563 Ok(_) => return Err(Error::InvalidTag),
1564 Err(e) => return Err(e),
1565 }
1566 },
1567 weight: ok!(slice.load_u64()),
1568 adnl_addr: if with_adnl {
1569 Some(ok!(slice.load_u256()))
1570 } else {
1571 None
1572 },
1573 mc_seqno_since: if with_mc_seqno {
1574 ok!(slice.load_u32())
1575 } else {
1576 0
1577 },
1578 prev_total_weight: 0,
1579 })
1580 }
1581}
1582
1583#[derive(Debug, Clone, Eq, PartialEq)]
1585#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1586pub struct IndexedValidatorDescription {
1587 pub desc: ValidatorDescription,
1589 pub validator_idx: u16,
1591}
1592
1593impl AsRef<ValidatorDescription> for IndexedValidatorDescription {
1594 #[inline]
1595 fn as_ref(&self) -> &ValidatorDescription {
1596 &self.desc
1597 }
1598}
1599
1600impl AsMut<ValidatorDescription> for IndexedValidatorDescription {
1601 #[inline]
1602 fn as_mut(&mut self) -> &mut ValidatorDescription {
1603 &mut self.desc
1604 }
1605}
1606
1607impl std::ops::Deref for IndexedValidatorDescription {
1608 type Target = ValidatorDescription;
1609
1610 #[inline]
1611 fn deref(&self) -> &Self::Target {
1612 &self.desc
1613 }
1614}
1615
1616impl std::ops::DerefMut for IndexedValidatorDescription {
1617 #[inline]
1618 fn deref_mut(&mut self) -> &mut Self::Target {
1619 &mut self.desc
1620 }
1621}
1622
1623pub struct ValidatorSetPRNG {
1625 context: [u8; 48],
1626 bag: [u64; 8],
1627}
1628
1629impl ValidatorSetPRNG {
1630 pub fn new(shard_ident: ShardIdent, cc_seqno: u32) -> Self {
1632 let seed = [0; 32];
1633 Self::with_seed(shard_ident, cc_seqno, &seed)
1634 }
1635
1636 pub fn with_seed(shard_ident: ShardIdent, cc_seqno: u32, seed: &[u8; 32]) -> Self {
1638 let mut context = [0u8; 48];
1639 context[..32].copy_from_slice(seed);
1640 context[32..40].copy_from_slice(&shard_ident.prefix().to_be_bytes());
1641 context[40..44].copy_from_slice(&shard_ident.workchain().to_be_bytes());
1642 context[44..48].copy_from_slice(&cc_seqno.to_be_bytes());
1643
1644 let mut res = ValidatorSetPRNG {
1645 context,
1646 bag: [0; 8],
1647 };
1648 res.bag[0] = 8;
1649 res
1650 }
1651
1652 pub fn next_u64(&mut self) -> u64 {
1654 if self.cursor() < 7 {
1655 let next = self.bag[1 + self.cursor() as usize];
1656 self.bag[0] += 1;
1657 next
1658 } else {
1659 self.reset()
1660 }
1661 }
1662
1663 pub fn next_ranged(&mut self, range: u64) -> u64 {
1665 let val = self.next_u64();
1666 ((range as u128 * val as u128) >> 64) as u64
1667 }
1668
1669 fn reset(&mut self) -> u64 {
1670 use sha2::digest::Digest;
1671
1672 let hash: [u8; 64] = sha2::Sha512::digest(self.context).into();
1673
1674 for ctx in self.context[..32].iter_mut().rev() {
1675 *ctx = ctx.wrapping_add(1);
1676 if *ctx != 0 {
1677 break;
1678 }
1679 }
1680
1681 unsafe {
1683 std::ptr::copy_nonoverlapping(hash.as_ptr(), self.bag.as_mut_ptr() as *mut u8, 64);
1684 }
1685
1686 #[cfg(target_endian = "little")]
1688 self.bag
1689 .iter_mut()
1690 .for_each(|item| *item = item.swap_bytes());
1691
1692 std::mem::take(&mut self.bag[0])
1694 }
1695
1696 #[inline]
1697 const fn cursor(&self) -> u64 {
1698 self.bag[0]
1699 }
1700}
1701
1702#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
1714#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1715#[tlb(tag = "#02")]
1716pub struct SizeLimitsConfig {
1717 pub max_msg_bits: u32,
1719 pub max_msg_cells: u32,
1721 pub max_library_cells: u32,
1723 pub max_vm_data_depth: u16,
1725 pub max_ext_msg_size: u32,
1727 pub max_ext_msg_depth: u16,
1729 pub max_acc_state_cells: u32,
1731 pub max_acc_state_bits: u32,
1733 pub max_acc_public_libraries: u32,
1735 pub defer_out_queue_size_limit: u32,
1737}
1738
1739const fn shift_ceil_price(value: u128) -> u128 {
1740 let r = value & 0xffff != 0;
1741 (value >> 16) + r as u128
1742}
1743
1744#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
1746#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1747#[tlb(tag = "#01")]
1748pub struct AuthorityMarksConfig {
1749 pub authority_addresses: Dict<HashBytes, ()>,
1751 pub black_mark_id: u32,
1753 pub white_mark_id: u32,
1755}
1756
1757#[cfg(test)]
1758mod tests {
1759 use super::*;
1760
1761 #[test]
1762 fn validator_set_prng() {
1763 fn make_indices(cc_seqno: u32) -> Vec<usize> {
1764 let mut prng = ValidatorSetPRNG::new(ShardIdent::BASECHAIN, cc_seqno);
1765
1766 let count = 10;
1767 let mut indices = vec![0; count];
1768 for i in 0..count {
1769 let j = prng.next_ranged(i as u64 + 1) as usize;
1770 debug_assert!(j <= i);
1771 indices[i] = indices[j];
1772 indices[j] = i;
1773 }
1774
1775 indices
1776 }
1777
1778 let vs10_first = make_indices(10);
1779 let vs10_second = make_indices(10);
1780 assert_eq!(vs10_first, vs10_second);
1781
1782 let vs11_first = make_indices(11);
1783 let vs11_second = make_indices(11);
1784 assert_eq!(vs11_first, vs11_second);
1785 assert_ne!(vs10_first, vs11_second);
1786 }
1787}