jam_types/
types.rs

1use super::*;
2use bounded_collections::ConstU32;
3use simple::OpaqueBlsPublic;
4
5/// Plain-old-data struct of the same length and layout to `ValKeyset` struct. This does not
6/// bring in any cryptography.
7#[derive(Copy, Clone, Encode, Decode, Debug, Eq, PartialEq)]
8pub struct OpaqueValKeyset {
9	/// The opaque Ed25519 public key.
10	pub ed25519: OpaqueEd25519Public,
11	/// The opaque Bandersnatch public key.
12	pub bandersnatch: OpaqueBandersnatchPublic,
13	/// The opaque BLS public key.
14	pub bls: OpaqueBlsPublic,
15	/// The opaque metadata.
16	pub metadata: OpaqueValidatorMetadata,
17}
18impl Default for OpaqueValKeyset {
19	fn default() -> Self {
20		Self {
21			ed25519: OpaqueEd25519Public::zero(),
22			bandersnatch: OpaqueBandersnatchPublic::zero(),
23			bls: OpaqueBlsPublic::zero(),
24			metadata: OpaqueValidatorMetadata::zero(),
25		}
26	}
27}
28
29/// The opaque keys for each validator.
30pub type OpaqueValKeysets = FixedVec<OpaqueValKeyset, ConstU32<{ VAL_COUNT as u32 }>>;
31
32/// Reference to a sequence of import segments, which when combined with an index forms a
33/// commitment to a specific segment of data.
34#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
35pub enum RootIdentifier {
36	/// Direct cryptographic commitment to the export-segments tree root.
37	Direct(SegmentTreeRoot),
38	/// Indirect reference to the export-segments tree root via a hash of the work-package which
39	/// resulted in it.
40	Indirect(WorkPackageHash),
41}
42
43impl From<SegmentTreeRoot> for RootIdentifier {
44	fn from(root: SegmentTreeRoot) -> Self {
45		Self::Direct(root)
46	}
47}
48impl From<WorkPackageHash> for RootIdentifier {
49	fn from(hash: WorkPackageHash) -> Self {
50		Self::Indirect(hash)
51	}
52}
53impl TryFrom<RootIdentifier> for SegmentTreeRoot {
54	type Error = WorkPackageHash;
55	fn try_from(root: RootIdentifier) -> Result<Self, Self::Error> {
56		match root {
57			RootIdentifier::Direct(root) => Ok(root),
58			RootIdentifier::Indirect(hash) => Err(hash),
59		}
60	}
61}
62
63/// Import segments specification, which identifies a single exported segment.
64#[derive(Clone, Debug, Eq, PartialEq, Hash)]
65pub struct ImportSpec {
66	/// The identifier of a series of exported segments.
67	pub root: RootIdentifier,
68	/// The index into the identified series of exported segments.
69	pub index: u16,
70}
71
72impl Encode for ImportSpec {
73	fn encode_to<T: scale::Output + ?Sized>(&self, dest: &mut T) {
74		let off = match &self.root {
75			RootIdentifier::Direct(root) => {
76				root.encode_to(dest);
77				0
78			},
79			RootIdentifier::Indirect(hash) => {
80				hash.encode_to(dest);
81				1 << 15
82			},
83		};
84		(self.index + off).encode_to(dest);
85	}
86}
87
88impl Decode for ImportSpec {
89	fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
90		let h = Hash::decode(input)?;
91		let i = u16::decode(input)?;
92		let root = if i & (1 << 15) == 0 {
93			SegmentTreeRoot::from(h).into()
94		} else {
95			WorkPackageHash::from(h).into()
96		};
97		Ok(Self { root, index: i & !(1 << 15) })
98	}
99}
100
101/// Specification of a single piece of extrinsic data.
102#[derive(Clone, Encode, Decode, Debug)]
103pub struct ExtrinsicSpec {
104	/// The hash of the extrinsic data.
105	pub hash: ExtrinsicHash,
106	/// The length of the extrinsic data.
107	pub len: u32,
108}
109
110/// A definition of work to be done by the Refinement logic of a service and transformed into a
111/// [WorkOutput] for its Accumulation logic.
112///
113/// This is the generic version of the work-item, which is specialized depending on whether and how
114/// the extrinsic data is supplied.
115#[derive(Clone, Encode, Decode, Debug)]
116pub struct GenericWorkItem<Xt> {
117	/// Service identifier to which this work item relates.
118	pub service: ServiceId,
119	/// The service's code hash at the time of reporting. This must be available in-core at the
120	/// time of the lookup-anchor block.
121	pub code_hash: CodeHash,
122	/// Opaque data passed in to the service's Refinement logic to describe its workload.
123	pub payload: WorkPayload,
124	/// Gas limit with which to execute this work item's Refine logic.
125	pub refine_gas_limit: Gas,
126	/// Gas limit with which to execute this work item's Accumulate logic.
127	pub accumulate_gas_limit: Gas,
128	/// Sequence of imported data segments.
129	pub import_segments: WorkItemImportsVec,
130	/// Additional data available to the service's Refinement logic while doing its workload.
131	pub extrinsics: Vec<Xt>,
132	/// Number of segments exported by this work item.
133	pub export_count: u16,
134}
135
136/// A sequence of import specifications.
137pub type WorkItemImportsVec = BoundedVec<ImportSpec, ConstU32<{ MAX_IMPORTS }>>;
138
139/// Various pieces of information helpful to contextualize the Refinement process.
140#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq)]
141pub struct RefineContext {
142	/// The most recent header hash of the chain when building. This must be no more than
143	/// `RECENT_BLOCKS` blocks old when reported.
144	pub anchor: HeaderHash,
145	/// Must be state root of block `anchor`. This is checked on-chain when reported.
146	pub state_root: StateRootHash,
147	/// Must be Beefy root of block `anchor`. This is checked on-chain when reported.
148	pub beefy_root: MmrPeakHash,
149	/// The hash of a header of a block which is final. Availability will not succeed unless a
150	/// super-majority of validators have attested to this.
151	/// Preimage `lookup`s will be judged according to this block.
152	///
153	/// NOTE: Storage pallet may not cycle more frequently than 48 hours (24 hours above
154	///   plus 24 hours dispute period).
155	pub lookup_anchor: HeaderHash,
156	/// The slot of `lookup_anchor` on the chain. This is checked in availability and the
157	/// report's package will not be made available without it being correct.
158	/// This value must be at least `anchor_slot + 14400`.
159	pub lookup_anchor_slot: Slot,
160	/// An optional hash of a Work Package, a report of which must be reported prior to this one.
161	/// This is checked on-chain when reported.
162	pub prerequisites: VecSet<WorkPackageHash>,
163}
164
165// TODO: Refactor away from the generic `Xt` type parameter.
166
167/// A work-package, a collection of work-items together with authorization and contextual
168/// information. This is processed _in-core_ with Is-Authorized and Refine logic to produce a
169/// work-report.
170///
171/// This is the generic version of the work-package, which is specialized depending on whether the
172/// extrinsic data is actually bundled inside or merely committed to.
173#[derive(Clone, Encode, Decode, Debug)]
174pub struct GenericWorkPackage<Xt> {
175	/// Authorization token.
176	pub authorization: Authorization,
177	/// Service identifier.
178	pub auth_code_host: ServiceId,
179	/// Authorizer.
180	pub authorizer: Authorizer,
181	/// Refinement context.
182	pub context: RefineContext,
183	/// Sequence of work items.
184	pub items: GenericWorkItems<Xt>,
185}
186
187/// Sequence of [GenericWorkItem]s within a [GenericWorkPackage] and thus limited in length to
188/// [MAX_WORK_ITEMS].
189pub type GenericWorkItems<Xt> = BoundedVec<GenericWorkItem<Xt>, ConstU32<MAX_WORK_ITEMS>>;
190
191/// Sequence of [WorkItem]s within a [WorkPackage] and thus limited in length to
192/// [MAX_WORK_ITEMS].
193#[doc(hidden)]
194pub type WorkItems = GenericWorkItems<ExtrinsicSpec>;
195
196/// A definition of work to be done by the Refinement logic of a service and transformed into a
197/// [WorkOutput] for its Accumulation logic.
198///
199/// This is a concrete version of the work-item, which only commits to extrinsic data but does not
200/// supply it.
201pub type WorkItem = GenericWorkItem<ExtrinsicSpec>;
202
203#[doc(hidden)]
204pub type FatWorkItem = GenericWorkItem<Vec<u8>>;
205#[doc(hidden)]
206pub type RefWorkItem = GenericWorkItem<[u8]>;
207
208/// A work-package, a collection of work-items together with authorization and contextual
209/// information. This is processed _in-core_ with Is-Authorized and Refine logic to produce a
210/// work-report.
211///
212/// This is a concrete version of the work-package, which only commits to extrinsic data but does
213/// not supply it.
214pub type WorkPackage = GenericWorkPackage<ExtrinsicSpec>;
215#[doc(hidden)]
216pub type FatWorkPackage = GenericWorkPackage<Vec<u8>>;
217#[doc(hidden)]
218pub type RefWorkPackage = GenericWorkPackage<[u8]>;
219
220/// The authorizer tuple which together identifies a means of determining whether a Work Package is
221/// acceptable to execute on a core.
222#[derive(Clone, Encode, Decode, Debug)]
223pub struct Authorizer {
224	/// Authorization code hash.
225	pub code_hash: CodeHash,
226	/// Parameters blob.
227	pub param: AuthParam,
228}
229
230impl Authorizer {
231	pub fn any() -> Self {
232		Self { code_hash: CodeHash::zero(), param: Default::default() }
233	}
234}
235
236/// Information on the Work Package, passed into Refine.
237#[derive(Clone, Encode, Decode, Debug)]
238pub struct PackageInfo {
239	/// The hash of the Work Package.
240	pub package_hash: WorkPackageHash,
241	/// Various pieces of contextual information for the Refinement process.
242	pub context: RefineContext,
243	/// The authorizer which was used to authorize the Work Package.
244	pub authorizer: Authorizer,
245	/// The output of the Is-Authorized logic which authorized the execution of this work-package.
246	pub auth_output: AuthOutput,
247}
248
249/// Potential errors encountered during the refinement of a [`WorkItem`].
250///
251/// Although additional errors may be generated internally by the PVM engine,
252/// these are the specific errors designated by the GP for the [`WorkResult`]
253/// and that are eligible to be forwarded to the accumulate process as part
254/// of the [`AccumulateItem`].
255#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq)]
256#[doc(hidden)]
257pub enum WorkError {
258	/// Gas exhausted (∞).
259	OutOfGas = 1,
260	/// Unexpected termination (☇).
261	Panic = 2,
262	/// Invalid amount of segments exported.
263	BadExports = 3,
264	/// Bad code for the service (`BAD`).
265	///
266	/// This may occur due to an unknown service identifier or unavailable code preimage.
267	BadCode = 4,
268	/// Out of bounds code size (`BIG`).
269	CodeOversize = 5,
270}
271
272/// The result and surrounding context of a single Refinement operation passed as part of a Work
273/// Report.
274#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq)]
275#[doc(hidden)]
276pub struct WorkResult {
277	/// The service whose Refinement gave this result.
278	pub service: ServiceId,
279	/// The service's code hash at the time of reporting. This must be available in-core at the
280	/// time of the lookup-anchor block.
281	pub code_hash: CodeHash,
282	/// The hash of the payload data passed into Refinement which gave this result.
283	pub payload_hash: PayloadHash,
284	/// The amount of gas to be used for the accumulation of this result.
285	pub accumulate_gas: Gas,
286	/// The result of the Refinement operation itself.
287	#[codec(encoded_as = "CompactRefineResult")]
288	pub result: Result<WorkOutput, WorkError>,
289}
290
291/// The result and surrounding context of a single Refinement operation passed in to the
292/// Accumulation logic.
293#[derive(Debug, Encode, Decode)]
294pub struct AccumulateItem {
295	/// The hash of the work-package in which the work-item which gave this result was placed.
296	pub package: WorkPackageHash,
297	/// The output of the Is-Authorized logic which authorized the execution of the work-package
298	/// which generated this result.
299	pub auth_output: AuthOutput,
300	/// The hash of the payload data passed into Refinement which gave this result.
301	pub payload: PayloadHash,
302	/// The result of the Refinement operation itself.
303	#[codec(encoded_as = "CompactRefineResult")]
304	pub result: Result<WorkOutput, WorkError>,
305}
306
307/// Parameters for the invocation of Accumulate.
308#[derive(Debug, Encode, Decode)]
309#[doc(hidden)]
310pub struct AccumulateParams {
311	/// The current time slot.
312	pub slot: Slot,
313	/// The index of the service being accumulated.
314	pub id: ServiceId,
315	/// A sequence of work-results to accumulate.
316	pub results: Vec<AccumulateItem>,
317}
318
319/// A single deferred transfer of balance and/or data, passed in to the invocation of On Transfer.
320#[derive(Debug, Clone, Encode, Decode)]
321pub struct TransferRecord {
322	/// The index of the service from which the transfer was made.
323	pub source: ServiceId,
324	/// The index of the service which is the target of the transfer.
325	pub destination: ServiceId,
326	/// The balance passed from the `source` service to the `destination`.
327	pub amount: Balance,
328	/// The information passed from the `source` service to the `destination`.
329	pub memo: Memo,
330	/// The gas limit with which the `destination` On Transfer logic may execute in order to
331	/// process this transfer.
332	pub gas_limit: Gas,
333}
334
335impl Default for TransferRecord {
336	fn default() -> Self {
337		Self {
338			source: Default::default(),
339			destination: Default::default(),
340			amount: Default::default(),
341			memo: Memo::zero(),
342			gas_limit: Default::default(),
343		}
344	}
345}
346
347/// Parameters for the invocation of On Transfer.
348#[derive(Debug, Encode, Decode)]
349#[doc(hidden)]
350pub struct OnTransferParams {
351	/// The current time slot.
352	pub slot: Slot,
353	/// The index of the service to which the transfers are being made.
354	pub id: ServiceId,
355	/// The sequence of transfers to be processed.
356	pub transfers: Vec<TransferRecord>,
357}
358
359/// Parameters for the invocation of Refine.
360#[derive(Debug, Encode, Decode)]
361#[doc(hidden)]
362pub struct RefineParams {
363	/// The index of the service being refined.
364	pub id: ServiceId,
365	/// The payload data to process.
366	pub payload: WorkPayload,
367	/// Information on the package which caused this invocation.
368	pub package_info: PackageInfo,
369	/// The extrinsic data concerning this payload.
370	pub extrinsics: Vec<Vec<u8>>,
371}
372
373// TODO: @gav Consider moving to jam-node.
374/// Parameters for the invocation of Refine, reference variant.
375#[derive(Debug, Encode)]
376#[doc(hidden)]
377pub struct RefineParamsRef<'a, T: 'a + core::fmt::Debug + Encode> {
378	/// The index of the service being refined.
379	pub id: ServiceId,
380	/// The payload data to process.
381	pub payload: &'a WorkPayload,
382	/// Information on the package which caused this invocation.
383	pub package_info: &'a PackageInfo,
384	/// The extrinsic data concerning this payload.
385	pub extrinsics: &'a [T],
386}
387
388// TODO: @gav Consider moving to jam-node.
389/// Parameters for the invocation of On Transfer, reference variant.
390#[derive(Debug, Encode)]
391#[doc(hidden)]
392pub struct OnTransferParamsRef<'a> {
393	/// The current time slot.
394	pub slot: Slot,
395	/// The index of the service to which the transfers are being made.
396	pub id: ServiceId,
397	/// The sequence of transfers to be processed.
398	pub transfers: &'a [TransferRecord],
399}
400
401/// Information concerning a particular service's state.
402///
403/// This is used in the `service_info` host-call.
404#[derive(Debug, Clone, Encode, Decode, MaxEncodedLen)]
405pub struct ServiceInfo {
406	/// The hash of the code of the service.
407	pub code_hash: CodeHash,
408	/// The existing balance of the service.
409	pub balance: Balance,
410	/// The minimum balance which the service must satisfy.
411	pub threshold: Balance,
412	/// The minimum amount of gas which must be provided to this service's `accumulate` for each
413	/// work item it must process.
414	pub min_item_gas: Gas,
415	/// The minimum amount of gas which must be provided to this service's `on_transfer` for each
416	/// memo (i.e. transfer receipt) it must process.
417	pub min_memo_gas: Gas,
418	/// The total number of bytes used for data electively held for this service on-chain.
419	pub bytes: u64,
420	/// The total number of items of data electively held for this service on-chain.
421	pub items: u32,
422}
423
424impl scale::ConstEncodedLen for ServiceInfo {}
425/// Refine result used for compact encoding of work result as prescribed by GP.
426struct CompactRefineResult(Result<WorkOutput, WorkError>);
427struct CompactRefineResultRef<'a>(&'a Result<WorkOutput, WorkError>);
428
429impl From<CompactRefineResult> for Result<WorkOutput, WorkError> {
430	fn from(value: CompactRefineResult) -> Self {
431		value.0
432	}
433}
434
435impl<'a> From<&'a Result<WorkOutput, WorkError>> for CompactRefineResultRef<'a> {
436	fn from(value: &'a Result<WorkOutput, WorkError>) -> Self {
437		CompactRefineResultRef(value)
438	}
439}
440
441impl<'a> scale::EncodeAsRef<'a, Result<WorkOutput, WorkError>> for CompactRefineResult {
442	type RefType = CompactRefineResultRef<'a>;
443}
444
445impl Encode for CompactRefineResult {
446	fn encode_to<T: scale::Output + ?Sized>(&self, dest: &mut T) {
447		CompactRefineResultRef(&self.0).encode_to(dest)
448	}
449}
450
451impl Encode for CompactRefineResultRef<'_> {
452	fn encode_to<T: scale::Output + ?Sized>(&self, dest: &mut T) {
453		match &self.0 {
454			Ok(o) => {
455				dest.push_byte(0);
456				o.encode_to(dest)
457			},
458			Err(e) => e.encode_to(dest),
459		}
460	}
461}
462
463impl Decode for CompactRefineResult {
464	fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
465		match input.read_byte()? {
466			0 => Ok(Self(Ok(WorkOutput::decode(input)?))),
467			e => Ok(Self(Err(WorkError::decode(&mut &[e][..])?))),
468		}
469	}
470}
471
472#[cfg(test)]
473mod tests {
474	use super::*;
475
476	#[test]
477	fn compact_refine_result_codec() {
478		let enc_dec = |exp_res, exp_buf: &[u8]| {
479			let buf = CompactRefineResultRef(&exp_res).encode();
480			assert_eq!(buf, exp_buf);
481			let res = CompactRefineResult::decode(&mut &buf[..]).unwrap();
482			assert_eq!(res.0, exp_res);
483		};
484
485		enc_dec(Ok(vec![1, 2, 3].into()), &[0, 3, 1, 2, 3]);
486		enc_dec(Err(WorkError::OutOfGas), &[1]);
487		enc_dec(Err(WorkError::Panic), &[2]);
488		enc_dec(Err(WorkError::BadExports), &[3]);
489		enc_dec(Err(WorkError::BadCode), &[4]);
490		enc_dec(Err(WorkError::CodeOversize), &[5]);
491	}
492}