jam_std_common/
state.rs

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