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#[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 pub normal_params: Lazy<ConfigProposalSetup>,
20 pub critical_params: Lazy<ConfigProposalSetup>,
22}
23
24#[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 pub min_total_rounds: u8,
31 pub max_total_rounds: u8,
33 pub min_wins: u8,
35 pub max_losses: u8,
37 pub min_store_sec: u32,
39 pub max_store_sec: u32,
41 pub bit_price: u32,
43 pub cell_price: u32,
45}
46
47#[derive(Debug, Clone, Eq, PartialEq)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
50pub struct WorkchainDescription {
51 pub enabled_since: u32,
53 pub actual_min_split: u8,
55 pub min_split: u8,
57 pub max_split: u8,
59 pub active: bool,
61 pub accept_msgs: bool,
63 pub zerostate_root_hash: HashBytes,
65 pub zerostate_file_hash: HashBytes,
67 pub version: u32,
69 pub format: WorkchainFormat,
71}
72
73impl WorkchainDescription {
74 const TAG: u8 = 0xa6;
75
76 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#[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(WorkchainFormatBasic),
157 Extended(WorkchainFormatExtended),
159}
160
161impl WorkchainFormat {
162 pub fn is_valid(&self) -> bool {
164 match self {
165 Self::Basic(_) => true,
166 Self::Extended(format) => format.is_valid(),
167 }
168 }
169
170 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#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
209#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
210pub struct WorkchainFormatBasic {
211 pub vm_version: i32,
213 pub vm_mode: u64,
215}
216
217#[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 pub min_addr_len: Uint12,
224 pub max_addr_len: Uint12,
226 pub addr_len_step: Uint12,
228 pub workchain_type_id: NonZeroU32,
230}
231
232impl WorkchainFormatExtended {
233 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#[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 pub masterchain_block_fee: Tokens,
249 pub basechain_block_fee: Tokens,
251}
252
253#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
255#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
256pub struct ElectionTimings {
257 pub validators_elected_for: u32,
259 pub elections_start_before: u32,
261 pub elections_end_before: u32,
263 pub stake_held_for: u32,
265}
266
267#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
269#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
270pub struct ValidatorCountParams {
271 pub max_validators: u16,
273 pub max_main_validators: u16,
275 pub min_validators: u16,
277}
278
279#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
281#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
282pub struct ValidatorStakeParams {
283 pub min_stake: Tokens,
285 pub max_stake: Tokens,
287 pub min_total_stake: Tokens,
289 pub max_stake_factor: u32,
291}
292
293#[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 pub utime_since: u32,
300 pub bit_price_ps: u64,
302 pub cell_price_ps: u64,
304 pub mc_bit_price_ps: u64,
306 pub mc_cell_price_ps: u64,
308}
309
310#[derive(Default, Debug, Clone, Eq, PartialEq)]
312#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
313pub struct GasLimitsPrices {
314 pub gas_price: u64,
316 pub gas_limit: u64,
318 pub special_gas_limit: u64,
320 pub gas_credit: u64,
322 pub block_gas_limit: u64,
324 pub freeze_due_limit: u64,
326 pub delete_due_limit: u64,
328 pub flat_gas_limit: u64,
330 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#[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 pub underload: u32,
400 pub soft_limit: u32,
402 pub hard_limit: u32,
404}
405
406impl BlockParamLimits {
407 pub fn is_valid(&self) -> bool {
409 self.underload <= self.soft_limit && self.soft_limit <= self.hard_limit
410 }
411}
412
413#[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 pub bytes: BlockParamLimits,
420 pub gas: BlockParamLimits,
422 pub lt_delta: BlockParamLimits,
424}
425
426#[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 pub lump_price: u64,
433 pub bit_price: u64,
435 pub cell_price: u64,
437 pub ihr_price_factor: u32,
439 pub first_frac: u16,
441 pub next_frac: u16,
443}
444
445#[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 pub isolate_mc_validators: bool,
452 pub shuffle_mc_validators: bool,
454 pub mc_catchain_lifetime: u32,
456 pub shard_catchain_lifetime: u32,
458 pub shard_validators_lifetime: u32,
460 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#[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 pub shuffle_mc_validators: bool,
535
536 pub mc_block_min_interval_ms: u32,
538
539 #[tlb(since_tag = 1)]
541 pub empty_sc_block_interval_ms: u32,
542
543 pub max_uncommitted_chain_length: u8,
545 pub wu_used_to_import_next_anchor: u64,
547
548 pub msgs_exec_params: MsgsExecutionParams,
550
551 pub work_units_params: WorkUnitsParams,
553}
554
555#[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 pub buffer_limit: u32,
571 pub group_limit: u16,
574 pub group_vert_size: u16,
577}
578
579#[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 pub prepare: WorkUnitsParamsPrepare,
595 pub execute: WorkUnitsParamsExecute,
597 pub finalize: WorkUnitsParamsFinalize,
599}
600
601#[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 pub fixed_part: u32,
618 pub read_ext_msgs: u16,
620 pub read_int_msgs: u16,
622 pub read_new_msgs: u32,
624}
625
626#[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 pub prepare: u32,
647 pub execute: u16,
649 pub execute_err: u16,
651 pub execute_delimiter: u32,
653 pub serialize_enqueue: u16,
655 pub serialize_dequeue: u16,
657 pub insert_new_msgs_to_iterator: u16,
659 pub subgroup_size: u16,
661}
662
663#[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 pub build_transactions: u16,
686 pub build_accounts: u16,
688 pub build_in_msg: u16,
690 pub build_out_msg: u16,
692 pub serialize_min: u32,
694 pub serialize_accounts: u16,
696 pub serialize_msg: u16,
698 pub state_update_min: u32,
700 pub state_update_accounts: u32,
702 pub state_update_msg: u16,
704}
705
706#[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 pub clock_skew_millis: u16,
732
733 pub payload_batch_bytes: u32,
737
738 pub commit_history_rounds: u16,
742
743 pub deduplicate_rounds: u16,
747
748 pub max_consensus_lag_rounds: u16,
752
753 pub payload_buffer_bytes: u32,
755
756 pub broadcast_retry_millis: u16,
758
759 pub download_retry_millis: u16,
761
762 pub download_peers: u8,
764
765 pub download_tasks: u16,
767
768 pub sync_support_rounds: u16,
770}
771
772#[cfg(not(feature = "tycho"))]
774#[derive(Debug, Clone, Eq, PartialEq)]
775#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
776pub struct ConsensusConfig {
777 pub new_catchain_ids: bool,
779 pub round_candidates: NonZeroU32,
781 pub next_candidate_delay_ms: u32,
783 pub consensus_timeout_ms: u32,
785 pub fast_attempts: u32,
787 pub attempt_duration: u32,
789 pub catchain_max_deps: u32,
791 pub max_block_bytes: u32,
793 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#[derive(Debug, Clone, Eq, PartialEq)]
854#[cfg_attr(feature = "serde", derive(serde::Serialize))]
855pub struct ValidatorSet {
856 pub utime_since: u32,
858 pub utime_until: u32,
860 pub main: NonZeroU16,
862 pub total_weight: u64,
864 pub list: Vec<ValidatorDescription>,
866}
867
868impl ValidatorSet {
869 const TAG_V1: u8 = 0x11;
870 const TAG_V2: u8 = 0x12;
871
872 #[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 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 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; 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 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 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#[derive(Debug, Clone, Eq, PartialEq)]
1168#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1169pub struct ValidatorDescription {
1170 pub public_key: HashBytes, pub weight: u64,
1174 #[cfg_attr(feature = "serde", serde(default))]
1176 pub adnl_addr: Option<HashBytes>,
1177 #[cfg_attr(feature = "serde", serde(default))]
1179 pub mc_seqno_since: u32,
1180
1181 #[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 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
1272pub struct ValidatorSetPRNG {
1274 context: [u8; 48],
1275 bag: [u64; 8],
1276}
1277
1278impl ValidatorSetPRNG {
1279 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 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 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 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 unsafe {
1332 std::ptr::copy_nonoverlapping(hash.as_ptr(), self.bag.as_mut_ptr() as *mut u8, 64);
1333 }
1334
1335 #[cfg(target_endian = "little")]
1337 self.bag
1338 .iter_mut()
1339 .for_each(|item| *item = item.swap_bytes());
1340
1341 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}