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 codec::Encode;
8use core::{
9	mem::{size_of, size_of_val, MaybeUninit},
10	ptr,
11};
12use jam_types::*;
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	/// Protocol parameters.
95	ProtocolParameters,
96	/// Entropy.
97	Entropy,
98	/// Output from the parameterized authorizer code.
99	AuthTrace,
100	/// A particular extrinsic of a given work-item.
101	AnyExtrinsic {
102		/// The index of the work-item whose extrinsic should be fetched.
103		work_item: usize,
104		/// The index of the work-item's extrinsic to be fetched.
105		index: usize,
106	},
107	/// A particular extrinsic of the executing work-item.
108	OurExtrinsic(usize),
109	/// A particular import-segment of a given work-item.
110	AnyImport {
111		/// The index of the work-item whose import-segment should be fetched.
112		work_item: usize,
113		/// The index of the work-item's import-segment to be fetched.
114		index: usize,
115	},
116	/// A particular import-segment of the executing work-item.
117	OurImport(usize),
118	/// Current work-package.
119	WorkPackage,
120	/// Work package authorization code hash and config blob.
121	Authorizer,
122	/// Input provided to the parameterized authorizer code.
123	AuthToken,
124	/// Refine context.
125	RefineContext,
126	/// All work items summary.
127	ItemsSummary,
128	/// A particular work item summary.
129	AnyItemSummary(usize),
130	/// A particular work item payload.
131	AnyPayload(usize),
132	/// All accumulate items.
133	AccumulateItems,
134	/// A particular accumulate item.
135	AnyAccumulateItem(usize),
136}
137
138impl Fetch {
139	const fn args(self) -> (u64, u64, u64) {
140		use Fetch::*;
141		match self {
142			ProtocolParameters => (FetchKind::ProtocolParameters as _, 0, 0),
143			Entropy => (FetchKind::Entropy as _, 0, 0),
144			AuthTrace => (FetchKind::AuthTrace as _, 0, 0),
145			AnyExtrinsic { work_item, index } =>
146				(FetchKind::AnyExtrinsic as _, work_item as _, index as _),
147			OurExtrinsic(index) => (FetchKind::OurExtrinsic as _, index as _, 0),
148			AnyImport { work_item, index } =>
149				(FetchKind::AnyImport as _, work_item as _, index as _),
150			OurImport(index) => (FetchKind::OurImport as _, index as _, 0),
151			WorkPackage => (FetchKind::WorkPackage as _, 0, 0),
152			Authorizer => (FetchKind::Authorizer as _, 0, 0),
153			AuthToken => (FetchKind::AuthToken as _, 0, 0),
154			RefineContext => (FetchKind::RefineContext as _, 0, 0),
155			ItemsSummary => (FetchKind::ItemsSummary as _, 0, 0),
156			AnyItemSummary(index) => (FetchKind::AnyItemSummary as _, index as _, 0),
157			AnyPayload(index) => (FetchKind::AnyPayload as _, index as _, 0),
158			AccumulateItems => (FetchKind::AccumulateItems as _, 0, 0),
159			AnyAccumulateItem(index) => (FetchKind::AnyAccumulateItem as _, index as _, 0),
160		}
161	}
162
163	/// Fetch the data defined by this [Fetch] into the given target buffer.
164	///
165	/// - `target`: The buffer to write the fetched data into.
166	/// - `skip`: The number of bytes to skip from the start of the data to be fetched.
167	///
168	/// Returns the full length of the data which is being fetched. If this is smaller than the
169	/// `target`'s length, then some of the buffer will not be written to. If the request does not
170	/// identify any data to be fetched (e.g. because an index is out of range) then returns `None`.
171	pub fn fetch_into(self, target: &mut [u8], skip: usize) -> Option<usize> {
172		let (kind, a, b) = self.args();
173		let target_ptr = if target.is_empty() { ptr::null_mut() } else { target.as_mut_ptr() };
174		unsafe { imports::fetch(target_ptr, skip as _, target.len() as _, kind as _, a, b) }
175			.into_api_option()
176	}
177
178	/// Fetch the length of the data defined by this [Fetch].
179	///
180	/// Returns the length of the data which is being fetched. If the request does not identify any
181	/// data to be fetched (e.g. because an index is out of range) then returns `None`.
182	#[allow(clippy::len_without_is_empty)]
183	pub fn len(self) -> Option<usize> {
184		self.fetch_into(&mut [], 0)
185	}
186
187	/// Fetch the data defined by this [Fetch] into a newly allocated [Vec].
188	///
189	/// Returns a [Vec] containing the data identified by the value of `self`. If the request does
190	/// not identify any data to be fetched (e.g. because an index is out of range) then returns
191	/// `None`.
192	pub fn fetch(self) -> Option<Vec<u8>> {
193		let len = self.len()?;
194		let mut incoming = vec![0u8; len];
195		self.fetch_into(&mut incoming, 0)?;
196		Some(incoming)
197	}
198
199	/// Fetch the data defined by this [Fetch] and decode it as type `T`.
200	fn fetch_as<T: Decode>(self) -> Option<T> {
201		self.fetch().map(|bytes| {
202			T::decode(&mut bytes.as_slice()).expect("host call returns correct type; qed")
203		})
204	}
205}
206
207pub(crate) mod fetch_wrappers {
208	use super::*;
209
210	/// Fetch the protocol parameters.
211	pub fn protocol_parameters() -> ProtocolParameters {
212		Fetch::ProtocolParameters.fetch_as().expect("item must be available; qed")
213	}
214
215	/// Fetch the entropy value.
216	pub fn entropy() -> [u8; 32] {
217		let mut res = [0_u8; 32];
218		Fetch::Entropy
219			.fetch_into(res.as_mut(), 0)
220			.map(|_| res)
221			.expect("item must be available; qed")
222	}
223
224	/// Fetch the output from the parameterized authorizer code.
225	pub fn auth_trace() -> AuthTrace {
226		Fetch::AuthTrace.fetch().expect("item must be available; qed").into()
227	}
228
229	/// Fetch the current work-package.
230	pub fn work_package() -> WorkPackage {
231		Fetch::WorkPackage.fetch_as().expect("item must be available; qed")
232	}
233
234	/// Fetch the work package authorizer code hash and config blob.
235	pub fn authorizer() -> Authorizer {
236		Fetch::Authorizer.fetch_as().expect("item must be available; qed")
237	}
238
239	/// Fetch the input provided to the authorizer code.
240	pub fn auth_token() -> Authorization {
241		Fetch::AuthToken.fetch().expect("item must be available; qed").into()
242	}
243
244	/// Fetch the refine context.
245	pub fn refine_context() -> RefineContext {
246		Fetch::RefineContext.fetch_as().expect("item must be available; qed")
247	}
248
249	/// Fetch all work items summary.
250	pub fn work_items_summary() -> Vec<WorkItemSummary> {
251		Fetch::ItemsSummary.fetch_as().expect("item must be available; qed")
252	}
253
254	/// Fetch a particular work item summary.
255	///
256	/// Returns `None` if fewer work items are available than the requested index.
257	pub fn work_item_summary(index: usize) -> Option<WorkItemSummary> {
258		Fetch::AnyItemSummary(index).fetch_as()
259	}
260
261	/// Fetch a particular work item payload.
262	///
263	/// Returns `None` if fewer work items are available than the requested index.
264	pub fn work_item_payload(index: usize) -> Option<Vec<u8>> {
265		Fetch::AnyPayload(index).fetch()
266	}
267
268	/// Fetch all accumulate items.
269	pub fn accumulate_items() -> Vec<AccumulateItem> {
270		Fetch::AccumulateItems.fetch_as().expect("item must be available; qed")
271	}
272
273	/// Fetch a particular accumulate item.
274	///
275	/// Returns `None` if fewer accumulate items are available than the requested index.
276	pub fn accumulate_item(index: usize) -> Option<AccumulateItem> {
277		Fetch::AnyAccumulateItem(index).fetch_as()
278	}
279
280	/// Fetch an extrinsic of our Work Item.
281	///
282	/// - `index`: The index of the extrinsic.
283	///
284	/// Returns `Some` extrinsic or `None` depending on whether the index references an extrinsic.
285	pub fn extrinsic(index: usize) -> Option<Vec<u8>> {
286		Fetch::OurExtrinsic(index).fetch()
287	}
288
289	/// Fetch a partial extrinsic of our Work Item.
290	///
291	/// - `index`: The index of the extrinsic.
292	/// - `offset`: Offset into the extrinsic data.
293	/// - `len`: Number or bytes to read.
294	///
295	/// Returns `Some` with the portion of the data that was actually read or `None` if the index is
296	/// invalid.
297	pub fn extrinsic_slice(index: usize, offset: usize, len: usize) -> Option<Vec<u8>> {
298		let mut incoming = vec![0u8; len];
299		let full_len = Fetch::OurExtrinsic(index).fetch_into(&mut incoming, offset)?;
300		if offset + len > full_len {
301			incoming.truncate(full_len.saturating_sub(offset));
302		}
303		Some(incoming)
304	}
305
306	/// Fetch an extrinsic of a given Work Item.
307	///
308	/// - `work_item`: The index of the work item to fetch an extrinsic of.
309	/// - `index`: The index of the extrinsic.
310	///
311	/// Returns `Some` extrinsic or `None` depending on whether the index references an extrinsic.
312	pub fn any_extrinsic(work_item: usize, index: usize) -> Option<Vec<u8>> {
313		Fetch::AnyExtrinsic { work_item, index }.fetch()
314	}
315
316	/// Fetch a segment of data specified in the Work Item's import manifest.
317	///
318	/// - `index`: The index of the segment within the Work Items's import manifest to import.
319	///
320	/// Returns `Some` segment or `None` depending on whether the index references an import or not.
321	pub fn import(index: usize) -> Option<Segment> {
322		let mut incoming = Segment::default();
323		Fetch::OurImport(index).fetch_into(incoming.as_mut(), 0).map(|_| incoming)
324	}
325
326	/// Fetch a segment of data specified in a given Work Item's import manifest.
327	///
328	/// - `work_item`: The index of the work item to fetch an imported segment of.
329	/// - `index`: The index of the segment within the Work Items's import manifest to import.
330	///
331	/// Returns `Some` segment or `None` depending on whether the indices reference an import or
332	/// not.
333	pub fn any_import(work_item: usize, index: usize) -> Option<Segment> {
334		let mut incoming = Segment::default();
335		Fetch::AnyImport { work_item, index }
336			.fetch_into(incoming.as_mut(), 0)
337			.map(|_| incoming)
338	}
339}
340
341/// Export a segment of data into the JAM Data Lake.
342///
343/// - `segment`: The segment of data to export.
344///
345/// Returns the export index or `Err` if the export was unsuccessful.
346pub fn export(segment: &Segment) -> ApiResult<u64> {
347	unsafe { imports::export(segment.as_ref().as_ptr(), segment.len() as u64) }.into_api_result()
348}
349
350/// Export a slice of data into the JAM Data Lake.
351///
352/// - `segment`: The slice of data to export, which may be no longer than [jam_types::SEGMENT_LEN].
353///   If it's shorter, the rest of of the bytes are zeroed.
354///
355/// Returns the export index or `Err` if the export was unsuccessful.
356pub fn export_slice(segment: &[u8]) -> ApiResult<u64> {
357	unsafe { imports::export(segment.as_ptr(), segment.len() as u64) }.into_api_result()
358}
359
360/// Create a new instance of a PVM.
361///
362/// - `code`: The code of the PVM.
363/// - `program_counter`: The initial program counter value of the PVM.
364///
365/// Returns the handle of the PVM or `Err` if the creation was unsuccessful.
366pub fn machine(code: &[u8], program_counter: u64) -> ApiResult<u64> {
367	unsafe { imports::machine(code.as_ptr(), code.len() as u64, program_counter) }.into_api_result()
368}
369
370/// Inspect the raw memory of an inner PVM.
371///
372/// - `vm_handle`: The handle of the PVM whose memory to inspect.
373/// - `inner_src`: The address in the PVM's memory to start reading from.
374/// - `len`: The number of bytes to read.
375///
376/// Returns the data in the PVM `vm_handle` at memory `inner_src` or `Err` if the inspection failed.
377pub fn peek(vm_handle: u64, inner_src: u64, len: u64) -> ApiResult<Vec<u8>> {
378	let mut incoming = vec![0; len as usize];
379	unsafe { imports::peek(vm_handle, incoming.as_mut_ptr(), inner_src, len) }
380		.into_api_result()
381		.map(|()| incoming)
382}
383
384/// Inspect the raw memory of an inner PVM.
385///
386/// - `vm_handle`: The handle of the PVM whose memory to inspect.
387/// - `outer_dst`: The buffer to write the memory into.
388/// - `inner_src`: The address in the PVM's memory to start reading from.
389///
390/// Returns `Ok` on success or `Err` if the inspection failed.
391pub fn peek_into(vm_handle: u64, outer_dst: &mut [u8], inner_src: u64) -> ApiResult<()> {
392	unsafe {
393		imports::peek(vm_handle, outer_dst.as_mut_ptr(), inner_src, size_of_val(outer_dst) as u64)
394	}
395	.into_api_result()
396}
397
398/// Inspect a plain-old-data value in the memory of an inner PVM.
399///
400/// - `vm_handle`: The handle of the PVM whose memory to inspect.
401/// - `inner_src`: The address in the PVM's memory to inspect a value of type `T`.
402///
403/// Returns the value of type `T` at `inner_src` of the PVM `vm_handle` or `Err` if the inspection
404/// failed.
405///
406/// NOTE: This will only work with types `T` which have exactly the same memory layout in the host
407/// and the inner PVM. Avoid things like references.
408pub fn peek_value<T>(vm_handle: u64, inner_src: u64) -> ApiResult<T> {
409	let mut t = MaybeUninit::<T>::uninit();
410	unsafe {
411		imports::peek(vm_handle, t.as_mut_ptr() as *mut u8, inner_src, size_of::<T>() as u64)
412			.into_api_result()
413			.map(|()| t.assume_init())
414	}
415}
416
417/// Copy some data into the memory of an inner PVM.
418///
419/// - `vm_handle`: The handle of the PVM whose memory to mutate.
420/// - `outer_src`: The data to be copied.
421/// - `inner_dst`: The address in memory of inner PVM `vm_handle` to copy the data to.
422///
423/// Returns `Ok` on success or `Err` if the inspection failed.
424pub fn poke(vm_handle: u64, outer_src: &[u8], inner_dst: u64) -> ApiResult<()> {
425	unsafe { imports::poke(vm_handle, outer_src.as_ptr(), inner_dst, outer_src.len() as u64) }
426		.into_api_result()
427}
428
429/// Copy a plain-old-data value into the memory of an inner PVM.
430///
431/// - `vm_handle`: The handle of the PVM whose memory to mutate.
432/// - `outer_src`: The value whose memory representation is to be copied.
433/// - `inner_dst`: The address in memory of inner PVM `vm_handle` to copy the value to.
434///
435/// Returns `Ok` on success or `Err` if the inspection failed.
436pub fn poke_value<T>(vm_handle: u64, outer_src: &T, inner_dst: u64) -> ApiResult<()> {
437	unsafe {
438		imports::poke(
439			vm_handle,
440			outer_src as *const T as *const u8,
441			inner_dst,
442			size_of_val(outer_src) as u64,
443		)
444	}
445	.into_api_result()
446}
447
448/// Initialize memory pages in an inner PVM with zeros, allocating if needed.
449///
450/// - `vm_handle`: The handle of the PVM whose memory to mutate.
451/// - `page`: The index of the first page of inner PVM `vm_handle` to initialize.
452/// - `count`: The number of pages to initialize.
453/// - `mode`: Memory access mode.
454///
455/// Returns `Ok` on success or `Err` if the operation failed.
456///
457/// Pages are initialized to be filled with zeroes. If the pages are not yet allocated, they will
458/// be allocated.
459pub fn zero(vm_handle: u64, page: u64, count: u64, mode: PageMode) -> ApiResult<()> {
460	unsafe { imports::pages(vm_handle, page, count, PageOperation::Alloc(mode).into()) }
461		.into_api_result()
462}
463
464/// Deallocate memory pages in an inner PVM.
465///
466/// - `vm_handle`: The handle of the PVM whose memory to mutate.
467/// - `page`: The index of the first page of inner PVM `vm_handle` to deallocate.
468/// - `count`: The number of pages to deallocate.
469///
470/// Returns `Ok` on success or `Err` if the operation failed.
471pub fn void(vm_handle: u64, page: u64, count: u64) -> ApiResult<()> {
472	unsafe { imports::pages(vm_handle, page, count, PageOperation::Free.into()) }.into_api_result()
473}
474
475/// Set memory pages access mode.
476///
477/// - `vm_handle`: The handle of the PVM whose memory to mutate.
478/// - `page`: The index of the first page of inner PVM `vm_handle` to initialize.
479/// - `count`: The number of pages to initialize.
480/// - `mode`: Memory access mode.
481///
482/// Returns `Ok` on success or `Err` if the operation failed.
483///
484/// Pages need to be initialized with [`zero`] for this operation to succeed.
485pub fn protect(vm_handle: u64, page: u64, count: u64, mode: PageMode) -> ApiResult<()> {
486	unsafe { imports::pages(vm_handle, page, count, PageOperation::SetMode(mode).into()) }
487		.into_api_result()
488}
489
490/// Invoke an inner PVM.
491///
492/// - `vm_handle`: The handle of the PVM to invoke.
493/// - `gas`: The maximum amount of gas which the inner PVM may use in this invocation.
494/// - `regs`: The initial register values of the inner PVM.
495///
496/// Returns the outcome of the invocation, together with any remaining gas, and the final register
497/// values.
498pub fn invoke(
499	vm_handle: u64,
500	gas: SignedGas,
501	regs: [u64; 13],
502) -> ApiResult<(InvokeOutcome, SignedGas, [u64; 13])> {
503	let mut args = InvokeArgs { gas, regs };
504	let outcome = unsafe { imports::invoke(vm_handle, core::ptr::from_mut(&mut args).cast()) }
505		.into_invoke_result()?;
506	Ok((outcome, args.gas, args.regs))
507}
508
509/// Delete an inner PVM instance, freeing any associated resources.
510///
511/// - `vm_handle`: The handle of the PVM to delete.
512///
513/// Returns the inner PVM's final instruction counter value on success or `Err` if the operation
514/// failed.
515pub fn expunge(vm_handle: u64) -> ApiResult<u64> {
516	unsafe { imports::expunge(vm_handle) }.into_api_result()
517}
518
519/// Inspect the gas meter.
520///
521/// Returns the post-hostcall gas meter value.
522pub fn gas() -> UnsignedGas {
523	unsafe { imports::gas() }
524}
525
526/// Check whether a preimage is available for lookup.
527///
528/// - `hash`: The hash of the preimage to check availability.
529///
530/// Returns `true` if the preimage is available, `false` otherwise.
531///
532/// NOTE: Internally this uses the `lookup` host call.
533pub fn is_available(hash: &[u8; 32]) -> bool {
534	raw_foreign_lookup_into(u64::MAX, hash, &mut []).is_some()
535}
536
537/// Check whether a preimage is available for foreign lookup.
538///
539/// - `service_id`: The service in whose preimage store to check availability.
540/// - `hash`: The hash of the preimage to check availability.
541///
542/// Returns `true` if the preimage is available, `false` otherwise.
543///
544/// NOTE: Internally this uses the `lookup` host call.
545pub fn is_foreign_available(service_id: ServiceId, hash: &[u8; 32]) -> bool {
546	raw_foreign_lookup_into(service_id as _, hash, &mut []).is_some()
547}
548
549/// Make a lookup into the service's preimage store without allocating.
550///
551/// - `hash`: The hash of the preimage to look up.
552/// - `output`: The buffer to write the preimage into.
553///
554/// Returns the number of bytes written into the output buffer or `None` if the preimage was not
555/// available.
556///
557/// NOTE: Internally this uses the `lookup` host call.
558pub fn lookup_into(hash: &[u8; 32], output: &mut [u8]) -> Option<usize> {
559	raw_foreign_lookup_into(u64::MAX, hash, output)
560}
561
562/// Make a lookup into another service's preimage store without allocating.
563///
564/// - `service_id`: The service in whose preimage store to find the preimage.
565/// - `hash`: The hash of the preimage to look up.
566/// - `output`: The buffer to write the preimage into.
567///
568/// Returns the number of bytes written into the output buffer or `None` if the preimage was not
569/// available.
570///
571/// NOTE: Internally this uses the `lookup` host call.
572pub fn foreign_lookup_into(
573	service_id: ServiceId,
574	hash: &[u8; 32],
575	output: &mut [u8],
576) -> Option<usize> {
577	raw_foreign_lookup_into(service_id as _, hash, output)
578}
579
580/// Make a lookup into the service's preimage store.
581///
582/// - `hash`: The hash of the preimage to look up.
583///
584/// Returns the preimage or `None` if the preimage was not available.
585///
586/// NOTE: Internally this uses the `lookup` host call.
587pub fn lookup(hash: &[u8; 32]) -> Option<Vec<u8>> {
588	raw_foreign_lookup(u64::MAX, hash)
589}
590
591/// Make a lookup into another service's preimage store.
592///
593/// - `service_id`: The service in whose preimage store to find the preimage.
594/// - `hash`: The hash of the preimage to look up.
595///
596/// Returns the preimage or `None` if the preimage was not available.
597///
598/// NOTE: Internally this uses the `lookup` host call.
599pub fn foreign_lookup(service_id: ServiceId, hash: &[u8; 32]) -> Option<Vec<u8>> {
600	raw_foreign_lookup(service_id as _, hash)
601}
602
603/// The status of a lookup request.
604#[derive(Debug)]
605pub enum LookupRequestStatus {
606	/// The request has never had its preimage provided; corresponds to an empty GP array.
607	Unprovided,
608	/// The requested preimage is provided; corresponds to a single-item GP array.
609	Provided {
610		/// The slot at which the preimage was provided.
611		since: Slot,
612	},
613	/// The request was provided and has since been unrequested; corresponds to a two-item GP
614	/// array.
615	Unrequested {
616		/// The slot at which the preimage was provided.
617		provided_since: Slot,
618		/// The slot at which the preimage was unrequested.
619		unrequested_since: Slot,
620	},
621	/// The request was provided, was since unrequested and is now requested again. Corresponds to
622	/// a three-item GP array.
623	Rerequested {
624		/// The slot at which the preimage was provided.
625		provided_since: Slot,
626		/// The slot at which the preimage was unrequested.
627		unrequested_at: Slot,
628		/// The slot at which the preimage was requested again.
629		rerequested_since: Slot,
630	},
631}
632
633/// A summary of the implication of calling `forget` on a preimage request.
634#[derive(Debug)]
635pub enum ForgetImplication {
636	/// The request will be dropped altogether since it was never provided. The deposit criteria
637	/// will be lifted.
638	Drop,
639	/// The preimage will be unrequested and unavailable for lookup. No change in the deposit
640	/// criteria.
641	Unrequest,
642	/// The preimage remain unavailable and be expunged from the state. The deposit criteria
643	/// will be lifted.
644	Expunge,
645	/// The `forget` call is invalid and no change in state will be made.
646	///
647	/// If called in future, after `success_after`, it will be have the effect of `Unrequest`.
648	NotYetUnrequest {
649		/// The earliest slot at which a call to `forget` can succeed.
650		success_after: Slot,
651	},
652	/// The `forget` call is invalid and no change in state will be made.
653	///
654	/// If called in future, after `success_after`, it will be have the effect of `Expunge`.
655	NotYetExpunge {
656		/// The earliest slot at which a call to `forget` can succeed.
657		success_after: Slot,
658	},
659}
660
661impl LookupRequestStatus {
662	/// Return the implication of calling `forget` on the current state of the preimage request
663	/// given the current timeslot is `now`.
664	pub fn forget_implication(&self, now: Slot) -> ForgetImplication {
665		match self {
666			Self::Unprovided => ForgetImplication::Drop,
667			Self::Provided { .. } => ForgetImplication::Unrequest,
668			Self::Unrequested { unrequested_since, .. }
669				if now > unrequested_since + min_turnaround_period() =>
670				ForgetImplication::Drop,
671			Self::Unrequested { unrequested_since, .. } => ForgetImplication::NotYetExpunge {
672				success_after: unrequested_since + min_turnaround_period(),
673			},
674			Self::Rerequested { unrequested_at, .. }
675				if now > unrequested_at + min_turnaround_period() =>
676				ForgetImplication::Unrequest,
677			Self::Rerequested { unrequested_at, .. } => ForgetImplication::NotYetUnrequest {
678				success_after: unrequested_at + min_turnaround_period(),
679			},
680		}
681	}
682}
683
684/// Query the status of a preimage.
685///
686/// - `hash`: The hash of the preimage to be queried.
687/// - `len`: The length of the preimage to be queried.
688///
689/// Returns `Some` if `hash`/`len` has an active solicitation outstanding or `None` if not.
690pub fn query(hash: &[u8; 32], len: usize) -> Option<LookupRequestStatus> {
691	let (r0, r1): (u64, u64) = unsafe { imports::query(hash.as_ptr(), len as u64) };
692	let n = r0 as u32;
693	let x = (r0 >> 32) as Slot;
694	let y = r1 as Slot;
695	Some(match n {
696		0 => LookupRequestStatus::Unprovided,
697		1 => LookupRequestStatus::Provided { since: x },
698		2 => LookupRequestStatus::Unrequested { provided_since: x, unrequested_since: y },
699		3 => LookupRequestStatus::Rerequested {
700			provided_since: x,
701			unrequested_at: y,
702			rerequested_since: (r1 >> 32) as Slot,
703		},
704		_ => return None,
705	})
706}
707
708/// Request that preimage data be available for lookup.
709///
710/// - `hash`: The hash of the preimage to be made available.
711/// - `len`: The length of the preimage to be made available.
712///
713/// Returns `Ok` on success or `Err` if the request failed.
714///
715/// [is_available] may be used to determine availability; once available, the preimage may be
716/// fetched with [lookup] or its variants.
717///
718/// A preimage may only be solicited once for any service and soliciting a preimage raises the
719/// minimum balance required to be held by the service.
720pub fn solicit(hash: &[u8; 32], len: usize) -> Result<(), ApiError> {
721	unsafe { imports::solicit(hash.as_ptr(), len as u64) }.into_api_result()
722}
723
724/// No longer request that preimage data be available for lookup, or drop preimage data once time
725/// limit has passed.
726///
727/// - `hash`: The hash of the preimage to be forgotten.
728/// - `len`: The length of the preimage to be forgotten.
729///
730/// Returns `Ok` on success or `Err` if the request failed.
731///
732/// This function is used twice in the lifetime of a requested preimage; once to indicate that the
733/// preimage is no longer needed and again to "clean up" the preimage once the required duration
734/// has passed. Whether it does one or the other is determined by the current state of the preimage
735/// request.
736pub fn forget(hash: &[u8; 32], len: usize) -> Result<(), ApiError> {
737	unsafe { imports::forget(hash.as_ptr(), len as u64) }.into_api_result()
738}
739
740/// Set the default result hash of Accumulation.
741///
742/// - `hash`: The hash to be used as the Accumulation result.
743///
744/// This value will be returned from Accumulation on success. It may be overridden by further
745/// calls to this function or by explicitly returning `Some` value from the
746/// [crate::Service::accumulate] function. The [checkpoint] function may be used after a call to
747/// this function to ensure that this value is returned in the case of an irregular termination.
748pub fn yield_hash(hash: &[u8; 32]) {
749	unsafe { imports::yield_hash(hash.as_ptr()) }
750		.into_api_result()
751		.expect("Cannot fail except for memory access; we provide a good address; qed")
752}
753
754/// Provide a requested preimage to any service.
755pub fn provide(service_id: ServiceId, preimage: &[u8]) -> Result<(), ApiError> {
756	unsafe { imports::provide(service_id as u64, preimage.as_ptr(), preimage.len() as _) }
757		.into_api_result()
758}
759
760/// Fetch raw data from the service's key/value store.
761///
762/// - `key`: The key of the data to fetch.
763///
764/// Returns the data associated with the key or `None` if the key is not present.
765pub fn get_storage(key: &[u8]) -> Option<Vec<u8>> {
766	raw_get_foreign_storage(u64::MAX, key)
767}
768
769/// Fetch raw data from the service's key/value store into a buffer.
770///
771/// - `key`: The key of the data to fetch.
772/// - `value`: The buffer to write the data into; on success, this is overwritten with the value
773///   associated with `key` in the service's store, leaving any portions unchanged if the buffer is
774///   longer than the value.
775///
776/// Returns the size of the data associated with the key or `None` if the key is not present.
777pub fn get_storage_into(key: &[u8], value: &mut [u8]) -> Option<usize> {
778	raw_get_foreign_storage_into(u64::MAX, key, value)
779}
780
781/// Fetch raw data from another service's key/value store.
782///
783/// - `id`: The ID of the service whose key/value store to fetch from.
784/// - `key`: The key of the data to fetch.
785///
786/// Returns the data associated with the key in the key/value store of service `id` or `None` if
787/// the key is not present.
788pub fn get_foreign_storage(id: ServiceId, key: &[u8]) -> Option<Vec<u8>> {
789	raw_get_foreign_storage(id as u64, key)
790}
791
792/// Fetch raw data from another service's key/value store into a buffer.
793///
794/// - `id`: The ID of the service whose key/value store to fetch from.
795/// - `key`: The key of the data to fetch.
796/// - `value`: The buffer to write the data into; on success, this is overwritten with the value
797///   associated with `key` in said service's store, leaving any portions unchanged if the buffer is
798///   longer than the value.
799///
800/// Returns the size of the data associated with the key in the key/value store of service `id` or
801/// `None` if the key is not present.
802pub fn get_foreign_storage_into(id: ServiceId, key: &[u8], value: &mut [u8]) -> Option<usize> {
803	raw_get_foreign_storage_into(id as u64, key, value)
804}
805
806/// Fetch typed data from the service's key/value store.
807///
808/// - `key`: A value, whose encoded representation is the the key of the data to fetch.
809///
810/// Returns the decoded data associated with the key or `None` if the key is not present or the data
811/// cannot be decoded into the type `R`.
812pub fn get<R: Decode>(key: impl Encode) -> Option<R> {
813	Decode::decode(&mut &key.using_encoded(get_storage)?[..]).ok()
814}
815
816/// Fetch typed data from another service's key/value store.
817///
818/// - `id`: The ID of the service whose key/value store to fetch from.
819/// - `key`: A value, whose encoded representation is the the key of the data to fetch.
820///
821/// Returns the decoded data associated with the key in the key/value store of service `id` or
822/// `None` if the key is not present or the data cannot be decoded into the type `R`.
823pub fn get_foreign<R: Decode>(id: ServiceId, key: impl Encode) -> Option<R> {
824	Decode::decode(&mut &key.using_encoded(|k| get_foreign_storage(id, k))?[..]).ok()
825}
826
827/// Set the value of a key to raw data in the service's key/value store.
828///
829/// - `key`: The key to be set.
830/// - `data`: The data to be associated with the key.
831///
832/// Returns the previous value's length, which can be `None` if no value was associated
833/// with the given `key`, or `Err` if the operation failed.
834///
835/// NOTE: If this key was not previously set or if the data is larger than the previous value, then
836/// the minimum balance which the service must hold is raised and if the service has too little
837/// balance then the call with fail with [ApiError::StorageFull].
838pub fn set_storage(key: &[u8], data: &[u8]) -> Result<Option<usize>, ApiError> {
839	unsafe { imports::write(key.as_ptr(), key.len() as u64, data.as_ptr(), data.len() as u64) }
840		.into_api_result()
841}
842
843/// Remove a pair from the service's key/value store.
844///
845/// - `key`: The key to be removed.
846///
847/// Returns `Some` on success with the previous value's length, or `None` if the key does not exist.
848///
849/// NOTE: If the key does not exist, then the operation is a no-op.
850pub fn remove_storage(key: &[u8]) -> Option<usize> {
851	unsafe { imports::write(key.as_ptr(), key.len() as u64, ptr::null(), 0) }
852		.into_api_result()
853		.expect("Cannot fail except for memory access; we provide a good address; qed")
854}
855
856/// Set the value of a typed key to typed data in the service's key/value store.
857///
858/// - `key`: The value of an encodable type whose encoding is the key to be set.
859/// - `value`: The value of an encodable type whose encoding be associated with said key.
860///
861/// Returns `Ok` on success or `Err` if the operation failed.
862///
863/// NOTE: If this key was not previously set or if the data is larger than the previous value, then
864/// the minimum balance which the service must hold is raised and if the service has too little
865/// balance then the call with fail with [ApiError::StorageFull].
866pub fn set(key: impl Encode, value: impl Encode) -> Result<(), ApiError> {
867	value.using_encoded(|v| key.using_encoded(|k| set_storage(k, v).map(|_| ())))
868}
869
870/// Remove a typed key from the service's key/value store.
871///
872/// - `key`: The value of an encodable type whose encoding is the key to be removed.
873///
874/// NOTE: If the key does not exist, then the operation is a no-op.
875pub fn remove(key: impl Encode) {
876	let _ = key.using_encoded(remove_storage);
877}
878
879/// Get information on the service.
880///
881/// Returns the value of [ServiceInfo] which describes the current state of the service.
882pub fn my_info() -> ServiceInfo {
883	raw_service_info(u64::MAX).expect("Current service must exist; qed")
884}
885
886/// Get information on another service.
887///
888/// - `id`: The ID of the service to get information on.
889///
890/// Returns the value of [ServiceInfo] which describes the current state of service `id`.
891pub fn service_info(id: ServiceId) -> Option<ServiceInfo> {
892	raw_service_info(id as _)
893}
894
895#[doc(hidden)]
896pub fn raw_service_info_field<T: Decode, const N: usize>(service: u64, offset: u64) -> Option<T> {
897	let mut buffer = [0u8; N];
898	let maybe_ok: Option<()> =
899		unsafe { imports::info(service as _, buffer.as_mut_ptr(), offset, N as u64) }
900			.into_api_result()
901			.expect("Cannot fail except for memory access; we provide a good address; qed");
902	maybe_ok?;
903	T::decode(&mut &buffer[..]).ok()
904}
905
906#[doc(hidden)]
907#[rustfmt::skip]
908#[macro_export]
909macro_rules! service_info_field_type {
910    (code_hash) => {::jam_types::CodeHash};
911    (balance) => {::jam_types::Balance};
912    (threshold) => {::jam_types::Balance};
913    (min_item_gas) => {::jam_types::UnsignedGas};
914    (min_memo_gas) => {::jam_types::UnsignedGas};
915    (bytes) => {u64};
916    (items) => {u32};
917    (deposit_offset) => {::jam_types::Balance};
918    (creation_slot) => {::jam_types::Slot};
919    (last_accumulation_slot) => {::jam_types::Slot};
920    (parent_service) => {::jam_types::ServiceId};
921}
922
923#[doc(hidden)]
924#[rustfmt::skip]
925#[macro_export]
926macro_rules! service_info_field_offset {
927    (code_hash) => {::jam_types::ServiceInfo::CODE_HASH_OFFSET};
928    (balance) => {::jam_types::ServiceInfo::BALANCE_OFFSET};
929    (threshold) => {::jam_types::ServiceInfo::THRESHOLD_OFFSET};
930    (min_item_gas) => {::jam_types::ServiceInfo::MIN_ITEM_GAS_OFFSET};
931    (min_memo_gas) => {::jam_types::ServiceInfo::MIN_MEMO_GAS_OFFSET};
932    (bytes) => {::jam_types::ServiceInfo::BYTES_OFFSET};
933    (items) => {::jam_types::ServiceInfo::ITEMS_OFFSET};
934    (deposit_offset) => {::jam_types::ServiceInfo::DEPOSIT_OFFSET_OFFSET};
935    (creation_slot) => {::jam_types::ServiceInfo::CREATION_SLOT_OFFSET};
936    (last_accumulation_slot) => {::jam_types::ServiceInfo::LAST_ACCUMULATION_SLOT_OFFSET};
937    (parent_service) => {::jam_types::ServiceInfo::PARENT_SERVICE_OFFSET};
938}
939
940/// Get specific field from a another service info.
941#[macro_export]
942macro_rules! service_info_field {
943	($service: expr, $field: ident) => {{
944		type T = $crate::service_info_field_type!($field);
945		const OFFSET: usize = $crate::service_info_field_offset!($field);
946		const LEN: usize = ::core::mem::size_of::<T>();
947		$crate::internal::raw_service_info_field::<T, LEN>($service, OFFSET as u64)
948	}};
949}
950
951/// Get specific field of the current service info.
952#[macro_export]
953macro_rules! my_info_field {
954	($field: ident) => {
955		$crate::service_info_field!(u64::MAX, $field).expect("Current service must exist; qed")
956	};
957}
958
959/// Create a new service.
960///
961/// This is a convenience function that calls [`create_service_ext`] with no `deposit_offset`
962/// and no requirements for `new_service_id`.
963///
964/// Returns the new service ID or `Err` if the operation failed.
965pub fn create_service(
966	code_hash: &CodeHash,
967	code_len: usize,
968	min_item_gas: UnsignedGas,
969	min_memo_gas: UnsignedGas,
970) -> Result<ServiceId, ApiError> {
971	create_service_ext(code_hash, code_len, min_item_gas, min_memo_gas, None, None)
972}
973
974/// Create a new service with extended options.
975///
976/// - `code_hash`: The hash of the code of the service to create. The preimage of this hash will be
977///   solicited by the new service and its according minimum balance will be transferred from the
978///   executing service to the new service in order to fund it.
979/// - `code_len`: The length of the code of the service to create.
980/// - `min_item_gas`: The minimum gas required to be set aside for the accumulation of a single Work
981///   Item in the new service.
982/// - `min_memo_gas`: The minimum gas required to be set aside for any single transfer of funds and
983///   corresponding processing of a memo in the new service.
984/// - `deposit_offset`: The deposit offset for the new service. This represents gratis storage up to
985///   the amount allowed by `deposit_offset`. Setting a non-zero value requires the caller to have
986///   "manager" privileges.
987/// - `new_service_id`: The desired service ID for the new service. This allows to request for a new
988///   service ID in the reserved low range (< S, with S prescribed by the GP). Requesting such low
989///   ID requires the caller to have "registrar" privileges.
990///
991/// Returns the new service ID or `Err` if the operation failed.
992///
993/// NOTE: This operation requires a balance transfer from the executing service to the new service
994/// in order to succeed; if this would reduce the balance to below the minimum balance required,
995/// then it will fail.
996///
997/// NOTE: This commits to the code of the new service but does not yet instantiate it; the code
998/// preimage must be provided before the first Work Items of the new service can be processed.
999pub fn create_service_ext(
1000	code_hash: &CodeHash,
1001	code_len: usize,
1002	min_item_gas: UnsignedGas,
1003	min_memo_gas: UnsignedGas,
1004	deposit_offset: Option<Balance>,
1005	new_service_id: Option<ServiceId>,
1006) -> Result<ServiceId, ApiError> {
1007	unsafe {
1008		imports::new(
1009			code_hash.as_ptr(),
1010			code_len as u64,
1011			min_item_gas,
1012			min_memo_gas,
1013			deposit_offset.unwrap_or_default(),
1014			new_service_id.unwrap_or(ServiceId::MAX) as u64,
1015		)
1016		.into_api_result()
1017	}
1018}
1019
1020/// Upgrade the code of the service.
1021///
1022/// - `code_hash`: The hash of the code to upgrade to, to be found in the service's preimage store.
1023/// - `min_item_gas`: The minimum gas required to be set aside for the accumulation of a single Work
1024///   Item in the new service.
1025/// - `min_memo_gas`: The minimum gas required to be set aside for any single transfer of funds and
1026///   corresponding processing of a memo in the new service.
1027///
1028/// NOTE: This commits to the new code of the service but does not yet instantiate it; the new code
1029/// preimage must be provided before the first Work Items of the new service can be processed.
1030/// Generally you should use [solicit] and [is_available] to ensure that the new code is already
1031/// in the service's preimage store and call this only as the final step in the process.
1032pub fn upgrade(code_hash: &CodeHash, min_item_gas: UnsignedGas, min_memo_gas: UnsignedGas) {
1033	unsafe { imports::upgrade(code_hash.as_ptr(), min_item_gas, min_memo_gas) }
1034		.into_api_result()
1035		.expect("Failure only in case of bad memory; it is good; qed")
1036}
1037
1038/// "Upgrade" the service into an unexecutable zombie.
1039///
1040/// - `ejector`: The index of the service which will be able to call [eject] on the caller service
1041///   in order to finally delete it.
1042///
1043/// NOTE: This only sets the new code hash of the service but does not clear storage/preimages nor
1044/// [forget] the current code hash. Do these first!
1045pub fn zombify(ejector: ServiceId) {
1046	(ejector, [0; 28]).using_encoded(|data| {
1047		unsafe { imports::upgrade(data.as_ptr(), 0, 0) }
1048			.into_api_result()
1049			.expect("Failure only in case of bad memory; it is good; qed")
1050	})
1051}
1052
1053/// Transfer data and/or funds to another service asynchronously.
1054///
1055/// - `destination`: The ID of the service to transfer to. This service must exist at present.
1056/// - `amount`: The amount of funds to transfer to the `destination` service. Reducing the services
1057///   balance by this amount must not result in it falling below the minimum balance required.
1058/// - `gas_limit`: The amount of gas to set aside for the processing of the transfer by the
1059///   `destination` service. This must be at least the service's [ServiceInfo::min_memo_gas]. The
1060///   effective gas cost of this call is increased by this amount.
1061/// - `memo`: A piece of data to give the `destination` service.
1062///
1063/// Returns `Ok` on success or `Err` if the operation failed.
1064///
1065/// NOTE: All transfers are deferred; they are guaranteed to be received by the destination service
1066/// in same time slot, but will not be processed synchronously with this call.
1067pub fn transfer(
1068	destination: ServiceId,
1069	amount: Balance,
1070	gas_limit: UnsignedGas,
1071	memo: &Memo,
1072) -> Result<(), ApiError> {
1073	unsafe {
1074		imports::transfer(destination as _, amount, gas_limit, memo.as_ref().as_ptr())
1075			.into_api_result()
1076	}
1077}
1078
1079/// Remove the `target` zombie service, drop its final preimage item `code_hash` and transfer
1080/// remaining balance to this service.
1081///
1082/// - `target`: The ID of a zombie service which nominated the caller service as its ejector.
1083/// - `code_hash`: The hash of the only preimage item of the `target` service. It must be
1084///   unrequested and droppable.
1085///
1086/// Target must therefore satisfy several requirements:
1087/// - it should have a code hash which is simply the LE32-encoding of the caller service's ID;
1088/// - it should have only one preimage lookup item, `code_hash`;
1089/// - it should have nothing in its storage.
1090///
1091/// Returns `Ok` on success or `Err` if the operation failed.
1092pub fn eject(target: ServiceId, code_hash: &CodeHash) -> Result<(), ApiError> {
1093	unsafe { imports::eject(target as _, code_hash.as_ref().as_ptr()) }.into_api_result()
1094}
1095
1096/// Reset the privileged services.
1097///
1098/// - `manager`: The ID of the service which may effectually call [bless] in the future.
1099/// - `assigner`: The ID of the service which may effectually call [assign] in the future. NOTE: All
1100///   cores are assigned to this service.
1101/// - `designator`: The ID of the service which may effectually call [designate] in the future.
1102/// - `registrar`: The ID of the service which may register new service ids in the reserved range.
1103/// - `always_acc`: The list of service IDs which accumulate at least once in every JAM block,
1104///   together with the baseline gas they get for accumulation. This may be supplemented with
1105///   additional gas should there be Work Items for the service.
1106///
1107/// Returns `Ok` on success or `Err` if the operation failed.
1108///
1109/// NOTE: This call fails if, according to the state snapshot taken at the start of service
1110/// accumulation, this service is not the _manager_. As service accumulation proceeds, a prior
1111/// `bless` may update the state to bless a different service as manager. In that case, any
1112/// subsequent `bless` attempt by this service will also fail.
1113pub fn bless<'a>(
1114	manager: ServiceId,
1115	assigner: ServiceId,
1116	designator: ServiceId,
1117	registrar: ServiceId,
1118	always_acc: impl IntoIterator<Item = &'a (ServiceId, UnsignedGas)>,
1119) {
1120	let mut aa_count = 0;
1121	let aa_data: Vec<u8> = always_acc
1122		.into_iter()
1123		.flat_map(|x| {
1124			aa_count += 1;
1125			x.encode()
1126		})
1127		.collect();
1128	let assigners_data = FixedVec::<ServiceId, CoreCount>::new(assigner).encode();
1129	unsafe {
1130		imports::bless(
1131			manager as _,
1132			assigners_data.as_ptr() as _,
1133			designator as _,
1134			registrar as _,
1135			aa_data.as_ptr(),
1136			aa_count,
1137		)
1138	}
1139	.into_api_result()
1140	.expect("Failure only in case of bad memory or bad service ID; both are good; qed")
1141}
1142
1143/// Assign a series of authorizers to a core.
1144///
1145/// - `core`: The index of the core to assign the authorizers to.
1146/// - `auth_queue`: The authorizer-queue to assign to the core. These are a series of
1147///   [AuthorizerHash] values, which determine what kinds of Work Packages are allowed to be
1148///   executed on the core.
1149/// - `assigner`: The ID of the service which may call [`assign`] in the future.
1150///
1151/// Returns `Ok` on success or `Err` if the operation failed.
1152///
1153/// NOTE: This call fails if, according to the state snapshot taken at the start of service
1154/// accumulation, this service is not the _assigner_ for the given `core`. As service accumulation
1155/// proceeds, a prior `assign` may update the state to assign the core to a different service. In
1156/// that case, any subsequent `assign` attempt by this service will also fail.
1157pub fn assign(
1158	core: CoreIndex,
1159	auth_queue: &AuthQueue,
1160	assigner: ServiceId,
1161) -> Result<(), ApiError> {
1162	auth_queue
1163		.using_encoded(|d| unsafe { imports::assign(core as _, d.as_ptr(), assigner as _) })
1164		.into_api_result()
1165}
1166
1167/// Designate the new validator keys.
1168///
1169/// - `keys`: The new validator keys.
1170///
1171/// Returns `Ok` on success or `Err` if the operation failed.
1172///
1173/// NOTE: This call fails if, according to the state snapshot taken at the start of service
1174/// accumulation, this service is not the _designator_. As service accumulation proceeds, a prior
1175/// `designate` may update the state to assign the designator privilege to a different service. In
1176/// that case, any subsequent `designate` attempt by this service will also fail.
1177pub fn designate(keys: &OpaqueValKeysets) -> Result<(), ApiError> {
1178	keys.using_encoded(|d| unsafe { imports::designate(d.as_ptr()) })
1179		.into_api_result()
1180}
1181
1182/// Checkpoint the state of the accumulation at present.
1183///
1184/// In the case that accumulation runs out of gas or otherwise terminates unexpectedly, all
1185/// changes extrinsic to the machine state, such as storage writes and transfers, will be rolled
1186/// back to the most recent call to [checkpoint], or the beginning of the accumulation if no
1187/// checkpoint has been made.
1188///
1189/// Returns the post-hostcall gas meter value.
1190pub fn checkpoint() -> UnsignedGas {
1191	unsafe { imports::checkpoint() }
1192}
1193
1194fn raw_foreign_lookup(service_id: u64, hash: &[u8; 32]) -> Option<Vec<u8>> {
1195	let maybe_len: Option<u64> =
1196		unsafe { imports::lookup(service_id, hash.as_ptr(), ptr::null_mut(), 0, 0) }
1197			.into_api_result()
1198			.expect("Cannot fail except for memory access; we provide a good address; qed");
1199	let len = maybe_len?;
1200	let mut incoming = vec![0; len as usize];
1201	unsafe {
1202		imports::lookup(service_id, hash.as_ptr(), incoming.as_mut_ptr(), 0, len);
1203	}
1204	Some(incoming)
1205}
1206
1207fn raw_foreign_lookup_into(service_id: u64, hash: &[u8; 32], output: &mut [u8]) -> Option<usize> {
1208	let maybe_len: Option<u64> = unsafe {
1209		imports::lookup(service_id, hash.as_ptr(), output.as_mut_ptr(), 0, output.len() as u64)
1210	}
1211	.into_api_result()
1212	.expect("Cannot fail except for memory access; we provide a good address; qed");
1213	Some(maybe_len? as usize)
1214}
1215
1216fn raw_foreign_historical_lookup(service_id: u64, hash: &[u8; 32]) -> Option<Vec<u8>> {
1217	let maybe_len: Option<u64> =
1218		unsafe { imports::historical_lookup(service_id, hash.as_ptr(), ptr::null_mut(), 0, 0) }
1219			.into_api_result()
1220			.expect("Cannot fail except for memory access; we provide a good address; qed");
1221	let len = maybe_len?;
1222	let mut incoming = vec![0; len as usize];
1223	unsafe {
1224		imports::historical_lookup(service_id, hash.as_ptr(), incoming.as_mut_ptr(), 0, len);
1225	}
1226	Some(incoming)
1227}
1228
1229fn raw_foreign_historical_lookup_into(
1230	service_id: u64,
1231	hash: &[u8; 32],
1232	output: &mut [u8],
1233) -> Option<usize> {
1234	let maybe_len: Option<u64> = unsafe {
1235		imports::historical_lookup(
1236			service_id,
1237			hash.as_ptr(),
1238			output.as_mut_ptr(),
1239			0,
1240			output.len() as u64,
1241		)
1242	}
1243	.into_api_result()
1244	.expect("Cannot fail except for memory access; we provide a good address; qed");
1245	Some(maybe_len? as usize)
1246}
1247
1248fn raw_service_info(service: u64) -> Option<ServiceInfo> {
1249	let mut buffer = [0u8; ServiceInfo::ENCODED_LEN];
1250	let maybe_ok: Option<()> =
1251		unsafe { imports::info(service as _, buffer.as_mut_ptr(), 0, buffer.len() as u64) }
1252			.into_api_result()
1253			.expect("Cannot fail except for memory access; we provide a good address; qed");
1254	maybe_ok?;
1255	ServiceInfo::decode(&mut &buffer[..]).ok()
1256}
1257
1258fn raw_get_foreign_storage(id: u64, key: &[u8]) -> Option<Vec<u8>> {
1259	let maybe_len: Option<u64> =
1260		unsafe { imports::read(id as _, key.as_ptr(), key.len() as u64, ptr::null_mut(), 0, 0) }
1261			.into_api_result()
1262			.expect("Cannot fail except for memory access; we provide a good address; qed");
1263	let len = maybe_len?;
1264	if len == 0 {
1265		Some(vec![])
1266	} else {
1267		let mut incoming = vec![0; len as usize];
1268		unsafe {
1269			imports::read(id as _, key.as_ptr(), key.len() as u64, incoming.as_mut_ptr(), 0, len);
1270		}
1271		Some(incoming)
1272	}
1273}
1274
1275fn raw_get_foreign_storage_into(id: u64, key: &[u8], value: &mut [u8]) -> Option<usize> {
1276	let r: ApiResult<Option<u64>> = unsafe {
1277		imports::read(
1278			id as _,
1279			key.as_ptr(),
1280			key.len() as _,
1281			value.as_mut_ptr(),
1282			0,
1283			value.len() as _,
1284		)
1285	}
1286	.into_api_result();
1287	Some(r.expect("Only fail is memory access; address is good; qed")? as usize)
1288}