jam_std_common/
simple.rs

1use super::{Extrinsic, Header};
2use codec::{Decode, Encode};
3use core::fmt;
4use jam_types::{
5	chain_params, max_exports, max_imports, max_input, opaque, val_count, Balance, CoreIndex,
6	FixedVec, SegmentSliceLen, Slot, UnsignedGas, ValIndex,
7};
8use std::{sync::atomic::Ordering::Relaxed, time::Duration};
9
10/// Duration of a time slot, as a Rust-native `Duration`.
11pub fn slot_duration() -> Duration {
12	Duration::from_nanos(slot_period_ns())
13}
14
15/// Index for an attempt at producing a ticket for the right to produce a block.
16pub type TicketAttempt = u8;
17
18#[cfg(feature = "tiny")]
19mod defaults {
20	use super::{Slot, TicketAttempt};
21	pub(super) const EPOCH_PERIOD: Slot = 12;
22	pub(super) const EPOCH_TAIL_START: Slot = 10;
23	pub(super) const ROTATION_PERIOD: Slot = 4;
24	pub(super) const MAX_TICKETS_PER_BLOCK: usize = 3;
25	pub(super) const TICKETS_ATTEMPTS_NUMBER: TicketAttempt = 3;
26}
27#[cfg(not(feature = "tiny"))]
28mod defaults {
29	use super::{Slot, TicketAttempt};
30	pub(super) const EPOCH_PERIOD: Slot = 600;
31	pub(super) const EPOCH_TAIL_START: Slot = 500;
32	pub(super) const ROTATION_PERIOD: Slot = 10;
33	pub(super) const MAX_TICKETS_PER_BLOCK: usize = 16;
34	pub(super) const TICKETS_ATTEMPTS_NUMBER: TicketAttempt = 2;
35}
36
37chain_params! {
38	/// Number of nanoseconds in a time slot.
39	static SLOT_PERIOD_NS : _ = _(6_000_000_000);
40	pub fn slot_period_ns() -> u64;
41
42	/// The rotation period, defined in number of slots.
43	static ROTATION_PERIOD: _ = _(defaults::ROTATION_PERIOD);
44	pub fn rotation_period() -> Slot;
45	pub struct RotationPeriod; impl Get<_> for _ {}
46
47	/// The epoch period, defined in number of slots.
48	static EPOCH_PERIOD: _ = _(defaults::EPOCH_PERIOD);
49	pub fn epoch_period() -> Slot;
50	pub struct EpochPeriod; impl Get<_> for _ {}
51
52	/// The length of the contest, defined in number of slots.
53	static EPOCH_TAIL_START: _ = _(defaults::EPOCH_TAIL_START);
54	pub fn epoch_tail_start() -> Slot;
55
56	/// Max tickets allowed to be embedded in each block extrinsic.
57	static MAX_TICKETS_PER_BLOCK: _ = _(defaults::MAX_TICKETS_PER_BLOCK);
58	pub fn max_tickets_per_block() -> usize;
59	pub struct MaxTicketsPerBlock; impl Get<_> for _ {}
60
61	/// Maximum number of tickets which each validator may create.
62	///
63	/// Influences the anonymity of block producers. As all published tickets have a public
64	/// attempt number less than `attempts_number` if two tickets share an attempt number
65	/// then they must belong to two different validators, which reduces anonymity late as
66	/// we approach the epoch tail.
67	static TICKETS_ATTEMPTS_NUMBER: _ = _(defaults::TICKETS_ATTEMPTS_NUMBER);
68	pub fn tickets_attempts_number() -> TicketAttempt;
69
70	static DEPOSIT_PER_ACCOUNT: _ = _(100);
71	pub fn deposit_per_account() -> Balance;
72
73	static DEPOSIT_PER_ITEM: _ = _(10);
74	pub fn deposit_per_item() -> Balance;
75
76	static DEPOSIT_PER_BYTE: _ = _(1);
77	pub fn deposit_per_byte() -> Balance;
78
79	/// The maximum gas which may be used in a single block. At present this is just the product of
80	/// the maximum gas per work-report and the number of cores. In the future it should probably be
81	/// more to account for the possibility of always-accumulate services.
82	static BLOCK_GAS_LIMIT: _ = _(3_500_000_000);
83	pub fn block_gas_limit() -> UnsignedGas;
84
85	/// Maximum gas which may be used to Authorize a single work-package.
86	static MAX_IS_AUTHORIZED_GAS: _ = _(50_000_000);
87	pub fn max_is_authorized_gas() -> UnsignedGas;
88
89	/// Maximum gas which may be used to Refine a single work-report.
90	static MAX_REFINE_GAS: _ = _(5_000_000_000);
91	pub fn max_refine_gas() -> UnsignedGas;
92
93	/// Maximum gas which may be used to Accumulate a single work-report.
94	static MAX_ACCUMULATE_GAS: _ = _(10_000_000);
95	pub fn max_accumulate_gas() -> UnsignedGas;
96
97	static MAX_SERVICE_CODE_SIZE: _ = _(4_000_000);
98	pub fn max_service_code_size() -> usize;
99
100	static MAX_AUTHORIZER_CODE_SIZE: _ = _(64_000);
101	pub fn max_authorizer_code_size() -> usize;
102
103	static MAX_REFINE_MEMORY: _ = _(1024 * 1024 * 1024);
104	pub fn max_refine_memory() -> usize;
105
106	static MAX_IS_AUTHORIZED_MEMORY: _ = _(256 * 1024);
107	pub fn max_is_authorized_memory() -> usize;
108
109	/// The number of blocks which are kept in the recent block cache. We use a `u8` to index into
110	/// this and therefore it may be no larger than 255.
111	static RECENT_BLOCK_COUNT: _ = _(8);
112	pub fn recent_block_count() -> Slot;
113	pub struct RecentBlockCount; impl Get<_> for _ {}
114
115	/// The number of validators on each core.
116	pub const VALS_PER_CORE: _ = 3;
117	pub fn vals_per_core() -> usize;
118	pub struct ValsPerCore; impl Get<_> for _ {}
119
120	/// Number of cores in the JAM.
121	pub fn core_count() -> CoreIndex {
122		(val_count() as usize / VALS_PER_CORE) as CoreIndex
123	}
124	pub struct CoreCount; impl Get<_> for _ {}
125
126	/// Maximum number of DA segments that a Work Package can produce.
127	pub fn max_export_segments() -> u32 {
128		max_exports() + max_exports().div_ceil(PROVEN_PER_SEGMENT as u32)
129	}
130	pub struct MaxExportSegments; impl Get<_> for _ {}
131
132	/// Maximum number of DA segments that a Work Package can require as imports. This is twice the
133	/// maximum number of imports because each import may require a proof segment.
134	pub fn max_import_segments() -> u32 { max_imports() * 2 }
135	pub struct MaxImportSegments; impl Get<_> for _ {}
136
137	/// The period in timeslots after which reported but unavailable work may be replaced.
138	static AVAILABILITY_TIMEOUT: _ = _(5);
139	pub fn availability_timeout() -> Slot;
140
141	/// Number of items in the authorization window.
142	static AUTH_WINDOW: _ = _(8);
143	pub fn auth_window() -> usize;
144	pub struct AuthWindow; impl Get<_> for _ {}
145
146	/// Maximum age, in blocks, that the lookup anchor may be, taken from the regular anchor.
147	static MAX_LOOKUP_ANCHOR_AGE: _ = _(24 * 600);
148	pub fn max_lookup_anchor_age() -> Slot;
149}
150
151pub fn max_import_proof_size() -> usize {
152	// 6 hashes of page proof path + 6 hashes or segment tree path + 1 byte for size encoding
153	(PROVEN_PER_SEGMENT.next_power_of_two().ilog2() +
154		(max_imports() as usize)
155			.div_ceil(PROVEN_PER_SEGMENT)
156			.next_power_of_two()
157			.ilog2()) as usize *
158		32 + 1
159}
160
161/// The conservative maximum size of a Work Package Bundle. This is basically `MAX_INPUT` plus sone
162/// extra for the import proofs.
163pub fn max_bundle_size() -> usize {
164	max_input() as usize + max_import_proof_size() * max_imports() as usize
165}
166
167/// Parameters for the JAM protocol.
168#[derive(
169	Copy, Clone, Eq, PartialEq, Debug, Encode, Decode, serde::Serialize, serde::Deserialize,
170)]
171pub struct Parameters {
172	/// Total number of validators. Must by divisible by guarantor group size (3).
173	pub val_count: ValIndex,
174	/// Number of octets in a basic piece. Must be even and divide into segment length (4,104).
175	pub basic_piece_len: u32,
176	/// Number of authorizations in a queue allocated to a core.
177	pub auth_queue_len: u32,
178	/// Minimum period in blocks between going from becoming `Available` to `Zombie`, and then
179	/// again from `Zombie` to non-existent.
180	pub min_turnaround_period: Slot,
181	/// Maximum number of Work Items in a Work Package.
182	pub max_work_items: u32,
183	/// Maximum number of imports in a Work Package.
184	pub max_imports: u32,
185	/// Maximum number of exports in a Work Package.
186	pub max_exports: u32,
187	/// Maximum number of extrinsics in a Work Package.
188	pub max_extrinsics: u32,
189	/// Maximum number of dependencies (total of prerequisites and SR lookup entries).
190	pub max_dependencies: u32,
191	/// Maximum size of a Work Package together with all extrinsic data and imported segments.
192	pub max_input: u32,
193
194	/// The period for each JAM time slot, defined in nanoseconds.
195	pub slot_period_ns: u64,
196	/// The epoch period, defined in number of slots.
197	pub epoch_period: Slot,
198	/// The length of the contest, defined in number of slots.
199	pub epoch_tail_start: Slot,
200	/// The rotation period, defined in number of slots.
201	pub rotation_period: Slot,
202	/// Maximum gas which can be processed in a single block.
203	pub block_gas_limit: UnsignedGas,
204	/// The number of blocks which are kept in the recent block cache.
205	pub recent_block_count: Slot,
206	/// Max tickets allowed to be embedded in each block extrinsic.
207	pub max_tickets_per_block: u32,
208	/// The number of distinct tickets which may be created and submitted by each validator on each
209	/// epoch.
210	pub tickets_attempts_number: TicketAttempt,
211	/// The base deposit required to retain an account.
212	pub deposit_per_account: Balance,
213	/// The additional deposit required for each preimage or storage item in an account.
214	pub deposit_per_item: Balance,
215	/// The additional deposit required for each byte of each storage item in an account and
216	/// preimage of an account.
217	pub deposit_per_byte: Balance,
218	/// Maximum gas which may be used to Authorize a single work-package.
219	pub max_is_authorized_gas: UnsignedGas,
220	/// Maximum gas which may be used to Refine a single work-report.
221	pub max_refine_gas: UnsignedGas,
222	/// Maximum gas which may be used to Accumulate a single work-report.
223	pub max_accumulate_gas: UnsignedGas,
224	/// The maximum size of service code (used by Refine/Accumulate/OnTransfer procedures).
225	pub max_service_code_size: u32,
226	/// The maximum size of authorizer code (used by Is-Authorized procedure).
227	pub max_authorizer_code_size: u32,
228	/// The maximum amount of RAM which may be used by Refine/Accumulate code.
229	pub max_refine_memory: u32,
230	/// The maximum amount of RAM which may be used by IsAuthorized code.
231	pub max_is_authorized_memory: u32,
232	/// The period in timeslots after which reported but unavailable work may be replaced.
233	pub availability_timeout: Slot,
234	/// Number of items in the authorization window.
235	pub auth_window: u32,
236	/// Maximum age, in blocks, that the lookup anchor may be, taken from the regular anchor.
237	pub max_lookup_anchor_age: Slot,
238}
239impl fmt::Display for Parameters {
240	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241		write!(
242			f,
243			"\n\
244			val_count: {}\n\
245			basic_piece_len: {}\n\
246			auth_queue_len: {}\n\
247			min_turnaround_period: {}\n\
248			max_work_items: {}\n\
249			max_imports: {}\n\
250			max_exports: {}\n\
251			max_extrinsics: {}\n\
252			max_dependencies: {}\n\
253			max_input: {}\n\
254			slot_period_ns: {}\n\
255			epoch_period: {}\n\
256			epoch_tail_start: {}\n\
257			rotation_period: {}\n\
258			block_gas_limit: {}\n\
259			recent_block_count: {}\n\
260			max_tickets_per_block: {}\n\
261			tickets_attempts_number: {}\n\
262			deposit_per_account: {}\n\
263			deposit_per_item: {}\n\
264			deposit_per_byte: {}\n\
265			max_is_authorized_gas: {}\n\
266			max_refine_gas: {}\n\
267			max_accumulate_gas: {}\n\
268			max_service_code_size: {}\n\
269			max_authorizer_code_size: {}\n\
270			max_refine_memory: {}\n\
271			max_is_authorized_memory: {}\n\
272			availability_timeout: {}\n\
273			auth_window: {}\n\
274			max_lookup_anchor_age: {}\n\
275			",
276			self.val_count,
277			self.basic_piece_len,
278			self.auth_queue_len,
279			self.min_turnaround_period,
280			self.max_work_items,
281			self.max_imports,
282			self.max_exports,
283			self.max_extrinsics,
284			self.max_dependencies,
285			self.max_input,
286			self.slot_period_ns,
287			self.epoch_period,
288			self.epoch_tail_start,
289			self.rotation_period,
290			self.block_gas_limit,
291			self.recent_block_count,
292			self.max_tickets_per_block,
293			self.tickets_attempts_number,
294			self.deposit_per_account,
295			self.deposit_per_item,
296			self.deposit_per_byte,
297			self.max_is_authorized_gas,
298			self.max_refine_gas,
299			self.max_accumulate_gas,
300			self.max_service_code_size,
301			self.max_authorizer_code_size,
302			self.max_refine_memory,
303			self.max_is_authorized_memory,
304			self.availability_timeout,
305			self.auth_window,
306			self.max_lookup_anchor_age,
307		)
308	}
309}
310
311impl Parameters {
312	pub fn tiny() -> Self {
313		Self {
314			val_count: 6,
315			basic_piece_len: 4,
316			auth_queue_len: 80,
317			min_turnaround_period: 28_800,
318			max_work_items: 16,
319			max_imports: 3072,
320			max_exports: 3072,
321			max_extrinsics: 128,
322			max_dependencies: 8,
323			max_input: 12 * 1024 * 1024,
324			slot_period_ns: 6_000_000_000,
325			epoch_period: 12,
326			epoch_tail_start: 10,
327			rotation_period: 4,
328			block_gas_limit: 20_000_000,
329			recent_block_count: 8,
330			max_tickets_per_block: 3,
331			tickets_attempts_number: 3,
332			deposit_per_account: 100,
333			deposit_per_item: 10,
334			deposit_per_byte: 1,
335			max_is_authorized_gas: 50_000_000,
336			max_refine_gas: 1_000_000_000,
337			max_accumulate_gas: 10_000_000,
338			max_service_code_size: 4_000_000,
339			max_authorizer_code_size: 64_000,
340			max_refine_memory: 1024 * 1024 * 1024,
341			max_is_authorized_memory: 256 * 1024,
342			availability_timeout: 5,
343			auth_window: 8,
344			max_lookup_anchor_age: 24 * 600,
345		}
346	}
347	pub fn full() -> Self {
348		Self {
349			val_count: 1023,
350			basic_piece_len: 684,
351			auth_queue_len: 80,
352			min_turnaround_period: 28_800,
353			max_work_items: 16,
354			max_imports: 3072,
355			max_exports: 3072,
356			max_extrinsics: 128,
357			max_dependencies: 8,
358			max_input: 12 * 1024 * 1024,
359			slot_period_ns: 6_000_000_000,
360			epoch_period: 600,
361			epoch_tail_start: 500,
362			rotation_period: 10,
363			block_gas_limit: 3_500_000_000,
364			recent_block_count: 8,
365			max_tickets_per_block: 16,
366			tickets_attempts_number: 2,
367			deposit_per_account: 100,
368			deposit_per_item: 10,
369			deposit_per_byte: 1,
370			max_is_authorized_gas: 50_000_000,
371			max_refine_gas: 5_000_000_000,
372			max_accumulate_gas: 10_000_000,
373			max_service_code_size: 4_000_000,
374			max_authorizer_code_size: 64_000,
375			max_refine_memory: 1024 * 1024 * 1024,
376			max_is_authorized_memory: 256 * 1024,
377			availability_timeout: 5,
378			auth_window: 8,
379			max_lookup_anchor_age: 24 * 600,
380		}
381	}
382	pub fn get() -> Self {
383		let jam_types::Parameters {
384			val_count,
385			basic_piece_len,
386			auth_queue_len,
387			min_turnaround_period,
388			max_work_items,
389			max_imports,
390			max_exports,
391			max_extrinsics,
392			max_dependencies,
393			max_input,
394		} = jam_types::Parameters::get();
395		Self {
396			val_count,
397			basic_piece_len,
398			auth_queue_len,
399			min_turnaround_period,
400			max_work_items,
401			max_imports,
402			max_exports,
403			max_extrinsics,
404			max_dependencies,
405			max_input,
406			slot_period_ns: SLOT_PERIOD_NS.load(Relaxed),
407			epoch_period: EPOCH_PERIOD.load(Relaxed),
408			epoch_tail_start: EPOCH_TAIL_START.load(Relaxed),
409			rotation_period: ROTATION_PERIOD.load(Relaxed),
410			block_gas_limit: BLOCK_GAS_LIMIT.load(Relaxed),
411			recent_block_count: RECENT_BLOCK_COUNT.load(Relaxed),
412			max_tickets_per_block: MAX_TICKETS_PER_BLOCK.load(Relaxed) as _,
413			tickets_attempts_number: TICKETS_ATTEMPTS_NUMBER.load(Relaxed),
414			deposit_per_account: DEPOSIT_PER_ACCOUNT.load(Relaxed),
415			deposit_per_item: DEPOSIT_PER_ITEM.load(Relaxed),
416			deposit_per_byte: DEPOSIT_PER_BYTE.load(Relaxed),
417			max_is_authorized_gas: MAX_IS_AUTHORIZED_GAS.load(Relaxed),
418			max_refine_gas: MAX_REFINE_GAS.load(Relaxed),
419			max_accumulate_gas: MAX_ACCUMULATE_GAS.load(Relaxed),
420			max_service_code_size: MAX_SERVICE_CODE_SIZE.load(Relaxed) as _,
421			max_authorizer_code_size: MAX_AUTHORIZER_CODE_SIZE.load(Relaxed) as _,
422			max_refine_memory: MAX_REFINE_MEMORY.load(Relaxed) as _,
423			max_is_authorized_memory: MAX_IS_AUTHORIZED_MEMORY.load(Relaxed) as _,
424			availability_timeout: AVAILABILITY_TIMEOUT.load(Relaxed),
425			auth_window: AUTH_WINDOW.load(Relaxed) as _,
426			max_lookup_anchor_age: MAX_LOOKUP_ANCHOR_AGE.load(Relaxed),
427		}
428	}
429	pub fn validate(self) -> Result<jam_types::Parameters, &'static str> {
430		let r = jam_types::Parameters {
431			val_count: self.val_count,
432			basic_piece_len: self.basic_piece_len,
433			auth_queue_len: self.auth_queue_len,
434			min_turnaround_period: self.min_turnaround_period,
435			max_work_items: self.max_work_items,
436			max_imports: self.max_imports,
437			max_exports: self.max_exports,
438			max_extrinsics: self.max_extrinsics,
439			max_dependencies: self.max_dependencies,
440			max_input: self.max_input,
441		};
442		r.validate()?;
443		if self.epoch_period % self.rotation_period != 0 {
444			return Err("`rotation_period` does not divide into `epoch_period`")
445		}
446		if self.epoch_tail_start >= self.epoch_period {
447			return Err("`epoch_tail_start` must be less than `epoch_period`")
448		}
449		if self.val_count as usize % VALS_PER_CORE != 0 {
450			return Err("`val_count` does not divide by `VALS_PER_CORE` (3)")
451		}
452		if recent_block_count() > 255 {
453			return Err("`recent_block_count` may be no larger than `BlockIndex::MAX` (255)")
454		}
455		Ok(r)
456	}
457	pub fn apply(self) -> Result<(), &'static str> {
458		let base = self.validate()?;
459		base.apply()?;
460		EPOCH_PERIOD.store(self.epoch_period, Relaxed);
461		EPOCH_TAIL_START.store(self.epoch_tail_start, Relaxed);
462		ROTATION_PERIOD.store(self.rotation_period, Relaxed);
463		BLOCK_GAS_LIMIT.store(self.block_gas_limit, Relaxed);
464		RECENT_BLOCK_COUNT.store(self.recent_block_count, Relaxed);
465		MAX_TICKETS_PER_BLOCK.store(self.max_tickets_per_block as _, Relaxed);
466		TICKETS_ATTEMPTS_NUMBER.store(self.tickets_attempts_number, Relaxed);
467		DEPOSIT_PER_ACCOUNT.store(self.deposit_per_account, Relaxed);
468		DEPOSIT_PER_ITEM.store(self.deposit_per_item, Relaxed);
469		DEPOSIT_PER_BYTE.store(self.deposit_per_byte, Relaxed);
470		MAX_IS_AUTHORIZED_GAS.store(self.max_is_authorized_gas, Relaxed);
471		MAX_REFINE_GAS.store(self.max_refine_gas, Relaxed);
472		MAX_ACCUMULATE_GAS.store(self.max_accumulate_gas, Relaxed);
473		MAX_SERVICE_CODE_SIZE.store(self.max_service_code_size as _, Relaxed);
474		MAX_AUTHORIZER_CODE_SIZE.store(self.max_authorizer_code_size as _, Relaxed);
475		MAX_REFINE_MEMORY.store(self.max_refine_memory as _, Relaxed);
476		MAX_IS_AUTHORIZED_MEMORY.store(self.max_is_authorized_memory as _, Relaxed);
477		RECENT_BLOCK_COUNT.store(self.recent_block_count, Relaxed);
478		AVAILABILITY_TIMEOUT.store(self.availability_timeout, Relaxed);
479		AUTH_WINDOW.store(self.auth_window as _, Relaxed);
480		MAX_LOOKUP_ANCHOR_AGE.store(self.max_lookup_anchor_age, Relaxed);
481		Ok(())
482	}
483}
484
485/// Maximum basic size of a Work Package that we accept.
486/// This should be at least ImportSpec::encoded_fixed_size() * max_imports()
487pub const SANE_MAX_PACKAGE_SIZE: usize = 200 * 1024;
488
489/// Maximum total output blobs which can be in a Work Report. The maximum encoded size of a Work
490/// Report is this together with the portions which are constant or based on the (very limited)
491/// number of Work Items.
492pub const MAX_REPORT_ELECTIVE_DATA: usize = 48 * 1024;
493
494/// The min. no. of signatures required for a guarantee to be valid.
495///
496/// Equals 2/3 of guarantors assigned to a core.
497pub const GUARANTEE_MIN_SIGNATURES: usize = (VALS_PER_CORE * 2).div_ceil(3);
498
499// Entropy required by some protocol's operations.
500opaque! { pub struct Entropy(pub [u8; 32]); }
501
502#[derive(Clone, Encode, Decode, Debug)]
503pub struct Block {
504	pub header: Header,
505	pub extrinsic: Extrinsic,
506}
507
508/// The maximum number of levels in the implicit tree derived from the segment hashes in a
509/// proof-page.
510//pub const SUFFIX_SKIP_LEN: usize = (SEGMENT_LEN / 2 / 32).trailing_zeros() as usize;
511pub const SUFFIX_SKIP_LEN: usize = 6;
512
513/// The maximum number of segments whose justification can be included in a single segment-sized
514/// proof-page. Basically just the highest power of 2 which when multiplied by 32 (the size of a
515/// hash) leaves enough room from 4104 (the size of a segment) to fit a partial Merkle proof of
516/// 6 branches (6 * 32 = 192) plus the overhead for storing the actual number of hashes (should be
517/// one byte for `PROVEN_PER_SEGMENT < 128`). It turns out that this is 2^6 = 64.
518///
519/// See `max_prefix_proof_len` for the actual formula.
520pub const PROVEN_PER_SEGMENT: usize = 1 << SUFFIX_SKIP_LEN;
521
522pub type SegmentSlice = FixedVec<u8, SegmentSliceLen>;
523
524/// Type to represent the index of a tranche.
525pub type TrancheIndex = u8;