1use std::num::{NonZeroU16, NonZeroU32};
2
3use tycho_crypto::ed25519;
4
5use crate::cell::*;
6use crate::dict::Dict;
7use crate::error::Error;
8use crate::models::block::ShardIdent;
9use crate::models::{CurrencyCollection, Signature};
10use crate::num::{Tokens, Uint12, VarUint248};
11
12#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15#[tlb(tag = "#01", validate_with = "Self::is_valid")]
16pub struct BurningConfig {
17 pub blackhole_addr: Option<HashBytes>,
19 pub fee_burn_num: u32,
21 pub fee_burn_denom: NonZeroU32,
23}
24
25impl Default for BurningConfig {
26 #[inline]
27 fn default() -> Self {
28 Self {
29 blackhole_addr: None,
30 fee_burn_num: 0,
31 fee_burn_denom: NonZeroU32::MIN,
32 }
33 }
34}
35
36impl BurningConfig {
37 pub fn is_valid(&self) -> bool {
39 self.fee_burn_num <= self.fee_burn_denom.get()
40 }
41
42 pub fn compute_burned_fees(&self, tokens: Tokens) -> Result<Tokens, Error> {
47 if self.fee_burn_num == 0 {
48 return Ok(Tokens::ZERO);
49 } else if !self.is_valid() {
50 return Err(Error::InvalidData);
51 }
52
53 let mut tokens = VarUint248::new(tokens.into_inner());
54 tokens *= self.fee_burn_num as u128;
55 tokens /= self.fee_burn_denom.get() as u128;
56 let (hi, lo) = tokens.into_words();
57 debug_assert_eq!(
58 hi, 0,
59 "burned fees must never be greater than original fees"
60 );
61 Ok(Tokens::new(lo))
62 }
63}
64
65#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
67#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
68#[tlb(tag = "#01")]
69pub struct MintOnceConfig {
70 pub mint_at: u32,
72 pub delta: CurrencyCollection,
74}
75
76#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
78#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
79#[tlb(tag = "#91")]
80pub struct ConfigVotingSetup {
81 pub normal_params: Lazy<ConfigProposalSetup>,
83 pub critical_params: Lazy<ConfigProposalSetup>,
85}
86
87#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
89#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
90#[tlb(tag = "#36")]
91pub struct ConfigProposalSetup {
92 pub min_total_rounds: u8,
94 pub max_total_rounds: u8,
96 pub min_wins: u8,
98 pub max_losses: u8,
100 pub min_store_sec: u32,
102 pub max_store_sec: u32,
104 pub bit_price: u32,
106 pub cell_price: u32,
108}
109
110#[derive(Debug, Clone, Eq, PartialEq)]
112#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
113pub struct WorkchainDescription {
114 pub enabled_since: u32,
116 pub actual_min_split: u8,
118 pub min_split: u8,
120 pub max_split: u8,
122 pub active: bool,
124 pub accept_msgs: bool,
126 pub zerostate_root_hash: HashBytes,
128 pub zerostate_file_hash: HashBytes,
130 pub version: u32,
132 pub format: WorkchainFormat,
134}
135
136impl WorkchainDescription {
137 const TAG: u8 = 0xa6;
138
139 pub fn is_valid(&self) -> bool {
141 self.min_split <= self.max_split
142 && self.max_split <= ShardIdent::MAX_SPLIT_DEPTH
143 && self.format.is_valid()
144 }
145}
146
147impl Store for WorkchainDescription {
148 fn store_into(
149 &self,
150 builder: &mut CellBuilder,
151 context: &dyn CellContext,
152 ) -> Result<(), Error> {
153 if !self.is_valid() {
154 return Err(Error::InvalidData);
155 }
156
157 let flags: u16 = ((self.format.is_basic() as u16) << 15)
158 | ((self.active as u16) << 14)
159 | ((self.accept_msgs as u16) << 13);
160
161 ok!(builder.store_u8(Self::TAG));
162 ok!(builder.store_u32(self.enabled_since));
163 ok!(builder.store_u8(self.actual_min_split));
164 ok!(builder.store_u8(self.min_split));
165 ok!(builder.store_u8(self.max_split));
166 ok!(builder.store_u16(flags));
167 ok!(builder.store_u256(&self.zerostate_root_hash));
168 ok!(builder.store_u256(&self.zerostate_file_hash));
169 ok!(builder.store_u32(self.version));
170 self.format.store_into(builder, context)
171 }
172}
173
174impl<'a> Load<'a> for WorkchainDescription {
175 fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
176 match slice.load_u8() {
177 Ok(Self::TAG) => {}
178 Ok(_) => return Err(Error::InvalidTag),
179 Err(e) => return Err(e),
180 }
181
182 let enabled_since = ok!(slice.load_u32());
183 let actual_min_split = ok!(slice.load_u8());
184 let min_split = ok!(slice.load_u8());
185 let max_split = ok!(slice.load_u8());
186 let flags = ok!(slice.load_u16());
187 if flags << 3 != 0 {
188 return Err(Error::InvalidData);
189 }
190
191 let result = Self {
192 enabled_since,
193 actual_min_split,
194 min_split,
195 max_split,
196 active: flags & 0b0100_0000_0000_0000 != 0,
197 accept_msgs: flags & 0b0010_0000_0000_0000 != 0,
198 zerostate_root_hash: ok!(slice.load_u256()),
199 zerostate_file_hash: ok!(slice.load_u256()),
200 version: ok!(slice.load_u32()),
201 format: ok!(WorkchainFormat::load_from(slice)),
202 };
203
204 let basic = flags & 0b1000_0000_0000_0000 != 0;
205 if basic != result.format.is_basic() {
206 return Err(Error::InvalidData);
207 }
208
209 Ok(result)
210 }
211}
212
213#[derive(Debug, Copy, Clone, Eq, PartialEq)]
215#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
216#[cfg_attr(feature = "serde", serde(tag = "ty"))]
217pub enum WorkchainFormat {
218 Basic(WorkchainFormatBasic),
220 Extended(WorkchainFormatExtended),
222}
223
224impl WorkchainFormat {
225 pub fn is_valid(&self) -> bool {
227 match self {
228 Self::Basic(_) => true,
229 Self::Extended(format) => format.is_valid(),
230 }
231 }
232
233 pub fn is_basic(&self) -> bool {
237 matches!(self, Self::Basic(_))
238 }
239}
240
241impl Store for WorkchainFormat {
242 fn store_into(
243 &self,
244 builder: &mut CellBuilder,
245 context: &dyn CellContext,
246 ) -> Result<(), Error> {
247 match self {
248 Self::Basic(value) => {
249 ok!(builder.store_small_uint(0x1, 4));
250 value.store_into(builder, context)
251 }
252 Self::Extended(value) => {
253 ok!(builder.store_small_uint(0x0, 4));
254 value.store_into(builder, context)
255 }
256 }
257 }
258}
259
260impl<'a> Load<'a> for WorkchainFormat {
261 fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
262 Ok(match ok!(slice.load_small_uint(4)) {
263 0x1 => Self::Basic(ok!(WorkchainFormatBasic::load_from(slice))),
264 0x0 => Self::Extended(ok!(WorkchainFormatExtended::load_from(slice))),
265 _ => return Err(Error::InvalidTag),
266 })
267 }
268}
269
270#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
272#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
273pub struct WorkchainFormatBasic {
274 pub vm_version: i32,
276 pub vm_mode: u64,
278}
279
280#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
282#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
283#[tlb(validate_with = "Self::is_valid")]
284pub struct WorkchainFormatExtended {
285 pub min_addr_len: Uint12,
287 pub max_addr_len: Uint12,
289 pub addr_len_step: Uint12,
291 pub workchain_type_id: NonZeroU32,
293}
294
295impl WorkchainFormatExtended {
296 pub fn is_valid(&self) -> bool {
298 self.min_addr_len >= Uint12::new(64)
299 && self.min_addr_len <= self.max_addr_len
300 && self.max_addr_len <= Uint12::new(1023)
301 && self.addr_len_step <= Uint12::new(1023)
302 }
303
304 pub fn check_addr_len(&self, addr_len: u16) -> bool {
306 let addr_len = Uint12::new(addr_len);
307
308 let is_aligned = || {
309 if self.addr_len_step.is_zero() {
310 return false;
311 }
312
313 let var_part = addr_len - self.min_addr_len;
314 let step_rem = var_part.into_inner() % self.addr_len_step.into_inner();
315 step_rem == 0
316 };
317
318 addr_len >= self.min_addr_len
319 && addr_len <= self.max_addr_len
320 && (addr_len == self.min_addr_len || addr_len == self.max_addr_len || is_aligned())
321 }
322}
323
324#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
326#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
327#[tlb(tag = "#6b")]
328pub struct BlockCreationRewards {
329 pub masterchain_block_fee: Tokens,
331 pub basechain_block_fee: Tokens,
333}
334
335#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
337#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
338pub struct ElectionTimings {
339 pub validators_elected_for: u32,
341 pub elections_start_before: u32,
343 pub elections_end_before: u32,
345 pub stake_held_for: u32,
347}
348
349#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
351#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
352pub struct ValidatorCountParams {
353 pub max_validators: u16,
355 pub max_main_validators: u16,
357 pub min_validators: u16,
359}
360
361#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
363#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
364pub struct ValidatorStakeParams {
365 pub min_stake: Tokens,
367 pub max_stake: Tokens,
369 pub min_total_stake: Tokens,
371 pub max_stake_factor: u32,
373}
374
375#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
377#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
378#[tlb(tag = "#cc")]
379pub struct StoragePrices {
380 pub utime_since: u32,
382 pub bit_price_ps: u64,
384 pub cell_price_ps: u64,
386 pub mc_bit_price_ps: u64,
388 pub mc_cell_price_ps: u64,
390}
391
392impl StoragePrices {
393 pub fn compute_storage_fee(
395 &self,
396 is_masterchain: bool,
397 delta: u64,
398 stats: CellTreeStats,
399 ) -> Tokens {
400 let mut res = if is_masterchain {
401 (stats.cell_count as u128 * self.mc_cell_price_ps as u128)
402 .saturating_add(stats.bit_count as u128 * self.mc_bit_price_ps as u128)
403 } else {
404 (stats.cell_count as u128 * self.cell_price_ps as u128)
405 .saturating_add(stats.bit_count as u128 * self.bit_price_ps as u128)
406 };
407 res = res.saturating_mul(delta as u128);
408 Tokens::new(shift_ceil_price(res))
409 }
410}
411
412#[derive(Default, Debug, Clone, Eq, PartialEq)]
414#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
415pub struct GasLimitsPrices {
416 pub gas_price: u64,
418 pub gas_limit: u64,
420 pub special_gas_limit: u64,
422 pub gas_credit: u64,
424 pub block_gas_limit: u64,
426 pub freeze_due_limit: u64,
428 pub delete_due_limit: u64,
430 pub flat_gas_limit: u64,
432 pub flat_gas_price: u64,
436}
437
438impl GasLimitsPrices {
439 pub fn compute_gas_fee(&self, gas_used: u64) -> Tokens {
441 let mut res = self.flat_gas_price as u128;
442 if let Some(extra_gas) = gas_used.checked_sub(self.flat_gas_limit) {
443 res = res.saturating_add(shift_ceil_price(self.gas_price as u128 * extra_gas as u128));
444 }
445 Tokens::new(res)
446 }
447}
448
449impl GasLimitsPrices {
450 const TAG_BASE: u8 = 0xdd;
451 const TAG_EXT: u8 = 0xde;
452 const TAG_FLAT_PFX: u8 = 0xd1;
453}
454
455impl Store for GasLimitsPrices {
456 fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
457 ok!(builder.store_u8(Self::TAG_FLAT_PFX));
458 ok!(builder.store_u64(self.flat_gas_limit));
459 ok!(builder.store_u64(self.flat_gas_price));
460 ok!(builder.store_u8(Self::TAG_EXT));
461 ok!(builder.store_u64(self.gas_price));
462 ok!(builder.store_u64(self.gas_limit));
463 ok!(builder.store_u64(self.special_gas_limit));
464 ok!(builder.store_u64(self.gas_credit));
465 ok!(builder.store_u64(self.block_gas_limit));
466 ok!(builder.store_u64(self.freeze_due_limit));
467 builder.store_u64(self.delete_due_limit)
468 }
469}
470
471impl<'a> Load<'a> for GasLimitsPrices {
472 fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
473 let mut result = Self::default();
474 loop {
475 match slice.load_u8() {
476 Ok(Self::TAG_FLAT_PFX) => {
477 result.flat_gas_limit = ok!(slice.load_u64());
478 result.flat_gas_price = ok!(slice.load_u64());
479 }
480 Ok(Self::TAG_EXT) => {
481 result.gas_price = ok!(slice.load_u64());
482 result.gas_limit = ok!(slice.load_u64());
483 result.special_gas_limit = ok!(slice.load_u64());
484 result.gas_credit = ok!(slice.load_u64());
485 result.block_gas_limit = ok!(slice.load_u64());
486 result.freeze_due_limit = ok!(slice.load_u64());
487 result.delete_due_limit = ok!(slice.load_u64());
488 return Ok(result);
489 }
490 Ok(Self::TAG_BASE) => {
491 result.gas_price = ok!(slice.load_u64());
492 result.gas_limit = ok!(slice.load_u64());
493 result.gas_credit = ok!(slice.load_u64());
494 result.block_gas_limit = ok!(slice.load_u64());
495 result.freeze_due_limit = ok!(slice.load_u64());
496 result.delete_due_limit = ok!(slice.load_u64());
497 return Ok(result);
498 }
499 Ok(_) => return Err(Error::InvalidTag),
500 Err(e) => return Err(e),
501 }
502 }
503 }
504}
505
506#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
508#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
509#[tlb(tag = "#c3", validate_with = "Self::is_valid")]
510pub struct BlockParamLimits {
511 pub underload: u32,
513 pub soft_limit: u32,
515 pub hard_limit: u32,
517}
518
519impl BlockParamLimits {
520 pub fn is_valid(&self) -> bool {
522 self.underload <= self.soft_limit && self.soft_limit <= self.hard_limit
523 }
524}
525
526#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
528#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
529#[tlb(tag = "#5d")]
530pub struct BlockLimits {
531 pub bytes: BlockParamLimits,
533 pub gas: BlockParamLimits,
535 pub lt_delta: BlockParamLimits,
537}
538
539#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
541#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
542#[tlb(tag = "#ea")]
543pub struct MsgForwardPrices {
544 pub lump_price: u64,
546 pub bit_price: u64,
548 pub cell_price: u64,
550 pub ihr_price_factor: u32,
552 pub first_frac: u16,
554 pub next_frac: u16,
556}
557
558impl MsgForwardPrices {
559 pub fn compute_fwd_fee(&self, stats: CellTreeStats) -> Tokens {
561 let lump = self.lump_price as u128;
562 let extra = shift_ceil_price(
563 (stats.cell_count as u128 * self.cell_price as u128)
564 .saturating_add(stats.bit_count as u128 * self.bit_price as u128),
565 );
566 Tokens::new(lump.saturating_add(extra))
567 }
568
569 pub fn get_first_part(&self, total: Tokens) -> Tokens {
571 Tokens::new(total.into_inner().saturating_mul(self.first_frac as _) >> 16)
572 }
573
574 pub fn get_next_part(&self, total: Tokens) -> Tokens {
576 Tokens::new(total.into_inner().saturating_mul(self.next_frac as _) >> 16)
577 }
578}
579
580#[cfg(not(feature = "tycho"))]
582#[derive(Debug, Copy, Clone, Eq, PartialEq)]
583#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
584pub struct CatchainConfig {
585 pub isolate_mc_validators: bool,
587 pub shuffle_mc_validators: bool,
589 pub mc_catchain_lifetime: u32,
591 pub shard_catchain_lifetime: u32,
593 pub shard_validators_lifetime: u32,
595 pub shard_validators_num: u32,
597}
598
599#[cfg(not(feature = "tycho"))]
600impl CatchainConfig {
601 const TAG_V1: u8 = 0xc1;
602 const TAG_V2: u8 = 0xc2;
603}
604
605#[cfg(not(feature = "tycho"))]
606impl Store for CatchainConfig {
607 fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
608 let flags = ((self.isolate_mc_validators as u8) << 1) | (self.shuffle_mc_validators as u8);
609 ok!(builder.store_u8(Self::TAG_V2));
610 ok!(builder.store_u8(flags));
611 ok!(builder.store_u32(self.mc_catchain_lifetime));
612 ok!(builder.store_u32(self.shard_catchain_lifetime));
613 ok!(builder.store_u32(self.shard_validators_lifetime));
614 builder.store_u32(self.shard_validators_num)
615 }
616}
617
618#[cfg(not(feature = "tycho"))]
619impl<'a> Load<'a> for CatchainConfig {
620 fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
621 let flags = match slice.load_u8() {
622 Ok(Self::TAG_V1) => 0,
623 Ok(Self::TAG_V2) => ok!(slice.load_u8()),
624 Ok(_) => return Err(Error::InvalidTag),
625 Err(e) => return Err(e),
626 };
627 if flags >> 2 != 0 {
628 return Err(Error::InvalidData);
629 }
630 Ok(Self {
631 isolate_mc_validators: flags & 0b10 != 0,
632 shuffle_mc_validators: flags & 0b01 != 0,
633 mc_catchain_lifetime: ok!(slice.load_u32()),
634 shard_catchain_lifetime: ok!(slice.load_u32()),
635 shard_validators_lifetime: ok!(slice.load_u32()),
636 shard_validators_num: ok!(slice.load_u32()),
637 })
638 }
639}
640
641#[cfg(feature = "tycho")]
664#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
665#[tlb(tag = "#a6")]
666#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
667pub struct CollationConfig {
668 pub shuffle_mc_validators: bool,
670
671 pub mc_block_min_interval_ms: u32,
673
674 pub empty_sc_block_interval_ms: u32,
676
677 pub max_uncommitted_chain_length: u8,
679 pub wu_used_to_import_next_anchor: u64,
681
682 pub msgs_exec_params: MsgsExecutionParams,
684
685 pub work_units_params: WorkUnitsParams,
687}
688
689#[cfg(feature = "tycho")]
704#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
705#[tlb(tag = ["#00", "#01"])]
706#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
707pub struct MsgsExecutionParams {
708 pub buffer_limit: u32,
710
711 pub group_limit: u16,
716 pub group_vert_size: u16,
719
720 pub externals_expire_timeout: u16,
722
723 pub open_ranges_limit: u16,
726
727 pub par_0_int_msgs_count_limit: u32,
731
732 pub par_0_ext_msgs_count_limit: u32,
736
737 pub group_slots_fractions: Dict<u16, u8>,
740
741 #[tlb(since_tag = 1)]
743 pub range_messages_limit: u32,
744}
745
746#[cfg(feature = "tycho")]
756#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
757#[tlb(tag = "#00")]
758#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
759pub struct WorkUnitsParams {
760 pub prepare: WorkUnitsParamsPrepare,
762 pub execute: WorkUnitsParamsExecute,
764 pub finalize: WorkUnitsParamsFinalize,
766}
767
768#[cfg(feature = "tycho")]
782#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
783#[tlb(tag = "#00")]
784#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
785pub struct WorkUnitsParamsPrepare {
786 pub fixed_part: u32,
788 pub msgs_stats: u16,
790 pub remaning_msgs_stats: u16,
792 pub read_ext_msgs: u16,
794 pub read_int_msgs: u16,
796 pub read_new_msgs: u16,
798 pub add_to_msg_groups: u16,
800}
801
802#[cfg(feature = "tycho")]
817#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
818#[tlb(tag = "#00")]
819#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
820pub struct WorkUnitsParamsExecute {
821 pub prepare: u32,
823 pub execute: u16,
825 pub execute_err: u16,
827 pub execute_delimiter: u32,
829 pub serialize_enqueue: u16,
831 pub serialize_dequeue: u16,
833 pub insert_new_msgs: u16,
835 pub subgroup_size: u16,
837}
838
839#[cfg(feature = "tycho")]
860#[derive(Debug, Clone, Eq, PartialEq, Store, Load, Default)]
861#[tlb(tag = "#00")]
862#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
863pub struct WorkUnitsParamsFinalize {
864 pub build_transactions: u16,
866 pub build_accounts: u16,
868 pub build_in_msg: u16,
870 pub build_out_msg: u16,
872 pub serialize_min: u32,
874 pub serialize_accounts: u16,
876 pub serialize_msg: u16,
878 pub state_update_min: u32,
880 pub state_update_accounts: u16,
882 pub state_update_msg: u16,
884 pub create_diff: u16,
886 pub serialize_diff: u16,
888 pub apply_diff: u16,
890 pub diff_tail_len: u16,
892}
893
894#[cfg(feature = "tycho")]
912#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
913#[tlb(tag = "#d8")]
914#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
915pub struct ConsensusConfig {
916 pub clock_skew_millis: u16,
923
924 pub payload_batch_bytes: u32,
928
929 pub commit_history_rounds: u16,
933
934 pub deduplicate_rounds: u16,
938
939 pub max_consensus_lag_rounds: u16,
956
957 pub payload_buffer_bytes: u32,
960
961 pub broadcast_retry_millis: u16,
965
966 pub download_retry_millis: u16,
970
971 pub download_peers: u8,
975
976 pub download_tasks: u16,
978
979 pub sync_support_rounds: u16,
982}
983
984#[cfg(not(feature = "tycho"))]
986#[derive(Debug, Clone, Eq, PartialEq)]
987#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
988pub struct ConsensusConfig {
989 pub new_catchain_ids: bool,
991 pub round_candidates: NonZeroU32,
993 pub next_candidate_delay_ms: u32,
995 pub consensus_timeout_ms: u32,
997 pub fast_attempts: u32,
999 pub attempt_duration: u32,
1001 pub catchain_max_deps: u32,
1003 pub max_block_bytes: u32,
1005 pub max_collated_bytes: u32,
1007}
1008
1009#[cfg(not(feature = "tycho"))]
1010impl ConsensusConfig {
1011 const TAG_V1: u8 = 0xd6;
1012 const TAG_V2: u8 = 0xd7;
1013}
1014
1015#[cfg(not(feature = "tycho"))]
1016impl Store for ConsensusConfig {
1017 fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
1018 let flags = self.new_catchain_ids as u8;
1019
1020 ok!(builder.store_u8(Self::TAG_V2));
1021 ok!(builder.store_u8(flags));
1022 ok!(builder.store_u8(self.round_candidates.get() as u8));
1023 ok!(builder.store_u32(self.next_candidate_delay_ms));
1024 ok!(builder.store_u32(self.consensus_timeout_ms));
1025 ok!(builder.store_u32(self.fast_attempts));
1026 ok!(builder.store_u32(self.attempt_duration));
1027 ok!(builder.store_u32(self.catchain_max_deps));
1028 ok!(builder.store_u32(self.max_block_bytes));
1029 builder.store_u32(self.max_collated_bytes)
1030 }
1031}
1032
1033#[cfg(not(feature = "tycho"))]
1034impl<'a> Load<'a> for ConsensusConfig {
1035 fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
1036 use std::num::NonZeroU8;
1037
1038 let (flags, round_candidates) = match slice.load_u8() {
1039 Ok(Self::TAG_V1) => (0, ok!(NonZeroU32::load_from(slice))),
1040 Ok(Self::TAG_V2) => {
1041 let flags = ok!(slice.load_u8());
1042 if flags >> 1 != 0 {
1043 return Err(Error::InvalidData);
1044 }
1045 (flags, ok!(NonZeroU8::load_from(slice)).into())
1046 }
1047 Ok(_) => return Err(Error::InvalidTag),
1048 Err(e) => return Err(e),
1049 };
1050 Ok(Self {
1051 new_catchain_ids: flags & 0b1 != 0,
1052 round_candidates,
1053 next_candidate_delay_ms: ok!(slice.load_u32()),
1054 consensus_timeout_ms: ok!(slice.load_u32()),
1055 fast_attempts: ok!(slice.load_u32()),
1056 attempt_duration: ok!(slice.load_u32()),
1057 catchain_max_deps: ok!(slice.load_u32()),
1058 max_block_bytes: ok!(slice.load_u32()),
1059 max_collated_bytes: ok!(slice.load_u32()),
1060 })
1061 }
1062}
1063
1064#[derive(Debug, Clone, Eq, PartialEq)]
1066#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1067pub struct ValidatorSet {
1068 pub utime_since: u32,
1070 pub utime_until: u32,
1072 pub main: NonZeroU16,
1074 pub total_weight: u64,
1076 pub list: Vec<ValidatorDescription>,
1078}
1079
1080impl ValidatorSet {
1081 const TAG_V1: u8 = 0x11;
1082 const TAG_V2: u8 = 0x12;
1083
1084 #[cfg(not(feature = "tycho"))]
1086 pub fn compute_subset(
1087 &self,
1088 shard_ident: ShardIdent,
1089 cc_config: &CatchainConfig,
1090 cc_seqno: u32,
1091 ) -> Option<(Vec<ValidatorDescription>, u32)> {
1092 if shard_ident.is_masterchain() {
1093 return self.compute_mc_subset(cc_seqno, cc_config.shuffle_mc_validators);
1094 }
1095
1096 let total = self.list.len();
1097 let main = self.main.get() as usize;
1098
1099 let mut prng = ValidatorSetPRNG::new(shard_ident, cc_seqno);
1100
1101 let vset = if cc_config.isolate_mc_validators {
1102 if total <= main {
1103 return None;
1104 }
1105
1106 let mut list = self.list[main..].to_vec();
1107
1108 let mut total_weight = 0u64;
1109 for descr in &mut list {
1110 descr.prev_total_weight = total_weight;
1111 total_weight += descr.weight;
1112 }
1113
1114 std::borrow::Cow::Owned(Self {
1115 utime_since: self.utime_since,
1116 utime_until: self.utime_until,
1117 main: self.main,
1118 total_weight,
1119 list,
1120 })
1121 } else {
1122 std::borrow::Cow::Borrowed(self)
1123 };
1124
1125 let count = std::cmp::min(vset.list.len(), cc_config.shard_validators_num as usize);
1126
1127 let mut nodes = Vec::with_capacity(count);
1128 let mut holes = Vec::<(u64, u64)>::with_capacity(count);
1129 let mut total_wt = vset.total_weight;
1130
1131 for _ in 0..count {
1132 debug_assert!(total_wt > 0);
1133
1134 let mut p = prng.next_ranged(total_wt);
1136
1137 for (prev_total_weight, weight) in &holes {
1138 if p < *prev_total_weight {
1139 break;
1140 }
1141 p += weight;
1142 }
1143
1144 let entry = vset.at_weight(p);
1145
1146 nodes.push(ValidatorDescription {
1147 public_key: entry.public_key,
1148 weight: 1,
1149 adnl_addr: entry.adnl_addr,
1150 mc_seqno_since: 0,
1151 prev_total_weight: 0,
1152 });
1153 debug_assert!(total_wt >= entry.weight);
1154 total_wt -= entry.weight;
1155
1156 let new_hole = (entry.prev_total_weight, entry.weight);
1157 let i = holes.partition_point(|item| item <= &new_hole);
1158 debug_assert!(i == 0 || holes[i - 1] < new_hole);
1159
1160 holes.insert(i, new_hole);
1161 }
1162
1163 let hash_short = Self::compute_subset_hash_short(&nodes, cc_seqno);
1164
1165 Some((nodes, hash_short))
1166 }
1167
1168 pub fn compute_mc_subset(
1172 &self,
1173 cc_seqno: u32,
1174 shuffle: bool,
1175 ) -> Option<(Vec<ValidatorDescription>, u32)> {
1176 let total = self.list.len();
1177 let main = self.main.get() as usize;
1178
1179 let count = std::cmp::min(total, main);
1180 let subset = if !shuffle {
1181 self.list[0..count].to_vec()
1182 } else {
1183 let mut prng = ValidatorSetPRNG::new(ShardIdent::MASTERCHAIN, cc_seqno);
1184
1185 let mut indices = vec![0; count];
1186 for i in 0..count {
1187 let j = prng.next_ranged(i as u64 + 1) as usize; debug_assert!(j <= i);
1189 indices[i] = indices[j];
1190 indices[j] = i;
1191 }
1192
1193 let mut subset = Vec::with_capacity(count);
1194 for index in indices.into_iter().take(count) {
1195 subset.push(self.list[index].clone());
1196 }
1197 subset
1198 };
1199
1200 let hash_short = Self::compute_subset_hash_short(&subset, cc_seqno);
1201 Some((subset, hash_short))
1202 }
1203
1204 pub fn compute_subset_hash_short(subset: &[ValidatorDescription], cc_seqno: u32) -> u32 {
1206 const HASH_SHORT_MAGIC: u32 = 0x901660ED;
1207
1208 let mut hash = crc32c::crc32c(&HASH_SHORT_MAGIC.to_le_bytes());
1209 hash = crc32c::crc32c_append(hash, &cc_seqno.to_le_bytes());
1210 hash = crc32c::crc32c_append(hash, &(subset.len() as u32).to_le_bytes());
1211
1212 for node in subset {
1213 hash = crc32c::crc32c_append(hash, node.public_key.as_slice());
1214 hash = crc32c::crc32c_append(hash, &node.weight.to_le_bytes());
1215 hash = crc32c::crc32c_append(
1216 hash,
1217 node.adnl_addr
1218 .as_ref()
1219 .unwrap_or(HashBytes::wrap(&[0u8; 32]))
1220 .as_ref(),
1221 );
1222 }
1223
1224 hash
1225 }
1226
1227 #[cfg(not(feature = "tycho"))]
1228 fn at_weight(&self, weight_pos: u64) -> &ValidatorDescription {
1229 debug_assert!(weight_pos < self.total_weight);
1230 debug_assert!(!self.list.is_empty());
1231 let i = self
1232 .list
1233 .partition_point(|item| item.prev_total_weight <= weight_pos);
1234 debug_assert!(i != 0);
1235 &self.list[i - 1]
1236 }
1237}
1238
1239impl Store for ValidatorSet {
1240 fn store_into(
1241 &self,
1242 builder: &mut CellBuilder,
1243 context: &dyn CellContext,
1244 ) -> Result<(), Error> {
1245 let Ok(total) = u16::try_from(self.list.len()) else {
1246 return Err(Error::IntOverflow);
1247 };
1248
1249 let mut validators = Dict::<u16, ValidatorDescription>::new();
1251 for (i, item) in self.list.iter().enumerate() {
1252 ok!(validators.set_ext(i as u16, item, context));
1253 }
1254
1255 ok!(builder.store_u8(Self::TAG_V2));
1256 ok!(builder.store_u32(self.utime_since));
1257 ok!(builder.store_u32(self.utime_until));
1258 ok!(builder.store_u16(total));
1259 ok!(builder.store_u16(self.main.get()));
1260 ok!(builder.store_u64(self.total_weight));
1261 validators.store_into(builder, context)
1262 }
1263}
1264
1265impl<'a> Load<'a> for ValidatorSet {
1266 fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
1267 let with_total_weight = match slice.load_u8() {
1268 Ok(Self::TAG_V1) => false,
1269 Ok(Self::TAG_V2) => true,
1270 Ok(_) => return Err(Error::InvalidTag),
1271 Err(e) => return Err(e),
1272 };
1273
1274 let utime_since = ok!(slice.load_u32());
1275 let utime_until = ok!(slice.load_u32());
1276 let total = ok!(slice.load_u16()) as usize;
1277 let main = ok!(NonZeroU16::load_from(slice));
1278 if main.get() as usize > total {
1279 return Err(Error::InvalidData);
1280 }
1281
1282 let context = Cell::empty_context();
1283
1284 let (mut total_weight, validators) = if with_total_weight {
1285 let total_weight = ok!(slice.load_u64());
1286 let dict = ok!(Dict::<u16, ValidatorDescription>::load_from(slice));
1287 (total_weight, dict)
1288 } else {
1289 let dict = ok!(Dict::<u16, ValidatorDescription>::load_from_root_ext(
1290 slice, context
1291 ));
1292 (0, dict)
1293 };
1294
1295 let mut computed_total_weight = 0u64;
1296 let mut list = Vec::with_capacity(std::cmp::min(total, 512));
1297 for (i, entry) in validators.iter().enumerate().take(total) {
1298 let mut descr = match entry {
1299 Ok((idx, descr)) if idx as usize == i => descr,
1300 Ok(_) => return Err(Error::InvalidData),
1301 Err(e) => return Err(e),
1302 };
1303
1304 descr.prev_total_weight = computed_total_weight;
1305 computed_total_weight = match computed_total_weight.checked_add(descr.weight) {
1306 Some(weight) => weight,
1307 None => return Err(Error::InvalidData),
1308 };
1309 list.push(descr);
1310 }
1311
1312 if list.is_empty() {
1313 return Err(Error::InvalidData);
1314 }
1315
1316 if with_total_weight {
1317 if total_weight != computed_total_weight {
1318 return Err(Error::InvalidData);
1319 }
1320 } else {
1321 total_weight = computed_total_weight;
1322 }
1323
1324 Ok(Self {
1325 utime_since,
1326 utime_until,
1327 main,
1328 total_weight,
1329 list,
1330 })
1331 }
1332}
1333
1334#[cfg(feature = "serde")]
1335impl<'de> serde::Deserialize<'de> for ValidatorSet {
1336 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1337 use serde::de::Error;
1338
1339 #[derive(serde::Deserialize)]
1340 struct ValidatorSetHelper {
1341 utime_since: u32,
1342 utime_until: u32,
1343 main: NonZeroU16,
1344 #[serde(default)]
1345 total_weight: u64,
1346 list: Vec<ValidatorDescription>,
1347 }
1348
1349 let parsed = ValidatorSetHelper::deserialize(deserializer)?;
1350 if parsed.list.is_empty() {
1351 return Err(Error::custom("empty validators list"));
1352 }
1353
1354 let mut result = Self {
1355 utime_since: parsed.utime_since,
1356 utime_until: parsed.utime_until,
1357 main: parsed.main,
1358 total_weight: 0,
1359 list: parsed.list,
1360 };
1361
1362 for descr in &mut result.list {
1363 descr.prev_total_weight = result.total_weight;
1364 let Some(new_total_weight) = result.total_weight.checked_add(descr.weight) else {
1365 return Err(Error::custom("total weight overflow"));
1366 };
1367 result.total_weight = new_total_weight;
1368 }
1369
1370 if parsed.total_weight > 0 && parsed.total_weight != result.total_weight {
1371 return Err(Error::custom("total weight mismatch"));
1372 }
1373
1374 Ok(result)
1375 }
1376}
1377
1378#[derive(Debug, Clone, Eq, PartialEq)]
1380#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1381pub struct ValidatorDescription {
1382 pub public_key: HashBytes, pub weight: u64,
1386 #[cfg_attr(feature = "serde", serde(default))]
1388 pub adnl_addr: Option<HashBytes>,
1389 #[cfg_attr(feature = "serde", serde(default))]
1391 pub mc_seqno_since: u32,
1392
1393 #[cfg_attr(feature = "serde", serde(skip))]
1396 pub prev_total_weight: u64,
1397}
1398
1399impl ValidatorDescription {
1400 const TAG_BASIC: u8 = 0x53;
1401 const TAG_WITH_ADNL: u8 = 0x73;
1402 const TAG_WITH_MC_SEQNO: u8 = 0x93;
1403
1404 const PUBKEY_TAG: u32 = 0x8e81278a;
1405
1406 pub fn verify_signature(&self, data: &[u8], signature: &Signature) -> bool {
1408 if let Some(public_key) = ed25519::PublicKey::from_bytes(self.public_key.0) {
1409 public_key.verify_raw(data, signature.as_ref())
1410 } else {
1411 false
1412 }
1413 }
1414}
1415
1416impl Store for ValidatorDescription {
1417 fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
1418 let with_mc_seqno = self.mc_seqno_since != 0;
1419
1420 let tag = if with_mc_seqno {
1421 Self::TAG_WITH_MC_SEQNO
1422 } else if self.adnl_addr.is_some() {
1423 Self::TAG_WITH_ADNL
1424 } else {
1425 Self::TAG_BASIC
1426 };
1427
1428 ok!(builder.store_u8(tag));
1429 ok!(builder.store_u32(Self::PUBKEY_TAG));
1430 ok!(builder.store_u256(&self.public_key));
1431 ok!(builder.store_u64(self.weight));
1432
1433 let mut adnl = self.adnl_addr.as_ref();
1434 if with_mc_seqno {
1435 adnl = Some(HashBytes::wrap(&[0; 32]));
1436 }
1437
1438 if let Some(adnl) = adnl {
1439 ok!(builder.store_u256(adnl));
1440 }
1441
1442 if with_mc_seqno {
1443 builder.store_u32(self.mc_seqno_since)
1444 } else {
1445 Ok(())
1446 }
1447 }
1448}
1449
1450impl<'a> Load<'a> for ValidatorDescription {
1451 fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
1452 let (with_adnl, with_mc_seqno) = match slice.load_u8() {
1453 Ok(Self::TAG_BASIC) => (false, false),
1454 Ok(Self::TAG_WITH_ADNL) => (true, false),
1455 Ok(Self::TAG_WITH_MC_SEQNO) => (true, true),
1456 Ok(_) => return Err(Error::InvalidTag),
1457 Err(e) => return Err(e),
1458 };
1459
1460 Ok(Self {
1461 public_key: {
1462 match slice.load_u32() {
1463 Ok(Self::PUBKEY_TAG) => ok!(slice.load_u256()),
1464 Ok(_) => return Err(Error::InvalidTag),
1465 Err(e) => return Err(e),
1466 }
1467 },
1468 weight: ok!(slice.load_u64()),
1469 adnl_addr: if with_adnl {
1470 Some(ok!(slice.load_u256()))
1471 } else {
1472 None
1473 },
1474 mc_seqno_since: if with_mc_seqno {
1475 ok!(slice.load_u32())
1476 } else {
1477 0
1478 },
1479 prev_total_weight: 0,
1480 })
1481 }
1482}
1483
1484pub struct ValidatorSetPRNG {
1486 context: [u8; 48],
1487 bag: [u64; 8],
1488}
1489
1490impl ValidatorSetPRNG {
1491 pub fn new(shard_ident: ShardIdent, cc_seqno: u32) -> Self {
1493 let seed = [0; 32];
1494 Self::with_seed(shard_ident, cc_seqno, &seed)
1495 }
1496
1497 pub fn with_seed(shard_ident: ShardIdent, cc_seqno: u32, seed: &[u8; 32]) -> Self {
1499 let mut context = [0u8; 48];
1500 context[..32].copy_from_slice(seed);
1501 context[32..40].copy_from_slice(&shard_ident.prefix().to_be_bytes());
1502 context[40..44].copy_from_slice(&shard_ident.workchain().to_be_bytes());
1503 context[44..48].copy_from_slice(&cc_seqno.to_be_bytes());
1504
1505 let mut res = ValidatorSetPRNG {
1506 context,
1507 bag: [0; 8],
1508 };
1509 res.bag[0] = 8;
1510 res
1511 }
1512
1513 pub fn next_u64(&mut self) -> u64 {
1515 if self.cursor() < 7 {
1516 let next = self.bag[1 + self.cursor() as usize];
1517 self.bag[0] += 1;
1518 next
1519 } else {
1520 self.reset()
1521 }
1522 }
1523
1524 pub fn next_ranged(&mut self, range: u64) -> u64 {
1526 let val = self.next_u64();
1527 ((range as u128 * val as u128) >> 64) as u64
1528 }
1529
1530 fn reset(&mut self) -> u64 {
1531 use sha2::digest::Digest;
1532
1533 let hash: [u8; 64] = sha2::Sha512::digest(self.context).into();
1534
1535 for ctx in self.context[..32].iter_mut().rev() {
1536 *ctx = ctx.wrapping_add(1);
1537 if *ctx != 0 {
1538 break;
1539 }
1540 }
1541
1542 unsafe {
1544 std::ptr::copy_nonoverlapping(hash.as_ptr(), self.bag.as_mut_ptr() as *mut u8, 64);
1545 }
1546
1547 #[cfg(target_endian = "little")]
1549 self.bag
1550 .iter_mut()
1551 .for_each(|item| *item = item.swap_bytes());
1552
1553 std::mem::take(&mut self.bag[0])
1555 }
1556
1557 #[inline]
1558 const fn cursor(&self) -> u64 {
1559 self.bag[0]
1560 }
1561}
1562
1563#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
1575#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1576#[tlb(tag = "#02")]
1577pub struct SizeLimitsConfig {
1578 pub max_msg_bits: u32,
1580 pub max_msg_cells: u32,
1582 pub max_library_cells: u32,
1584 pub max_vm_data_depth: u16,
1586 pub max_ext_msg_size: u32,
1588 pub max_ext_msg_depth: u16,
1590 pub max_acc_state_cells: u32,
1592 pub max_acc_state_bits: u32,
1594 pub max_acc_public_libraries: u32,
1596 pub defer_out_queue_size_limit: u32,
1598}
1599
1600const fn shift_ceil_price(value: u128) -> u128 {
1601 let r = value & 0xffff != 0;
1602 (value >> 16) + r as u128
1603}
1604
1605#[cfg(test)]
1606mod tests {
1607 use super::*;
1608
1609 #[test]
1610 fn validator_set_prng() {
1611 fn make_indices(cc_seqno: u32) -> Vec<usize> {
1612 let mut prng = ValidatorSetPRNG::new(ShardIdent::BASECHAIN, cc_seqno);
1613
1614 let count = 10;
1615 let mut indices = vec![0; count];
1616 for i in 0..count {
1617 let j = prng.next_ranged(i as u64 + 1) as usize;
1618 debug_assert!(j <= i);
1619 indices[i] = indices[j];
1620 indices[j] = i;
1621 }
1622
1623 indices
1624 }
1625
1626 let vs10_first = make_indices(10);
1627 let vs10_second = make_indices(10);
1628 assert_eq!(vs10_first, vs10_second);
1629
1630 let vs11_first = make_indices(11);
1631 let vs11_second = make_indices(11);
1632 assert_eq!(vs11_first, vs11_second);
1633 assert_ne!(vs10_first, vs11_second);
1634 }
1635}