Skip to main content

jam_types/
simple.rs

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
8/// Version of the Gray Paper implemented by this and dependent crates.
9pub const GP_VERSION: &str = "0.8.0";
10
11/// Beginning of the Jam "Common Era" (1200 UTC on January 1, 2025),
12/// as seconds after the Unix epoch, excluding leap seconds.
13pub const JAM_COMMON_ERA: u64 = 1_735_732_800;
14
15/// Length of a transfer memo in bytes.
16pub const MEMO_LEN: usize = 128;
17
18/// Maximum length of the preimage in the encoded form.
19#[doc(hidden)]
20// TODO @ivan This is temporary value. Replace with the one from the GP when it's updated.
21pub const MAX_PREIMAGE_LEN: usize = 32 * 1024 * 1024;
22
23/// Maximum length of the preimage blob.
24///
25/// Equals `MAX_PREIMAGE_LEN` minus the overhead.
26// TODO @ivan Unhide when the GP is updated.
27#[doc(hidden)]
28pub const MAX_PREIMAGE_BLOB_LEN: usize = MAX_PREIMAGE_LEN - 8;
29
30/// PolkaVM page size in bytes.
31pub const PAGE_SIZE: u32 = 4096;
32
33/// The maximum number of levels in the implicit tree derived from the segment hashes in a
34/// proof-page.
35pub const SUFFIX_SKIP_LEN: usize = 6;
36
37/// The maximum number of segments whose justification can be included in a single segment-sized
38/// proof-page. Basically just the highest power of 2 which when multiplied by 32 (the size of a
39/// hash) leaves enough room from 4104 (the size of a segment) to fit a partial Merkle proof of
40/// 6 branches (6 * 32 = 192) plus the overhead for storing the actual number of hashes (should be
41/// one byte for `PROVEN_PER_SEGMENT < 128`). It turns out that this is 2^6 = 64.
42///
43/// See `max_prefix_proof_len` for the actual formula.
44pub const PROVEN_PER_SEGMENT: usize = 1 << SUFFIX_SKIP_LEN;
45
46/// Fixed number of octets a storage item contributes to a service's footprint, over and above the
47/// size of its key and value (GP: a 2-byte tag plus the 32-byte key hash).
48pub const FOOTPRINT_STORAGE_OVERHEAD: u64 = 34;
49
50/// Fixed number of octets a preimage lookup entry contributes to a service's footprint, over and
51/// above the size of the preimage (GP account footprint formula).
52pub const FOOTPRINT_PREIMAGE_OVERHEAD: u64 = 81;
53
54/// Index for an attempt at producing a ticket for the right to produce a block.
55pub type TicketAttempt = u8;
56
57const DEFAULT_PARAMS: ProtocolParameters = ProtocolParameters::full();
58
59chain_params! {
60	/// Maximum number of validators in the JAM.
61	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	/// Number of authorizations in a queue allocated to a core.
67	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	/// Minimum period in blocks between going from becoming `Available` to `Zombie`, and then
72	/// again from `Zombie` to non-existent.
73	///
74	/// This ensures firstly that any data added and referenced in a Work Report's lookup anchor
75	/// block will remain on-chain right up until the latest possible time a dispute might
76	/// conclude. Secondly, it ensure that we only need to record one "flip-flop" of the data's
77	/// availability in order to be able to determine whether it's available or not at any block
78	/// within this period.
79	static MIN_TURNAROUND_PERIOD: _ = _(DEFAULT_PARAMS.min_turnaround_period);
80	pub fn min_turnaround_period() -> Slot;
81
82	/// Maximum number of Work Items in a Work Package.
83	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	/// Maximum number of imports in a Work Package.
88	static MAX_IMPORTS: _ = _(DEFAULT_PARAMS.max_imports);
89	pub fn max_imports() -> u32;
90	pub struct MaxImports; impl Get<_> for _ {}
91
92	/// Maximum number of exports in a Work Package.
93	static MAX_EXPORTS: _ = _(DEFAULT_PARAMS.max_exports);
94	pub fn max_exports() -> u32;
95
96	/// Maximum number of extrinsics in a Work Package.
97	static MAX_EXTRINSICS: _ = _(DEFAULT_PARAMS.max_extrinsics as _);
98	pub fn max_extrinsics() -> u32;
99	pub struct MaxExtrinsics; impl Get<_> for _ {}
100
101	/// Maximum number of dependencies (total of prerequisites and SR lookup entries).
102	static MAX_DEPENDENCIES: _ = _(DEFAULT_PARAMS.max_dependencies as _);
103	pub fn max_dependencies() -> usize;
104	pub struct MaxDependencies; impl Get<_> for _ {}
105
106	/// Maximum total size of the authorization token, the authorizer config, the work-item
107	/// payloads, the extrinsic data and the imports of a Work Package. Each import counts
108	/// as one segment plus its maximum justification.
109	static MAX_INPUT: _ = _(DEFAULT_PARAMS.max_input);
110	pub fn max_input() -> u32;
111	pub struct MaxInput; impl Get<_> for _ {}
112
113	/// Number of bytes in a segment. This is fixed.
114	pub const SEGMENT_LEN: _ = 4104;
115	pub fn segment_len() -> usize;
116	pub struct SegmentLen; impl Get<_> for _ {}
117
118	/// Number of seconds in a time slot.
119	static SLOT_PERIOD_SEC : _ = _(DEFAULT_PARAMS.slot_period_sec as _);
120	pub fn slot_period_sec() -> u64;
121
122	/// The rotation period, defined in number of slots.
123	static ROTATION_PERIOD: _ = _(DEFAULT_PARAMS.rotation_period as _);
124	pub fn rotation_period() -> Slot;
125
126	/// The epoch period, defined in number of slots.
127	static EPOCH_PERIOD: _ = _(DEFAULT_PARAMS.epoch_period);
128	pub fn epoch_period() -> Slot;
129	pub struct EpochPeriod; impl Get<_> for _ {}
130
131	/// The length of the contest, defined in number of slots.
132	static EPOCH_TAIL_START: _ = _(DEFAULT_PARAMS.epoch_tail_start);
133	pub fn epoch_tail_start() -> Slot;
134
135	/// Max tickets allowed to be embedded in each block extrinsic.
136	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	/// The maximum gas which may be used in a single block. At present this is just the product of
150	/// the maximum gas per work-report and the number of cores. In the future it should probably be
151	/// more to account for the possibility of always-accumulate services.
152	static BLOCK_GAS_LIMIT: _ = _(DEFAULT_PARAMS.block_gas_limit);
153	pub fn block_gas_limit() -> UnsignedGas;
154
155	/// Maximum gas which may be used to Authorize a single work-package.
156	static MAX_IS_AUTHORIZED_GAS: _ = _(DEFAULT_PARAMS.max_is_authorized_gas);
157	pub fn max_is_authorized_gas() -> UnsignedGas;
158
159	/// Maximum gas which may be used to Refine a single work-report.
160	static MAX_REFINE_GAS: _ = _(DEFAULT_PARAMS.max_refine_gas);
161	pub fn max_refine_gas() -> UnsignedGas;
162
163	/// Maximum gas which may be used to Accumulate a single work-report.
164	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	/// The number of blocks which are kept in the recent block cache. We use a `u8` to index into
174	/// this and therefore it may be no larger than 255.
175	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	/// The number of validators on each core.
180	pub const VALS_PER_CORE: _ = 3;
181	pub fn _vals_per_core() -> usize;
182	pub struct _ValsPerCore; impl Get<_> for _ {}
183
184	/// Number of cores in the JAM, including inactive cores.
185	static CORE_COUNT: _ = _(DEFAULT_PARAMS.core_count);
186	pub fn core_count() -> CoreIndex;
187	pub struct CoreCount; impl Get<_> for _ {}
188
189	/// Maximum number of DA segments that a Work Package can produce.
190	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	/// Maximum number of DA segments that a Work Package can require as imports. This is twice the
196	/// maximum number of imports because each import may require a proof segment.
197	pub fn max_import_segments() -> u32 { max_imports() * 2 }
198	pub struct MaxImportSegments; impl Get<_> for _ {}
199
200	/// The period in timeslots after which reported but unavailable work may be replaced.
201	static AVAILABILITY_TIMEOUT: _ = _(DEFAULT_PARAMS.availability_timeout as _);
202	pub fn availability_timeout() -> Slot;
203
204	/// Number of items in the authorization window.
205	static AUTH_WINDOW: _ = _(DEFAULT_PARAMS.auth_window as _);
206	pub fn auth_window() -> usize;
207	pub struct AuthWindow; impl Get<_> for _ {}
208
209	/// Maximum age, in blocks, that the lookup anchor may be, taken from the regular anchor.
210	static MAX_LOOKUP_ANCHOR_AGE: _ = _(DEFAULT_PARAMS.max_lookup_anchor_age);
211	pub fn max_lookup_anchor_age() -> Slot;
212
213	/// The maximum total size of all unbounded blobs in a work-report.
214	/// Maximum age, in blocks, that the lookup anchor may be, taken from the regular anchor.
215	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
220/// Whether `count` is an allowed validator-set size: a multiple of 3 in the
221/// inclusive range `[6, 3·C]`.
222pub 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
228/// Smallest allowed validator-set size.
229pub const MIN_VAL_COUNT: ValIndex = 2 * VALS_PER_CORE as ValIndex;
230
231/// Largest allowed core count: caps the validator set (`3·C`) at 1023, the bound assumed
232/// by the Bandersnatch ring and by the erasure-code rate analysis.
233pub const MAX_CORE_COUNT: CoreIndex = 341;
234
235/// Largest allowed epoch period: keeps [`tickets_per_validator`] within `TicketAttempt` at
236/// the smallest set size.
237///
238/// The allowance `⌈2E / v⌉` peaks at `v = MIN_VAL_COUNT` and fits `T=TicketAttempt::MAX`
239/// only if `2E <= T * v`, that is `E <= T * v /2`. Beyond that the cast wraps silently.
240pub const MAX_EPOCH_PERIOD: Slot = TicketAttempt::MAX as Slot * MIN_VAL_COUNT as Slot / 2;
241
242/// Maximum number of tickets which each validator may create in an epoch, for a
243/// non-empty pending validator set of size `val_count`.
244///
245/// The allowance grows as the validator set shrinks so the ticket accumulator can still
246/// be saturated.
247///
248/// Influences the anonymity of block producers. As all published tickets have a public
249/// attempt number less than this, if two tickets share an attempt number then they must
250/// belong to two different validators, which reduces anonymity late as we approach the
251/// epoch tail.
252pub fn tickets_per_validator(val_count: ValIndex) -> TicketAttempt {
253	((2 * epoch_period()).div_ceil(val_count as Slot)) as TicketAttempt
254}
255
256/// Erasure-coding recovery threshold `D(v)` for a validator set of size `val_count`:
257/// any `D(v)` of the `v` erasure-coded chunks reconstruct the data.
258///
259/// The ideal rate (`D = v/3 + 1`) is lowered when needed so that pieces divide segments
260/// without padding; it is ideal for the recommended set sizes `{6, 9, 15, 24, …, 1023}`.
261pub const fn erasure_recovery_count(val_count: usize) -> usize {
262	let mut recovery_count = val_count / VALS_PER_CORE + 1;
263	// `SEGMENT_LEN` is even, so the loop always terminates at `d = 1`.
264	while !SEGMENT_LEN.is_multiple_of(POINT_LEN * recovery_count) {
265		recovery_count -= 1;
266	}
267	recovery_count
268}
269
270/// Number of bytes in a basic EC piece for a validator set of size `val_count`.
271pub fn basic_piece_len(val_count: usize) -> usize {
272	erasure_recovery_count(val_count) * POINT_LEN
273}
274
275/// Number of pieces in a segment for a validator set of size `val_count`.
276pub fn pieces_per_segment(val_count: usize) -> usize {
277	SEGMENT_LEN / basic_piece_len(val_count)
278}
279
280/// Number of bytes in a segment slice, the per-validator share of one segment, for a
281/// validator set of size `val_count`.
282pub fn segment_slice_len(val_count: usize) -> usize {
283	SEGMENT_LEN / erasure_recovery_count(val_count)
284}
285
286/// Largest possible segment-slice length.
287pub 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/// Baseline parameters for the JAM protocol.
297#[derive(Copy, Clone, Eq, PartialEq, Debug, Encode, Decode, MaxEncodedLen)]
298#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
299pub struct ProtocolParameters {
300	/// (Bi) The additional deposit required for each preimage or storage item in an account.
301	pub deposit_per_item: Balance,
302	/// (Bl) The additional deposit required for each byte of each storage item in an account and
303	/// preimage of an account.
304	pub deposit_per_byte: Balance,
305	/// (Bs) The base deposit required to retain an account.
306	pub deposit_per_account: Balance,
307	/// (C) Number of JAM cores.
308	pub core_count: CoreIndex,
309	/// (D) The period in timeslots after which an unreferenced preimage may be expunged.
310	pub min_turnaround_period: Slot,
311	/// (E) The epoch period, defined in number of slots.
312	pub epoch_period: Slot,
313	/// (Ga) Maximum gas which may be used to Accumulate a single work-report.
314	pub max_accumulate_gas: UnsignedGas,
315	/// (Gi) Maximum gas which may be used to Authorize a single work-package.
316	pub max_is_authorized_gas: UnsignedGas,
317	/// (Gr) Maximum gas which may be used to Refine a single work-report.
318	pub max_refine_gas: UnsignedGas,
319	/// (Gt) Maximum gas which can be processed in a single block.
320	pub block_gas_limit: UnsignedGas,
321	/// (H) The number of blocks which are kept in the recent block cache.
322	pub recent_block_count: u16,
323	/// (I) Maximum number of Work Items in a Work Package.
324	pub max_work_items: u16,
325	/// (J) Maximum number of dependencies (total of prerequisites and SR lookup entries).
326	pub max_dependencies: u16,
327	/// (K) Max tickets allowed to be embedded in each block extrinsic.
328	pub max_tickets_per_block: u16,
329	/// (L) Maximum age, in blocks, that the lookup anchor may be, taken from the regular anchor.
330	pub max_lookup_anchor_age: Slot,
331	/// (O) Number of items in the authorization window.
332	pub auth_window: u16,
333	/// (P) The period for each JAM time slot, defined in seconds.
334	pub slot_period_sec: u16,
335	/// (Q) Number of authorizations in a queue allocated to a core.
336	pub auth_queue_len: u16,
337	/// (R) The rotation period, defined in number of slots.
338	pub rotation_period: u16,
339	/// (T) Maximum number of extrinsics in a Work Package.
340	pub max_extrinsics: u16,
341	/// (U) The period in timeslots after which reported but unavailable work may be replaced.
342	pub availability_timeout: u16,
343	/// (Wa) The maximum size of authorizer code (used by Is-Authorized procedure).
344	pub max_authorizer_code_size: u32,
345	/// (Wb) Maximum size of a Work Package together with all extrinsic data and imported segments.
346	pub max_input: u32,
347	/// (Wc) The maximum size of service code (used by Refine/Accumulate/OnTransfer procedures).
348	pub max_service_code_size: u32,
349	/// (Wm) Maximum number of imports in a Work Package.
350	pub max_imports: u32,
351	/// (Wr) The maximum total size of all unbounded blobs in a work-report, in octets.
352	pub max_report_elective_data: u32,
353	/// (Wt) The size of a transfer memo in octets. The size of a transfer memo in octets.
354	pub transfer_memo_size: u32,
355	/// (Wx) Maximum number of exports in a Work Package.
356	pub max_exports: u32,
357	/// (Y) The length of the contest, defined in number of slots.
358	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	/// Roughly a quarter of the full spec: the parameters that `tiny` alters, scaled down to about
397	/// a quarter of their full values. Large enough to resize the validator set over a wide range,
398	/// small enough to cross epochs quickly.
399	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		// Must divide the epoch; sits between tiny's 4 and full's 10.
405		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		// Remove unreferenced preimages after 32 slots.
430		// The short retention period simplifies testing with test vectors and fuzzing.
431		params.min_turnaround_period = 32;
432		// Same reasoning as `min_turnaround_period`
433		params.max_lookup_anchor_age = 24;
434		params
435	}
436
437	/// Maximum validator-set size for this configuration.
438	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
533/// Number of bytes in an erasure-coding point.
534pub const POINT_LEN: usize = 2;
535
536/// The maximum number of validators `f` which may safely be faulty.
537///
538/// The total number of validators is `n = 3f + e` where `e in { 1, 2, 3 }`.
539pub const fn byzantine_threshold(n: ValIndex) -> ValIndex {
540	n.saturating_sub(1) / 3
541}
542
543/// The supermajority threshold of validators (`⌊2n/3⌋ + 1`) which represents a subset
544/// guaranteed to have at least `f + 1` honest validators.
545pub const fn supermajority_threshold(n: ValIndex) -> ValIndex {
546	n - byzantine_threshold(n)
547}
548
549/// Max validators super-majority derived from the configured [`max_val_count`].
550///
551/// This is a codec/collection bound, not the protocol vote threshold: the exact
552/// number of votes a verdict must carry is `supermajority_threshold(|V|)` where
553/// `|V|` is the size of the validator set of the targeted epoch, which is checked
554/// against the epoch's keyset by the STF.
555#[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
564/// Type that represents a time slot of six seconds.
565///
566/// This can be either in a relative sense or as a period which has elapsed from the Polkadot
567/// Common Era, beginning 1200 UTC, 1 January 2025.
568pub type Slot = u32;
569/// Type to represent the index of a validator.
570pub type ValIndex = u16;
571/// Type to represent the index of a compute core.
572pub type CoreIndex = u16;
573/// Type to represent the index of a service.
574pub type ServiceId = u32;
575/// Type to represent a balance.
576pub type Balance = u64;
577/// Type which is double the length of Balance, for non-overflowing multiplies.
578pub type DoubleBalance = u128;
579/// Type to represent some gas which may be below zero. This is used primarily for the `invoke`
580/// hostcall API which must be able to return a negative gas amount in case of a gas overrun.
581pub type SignedGas = i64;
582/// Type to represent some gas which must be at least zero.
583pub type UnsignedGas = u64;
584/// Type which is double the length of Gas, for non-overflowing multiplies.
585pub type DoubleGas = u128;
586
587/// A basic 256-bit data value.
588///
589/// This should generally not be used directly in the rich data types, but instead one of the
590/// rich opaque hash types to avoid accidental misuse and provide pretty-print facilities.
591pub type Hash = [u8; 32];
592
593opaque! {
594	/// Hash of an encoded block header.
595	pub struct HeaderHash(pub [u8; 32]);
596
597	/// Hash of PVM program code.
598	pub struct CodeHash(pub [u8; 32]);
599
600	/// Hash of an encoded Work Package.
601	pub struct WorkPackageHash(pub [u8; 32]);
602
603	/// Hash of an encoded Work Report.
604	pub struct WorkReportHash(pub [u8; 32]);
605
606	/// Hash of a Work Item's [WorkPayload].
607	pub struct PayloadHash(pub [u8; 32]);
608
609	/// Hash of the JAM state root.
610	pub struct StateRootHash(pub [u8; 32]);
611
612	/// Hash of an MMR peak.
613	pub struct MmrPeakHash(pub [u8; 32]);
614
615	/// Hash of an accumulation tree root node.
616	pub struct AccumulateRootHash(pub [u8; 32]);
617
618	/// Hash of a piece of extrinsic data.
619	pub struct ExtrinsicHash(pub [u8; 32]);
620
621	/// Hash of an encoded [Authorizer] value.
622	pub struct AuthorizerHash(pub [u8; 32]);
623
624	/// Hash of a segment tree root node.
625	pub struct SegmentTreeRoot(pub [u8; 32]);
626
627	/// Hash of a [Segment] value.
628	pub struct SegmentHash(pub [u8; 32]);
629
630	/// Hash of a Merkle tree node.
631	pub struct MerkleNodeHash(pub [u8; 32]);
632
633	/// Non usage-specific hash.
634	///
635	/// This can be useful for pretty-printing [type@Hash] values.
636	pub struct AnyHash(pub [u8; 32]);
637
638	/// Transfer memo data, included with balance transfers between services.
639	pub struct Memo(pub [u8; MEMO_LEN]);
640
641	/// Data constituting the Authorization Token in a Work Package.
642	pub struct Authorization(pub Vec<u8>);
643
644	/// PVM Program code.
645	pub struct Code(pub Vec<u8>);
646
647	/// Payload data defining a Work Item.
648	pub struct WorkPayload(pub Vec<u8>);
649
650	/// Authorization parameter.
651	pub struct AuthConfig(pub Vec<u8>);
652
653	/// Non usage-specific data.
654	///
655	/// This can be useful for pretty-printing `Vec<u8>` values.
656	pub struct AnyVec(pub Vec<u8>);
657
658	/// Output data of Refinement operation, passed into Accumulation.
659	pub struct WorkOutput(pub Vec<u8>);
660
661	/// Output data of Is Authorized operation, passed into both Refinement and Accumulation.
662	pub struct AuthTrace(pub Vec<u8>);
663
664	/// Plain-old-data struct of the same length as an encoded Ed25519 public key.
665	///
666	/// This has no cryptographic functionality or dependencies.
667	pub struct OpaqueEd25519Public(pub [u8; 32]);
668
669	/// Plain-old-data struct of the same length as an encoded Bandersnatch public key.
670	///
671	/// This has no cryptographic functionality or dependencies.
672	pub struct OpaqueBandersnatchPublic(pub [u8; 32]);
673
674	/// Plain-old-data struct of the same length as an encoded BLS public key.
675	///
676	/// This has no cryptographic functionality or dependencies.
677	pub struct OpaqueBlsPublic(pub [u8; 144]);
678
679	/// Additional information on a validator, opaque to the actual usage.
680	pub struct OpaqueValidatorMetadata(pub [u8; 128]);
681}
682
683#[cfg(feature = "bytes")]
684opaque! {
685	/// Non usage-specific data.
686	///
687	/// This can be useful for pretty-printing `Bytes` values.
688	pub struct AnyBytes(pub Bytes);
689}
690
691/// A queue of [AuthorizerHash]s, each of which will be rotated into the authorizer pool for a core.
692pub type AuthQueue = FixedVec<AuthorizerHash, AuthQueueLen>;
693
694/// A segment of data.
695pub type Segment = FixedVec<u8, SegmentLen>;
696// TODO: ^^^ Measure performance penalty for this not being 4096.
697
698impl 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	// The constant tuple exposed to services via the `fetch` host-call (case 0) must not
793	// include the values GP #514 removed (V, N, WE, WP): they are no longer protocol
794	// constants but derived from the validator-set size.
795	#[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			// Every built-in spec must satisfy the parameter constraints.
804			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	// `D(v)` is defined as the largest `d <= v/3 + 1` such that `2d` divides the segment
813	// size, so segments are always erasure-coded without padding. The rate is optimal
814	// (`D(v) = v/3 + 1`) for the GP recommended set sizes.
815	#[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	// `core_count` outside `[2, 341]` must be rejected: below 2 no allowed validator-set
828	// size exists, above 341 the validator set would exceed 1023, the bound assumed by the
829	// Bandersnatch ring and by the erasure-code rate analysis.
830	#[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	// `⌈2E/|γP'|⌉` per-validator tickets: 2 with the full set, growing as the set shrinks
843	// so the epoch's ticket accumulator can still be saturated at any allowed size.
844	// Chain params default to the full spec here (E = 600, V = 1023).
845	#[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}