Skip to main content

jam_std_common/
state.rs

1use crate::{
2	Entropy, Mmr, ReportGuarantee, ValKeysets, WorkReport,
3	bandersnatch::ring_vrf::RingCommitment,
4	hash_raw_concat,
5	safrole::{EpochTicketsAccumulator, TicketsOrKeys},
6};
7use bounded_collections::ConstU32;
8use codec::{Decode, Encode};
9use jam_types::{
10	AuthQueue, AuthWindow, AuthorizerHash, Balance, BoundedVec, CodeHash, CoreCount, EpochPeriod,
11	FOOTPRINT_PREIMAGE_OVERHEAD, FOOTPRINT_STORAGE_OVERHEAD, FixedVec, Hash, HeaderHash,
12	MmrPeakHash, RecentBlockCount, SegmentTreeRoot, ServiceId, ServiceInfo, Slot, StateRootHash,
13	UnsignedGas, VecMap, VecSet, WorkPackageHash, WorkReportHash, deposit_per_account,
14	deposit_per_byte, deposit_per_item, opaque,
15};
16
17opaque! {
18	pub struct StorageKey(pub [u8; 31]);
19}
20
21#[derive(Debug, Encode, Decode, Copy, Clone, Eq, PartialEq)]
22pub enum SystemKey {
23	Reserved0 = 0,
24	AuthPools = 1,
25	AuthQueues = 2,
26	RecentBlocks = 3,
27	Safrole = 4,
28	Disputes = 5,
29	Entropy = 6,
30	Designates = 7,
31	Validators = 8,
32	PrevValidators = 9,
33	Availability = 10,
34	CurrentTime = 11,
35	Privileges = 12,
36	Statistics = 13,
37	ReadyQueue = 14,
38	Accumulated = 15,
39	AccumulationOutput = 16,
40}
41
42impl From<SystemKey> for StorageKey {
43	fn from(value: SystemKey) -> Self {
44		let k = value.encode();
45		let mut r = [0; StorageKey::LEN];
46		r[..k.len()].copy_from_slice(&k[..]);
47		r.into()
48	}
49}
50
51impl IntoStorageKey for SystemKey {}
52
53impl IntoStorageKey for StorageKey {}
54
55#[derive(Clone)]
56pub enum ServiceKey<'a> {
57	Info { id: ServiceId },
58	Value { id: ServiceId, key: &'a [u8] },
59	Request { id: ServiceId, len: u32, hash: [u8; 32] },
60	Preimage { id: ServiceId, hash: [u8; 32] },
61}
62
63pub trait IntoStorageKey: Into<StorageKey> {
64	fn service_id(&self) -> Option<ServiceId> {
65		None
66	}
67}
68
69impl IntoStorageKey for ServiceKey<'_> {
70	fn service_id(&self) -> Option<ServiceId> {
71		Some(match self {
72			Self::Info { id, .. } |
73			Self::Value { id, .. } |
74			Self::Request { id, .. } |
75			Self::Preimage { id, .. } => *id,
76		})
77	}
78}
79
80impl<'a> From<ServiceKey<'a>> for StorageKey {
81	fn from(k: ServiceKey<'a>) -> Self {
82		use ServiceKey::*;
83		let id = match k {
84			Info { id } | Value { id, .. } | Request { id, .. } | Preimage { id, .. } => id,
85		}
86		.to_le_bytes();
87		let mut r = [0; StorageKey::LEN];
88		let hash = match k {
89			Info { .. } => {
90				r[0] = 255;
91				r[1] = id[0];
92				r[3] = id[1];
93				r[5] = id[2];
94				r[7] = id[3];
95				return r.into();
96			},
97			Value { key, .. } => hash_raw_concat([&[0xff, 0xff, 0xff, 0xff], key]),
98			Preimage { hash, .. } => hash_raw_concat([&[0xfe, 0xff, 0xff, 0xff], &hash[..]]),
99			Request { len, hash, .. } => hash_raw_concat([&len.to_le_bytes(), &hash[..]]),
100		};
101		r[..8].copy_from_slice(&[id[0], hash[0], id[1], hash[1], id[2], hash[2], id[3], hash[3]]);
102		r[8..].copy_from_slice(&hash[4..27]);
103		r.into()
104	}
105}
106
107/// If `key` is a [`ServiceKey::Info`] state key, extract the service id.
108///
109/// This is the inverse of the `Info` arm of the [`StorageKey`] construction above.
110pub fn service_id_from_info_key(key: &[u8]) -> Option<ServiceId> {
111	if key.len() == StorageKey::LEN &&
112		key[0] == 255 &&
113		key[2] == 0 &&
114		key[4] == 0 &&
115		key[6] == 0 &&
116		key[8..] == [0; 23]
117	{
118		Some(ServiceId::from_le_bytes([key[1], key[3], key[5], key[7]]))
119	} else {
120		None
121	}
122}
123
124/// Current service struct version.
125const SERVICE_VERSION: u8 = 0;
126
127#[derive(Debug, Clone)]
128pub struct Service {
129	/// The hash of the code of the service.
130	pub code_hash: CodeHash,
131	/// The existing balance of the service.
132	pub balance: Balance,
133	/// The minimum amount of gas which must be provided to this service's `accumulate` for each
134	/// work item it must process.
135	pub min_item_gas: UnsignedGas,
136	/// The minimum amount of gas which must be provided to this service's `accumulate` for each
137	/// incoming transfer it must process.
138	pub min_memo_gas: UnsignedGas,
139	/// The total number of bytes used for data electively held for this service on-chain.
140	pub bytes: u64,
141	/// Offset of storage footprint only above which a minimum deposit is needed.
142	pub deposit_offset: Balance,
143	/// The total number of items of data electively held for this service on-chain.
144	pub items: u32,
145	/// Creation time slot.
146	pub creation_slot: Slot,
147	/// Most recent accumulation time slot.
148	pub last_accumulation_slot: Slot,
149	/// Parent service identifier.
150	pub parent_service: ServiceId,
151}
152
153impl From<Service> for ServiceInfo {
154	fn from(service: Service) -> Self {
155		ServiceInfo {
156			code_hash: service.code_hash,
157			balance: service.balance,
158			min_item_gas: service.min_item_gas,
159			min_memo_gas: service.min_memo_gas,
160			bytes: service.bytes,
161			items: service.items,
162			threshold: service.threshold(),
163			deposit_offset: service.deposit_offset,
164			creation_slot: service.creation_slot,
165			last_accumulation_slot: service.last_accumulation_slot,
166			parent_service: service.parent_service,
167		}
168	}
169}
170
171impl Encode for Service {
172	fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
173		dest.push_byte(SERVICE_VERSION);
174		self.code_hash.encode_to(dest);
175		self.balance.encode_to(dest);
176		self.min_item_gas.encode_to(dest);
177		self.min_memo_gas.encode_to(dest);
178		self.bytes.encode_to(dest);
179		self.deposit_offset.encode_to(dest);
180		self.items.encode_to(dest);
181		self.creation_slot.encode_to(dest);
182		self.last_accumulation_slot.encode_to(dest);
183		self.parent_service.encode_to(dest);
184	}
185}
186
187impl Decode for Service {
188	fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
189		if u8::decode(input)? != SERVICE_VERSION {
190			return Err("Invalid service version".into())
191		}
192		Ok(Self {
193			code_hash: CodeHash::decode(input)?,
194			balance: Balance::decode(input)?,
195			min_item_gas: UnsignedGas::decode(input)?,
196			min_memo_gas: UnsignedGas::decode(input)?,
197			bytes: u64::decode(input)?,
198			deposit_offset: Balance::decode(input)?,
199			items: u32::decode(input)?,
200			creation_slot: Slot::decode(input)?,
201			last_accumulation_slot: Slot::decode(input)?,
202			parent_service: ServiceId::decode(input)?,
203		})
204	}
205}
206
207impl Service {
208	pub fn threshold(&self) -> Balance {
209		Self::deposit_required(self.items, self.bytes, self.deposit_offset)
210	}
211	pub fn free(&self) -> Balance {
212		self.balance.saturating_sub(self.threshold())
213	}
214	pub fn deposit_required(items: u32, bytes: u64, deposit_offset: Balance) -> Balance {
215		let base_deposit =
216			items as u64 * deposit_per_item() + bytes * deposit_per_byte() + deposit_per_account();
217		base_deposit.saturating_sub(deposit_offset)
218	}
219}
220
221/// One electively held item of a service's footprint, for deposit accounting.
222#[derive(Debug, Clone, Copy, Eq, PartialEq)]
223pub enum FootprintItem {
224	/// No footprint.
225	None,
226	/// A storage entry, key plus value length.
227	Storage(u64),
228	/// A preimage, its length.
229	Lookup(u32),
230}
231
232impl From<Option<FootprintItem>> for FootprintItem {
233	fn from(item: Option<FootprintItem>) -> Self {
234		item.unwrap_or(Self::None)
235	}
236}
237
238impl FootprintItem {
239	/// The (items, bytes) footprint this entry contributes to a service's deposit.
240	pub fn implication(&self) -> (u32, u64) {
241		match self {
242			Self::None => (0, 0),
243			Self::Storage(size) => (1, *size + FOOTPRINT_STORAGE_OVERHEAD),
244			Self::Lookup(len) => (2, *len as u64 + FOOTPRINT_PREIMAGE_OVERHEAD),
245		}
246	}
247}
248
249/// Safrole consensus state.
250#[derive(Encode, Decode, Debug, Clone)]
251pub struct SafroleState {
252	/// Next epoch validator keys (gamma_k).
253	pub waiting_validators: ValKeysets,
254	/// Next epoch ring keys commitment (gamma_z).
255	pub ring_commitment: RingCommitment,
256	/// Tickets or keys used to claim the current epoch's slots (gamma_s).
257	pub tickets_or_keys: TicketsOrKeys,
258	/// Next epoch tickets accumulator (gamma_a).
259	pub ticket_accumulator: EpochTicketsAccumulator,
260}
261
262pub type AuthPool = BoundedVec<AuthorizerHash, AuthWindow>;
263pub type AuthPools = FixedVec<AuthPool, CoreCount>;
264pub type AuthQueues = FixedVec<AuthQueue, CoreCount>;
265
266#[derive(Debug, Encode, Decode, Eq, PartialEq, Clone)]
267pub struct AvailabilityAssignment {
268	/// Work report guarantee.
269	pub guarantee: ReportGuarantee,
270	/// Slot at which the guarantee was registered on-chain. Unlike `guarantee.slot`, this is the
271	/// reference point for the availability timeout.
272	pub registered_slot: Slot,
273}
274
275impl AvailabilityAssignment {
276	/// The guaranteed work report.
277	pub fn report(&self) -> &WorkReport {
278		&self.guarantee.report
279	}
280}
281
282#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, Default)]
283pub struct Privileges {
284	/// The service index with the ability to alter the privileges as well as bestow services with
285	/// storage deposit credits.
286	pub bless: ServiceId,
287	/// The service indices with the ability to assign authorizors to cores.
288	pub assign: FixedVec<ServiceId, CoreCount>,
289	/// The service index with the ability to set the staging validator keys.
290	pub designate: ServiceId,
291	/// The service index with the ability to register service ids in the protected range.
292	pub register: ServiceId,
293	/// The services which always accumulate, together with their free gas.
294	pub always_acc: VecMap<ServiceId, UnsignedGas>,
295}
296
297#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, Default)]
298pub struct Disputes {
299	/// The allow-set of work-reports, these are known to be good.
300	pub good: VecSet<WorkReportHash>,
301	/// The corrupt-set of work-reports, these are known to be bad.
302	pub bad: VecSet<WorkReportHash>,
303	/// The ban-set of work-reports, these have been determined of uncertain validity.
304	pub wonky: VecSet<WorkReportHash>,
305	/// The offenders' Ed25519 keys, these are known to have incorrectly judged a work-report's
306	/// validity.
307	pub offenders: VecSet<super::ed25519::Public>,
308}
309
310#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, Default)]
311pub struct ValActivityRecord {
312	// The number of blocks produced by the validator.
313	pub blocks: u32,
314	// The number of tickets introduced by the validator.
315	pub tickets: u32,
316	// The number of preimages introduced by the validator.
317	pub preimages: u32,
318	// The total number of octets across all preimages introduced by the validator.
319	pub preimages_size: u32,
320	// The number of reports guaranteed by the validator.
321	pub guarantees: u32,
322	// The number of availability assurances made by the validator.
323	pub assurances: u32,
324}
325
326#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, Default)]
327pub struct ServiceActivityRecord {
328	/// Number of preimages provided to this service.
329	#[codec(compact)]
330	pub provided_count: u16,
331	/// Total size of preimages provided to this service.
332	#[codec(compact)]
333	pub provided_size: u32,
334	/// Number of work-items refined by service for reported work.
335	#[codec(compact)]
336	pub refinement_count: u32,
337	/// Amount of gas used for refinement by service for reported work.
338	#[codec(compact)]
339	pub refinement_gas_used: UnsignedGas,
340	/// Number of segments imported from the DL by service for reported work.
341	#[codec(compact)]
342	pub imports: u32,
343	/// Total number of extrinsics used by service for reported work.
344	#[codec(compact)]
345	pub extrinsic_count: u32,
346	/// Total size of extrinsics used by service for reported work.
347	#[codec(compact)]
348	pub extrinsic_size: u32,
349	/// Number of segments exported into the DL by service for reported work.
350	#[codec(compact)]
351	pub exports: u32,
352	/// Number of work-items accumulated by service.
353	#[codec(compact)]
354	pub accumulate_count: u32,
355	/// Number of transfers processed by service during accumulation.
356	#[codec(compact)]
357	pub accumulate_transfer_count: u32,
358	/// Amount of gas used for accumulation by service.
359	#[codec(compact)]
360	pub accumulate_gas_used: UnsignedGas,
361}
362
363#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, Default)]
364pub struct CoreActivityRecord {
365	// Assurances coming in from general validators (but ultimately provided by guarantors)
366	/// Amount of bytes which are placed into either Audits or Segments DA.
367	/// This includes the work-bundle (including all extrinsics and imports) as well as all
368	/// (exported) segments.
369	#[codec(compact)]
370	pub da_load: u32,
371	/// Number of validators which formed super-majority for assurance.
372	#[codec(compact)]
373	pub popularity: u16,
374	/// Number of segments imported from DA made by core for reported work.
375	#[codec(compact)]
376	pub imports: u16,
377	/// Total number of extrinsics used by core for reported work.
378	#[codec(compact)]
379	pub extrinsic_count: u16,
380	/// Total size of extrinsics used by core for reported work.
381	#[codec(compact)]
382	pub extrinsic_size: u32,
383	/// Number of segments exported into DA made by core for reported work.
384	#[codec(compact)]
385	pub exports: u16,
386	/// The work-bundle size. This is the size of data being placed into Audits DA by the core.
387	#[codec(compact)]
388	pub bundle_size: u32,
389	// Reports coming in from guarantors
390	/// Total gas consumed by core for reported work. Includes all refinement and authorizations.
391	#[codec(compact)]
392	pub gas_used: UnsignedGas,
393}
394
395pub type ValidatorsStats = BoundedVec<ValActivityRecord, jam_types::MaxValCount>;
396pub type CoresStats = FixedVec<CoreActivityRecord, CoreCount>;
397pub type ServicesStats = VecMap<ServiceId, ServiceActivityRecord>;
398
399#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, Default)]
400pub struct Statistics {
401	pub vals_curr: ValidatorsStats,
402	pub vals_last: ValidatorsStats,
403	pub cores: CoresStats,
404	pub services: ServicesStats,
405}
406
407impl Statistics {
408	/// Reset the two accumulators.
409	pub fn reset(&mut self) {
410		self.cores = Default::default();
411		self.services = Default::default();
412	}
413}
414
415/// Entries relative to epoch `N`
416/// `[0]`: epoch `N` entropy accumulator. Updated on each block with fresh entropy.
417/// `[i]`, for 1 ≤ i ≤ 3: accumulator snapshot at the begin of epoch  `N-i+1`.
418/// If `N-i+1 < 0` then the corresponding entry is set to some fixed default value.
419pub type EntropyBuffer = FixedVec<Entropy, ConstU32<4>>;
420
421/// Block information.
422#[derive(Debug, Encode, Decode, Eq, PartialEq, Clone)]
423pub struct BlockInfo {
424	/// Header hash.
425	pub hash: HeaderHash,
426	/// Accumulation MMR root.
427	pub beefy_root: MmrPeakHash,
428	/// Posterior state root.
429	pub state_root: StateRootHash,
430	/// Block slot.
431	pub slot: Slot,
432	/// Hash of each work report made into the block which is no more than the
433	/// number of cores.
434	pub reported: VecMap<WorkPackageHash, SegmentTreeRoot>,
435}
436
437/// Recent blocks history
438pub type RecentBlocksHistory = BoundedVec<BlockInfo, RecentBlockCount>;
439
440/// Imported blocks information
441#[derive(Clone, Encode, Decode, Default, Debug)]
442pub struct RecentBlocks {
443	pub history: RecentBlocksHistory,
444	pub mmr: Mmr,
445}
446
447pub type AvailabilityAssignments = FixedVec<Option<AvailabilityAssignment>, CoreCount>;
448
449#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq)]
450pub struct ReadyRecord {
451	pub report: WorkReport,
452	pub deps: VecSet<WorkPackageHash>,
453}
454pub type ReadyQueue = FixedVec<Vec<ReadyRecord>, EpochPeriod>;
455pub type Accumulated = FixedVec<Vec<WorkPackageHash>, EpochPeriod>;
456
457pub type AccumulationOutput = Vec<(ServiceId, Hash)>;