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}