jam_pvm_common/
host_calls.rs

1use crate::{
2	imports,
3	result::{ApiResult, IntoApiOption as _, IntoApiResult as _, IntoInvokeResult as _},
4	ApiError, InvokeOutcome,
5};
6use alloc::{vec, vec::Vec};
7use core::{
8	mem::{size_of, size_of_val, MaybeUninit},
9	ptr,
10};
11use jam_types::*;
12use scale::Encode;
13
14/// Check whether a preimage is available for lookup in the service.
15///
16/// - `hash`: The hash of the preimage to check availability.
17///
18/// Returns `true` if the preimage is available, `false` otherwise.
19///
20/// NOTE: Internally this uses the `historical_lookup` host call.
21pub fn is_historical_available(hash: &[u8; 32]) -> bool {
22	raw_foreign_historical_lookup_into(u64::MAX, hash, &mut []).is_some()
23}
24
25/// Check whether a preimage is available for lookup in another service.
26///
27/// - `service_id`: The service in whose preimage store to check availability.
28/// - `hash`: The hash of the preimage to check availability.
29///
30/// Returns `true` if the preimage is available, `false` otherwise.
31///
32/// NOTE: Internally this uses the `historical_lookup` host call.
33pub fn is_foreign_historical_available(service_id: ServiceId, hash: &[u8; 32]) -> bool {
34	raw_foreign_historical_lookup_into(service_id as _, hash, &mut []).is_some()
35}
36
37/// Make a lookup into the service's preimage store without allocating.
38///
39/// - `hash`: The hash of the preimage to look up.
40/// - `output`: The buffer to write the preimage into.
41///
42/// Returns the number of bytes written into the output buffer or `None` if the preimage was not
43/// available.
44///
45/// NOTE: Internally this uses the `historical_lookup` host call.
46pub fn historical_lookup_into(hash: &[u8; 32], output: &mut [u8]) -> Option<usize> {
47	raw_foreign_historical_lookup_into(u64::MAX, hash, output)
48}
49
50/// Make a lookup into another service's preimage store without allocating.
51///
52/// - `service_id`: The service in whose preimage store to find the preimage.
53/// - `hash`: The hash of the preimage to look up.
54/// - `output`: The buffer to write the preimage into.
55///
56/// Returns the number of bytes written into the output buffer or `None` if the preimage was not
57/// available.
58///
59/// NOTE: Internally this uses the `historical_lookup` host call.
60pub fn foreign_historical_lookup_into(
61	service_id: ServiceId,
62	hash: &[u8; 32],
63	output: &mut [u8],
64) -> Option<usize> {
65	raw_foreign_historical_lookup_into(service_id as _, hash, output)
66}
67
68/// Make a lookup into the service's preimage store.
69///
70/// - `hash`: The hash of the preimage to look up.
71///
72/// Returns the preimage or `None` if the preimage was not available.
73///
74/// NOTE: Internally this uses the `historical_lookup` host call.
75pub fn historical_lookup(hash: &[u8; 32]) -> Option<Vec<u8>> {
76	raw_foreign_historical_lookup(u64::MAX, hash)
77}
78
79/// Make a lookup into another service's preimage store.
80///
81/// - `service_id`: The service in whose preimage store to find the preimage.
82/// - `hash`: The hash of the preimage to look up.
83///
84/// Returns the preimage or `None` if the preimage was not available.
85///
86/// NOTE: Internally this uses the `historical_lookup` host call.
87pub fn foreign_historical_lookup(service_id: ServiceId, hash: &[u8; 32]) -> Option<Vec<u8>> {
88	raw_foreign_historical_lookup(service_id as _, hash)
89}
90
91/// A definition of data to be fetched.
92#[derive(Copy, Clone, Debug)]
93pub enum Fetch {
94	/// The current work-package.
95	WorkPackage,
96	/// The parameterisation of the authorization code.
97	AuthCodeParam,
98	/// The input provided to the parameterized authorizer code.
99	AuthToken,
100	/// The output from the parameterized authorizer code.
101	AuthOutput,
102	/// A given work-item's payload.
103	AnyPayload(usize),
104	/// A particular extrinsic of a given work-item.
105	AnyExtrinsic {
106		/// The index of the work-item whose extrinsic should be fetched.
107		work_item: usize,
108		/// The index of the work-item's extrinsic to be fetched.
109		index: usize,
110	},
111	/// A particular extrinsic of the executing work-item.
112	OurExtrinsic(usize),
113	/// A particular import-segment of a given work-item.
114	AnyImport {
115		/// The index of the work-item whose import-segment should be fetched.
116		work_item: usize,
117		/// The index of the work-item's import-segment to be fetched.
118		index: usize,
119	},
120	/// A particular import-segment of the executing work-item.
121	OurImport(usize),
122}
123
124impl Fetch {
125	fn args(self) -> (u64, u64, u64) {
126		use Fetch::*;
127		match self {
128			WorkPackage => (0, 0, 0),
129			AuthCodeParam => (1, 0, 0),
130			AuthToken => (2, 0, 0),
131			AuthOutput => (3, 0, 0),
132			AnyPayload(index) => (4, index as _, 0),
133			AnyExtrinsic { work_item, index } => (5, work_item as _, index as _),
134			OurExtrinsic(index) => (6, index as _, 0),
135			AnyImport { work_item, index } => (7, work_item as _, index as _),
136			OurImport(index) => (8, index as _, 0),
137		}
138	}
139
140	/// Fetch the data defined by this [Fetch] into the given target buffer.
141	///
142	/// - `target`: The buffer to write the fetched data into.
143	/// - `skip`: The number of bytes to skip from the start of the data to be fetched.
144	///
145	/// Returns the full length of the data which is being fetched. If this is smaller than the
146	/// `target`'s length, then some of the buffer will not be written to. If the request does not
147	/// identify any data to be fetched (e.g. because an index is out of range) then returns `None`.
148	pub fn fetch_into(self, target: &mut [u8], skip: usize) -> Option<usize> {
149		let (kind, a, b) = self.args();
150		unsafe {
151			imports::fetch(target.as_mut_ptr(), skip as _, target.len() as _, kind as _, a, b)
152		}
153		.into_api_option()
154	}
155
156	/// Fetch the length of the data defined by this [Fetch].
157	///
158	/// Returns the length of the data which is being fetched. If the request does not identify any
159	/// data to be fetched (e.g. because an index is out of range) then returns `None`.
160	#[allow(clippy::len_without_is_empty)]
161	pub fn len(self) -> Option<usize> {
162		self.fetch_into(&mut [], 0)
163	}
164
165	/// Fetch the data defined by this [Fetch] into a newly allocated [Vec].
166	///
167	/// Returns a [Vec] containing the data identified by the value of `self`. If the request does
168	/// not identify any data to be fetched (e.g. because an index is out of range) then returns
169	/// `None`.
170	pub fn fetch(self) -> Option<Vec<u8>> {
171		let len = self.len()?;
172		let mut incoming = vec![0u8; len];
173		self.fetch_into(&mut incoming, 0)?;
174		Some(incoming)
175	}
176}
177
178/// Import a segment of data specified in the Work Item's import manifest.
179///
180/// - `index`: The index of the segment within the Work Items's import manifest to import.
181///
182/// Returns `Some` segment or `None` depending on whether the index references an import or not.
183pub fn import(index: usize) -> Option<Segment> {
184	let mut incoming = Segment::default();
185	Fetch::OurImport(index).fetch_into(incoming.as_mut(), 0).map(|_| incoming)
186}
187
188/// Import a segment of data specified in a given Work Item's import manifest.
189///
190/// - `work_item`: The index of the work item to fetch an imported segment of.
191/// - `index`: The index of the segment within the Work Items's import manifest to import.
192///
193/// Returns `Some` segment or `None` depending on whether the indices reference an import or not.
194pub fn any_import(work_item: usize, index: usize) -> Option<Segment> {
195	let mut incoming = Segment::default();
196	Fetch::AnyImport { work_item, index }
197		.fetch_into(incoming.as_mut(), 0)
198		.map(|_| incoming)
199}
200
201/// Export a segment of data into the JAM Data Lake.
202///
203/// - `segment`: The segment of data to export.
204///
205/// Returns the export index or `Err` if the export was unsuccessful.
206pub fn export(segment: &Segment) -> ApiResult<u64> {
207	unsafe { imports::export(segment.as_ref().as_ptr(), segment.len() as u64) }.into_api_result()
208}
209
210/// Export a slice of data into the JAM Data Lake.
211///
212/// - `segment`: The slice of data to export, which may be no longer than [jam_types::SEGMENT_LEN].
213///
214/// Returns the export index or `Err` if the export was unsuccessful.
215pub fn export_slice(segment: &[u8]) -> ApiResult<u64> {
216	unsafe { imports::export(segment.as_ptr(), segment.len() as u64) }.into_api_result()
217}
218
219/// Create a new instance of a PVM.
220///
221/// - `code`: The code of the PVM.
222/// - `program_counter`: The initial program counter value of the PVM.
223///
224/// Returns the handle of the PVM or `Err` if the creation was unsuccessful.
225pub fn machine(code: &[u8], program_counter: u64) -> ApiResult<u64> {
226	unsafe { imports::machine(code.as_ptr(), code.len() as u64, program_counter) }.into_api_result()
227}
228
229/// Inspect the raw memory of an inner PVM.
230///
231/// - `vm_handle`: The handle of the PVM whose memory to inspect.
232/// - `inner_src`: The address in the PVM's memory to start reading from.
233/// - `len`: The number of bytes to read.
234///
235/// Returns the data in the PVM `vm_handle` at memory `inner_src` or `Err` if the inspection failed.
236pub fn peek(vm_handle: u64, inner_src: u64, len: u64) -> ApiResult<Vec<u8>> {
237	let mut incoming = vec![0; len as usize];
238	unsafe { imports::peek(vm_handle, incoming.as_mut_ptr(), inner_src, len) }
239		.into_api_result()
240		.map(|()| incoming)
241}
242
243/// Inspect the raw memory of an inner PVM.
244///
245/// - `vm_handle`: The handle of the PVM whose memory to inspect.
246/// - `outer_dst`: The buffer to write the memory into.
247/// - `inner_src`: The address in the PVM's memory to start reading from.
248///
249/// Returns `Ok` on success or `Err` if the inspection failed.
250pub fn peek_into(vm_handle: u64, outer_dst: &mut [u8], inner_src: u64) -> ApiResult<()> {
251	unsafe {
252		imports::peek(vm_handle, outer_dst.as_mut_ptr(), inner_src, size_of_val(outer_dst) as u64)
253	}
254	.into_api_result()
255}
256
257/// Inspect a plain-old-data value in the memory of an inner PVM.
258///
259/// - `vm_handle`: The handle of the PVM whose memory to inspect.
260/// - `inner_src`: The address in the PVM's memory to inspect a value of type `T`.
261///
262/// Returns the value of type `T` at `inner_src` of the PVM `vm_handle` or `Err` if the inspection
263/// failed.
264///
265/// NOTE: This will only work with types `T` which have exactly the same memory layout in the host
266/// and the inner PVM. Avoid things like references.
267pub fn peek_value<T>(vm_handle: u64, inner_src: u64) -> ApiResult<T> {
268	let mut t = MaybeUninit::<T>::uninit();
269	unsafe {
270		imports::peek(vm_handle, t.as_mut_ptr() as *mut u8, inner_src, size_of::<T>() as u64)
271			.into_api_result()
272			.map(|()| t.assume_init())
273	}
274}
275
276/// Copy some data into the memory of an inner PVM.
277///
278/// - `vm_handle`: The handle of the PVM whose memory to mutate.
279/// - `outer_src`: The data to be copied.
280/// - `inner_dst`: The address in memory of inner PVM `vm_handle` to copy the data to.
281///
282/// Returns `Ok` on success or `Err` if the inspection failed.
283pub fn poke(vm_handle: u64, outer_src: &[u8], inner_dst: u64) -> ApiResult<()> {
284	unsafe { imports::poke(vm_handle, outer_src.as_ptr(), inner_dst, outer_src.len() as u64) }
285		.into_api_result()
286}
287
288/// Copy a plain-old-data value into the memory of an inner PVM.
289///
290/// - `vm_handle`: The handle of the PVM whose memory to mutate.
291/// - `outer_src`: The value whose memory representation is to be copied.
292/// - `inner_dst`: The address in memory of inner PVM `vm_handle` to copy the value to.
293///
294/// Returns `Ok` on success or `Err` if the inspection failed.
295pub fn poke_value<T>(vm_handle: u64, outer_src: &T, inner_dst: u64) -> ApiResult<()> {
296	unsafe {
297		imports::poke(
298			vm_handle,
299			outer_src as *const T as *const u8,
300			inner_dst,
301			size_of_val(outer_src) as u64,
302		)
303	}
304	.into_api_result()
305}
306
307/// Initialize memory pages in an inner PVM with zeros, allocating if needed.
308///
309/// - `vm_handle`: The handle of the PVM whose memory to mutate.
310/// - `page`: The index of the first page of inner PVM `vm_handle` to initialize.
311/// - `count`: The number of pages to initialize.
312///
313/// Returns `Ok` on success or `Err` if the operation failed.
314///
315/// Pages are initialized to be filled with zeroes. If the pages are not yet allocated, they will
316/// be allocated.
317pub fn zero(vm_handle: u64, page: u64, count: u64) -> ApiResult<()> {
318	unsafe { imports::zero(vm_handle, page, count) }.into_api_result()
319}
320
321/// Deallocate memory pages in an inner PVM.
322///
323/// - `vm_handle`: The handle of the PVM whose memory to mutate.
324/// - `page`: The index of the first page of inner PVM `vm_handle` to deallocate.
325/// - `count`: The number of pages to deallocate.
326///
327/// Returns `Ok` on success or `Err` if the operation failed.
328///
329/// NOTE: All pages from `page` to `page + count - 1` inclusive must have been allocated for this
330/// call to succeed.
331pub fn void(vm_handle: u64, page: u64, count: u64) -> ApiResult<()> {
332	unsafe { imports::void(vm_handle, page, count) }.into_api_result()
333}
334
335/// Invoke an inner PVM.
336///
337/// - `vm_handle`: The handle of the PVM to invoke.
338/// - `gas`: The maximum amount of gas which the inner PVM may use in this invocation.
339/// - `regs`: The initial register values of the inner PVM.
340///
341/// Returns the outcome of the invocation, together with any remaining gas, and the final register
342/// values.
343pub fn invoke(
344	vm_handle: u64,
345	gas: SignedGas,
346	regs: [u64; 13],
347) -> ApiResult<(InvokeOutcome, SignedGas, [u64; 13])> {
348	let mut args = InvokeArgs { gas, regs };
349	let outcome = unsafe { imports::invoke(vm_handle, core::ptr::from_mut(&mut args).cast()) }
350		.into_invoke_result()?;
351	Ok((outcome, args.gas, args.regs))
352}
353
354/// Delete an inner PVM instance, freeing any associated resources.
355///
356/// - `vm_handle`: The handle of the PVM to delete.
357///
358/// Returns the inner PVM's final instruction counter value on success or `Err` if the operation
359/// failed.
360pub fn expunge(vm_handle: u64) -> ApiResult<u64> {
361	unsafe { imports::expunge(vm_handle) }.into_api_result()
362}
363
364/// Inspect the gas meter.
365///
366/// Returns the post-instruction gas meter value.
367pub fn gas() -> UnsignedGas {
368	unsafe { imports::gas() }
369}
370
371/// Check whether a preimage is available for lookup.
372///
373/// - `hash`: The hash of the preimage to check availability.
374///
375/// Returns `true` if the preimage is available, `false` otherwise.
376///
377/// NOTE: Internally this uses the `lookup` host call.
378pub fn is_available(hash: &[u8; 32]) -> bool {
379	raw_foreign_lookup_into(u64::MAX, hash, &mut []).is_some()
380}
381
382/// Check whether a preimage is available for foreign lookup.
383///
384/// - `service_id`: The service in whose preimage store to check availability.
385/// - `hash`: The hash of the preimage to check availability.
386///
387/// Returns `true` if the preimage is available, `false` otherwise.
388///
389/// NOTE: Internally this uses the `lookup` host call.
390pub fn is_foreign_available(service_id: ServiceId, hash: &[u8; 32]) -> bool {
391	raw_foreign_lookup_into(service_id as _, hash, &mut []).is_some()
392}
393
394/// Make a lookup into the service's preimage store without allocating.
395///
396/// - `hash`: The hash of the preimage to look up.
397/// - `output`: The buffer to write the preimage into.
398///
399/// Returns the number of bytes written into the output buffer or `None` if the preimage was not
400/// available.
401///
402/// NOTE: Internally this uses the `lookup` host call.
403pub fn lookup_into(hash: &[u8; 32], output: &mut [u8]) -> Option<usize> {
404	raw_foreign_lookup_into(u64::MAX, hash, output)
405}
406
407/// Make a lookup into another service's preimage store without allocating.
408///
409/// - `service_id`: The service in whose preimage store to find the preimage.
410/// - `hash`: The hash of the preimage to look up.
411/// - `output`: The buffer to write the preimage into.
412///
413/// Returns the number of bytes written into the output buffer or `None` if the preimage was not
414/// available.
415///
416/// NOTE: Internally this uses the `lookup` host call.
417pub fn foreign_lookup_into(
418	service_id: ServiceId,
419	hash: &[u8; 32],
420	output: &mut [u8],
421) -> Option<usize> {
422	raw_foreign_lookup_into(service_id as _, hash, output)
423}
424
425/// Make a lookup into the service's preimage store.
426///
427/// - `hash`: The hash of the preimage to look up.
428///
429/// Returns the preimage or `None` if the preimage was not available.
430///
431/// NOTE: Internally this uses the `lookup` host call.
432pub fn lookup(hash: &[u8; 32]) -> Option<Vec<u8>> {
433	raw_foreign_lookup(u64::MAX, hash)
434}
435
436/// Make a lookup into another service's preimage store.
437///
438/// - `service_id`: The service in whose preimage store to find the preimage.
439/// - `hash`: The hash of the preimage to look up.
440///
441/// Returns the preimage or `None` if the preimage was not available.
442///
443/// NOTE: Internally this uses the `lookup` host call.
444pub fn foreign_lookup(service_id: ServiceId, hash: &[u8; 32]) -> Option<Vec<u8>> {
445	raw_foreign_lookup(service_id as _, hash)
446}
447
448/// The status of a lookup request.
449#[derive(Debug)]
450pub enum LookupRequestStatus {
451	/// The request has never had its preimage provided; corresponds to an empty GP array.
452	Unprovided,
453	/// The requested preimage is provided; corresponds to a single-item GP array.
454	Provided {
455		/// The slot at which the preimage was provided.
456		since: Slot,
457	},
458	/// The request was provided and has since been unrequested; corresponds to a two-item GP
459	/// array.
460	Unrequested {
461		/// The slot at which the preimage was provided.
462		provided_since: Slot,
463		/// The slot at which the preimage was unrequested.
464		unrequested_since: Slot,
465	},
466	/// The request was provided, was since unrequested and is now requested again. Corresponds to
467	/// a three-item GP array.
468	Rerequested {
469		/// The slot at which the preimage was provided.
470		provided_since: Slot,
471		/// The slot at which the preimage was unrequested.
472		unrequested_at: Slot,
473		/// The slot at which the preimage was requested again.
474		rerequested_since: Slot,
475	},
476}
477
478/// A summary of the implication of calling `forget` on a preimage request.
479#[derive(Debug)]
480pub enum ForgetImplication {
481	/// The request will be dropped altogether since it was never provided. The deposit criteria
482	/// will be lifted.
483	Drop,
484	/// The preimage will be unrequested and unavailable for lookup. No change in the deposit
485	/// criteria.
486	Unrequest,
487	/// The preimage remain unavailable and be expunged from the state. The deposit criteria
488	/// will be lifted.
489	Expunge,
490	/// The `forget` call is invalid and no change in state will be made.
491	///
492	/// If called in future, after `success_after`, it will be have the effect of `Unrequest`.
493	NotYetUnrequest {
494		/// The earliest slot at which a call to `forget` can succeed.
495		success_after: Slot,
496	},
497	/// The `forget` call is invalid and no change in state will be made.
498	///
499	/// If called in future, after `success_after`, it will be have the effect of `Expunge`.
500	NotYetExpunge {
501		/// The earliest slot at which a call to `forget` can succeed.
502		success_after: Slot,
503	},
504}
505
506impl LookupRequestStatus {
507	/// Return the implication of calling `forget` on the current state of the preimage request
508	/// given the current timeslot is `now`.
509	pub fn forget_implication(&self, now: Slot) -> ForgetImplication {
510		match self {
511			Self::Unprovided => ForgetImplication::Drop,
512			Self::Provided { .. } => ForgetImplication::Unrequest,
513			Self::Unrequested { unrequested_since, .. }
514				if now > unrequested_since + min_turnaround_period() =>
515				ForgetImplication::Drop,
516			Self::Unrequested { unrequested_since, .. } => ForgetImplication::NotYetExpunge {
517				success_after: unrequested_since + min_turnaround_period(),
518			},
519			Self::Rerequested { unrequested_at, .. }
520				if now > unrequested_at + min_turnaround_period() =>
521				ForgetImplication::Unrequest,
522			Self::Rerequested { unrequested_at, .. } => ForgetImplication::NotYetUnrequest {
523				success_after: unrequested_at + min_turnaround_period(),
524			},
525		}
526	}
527}
528
529/// Query the status of a preimage.
530///
531/// - `hash`: The hash of the preimage to be queried.
532/// - `len`: The length of the preimage to be queried.
533///
534/// Returns `Some` if `hash`/`len` has an active solicitation outstanding or `None` if not.
535pub fn query(hash: &[u8; 32], len: usize) -> Option<LookupRequestStatus> {
536	let (r0, r1): (u64, u64) = unsafe { imports::query(hash.as_ptr(), len as u64) };
537	let n = r0 as u32;
538	let x = (r0 >> 32) as Slot;
539	let y = r1 as Slot;
540	Some(match n {
541		0 => LookupRequestStatus::Unprovided,
542		1 => LookupRequestStatus::Provided { since: x },
543		2 => LookupRequestStatus::Unrequested { provided_since: x, unrequested_since: y },
544		3 => LookupRequestStatus::Rerequested {
545			provided_since: x,
546			unrequested_at: y,
547			rerequested_since: (r1 >> 32) as Slot,
548		},
549		_ => return None,
550	})
551}
552
553/// Request that preimage data be available for lookup.
554///
555/// - `hash`: The hash of the preimage to be made available.
556/// - `len`: The length of the preimage to be made available.
557///
558/// Returns `Ok` on success or `Err` if the request failed.
559///
560/// [is_available] may be used to determine availability; once available, the preimage may be
561/// fetched with [lookup] or its variants.
562///
563/// A preimage may only be solicited once for any service and soliciting a preimage raises the
564/// minimum balance required to be held by the service.
565pub fn solicit(hash: &[u8; 32], len: usize) -> Result<(), ApiError> {
566	unsafe { imports::solicit(hash.as_ptr(), len as u64) }.into_api_result()
567}
568
569/// No longer request that preimage data be available for lookup, or drop preimage data once time
570/// limit has passed.
571///
572/// - `hash`: The hash of the preimage to be forgotten.
573/// - `len`: The length of the preimage to be forgotten.
574///
575/// Returns `Ok` on success or `Err` if the request failed.
576///
577/// This function is used twice in the lifetime of a requested preimage; once to indicate that the
578/// preimage is no longer needed and again to "clean up" the preimage once the required duration
579/// has passed. Whether it does one or the other is determined by the current state of the preimage
580/// request.
581pub fn forget(hash: &[u8; 32], len: usize) -> Result<(), ApiError> {
582	unsafe { imports::forget(hash.as_ptr(), len as u64) }.into_api_result()
583}
584
585/// Set the default result hash of Accumulation.
586///
587/// - `hash`: The hash to be used as the Accumulation result.
588///
589/// This value will be returned from Accumulation on success. It may be overridden by further
590/// calls to this function or by explicitly returning `Some` value from the
591/// [crate::Service::accumulate] function. The [checkpoint] function may be used after a call to
592/// this function to ensure that this value is returned in the case of an irregular termination.
593pub fn yield_hash(hash: &[u8; 32]) {
594	unsafe { imports::yield_hash(hash.as_ptr()) }
595		.into_api_result()
596		.expect("Cannot fail except for memory access; we provide a good address; qed")
597}
598
599/// Fetch raw data from the service's key/value store.
600///
601/// - `key`: The key of the data to fetch.
602///
603/// Returns the data associated with the key or `None` if the key is not present.
604pub fn get_storage(key: &[u8]) -> Option<Vec<u8>> {
605	raw_get_foreign_storage(u64::MAX, key)
606}
607
608/// Fetch raw data from the service's key/value store into a buffer.
609///
610/// - `key`: The key of the data to fetch.
611/// - `value`: The buffer to write the data into; on success, this is overwritten with the value
612///   associated with `key` in the service's store, leaving any portions unchanged if the buffer is
613///   longer than the value.
614///
615/// Returns the size of the data associated with the key or `None` if the key is not present.
616pub fn get_storage_into(key: &[u8], value: &mut [u8]) -> Option<usize> {
617	raw_get_foreign_storage_into(u64::MAX, key, value)
618}
619
620/// Fetch raw data from another service's key/value store.
621///
622/// - `id`: The ID of the service whose key/value store to fetch from.
623/// - `key`: The key of the data to fetch.
624///
625/// Returns the data associated with the key in the key/value store of service `id` or `None` if
626/// the key is not present.
627pub fn get_foreign_storage(id: ServiceId, key: &[u8]) -> Option<Vec<u8>> {
628	raw_get_foreign_storage(id as u64, key)
629}
630
631/// Fetch raw data from another service's key/value store into a buffer.
632///
633/// - `id`: The ID of the service whose key/value store to fetch from.
634/// - `key`: The key of the data to fetch.
635/// - `value`: The buffer to write the data into; on success, this is overwritten with the value
636///   associated with `key` in said service's store, leaving any portions unchanged if the buffer is
637///   longer than the value.
638///
639/// Returns the size of the data associated with the key in the key/value store of service `id` or
640/// `None` if the key is not present.
641pub fn get_foreign_storage_into(id: ServiceId, key: &[u8], value: &mut [u8]) -> Option<usize> {
642	raw_get_foreign_storage_into(id as u64, key, value)
643}
644
645/// Fetch typed data from the service's key/value store.
646///
647/// - `key`: A value, whose encoded representation is the the key of the data to fetch.
648///
649/// Returns the decoded data associated with the key or `None` if the key is not present or the data
650/// cannot be decoded into the type `R`.
651pub fn get<R: Decode>(key: impl Encode) -> Option<R> {
652	Decode::decode(&mut &key.using_encoded(get_storage)?[..]).ok()
653}
654
655/// Fetch typed data from another service's key/value store.
656///
657/// - `id`: The ID of the service whose key/value store to fetch from.
658/// - `key`: A value, whose encoded representation is the the key of the data to fetch.
659///
660/// Returns the decoded data associated with the key in the key/value store of service `id` or
661/// `None` if the key is not present or the data cannot be decoded into the type `R`.
662pub fn get_foreign<R: Decode>(id: ServiceId, key: impl Encode) -> Option<R> {
663	Decode::decode(&mut &key.using_encoded(|k| get_foreign_storage(id, k))?[..]).ok()
664}
665
666/// Set the value of a key to raw data in the service's key/value store.
667///
668/// - `key`: The key to be set.
669/// - `data`: The data to be associated with the key.
670///
671/// Returns `Ok` on success or `Err` if the operation failed.
672///
673/// NOTE: If this key was not previously set or if the data is larger than the previous value, then
674/// the minimum balance which the service must hold is raised and if the service has too little
675/// balance then the call with fail with [ApiError::StorageFull].
676pub fn set_storage(key: &[u8], data: &[u8]) -> Result<(), ApiError> {
677	unsafe { imports::write(key.as_ptr(), key.len() as u64, data.as_ptr(), data.len() as u64) }
678		.into_api_result()
679}
680
681/// Remove a pair from the service's key/value store.
682///
683/// - `key`: The key to be removed.
684///
685/// Returns `Some` on success with the previous value's length, or `None` if the key does not exist.
686///
687/// NOTE: If the key does not exist, then the operation is a no-op.
688pub fn remove_storage(key: &[u8]) -> Option<u64> {
689	unsafe { imports::write(key.as_ptr(), key.len() as u64, ptr::null(), 0) }
690		.into_api_result()
691		.expect("Cannot fail except for memory access; we provide a good address; qed")
692}
693
694/// Set the value of a typed key to typed data in the service's key/value store.
695///
696/// - `key`: The value of an encodable type whose encoding is the key to be set.
697/// - `value`: The value of an encodable type whose encoding be associated with said key.
698///
699/// Returns `Ok` on success or `Err` if the operation failed.
700///
701/// NOTE: If this key was not previously set or if the data is larger than the previous value, then
702/// the minimum balance which the service must hold is raised and if the service has too little
703/// balance then the call with fail with [ApiError::StorageFull].
704pub fn set(key: impl Encode, value: impl Encode) -> Result<(), ApiError> {
705	value.using_encoded(|v| key.using_encoded(|k| set_storage(k, v)))
706}
707
708/// Remove a typed key from the service's key/value store.
709///
710/// - `key`: The value of an encodable type whose encoding is the key to be removed.
711///
712/// NOTE: If the key does not exist, then the operation is a no-op.
713pub fn remove(key: impl Encode) {
714	let _ = key.using_encoded(remove_storage);
715}
716
717/// Get information on the service.
718///
719/// Returns the value of [ServiceInfo] which describes the current state of the service.
720pub fn my_info() -> ServiceInfo {
721	raw_service_info(u64::MAX).expect("Current service must exist; qed")
722}
723
724/// Get information on another service.
725///
726/// - `id`: The ID of the service to get information on.
727///
728/// Returns the value of [ServiceInfo] which describes the current state of service `id`.
729pub fn service_info(id: ServiceId) -> Option<ServiceInfo> {
730	raw_service_info(id as _)
731}
732
733/// Create a new service.
734///
735/// - `code_hash`: The hash of the code of the service to create. The preimage of this hash will be
736///   solicited by the new service and its according minimum balance will be transferred from the
737///   executing service to the new service in order to fund it.
738/// - `code_len`: The length of the code of the service to create.
739/// - `min_item_gas`: The minimum gas required to be set aside for the accumulation of a single Work
740///   Item in the new service.
741/// - `min_memo_gas`: The minimum gas required to be set aside for any single transfer of funds and
742///   corresponding processing of a memo in the new service.
743///
744/// Returns the new service ID or `Err` if the operation failed.
745///
746/// NOTE: This operation requires a balance transfer from the executing service to the new service
747/// in order to succeed; if this would reduce the balance to below the minimum balance required,
748/// then it will fail.
749///
750/// NOTE: This commits to the code of the new service but does not yet instantiate it; the code
751/// preimage must be provided before the first Work Items of the new service can be processed.
752pub fn create_service(
753	code_hash: &CodeHash,
754	code_len: u64,
755	min_item_gas: UnsignedGas,
756	min_memo_gas: UnsignedGas,
757) -> Result<ServiceId, ApiError> {
758	unsafe {
759		imports::new(code_hash.as_ptr(), code_len, min_item_gas, min_memo_gas).into_api_result()
760	}
761}
762
763/// Upgrade the code of the service.
764///
765/// - `code_hash`: The hash of the code to upgrade to, to be found in the service's preimage store.
766/// - `min_item_gas`: The minimum gas required to be set aside for the accumulation of a single Work
767///   Item in the new service.
768/// - `min_memo_gas`: The minimum gas required to be set aside for any single transfer of funds and
769///   corresponding processing of a memo in the new service.
770///
771/// NOTE: This commits to the new code of the service but does not yet instantiate it; the new code
772/// preimage must be provided before the first Work Items of the new service can be processed.
773/// Generally you should use [solicit] and [is_available] to ensure that the new code is already
774/// in the service's preimage store and call this only as the final step in the process.
775pub fn upgrade(code_hash: &CodeHash, min_item_gas: UnsignedGas, min_memo_gas: UnsignedGas) {
776	unsafe { imports::upgrade(code_hash.as_ptr(), min_item_gas, min_memo_gas) }
777		.into_api_result()
778		.expect("Failure only in case of bad memory; it is good; qed")
779}
780
781/// "Upgrade" the service into an unexecutable zombie.
782///
783/// - `ejector`: The index of the service which will be able to call [eject] on the caller service
784///   in order to finally delete it.
785///
786/// NOTE: This only sets the new code hash of the service but does not clear storage/preimages nor
787/// [forget] the current code hash. Do these first!
788pub fn zombify(ejector: ServiceId) {
789	(ejector, [0; 28]).using_encoded(|data| {
790		unsafe { imports::upgrade(data.as_ptr(), 0, 0) }
791			.into_api_result()
792			.expect("Failure only in case of bad memory; it is good; qed")
793	})
794}
795
796/// Transfer data and/or funds to another service asynchronously.
797///
798/// - `destination`: The ID of the service to transfer to. This service must exist at present.
799/// - `amount`: The amount of funds to transfer to the `destination` service. Reducing the services
800///   balance by this amount must not result in it falling below the minimum balance required.
801/// - `gas_limit`: The amount of gas to set aside for the processing of the transfer by the
802///   `destination` service. This must be at least the service's [ServiceInfo::min_memo_gas]. The
803///   effective gas cost of this call is increased by this amount.
804/// - `memo`: A piece of data to give the `destination` service.
805///
806/// Returns `Ok` on success or `Err` if the operation failed.
807///
808/// NOTE: All transfers are deferred; they are guaranteed to be received by the destination service
809/// in same time slot, but will not be processed synchronously with this call.
810pub fn transfer(
811	destination: ServiceId,
812	amount: Balance,
813	gas_limit: UnsignedGas,
814	memo: &Memo,
815) -> Result<(), ApiError> {
816	unsafe {
817		imports::transfer(destination as _, amount, gas_limit, memo.as_ref().as_ptr())
818			.into_api_result()
819	}
820}
821
822/// Remove the `target` zombie service, drop its final preimage item `code_hash` and transfer
823/// remaining balance to this service.
824///
825/// - `target`: The ID of a zombie service which nominated the caller service as its ejector.
826/// - `code_hash`: The hash of the only preimage item of the `target` service. It must be
827///   unrequested and droppable.
828///
829/// Target must therefore satisfy several requirements:
830/// - it should have a code hash which is simply the LE32-encoding of the caller service's ID;
831/// - it should have only one preimage lookup item, `code_hash`;
832/// - it should have nothing in its storage.
833///
834/// Returns `Ok` on success or `Err` if the operation failed.
835pub fn eject(target: ServiceId, code_hash: &CodeHash) -> Result<(), ApiError> {
836	unsafe { imports::eject(target as _, code_hash.as_ref().as_ptr()) }.into_api_result()
837}
838
839/// Reset the privileged services.
840///
841/// - `manager`: The ID of the service which may effectually call [bless] in the future.
842/// - `assigner`: The ID of the service which may effectually call [assign] in the future.
843/// - `designator`: The ID of the service which may effectually call [designate] in the future.
844/// - `always_acc`: The list of service IDs which accumulate at least once in every JAM block,
845///   together with the baseline gas they get for accumulation. This may be supplemented with
846///   additional gas should there be Work Items for the service.
847///
848/// Returns `Ok` on success or `Err` if the operation failed.
849///
850/// NOTE: This service must be (or have been) the _manager_ service of JAM at the beginning of
851/// accumulate for this call to have any effect. If the service is/was not _manager_, then it is
852/// effectively a no-op.
853pub fn bless<'a>(
854	manager: ServiceId,
855	assigner: ServiceId,
856	designator: ServiceId,
857	always_acc: impl IntoIterator<Item = &'a (ServiceId, UnsignedGas)>,
858) {
859	let data: Vec<u8> = always_acc.into_iter().flat_map(|x| x.encode()).collect();
860	let len = data.len() as u64;
861	unsafe { imports::bless(manager as _, assigner as _, designator as _, data.as_ptr(), len) }
862		.into_api_result()
863		.expect("Failure only in case of bad memory or bad service ID; both are good; qed")
864}
865
866/// Assign a series of authorizers to a core.
867///
868/// - `core`: The index of the core to assign the authorizers to.
869/// - `auth_queue`: The authorizer-queue to assign to the core. These are a series of
870///   [AuthorizerHash] values, which determine what kinds of Work Packages are allowed to be
871///   executed on the core.
872///
873/// Returns `Ok` on success or `Err` if the operation failed. Failure can only happen if the value
874/// of `core` is out of range.
875///
876/// NOTE: This service must be (or have been) the _assigner_ service of JAM at the beginning of
877/// accumulate for this call to have any effect. If the service is/was not _assigner_, then it is
878/// effectively a no-op.
879pub fn assign(core: CoreIndex, auth_queue: &AuthQueue) -> Result<(), ApiError> {
880	auth_queue
881		.using_encoded(|d| unsafe { imports::assign(core as u64, d.as_ptr()) })
882		.into_api_result()
883}
884
885/// Designate the new validator keys.
886///
887/// - `keys`: The new validator keys.
888///
889/// NOTE: This service must be (or have been) the _designator_ service of JAM at the beginning of
890/// accumulate for this call to have any effect. If the service is/was not _designator_, then it is
891/// effectively a no-op.
892pub fn designate(keys: &OpaqueValKeysets) {
893	keys.using_encoded(|d| unsafe { imports::designate(d.as_ptr()) })
894		.into_api_result()
895		.expect("Failure only in case of bad memory; it is good; qed")
896}
897
898/// Checkpoint the state of the accumulation at present.
899///
900/// In the case that accumulation runs out of gas or otherwise terminates unexpectedly, all
901/// changes extrinsic to the machine state, such as storage writes and transfers, will be rolled
902/// back to the most recent call to [checkpoint], or the beginning of the accumulation if no
903/// checkpoint has been made.
904pub fn checkpoint() {
905	unsafe { imports::checkpoint() }
906}
907
908fn raw_foreign_lookup(service_id: u64, hash: &[u8; 32]) -> Option<Vec<u8>> {
909	let maybe_len: Option<u64> =
910		unsafe { imports::lookup(service_id, hash.as_ptr(), ptr::null_mut(), 0, 0) }
911			.into_api_result()
912			.expect("Cannot fail except for memory access; we provide a good address; qed");
913	let len = maybe_len?;
914	let mut incoming = vec![0; len as usize];
915	unsafe {
916		imports::lookup(service_id, hash.as_ptr(), incoming.as_mut_ptr(), 0, len);
917	}
918	Some(incoming)
919}
920
921fn raw_foreign_lookup_into(service_id: u64, hash: &[u8; 32], output: &mut [u8]) -> Option<usize> {
922	let maybe_len: Option<u64> = unsafe {
923		imports::lookup(service_id, hash.as_ptr(), output.as_mut_ptr(), 0, output.len() as u64)
924	}
925	.into_api_result()
926	.expect("Cannot fail except for memory access; we provide a good address; qed");
927	Some(maybe_len? as usize)
928}
929
930fn raw_foreign_historical_lookup(service_id: u64, hash: &[u8; 32]) -> Option<Vec<u8>> {
931	let maybe_len: Option<u64> =
932		unsafe { imports::historical_lookup(service_id, hash.as_ptr(), ptr::null_mut(), 0, 0) }
933			.into_api_result()
934			.expect("Cannot fail except for memory access; we provide a good address; qed");
935	let len = maybe_len?;
936	let mut incoming = vec![0; len as usize];
937	unsafe {
938		imports::historical_lookup(service_id, hash.as_ptr(), incoming.as_mut_ptr(), 0, len);
939	}
940	Some(incoming)
941}
942
943fn raw_foreign_historical_lookup_into(
944	service_id: u64,
945	hash: &[u8; 32],
946	output: &mut [u8],
947) -> Option<usize> {
948	let maybe_len: Option<u64> = unsafe {
949		imports::historical_lookup(
950			service_id,
951			hash.as_ptr(),
952			output.as_mut_ptr(),
953			0,
954			output.len() as u64,
955		)
956	}
957	.into_api_result()
958	.expect("Cannot fail except for memory access; we provide a good address; qed");
959	Some(maybe_len? as usize)
960}
961
962fn raw_service_info(service: u64) -> Option<ServiceInfo> {
963	let mut buffer = vec![0u8; ServiceInfo::max_encoded_len()];
964	let maybe_ok: Option<()> = unsafe { imports::info(service as _, buffer.as_mut_ptr()) }
965		.into_api_result()
966		.expect("Cannot fail except for memory access; we provide a good address; qed");
967	maybe_ok?;
968	ServiceInfo::decode(&mut &buffer[..]).ok()
969}
970
971fn raw_get_foreign_storage(id: u64, key: &[u8]) -> Option<Vec<u8>> {
972	let maybe_len: Option<u64> =
973		unsafe { imports::read(id as _, key.as_ptr(), key.len() as u64, ptr::null_mut(), 0, 0) }
974			.into_api_result()
975			.expect("Cannot fail except for memory access; we provide a good address; qed");
976	let len = maybe_len?;
977	if len == 0 {
978		Some(vec![])
979	} else {
980		let mut incoming = vec![0; len as usize];
981		unsafe {
982			imports::read(id as _, key.as_ptr(), key.len() as u64, incoming.as_mut_ptr(), 0, len);
983		}
984		Some(incoming)
985	}
986}
987
988fn raw_get_foreign_storage_into(id: u64, key: &[u8], value: &mut [u8]) -> Option<usize> {
989	let r: ApiResult<Option<u64>> = unsafe {
990		imports::read(
991			id as _,
992			key.as_ptr(),
993			key.len() as _,
994			value.as_mut_ptr(),
995			0,
996			value.len() as _,
997		)
998	}
999	.into_api_result();
1000	Some(r.expect("Only fail is memory access; address is good; qed")? as usize)
1001}