1#[allow(unused_imports)]
2use super::Authorizer;
3use crate::{FixedVec, chain_params, opaque};
4use bounded_collections::{BoundedVec, Get};
5use codec::{Decode, Encode, MaxEncodedLen};
6use core::sync::atomic::Ordering::Relaxed;
7
8pub const GP_VERSION: &str = "0.8.0";
10
11pub const JAM_COMMON_ERA: u64 = 1_735_732_800;
14
15pub const MEMO_LEN: usize = 128;
17
18#[doc(hidden)]
20pub const MAX_PREIMAGE_LEN: usize = 32 * 1024 * 1024;
22
23#[doc(hidden)]
28pub const MAX_PREIMAGE_BLOB_LEN: usize = MAX_PREIMAGE_LEN - 8;
29
30pub const PAGE_SIZE: u32 = 4096;
32
33pub const SUFFIX_SKIP_LEN: usize = 6;
36
37pub const PROVEN_PER_SEGMENT: usize = 1 << SUFFIX_SKIP_LEN;
45
46pub const FOOTPRINT_STORAGE_OVERHEAD: u64 = 34;
49
50pub const FOOTPRINT_PREIMAGE_OVERHEAD: u64 = 81;
53
54pub type TicketAttempt = u8;
56
57const DEFAULT_PARAMS: ProtocolParameters = ProtocolParameters::full();
58
59chain_params! {
60 pub fn max_val_count() -> ValIndex {
62 (core_count() as usize * VALS_PER_CORE) as ValIndex
63 }
64 pub struct MaxValCount; impl Get<_> for _ {}
65
66 static AUTH_QUEUE_LEN: _ = _(DEFAULT_PARAMS.auth_queue_len as _);
68 pub fn auth_queue_len() -> usize;
69 pub struct AuthQueueLen; impl Get<_> for _ {}
70
71 static MIN_TURNAROUND_PERIOD: _ = _(DEFAULT_PARAMS.min_turnaround_period);
80 pub fn min_turnaround_period() -> Slot;
81
82 static MAX_WORK_ITEMS: _ = _(DEFAULT_PARAMS.max_work_items as _);
84 pub fn max_work_items() -> usize;
85 pub struct MaxWorkItems; impl Get<_> for _ {}
86
87 static MAX_IMPORTS: _ = _(DEFAULT_PARAMS.max_imports);
89 pub fn max_imports() -> u32;
90 pub struct MaxImports; impl Get<_> for _ {}
91
92 static MAX_EXPORTS: _ = _(DEFAULT_PARAMS.max_exports);
94 pub fn max_exports() -> u32;
95
96 static MAX_EXTRINSICS: _ = _(DEFAULT_PARAMS.max_extrinsics as _);
98 pub fn max_extrinsics() -> u32;
99 pub struct MaxExtrinsics; impl Get<_> for _ {}
100
101 static MAX_DEPENDENCIES: _ = _(DEFAULT_PARAMS.max_dependencies as _);
103 pub fn max_dependencies() -> usize;
104 pub struct MaxDependencies; impl Get<_> for _ {}
105
106 static MAX_INPUT: _ = _(DEFAULT_PARAMS.max_input);
110 pub fn max_input() -> u32;
111 pub struct MaxInput; impl Get<_> for _ {}
112
113 pub const SEGMENT_LEN: _ = 4104;
115 pub fn segment_len() -> usize;
116 pub struct SegmentLen; impl Get<_> for _ {}
117
118 static SLOT_PERIOD_SEC : _ = _(DEFAULT_PARAMS.slot_period_sec as _);
120 pub fn slot_period_sec() -> u64;
121
122 static ROTATION_PERIOD: _ = _(DEFAULT_PARAMS.rotation_period as _);
124 pub fn rotation_period() -> Slot;
125
126 static EPOCH_PERIOD: _ = _(DEFAULT_PARAMS.epoch_period);
128 pub fn epoch_period() -> Slot;
129 pub struct EpochPeriod; impl Get<_> for _ {}
130
131 static EPOCH_TAIL_START: _ = _(DEFAULT_PARAMS.epoch_tail_start);
133 pub fn epoch_tail_start() -> Slot;
134
135 static MAX_TICKETS_PER_BLOCK: _ = _(DEFAULT_PARAMS.max_tickets_per_block as _);
137 pub fn max_tickets_per_block() -> usize;
138 pub struct MaxTicketsPerBlock; impl Get<_> for _ {}
139
140 static DEPOSIT_PER_ACCOUNT: _ = _(DEFAULT_PARAMS.deposit_per_account);
141 pub fn deposit_per_account() -> Balance;
142
143 static DEPOSIT_PER_ITEM: _ = _(DEFAULT_PARAMS.deposit_per_item);
144 pub fn deposit_per_item() -> Balance;
145
146 static DEPOSIT_PER_BYTE: _ = _(DEFAULT_PARAMS.deposit_per_byte);
147 pub fn deposit_per_byte() -> Balance;
148
149 static BLOCK_GAS_LIMIT: _ = _(DEFAULT_PARAMS.block_gas_limit);
153 pub fn block_gas_limit() -> UnsignedGas;
154
155 static MAX_IS_AUTHORIZED_GAS: _ = _(DEFAULT_PARAMS.max_is_authorized_gas);
157 pub fn max_is_authorized_gas() -> UnsignedGas;
158
159 static MAX_REFINE_GAS: _ = _(DEFAULT_PARAMS.max_refine_gas);
161 pub fn max_refine_gas() -> UnsignedGas;
162
163 static MAX_ACCUMULATE_GAS: _ = _(DEFAULT_PARAMS.max_accumulate_gas);
165 pub fn max_accumulate_gas() -> UnsignedGas;
166
167 static MAX_SERVICE_CODE_SIZE: _ = _(DEFAULT_PARAMS.max_service_code_size as _);
168 pub fn max_service_code_size() -> usize;
169
170 static MAX_AUTHORIZER_CODE_SIZE: _ = _(DEFAULT_PARAMS.max_authorizer_code_size as _);
171 pub fn max_authorizer_code_size() -> usize;
172
173 static RECENT_BLOCK_COUNT: _ = _(DEFAULT_PARAMS.recent_block_count as _);
176 pub fn recent_block_count() -> Slot;
177 pub struct RecentBlockCount; impl Get<_> for _ {}
178
179 pub const VALS_PER_CORE: _ = 3;
181 pub fn _vals_per_core() -> usize;
182 pub struct _ValsPerCore; impl Get<_> for _ {}
183
184 static CORE_COUNT: _ = _(DEFAULT_PARAMS.core_count);
186 pub fn core_count() -> CoreIndex;
187 pub struct CoreCount; impl Get<_> for _ {}
188
189 pub fn max_export_segments() -> u32 {
191 max_exports() + max_exports().div_ceil(PROVEN_PER_SEGMENT as u32)
192 }
193 pub struct MaxExportSegments; impl Get<_> for _ {}
194
195 pub fn max_import_segments() -> u32 { max_imports() * 2 }
198 pub struct MaxImportSegments; impl Get<_> for _ {}
199
200 static AVAILABILITY_TIMEOUT: _ = _(DEFAULT_PARAMS.availability_timeout as _);
202 pub fn availability_timeout() -> Slot;
203
204 static AUTH_WINDOW: _ = _(DEFAULT_PARAMS.auth_window as _);
206 pub fn auth_window() -> usize;
207 pub struct AuthWindow; impl Get<_> for _ {}
208
209 static MAX_LOOKUP_ANCHOR_AGE: _ = _(DEFAULT_PARAMS.max_lookup_anchor_age);
211 pub fn max_lookup_anchor_age() -> Slot;
212
213 static MAX_REPORT_ELECTIVE_DATA: _ = _(DEFAULT_PARAMS.max_report_elective_data);
216 pub fn max_report_elective_data() -> u32;
217 pub struct MaxReportElectiveData; impl Get<_> for _ {}
218}
219
220pub fn is_valid_val_count(count: ValIndex) -> bool {
223 count >= MIN_VAL_COUNT &&
224 count <= max_val_count() &&
225 (count as usize).is_multiple_of(VALS_PER_CORE)
226}
227
228pub const MIN_VAL_COUNT: ValIndex = 2 * VALS_PER_CORE as ValIndex;
230
231pub const MAX_CORE_COUNT: CoreIndex = 341;
234
235pub const MAX_EPOCH_PERIOD: Slot = TicketAttempt::MAX as Slot * MIN_VAL_COUNT as Slot / 2;
241
242pub fn tickets_per_validator(val_count: ValIndex) -> TicketAttempt {
253 ((2 * epoch_period()).div_ceil(val_count as Slot)) as TicketAttempt
254}
255
256pub const fn erasure_recovery_count(val_count: usize) -> usize {
262 let mut recovery_count = val_count / VALS_PER_CORE + 1;
263 while !SEGMENT_LEN.is_multiple_of(POINT_LEN * recovery_count) {
265 recovery_count -= 1;
266 }
267 recovery_count
268}
269
270pub fn basic_piece_len(val_count: usize) -> usize {
272 erasure_recovery_count(val_count) * POINT_LEN
273}
274
275pub fn pieces_per_segment(val_count: usize) -> usize {
277 SEGMENT_LEN / basic_piece_len(val_count)
278}
279
280pub fn segment_slice_len(val_count: usize) -> usize {
283 SEGMENT_LEN / erasure_recovery_count(val_count)
284}
285
286pub const MAX_SEGMENT_SLICE_LEN: usize =
288 SEGMENT_LEN / erasure_recovery_count(MIN_VAL_COUNT as usize);
289pub struct MaxSegmentSliceLen;
290impl Get<u32> for MaxSegmentSliceLen {
291 fn get() -> u32 {
292 MAX_SEGMENT_SLICE_LEN as u32
293 }
294}
295
296#[derive(Copy, Clone, Eq, PartialEq, Debug, Encode, Decode, MaxEncodedLen)]
298#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
299pub struct ProtocolParameters {
300 pub deposit_per_item: Balance,
302 pub deposit_per_byte: Balance,
305 pub deposit_per_account: Balance,
307 pub core_count: CoreIndex,
309 pub min_turnaround_period: Slot,
311 pub epoch_period: Slot,
313 pub max_accumulate_gas: UnsignedGas,
315 pub max_is_authorized_gas: UnsignedGas,
317 pub max_refine_gas: UnsignedGas,
319 pub block_gas_limit: UnsignedGas,
321 pub recent_block_count: u16,
323 pub max_work_items: u16,
325 pub max_dependencies: u16,
327 pub max_tickets_per_block: u16,
329 pub max_lookup_anchor_age: Slot,
331 pub auth_window: u16,
333 pub slot_period_sec: u16,
335 pub auth_queue_len: u16,
337 pub rotation_period: u16,
339 pub max_extrinsics: u16,
341 pub availability_timeout: u16,
343 pub max_authorizer_code_size: u32,
345 pub max_input: u32,
347 pub max_service_code_size: u32,
349 pub max_imports: u32,
351 pub max_report_elective_data: u32,
353 pub transfer_memo_size: u32,
355 pub max_exports: u32,
357 pub epoch_tail_start: Slot,
359}
360
361impl ProtocolParameters {
362 pub const fn full() -> Self {
363 Self {
364 core_count: 341,
365 auth_queue_len: 80,
366 min_turnaround_period: 19_200,
367 max_work_items: 16,
368 max_imports: 3072,
369 max_exports: 3072,
370 max_extrinsics: 128,
371 max_dependencies: 8,
372 max_input: 13_791_360,
373 slot_period_sec: 6,
374 epoch_period: 600,
375 epoch_tail_start: 500,
376 rotation_period: 10,
377 block_gas_limit: 3_500_000_000,
378 recent_block_count: 8,
379 max_tickets_per_block: 16,
380 deposit_per_account: 100,
381 deposit_per_item: 10,
382 deposit_per_byte: 1,
383 max_is_authorized_gas: 50_000_000,
384 max_refine_gas: 5_000_000_000,
385 max_accumulate_gas: 10_000_000,
386 max_service_code_size: 4_000_000,
387 max_authorizer_code_size: 64_000,
388 availability_timeout: 5,
389 auth_window: 8,
390 max_lookup_anchor_age: 24 * 600,
391 max_report_elective_data: 48 * 1024,
392 transfer_memo_size: 128,
393 }
394 }
395
396 pub const fn large() -> Self {
400 let mut params = Self::full();
401 params.core_count = 85;
402 params.epoch_period = 150;
403 params.epoch_tail_start = 125;
404 params.rotation_period = 5;
406 params.block_gas_limit = 875_000_000;
407 params.max_tickets_per_block = 4;
408 params.max_refine_gas = 1_250_000_000;
409 params.min_turnaround_period = 4_800;
410 params.max_lookup_anchor_age = 3_600;
411 params
412 }
413
414 pub const fn mini() -> Self {
415 let mut params = Self::full();
416 params.core_count = 26;
417 params
418 }
419
420 pub const fn tiny() -> Self {
421 let mut params = Self::full();
422 params.core_count = 2;
423 params.epoch_period = 12;
424 params.epoch_tail_start = 10;
425 params.rotation_period = 4;
426 params.block_gas_limit = 20_000_000;
427 params.max_tickets_per_block = 3;
428 params.max_refine_gas = 1_000_000_000;
429 params.min_turnaround_period = 32;
432 params.max_lookup_anchor_age = 24;
434 params
435 }
436
437 pub const fn max_val_count(&self) -> ValIndex {
439 (self.core_count as usize * VALS_PER_CORE) as ValIndex
440 }
441
442 pub fn get() -> Self {
443 Self {
444 deposit_per_item: deposit_per_item(),
445 deposit_per_byte: deposit_per_byte(),
446 deposit_per_account: deposit_per_account(),
447 core_count: core_count(),
448 min_turnaround_period: min_turnaround_period(),
449 epoch_period: epoch_period(),
450 max_accumulate_gas: max_accumulate_gas(),
451 max_is_authorized_gas: max_is_authorized_gas(),
452 max_refine_gas: max_refine_gas(),
453 block_gas_limit: block_gas_limit(),
454 recent_block_count: recent_block_count() as _,
455 max_work_items: max_work_items() as _,
456 max_dependencies: max_dependencies() as _,
457 max_tickets_per_block: max_tickets_per_block() as _,
458 max_lookup_anchor_age: max_lookup_anchor_age(),
459 auth_window: auth_window() as _,
460 slot_period_sec: slot_period_sec() as _,
461 auth_queue_len: auth_queue_len() as _,
462 rotation_period: rotation_period() as _,
463 max_extrinsics: max_extrinsics() as _,
464 availability_timeout: availability_timeout() as _,
465 max_authorizer_code_size: max_authorizer_code_size() as _,
466 max_input: max_input(),
467 max_service_code_size: max_service_code_size() as _,
468 max_imports: max_imports() as _,
469 max_report_elective_data: max_report_elective_data(),
470 transfer_memo_size: MEMO_LEN as _,
471 max_exports: max_exports(),
472 epoch_tail_start: epoch_tail_start(),
473 }
474 }
475 pub fn validate(&self) -> Result<(), &'static str> {
476 if !self.epoch_period.is_multiple_of(self.rotation_period as Slot) {
477 return Err("`rotation_period` does not divide into `epoch_period`")
478 }
479 if self.epoch_tail_start >= self.epoch_period {
480 return Err("`epoch_tail_start` must be less than `epoch_period`")
481 }
482 if self.epoch_period > MAX_EPOCH_PERIOD {
483 return Err("`epoch_period` may be no larger than `MAX_EPOCH_PERIOD` (765)")
484 }
485 if self.core_count < 2 {
486 return Err("`core_count` must be at least 2, so the minimum validator set fits")
487 }
488 if self.core_count > MAX_CORE_COUNT {
489 return Err("`core_count` may be no larger than `MAX_CORE_COUNT` (341)")
490 }
491 if self.transfer_memo_size != MEMO_LEN as u32 {
492 return Err("`transfer_memo_size` must be equal tor `MEMO_LEN` (128)")
493 }
494 if recent_block_count() > 255 {
495 return Err("`recent_block_count` may be no larger than `BlockIndex::MAX` (255)")
496 }
497 Ok(())
498 }
499 pub fn apply(self) -> Result<(), &'static str> {
500 self.validate()?;
501 DEPOSIT_PER_ITEM.store(self.deposit_per_item, Relaxed);
502 DEPOSIT_PER_BYTE.store(self.deposit_per_byte, Relaxed);
503 DEPOSIT_PER_ACCOUNT.store(self.deposit_per_account, Relaxed);
504 MIN_TURNAROUND_PERIOD.store(self.min_turnaround_period, Relaxed);
505 EPOCH_PERIOD.store(self.epoch_period, Relaxed);
506 MAX_ACCUMULATE_GAS.store(self.max_accumulate_gas, Relaxed);
507 MAX_IS_AUTHORIZED_GAS.store(self.max_is_authorized_gas, Relaxed);
508 MAX_REFINE_GAS.store(self.max_refine_gas, Relaxed);
509 BLOCK_GAS_LIMIT.store(self.block_gas_limit, Relaxed);
510 RECENT_BLOCK_COUNT.store(self.recent_block_count as _, Relaxed);
511 MAX_WORK_ITEMS.store(self.max_work_items as _, Relaxed);
512 MAX_DEPENDENCIES.store(self.max_dependencies as _, Relaxed);
513 MAX_TICKETS_PER_BLOCK.store(self.max_tickets_per_block as _, Relaxed);
514 MAX_LOOKUP_ANCHOR_AGE.store(self.max_lookup_anchor_age as _, Relaxed);
515 AUTH_WINDOW.store(self.auth_window as _, Relaxed);
516 SLOT_PERIOD_SEC.store(self.slot_period_sec as _, Relaxed);
517 AUTH_QUEUE_LEN.store(self.auth_queue_len as _, Relaxed);
518 ROTATION_PERIOD.store(self.rotation_period as _, Relaxed);
519 MAX_EXTRINSICS.store(self.max_extrinsics as _, Relaxed);
520 AVAILABILITY_TIMEOUT.store(self.availability_timeout as _, Relaxed);
521 CORE_COUNT.store(self.core_count, Relaxed);
522 MAX_AUTHORIZER_CODE_SIZE.store(self.max_authorizer_code_size as _, Relaxed);
523 MAX_INPUT.store(self.max_input, Relaxed);
524 MAX_SERVICE_CODE_SIZE.store(self.max_service_code_size as _, Relaxed);
525 MAX_IMPORTS.store(self.max_imports, Relaxed);
526 MAX_REPORT_ELECTIVE_DATA.store(self.max_report_elective_data, Relaxed);
527 MAX_EXPORTS.store(self.max_exports, Relaxed);
528 EPOCH_TAIL_START.store(self.epoch_tail_start, Relaxed);
529 Ok(())
530 }
531}
532
533pub const POINT_LEN: usize = 2;
535
536pub const fn byzantine_threshold(n: ValIndex) -> ValIndex {
540 n.saturating_sub(1) / 3
541}
542
543pub const fn supermajority_threshold(n: ValIndex) -> ValIndex {
546 n - byzantine_threshold(n)
547}
548
549#[doc(hidden)]
556#[derive(Copy, Clone, Eq, PartialEq, Default, Debug)]
557pub struct MaxValSuperMajority;
558impl Get<u32> for MaxValSuperMajority {
559 fn get() -> u32 {
560 supermajority_threshold(max_val_count()) as u32
561 }
562}
563
564pub type Slot = u32;
569pub type ValIndex = u16;
571pub type CoreIndex = u16;
573pub type ServiceId = u32;
575pub type Balance = u64;
577pub type DoubleBalance = u128;
579pub type SignedGas = i64;
582pub type UnsignedGas = u64;
584pub type DoubleGas = u128;
586
587pub type Hash = [u8; 32];
592
593opaque! {
594 pub struct HeaderHash(pub [u8; 32]);
596
597 pub struct CodeHash(pub [u8; 32]);
599
600 pub struct WorkPackageHash(pub [u8; 32]);
602
603 pub struct WorkReportHash(pub [u8; 32]);
605
606 pub struct PayloadHash(pub [u8; 32]);
608
609 pub struct StateRootHash(pub [u8; 32]);
611
612 pub struct MmrPeakHash(pub [u8; 32]);
614
615 pub struct AccumulateRootHash(pub [u8; 32]);
617
618 pub struct ExtrinsicHash(pub [u8; 32]);
620
621 pub struct AuthorizerHash(pub [u8; 32]);
623
624 pub struct SegmentTreeRoot(pub [u8; 32]);
626
627 pub struct SegmentHash(pub [u8; 32]);
629
630 pub struct MerkleNodeHash(pub [u8; 32]);
632
633 pub struct AnyHash(pub [u8; 32]);
637
638 pub struct Memo(pub [u8; MEMO_LEN]);
640
641 pub struct Authorization(pub Vec<u8>);
643
644 pub struct Code(pub Vec<u8>);
646
647 pub struct WorkPayload(pub Vec<u8>);
649
650 pub struct AuthConfig(pub Vec<u8>);
652
653 pub struct AnyVec(pub Vec<u8>);
657
658 pub struct WorkOutput(pub Vec<u8>);
660
661 pub struct AuthTrace(pub Vec<u8>);
663
664 pub struct OpaqueEd25519Public(pub [u8; 32]);
668
669 pub struct OpaqueBandersnatchPublic(pub [u8; 32]);
673
674 pub struct OpaqueBlsPublic(pub [u8; 144]);
678
679 pub struct OpaqueValidatorMetadata(pub [u8; 128]);
681}
682
683#[cfg(feature = "bytes")]
684opaque! {
685 pub struct AnyBytes(pub Bytes);
689}
690
691pub type AuthQueue = FixedVec<AuthorizerHash, AuthQueueLen>;
693
694pub type Segment = FixedVec<u8, SegmentLen>;
696impl Segment {
699 pub fn as_array(&self) -> &[u8; SEGMENT_LEN] {
700 self.as_slice().try_into().expect("Segment length is fixed")
701 }
702}
703
704impl AsRef<[u8; SEGMENT_LEN]> for Segment {
705 fn as_ref(&self) -> &[u8; SEGMENT_LEN] {
706 self.as_array()
707 }
708}
709
710pub trait ToAny {
711 type Any;
712 fn any(&self) -> Self::Any;
713 fn into_any(self) -> Self::Any;
714}
715
716impl ToAny for [u8; 32] {
717 type Any = AnyHash;
718 fn any(&self) -> Self::Any {
719 AnyHash(*self)
720 }
721 fn into_any(self) -> Self::Any {
722 AnyHash(self)
723 }
724}
725
726impl ToAny for alloc::vec::Vec<u8> {
727 type Any = AnyVec;
728 fn any(&self) -> Self::Any {
729 AnyVec(self.clone())
730 }
731 fn into_any(self) -> Self::Any {
732 AnyVec(self)
733 }
734}
735
736impl ToAny for &[u8] {
737 type Any = AnyVec;
738 fn any(&self) -> Self::Any {
739 AnyVec(self.to_vec())
740 }
741 fn into_any(self) -> Self::Any {
742 AnyVec(self.to_vec())
743 }
744}
745
746impl MaxEncodedLen for Authorization {
747 fn max_encoded_len() -> usize {
748 BoundedVec::<u8, MaxInput>::max_encoded_len()
749 }
750}
751
752impl MaxEncodedLen for WorkPayload {
753 fn max_encoded_len() -> usize {
754 BoundedVec::<u8, MaxInput>::max_encoded_len()
755 }
756}
757
758impl MaxEncodedLen for AuthConfig {
759 fn max_encoded_len() -> usize {
760 BoundedVec::<u8, MaxInput>::max_encoded_len()
761 }
762}
763
764impl MaxEncodedLen for AuthTrace {
765 fn max_encoded_len() -> usize {
766 BoundedVec::<u8, MaxReportElectiveData>::max_encoded_len()
767 }
768}
769
770impl AuthTrace {
771 pub fn leak(self) -> &'static [u8] {
772 self.0.leak()
773 }
774}
775
776impl MaxEncodedLen for WorkOutput {
777 fn max_encoded_len() -> usize {
778 BoundedVec::<u8, MaxReportElectiveData>::max_encoded_len()
779 }
780}
781
782impl WorkOutput {
783 pub fn leak(self) -> &'static [u8] {
784 self.0.leak()
785 }
786}
787
788#[cfg(test)]
789mod tests {
790 use super::*;
791
792 #[test]
796 fn protocol_parameters_fetch_encoding() {
797 for params in [
798 ProtocolParameters::full(),
799 ProtocolParameters::large(),
800 ProtocolParameters::mini(),
801 ProtocolParameters::tiny(),
802 ] {
803 assert!(params.validate().is_ok(), "{params:?}");
805 let encoded = params.encode();
806 assert_eq!(encoded.len(), 7 * 8 + 11 * 4 + 11 * 2);
807 let decoded = ProtocolParameters::decode(&mut &encoded[..]).unwrap();
808 assert_eq!(decoded, params);
809 }
810 }
811
812 #[test]
816 fn erasure_recovery_count_matches_gp() {
817 assert_eq!(erasure_recovery_count(6), 3);
818 assert_eq!(erasure_recovery_count(78), 27);
819 assert_eq!(erasure_recovery_count(1023), 342);
820 for count in (6..=1023).step_by(3) {
821 let d = erasure_recovery_count(count);
822 assert!(d <= count / VALS_PER_CORE + 1);
823 assert!(SEGMENT_LEN.is_multiple_of(POINT_LEN * d), "v={count}");
824 }
825 }
826
827 #[test]
831 fn core_count_bounds_enforced() {
832 let mut params = ProtocolParameters::full();
833 assert!(params.validate().is_ok());
834 params.core_count = 1;
835 assert!(params.validate().is_err());
836 params.core_count = MAX_CORE_COUNT;
837 assert!(params.validate().is_ok());
838 params.core_count = MAX_CORE_COUNT + 1;
839 assert!(params.validate().is_err());
840 }
841
842 #[test]
846 fn ticket_attempts_matches_gp() {
847 assert_eq!(tickets_per_validator(max_val_count()), 2);
848 assert_eq!(tickets_per_validator(MIN_VAL_COUNT), 200);
849 for count in (MIN_VAL_COUNT as usize..=max_val_count() as usize).step_by(VALS_PER_CORE) {
850 let attempts = tickets_per_validator(count as ValIndex) as usize;
851 assert!(attempts * count >= 2 * epoch_period() as usize, "v={count}");
852 assert!((attempts - 1) * count < 2 * epoch_period() as usize, "v={count}");
853 }
854 }
855}