use crate::{
imports,
result::{ApiResult, IntoApiOption as _, IntoApiResult as _, IntoInvokeResult as _},
ApiError, InvokeOutcome,
};
use alloc::{vec, vec::Vec};
use codec::Encode;
use core::{
mem::{size_of, size_of_val, MaybeUninit},
ptr,
};
use jam_types::*;
pub fn is_historical_available(hash: &[u8; 32]) -> bool {
raw_foreign_historical_lookup_into(u64::MAX, hash, &mut []).is_some()
}
pub fn is_foreign_historical_available(service_id: ServiceId, hash: &[u8; 32]) -> bool {
raw_foreign_historical_lookup_into(service_id as _, hash, &mut []).is_some()
}
pub fn historical_lookup_into(hash: &[u8; 32], output: &mut [u8]) -> Option<usize> {
raw_foreign_historical_lookup_into(u64::MAX, hash, output)
}
pub fn foreign_historical_lookup_into(
service_id: ServiceId,
hash: &[u8; 32],
output: &mut [u8],
) -> Option<usize> {
raw_foreign_historical_lookup_into(service_id as _, hash, output)
}
pub fn historical_lookup(hash: &[u8; 32]) -> Option<Vec<u8>> {
raw_foreign_historical_lookup(u64::MAX, hash)
}
pub fn foreign_historical_lookup(service_id: ServiceId, hash: &[u8; 32]) -> Option<Vec<u8>> {
raw_foreign_historical_lookup(service_id as _, hash)
}
#[derive(Copy, Clone, Debug)]
pub enum Fetch {
ProtocolParameters,
Entropy,
AuthTrace,
AnyExtrinsic {
work_item: usize,
index: usize,
},
OurExtrinsic(usize),
AnyImport {
work_item: usize,
index: usize,
},
OurImport(usize),
WorkPackage,
AuthConfig,
AuthToken,
RefineContext,
ItemsSummary,
AnyItemSummary(usize),
AnyPayload(usize),
AccumulateItems,
AnyAccumulateItem(usize),
}
impl Fetch {
const fn args(self) -> (u64, u64, u64) {
use Fetch::*;
match self {
ProtocolParameters => (FetchKind::ProtocolParameters as _, 0, 0),
Entropy => (FetchKind::Entropy as _, 0, 0),
AuthTrace => (FetchKind::AuthTrace as _, 0, 0),
AnyExtrinsic { work_item, index } =>
(FetchKind::AnyExtrinsic as _, work_item as _, index as _),
OurExtrinsic(index) => (FetchKind::OurExtrinsic as _, index as _, 0),
AnyImport { work_item, index } =>
(FetchKind::AnyImport as _, work_item as _, index as _),
OurImport(index) => (FetchKind::OurImport as _, index as _, 0),
WorkPackage => (FetchKind::WorkPackage as _, 0, 0),
AuthConfig => (FetchKind::AuthConfig as _, 0, 0),
AuthToken => (FetchKind::AuthToken as _, 0, 0),
RefineContext => (FetchKind::RefineContext as _, 0, 0),
ItemsSummary => (FetchKind::ItemsSummary as _, 0, 0),
AnyItemSummary(index) => (FetchKind::AnyItemSummary as _, index as _, 0),
AnyPayload(index) => (FetchKind::AnyPayload as _, index as _, 0),
AccumulateItems => (FetchKind::AccumulateItems as _, 0, 0),
AnyAccumulateItem(index) => (FetchKind::AnyAccumulateItem as _, index as _, 0),
}
}
pub fn fetch_into(self, target: &mut [u8], skip: usize) -> Option<usize> {
let (kind, a, b) = self.args();
let target_ptr = if target.is_empty() { ptr::null_mut() } else { target.as_mut_ptr() };
unsafe { imports::fetch(target_ptr, skip as _, target.len() as _, kind as _, a, b) }
.into_api_option()
}
#[allow(clippy::len_without_is_empty)]
pub fn len(self) -> Option<usize> {
self.fetch_into(&mut [], 0)
}
pub fn fetch(self) -> Option<Vec<u8>> {
let len = self.len()?;
let mut incoming = vec![0u8; len];
self.fetch_into(&mut incoming, 0)?;
Some(incoming)
}
fn fetch_as<T: Decode>(self) -> Option<T> {
self.fetch().map(|bytes| {
T::decode(&mut bytes.as_slice()).expect("host call returns correct type; qed")
})
}
}
pub(crate) mod fetch_wrappers {
use super::*;
pub fn protocol_parameters() -> ProtocolParameters {
Fetch::ProtocolParameters.fetch_as().expect("item must be available; qed")
}
pub fn entropy() -> [u8; 32] {
let mut res = [0_u8; 32];
Fetch::Entropy
.fetch_into(res.as_mut(), 0)
.map(|_| res)
.expect("item must be available; qed")
}
pub fn auth_trace() -> AuthTrace {
Fetch::AuthTrace.fetch().expect("item must be available; qed").into()
}
pub fn work_package() -> WorkPackage {
Fetch::WorkPackage.fetch_as().expect("item must be available; qed")
}
pub fn auth_config() -> AuthConfig {
Fetch::AuthConfig.fetch_as().expect("item must be available; qed")
}
pub fn auth_token() -> Authorization {
Fetch::AuthToken.fetch().expect("item must be available; qed").into()
}
pub fn refine_context() -> RefineContext {
Fetch::RefineContext.fetch_as().expect("item must be available; qed")
}
pub fn work_items_summary() -> Vec<WorkItemSummary> {
Fetch::ItemsSummary.fetch_as().expect("item must be available; qed")
}
pub fn work_item_summary(index: usize) -> Option<WorkItemSummary> {
Fetch::AnyItemSummary(index).fetch_as()
}
pub fn work_item_payload(index: usize) -> Option<Vec<u8>> {
Fetch::AnyPayload(index).fetch()
}
pub fn accumulate_items() -> Vec<AccumulateItem> {
Fetch::AccumulateItems.fetch_as().expect("item must be available; qed")
}
pub fn accumulate_item(index: usize) -> Option<AccumulateItem> {
Fetch::AnyAccumulateItem(index).fetch_as()
}
pub fn extrinsic(index: usize) -> Option<Vec<u8>> {
Fetch::OurExtrinsic(index).fetch()
}
pub fn extrinsic_slice(index: usize, offset: usize, len: usize) -> Option<Vec<u8>> {
let mut incoming = vec![0u8; len];
let full_len = Fetch::OurExtrinsic(index).fetch_into(&mut incoming, offset)?;
if offset + len > full_len {
incoming.truncate(full_len.saturating_sub(offset));
}
Some(incoming)
}
pub fn any_extrinsic(work_item: usize, index: usize) -> Option<Vec<u8>> {
Fetch::AnyExtrinsic { work_item, index }.fetch()
}
pub fn import(index: usize) -> Option<Segment> {
let mut incoming = Segment::default();
Fetch::OurImport(index).fetch_into(incoming.as_mut(), 0).map(|_| incoming)
}
pub fn any_import(work_item: usize, index: usize) -> Option<Segment> {
let mut incoming = Segment::default();
Fetch::AnyImport { work_item, index }
.fetch_into(incoming.as_mut(), 0)
.map(|_| incoming)
}
}
pub fn export(segment: &Segment) -> ApiResult<u64> {
unsafe { imports::export(segment.as_slice().as_ptr(), segment.len() as u64) }.into_api_result()
}
pub fn export_slice(segment: &[u8]) -> ApiResult<u64> {
unsafe { imports::export(segment.as_ptr(), segment.len() as u64) }.into_api_result()
}
pub fn machine(code: &[u8], program_counter: u64) -> ApiResult<u64> {
unsafe { imports::machine(code.as_ptr(), code.len() as u64, program_counter) }.into_api_result()
}
pub fn peek(vm_handle: u64, inner_src: u64, len: u64) -> ApiResult<Vec<u8>> {
let mut incoming = vec![0; len as usize];
unsafe { imports::peek(vm_handle, incoming.as_mut_ptr(), inner_src, len) }
.into_api_result()
.map(|()| incoming)
}
pub fn peek_into(vm_handle: u64, outer_dst: &mut [u8], inner_src: u64) -> ApiResult<()> {
unsafe {
imports::peek(vm_handle, outer_dst.as_mut_ptr(), inner_src, size_of_val(outer_dst) as u64)
}
.into_api_result()
}
pub fn peek_value<T>(vm_handle: u64, inner_src: u64) -> ApiResult<T> {
let mut t = MaybeUninit::<T>::uninit();
unsafe {
imports::peek(vm_handle, t.as_mut_ptr() as *mut u8, inner_src, size_of::<T>() as u64)
.into_api_result()
.map(|()| t.assume_init())
}
}
pub fn poke(vm_handle: u64, outer_src: &[u8], inner_dst: u64) -> ApiResult<()> {
unsafe { imports::poke(vm_handle, outer_src.as_ptr(), inner_dst, outer_src.len() as u64) }
.into_api_result()
}
pub fn poke_value<T>(vm_handle: u64, outer_src: &T, inner_dst: u64) -> ApiResult<()> {
unsafe {
imports::poke(
vm_handle,
outer_src as *const T as *const u8,
inner_dst,
size_of_val(outer_src) as u64,
)
}
.into_api_result()
}
pub fn zero(vm_handle: u64, page: u64, count: u64, mode: PageMode) -> ApiResult<()> {
unsafe { imports::pages(vm_handle, page, count, PageOperation::Alloc(mode).into()) }
.into_api_result()
}
pub fn void(vm_handle: u64, page: u64, count: u64) -> ApiResult<()> {
unsafe { imports::pages(vm_handle, page, count, PageOperation::Free.into()) }.into_api_result()
}
pub fn protect(vm_handle: u64, page: u64, count: u64, mode: PageMode) -> ApiResult<()> {
unsafe { imports::pages(vm_handle, page, count, PageOperation::SetMode(mode).into()) }
.into_api_result()
}
pub fn invoke(
vm_handle: u64,
gas: SignedGas,
regs: [u64; 13],
) -> ApiResult<(InvokeOutcome, SignedGas, [u64; 13])> {
let mut args = InvokeArgs { gas, regs };
let outcome = unsafe { imports::invoke(vm_handle, core::ptr::from_mut(&mut args).cast()) }
.into_invoke_result()?;
Ok((outcome, args.gas, args.regs))
}
pub fn expunge(vm_handle: u64) -> ApiResult<u64> {
unsafe { imports::expunge(vm_handle) }.into_api_result()
}
pub fn gas() -> UnsignedGas {
unsafe { imports::gas() }
}
pub fn is_available(hash: &[u8; 32]) -> bool {
raw_foreign_lookup_into(u64::MAX, hash, &mut []).is_some()
}
pub fn is_foreign_available(service_id: ServiceId, hash: &[u8; 32]) -> bool {
raw_foreign_lookup_into(service_id as _, hash, &mut []).is_some()
}
pub fn lookup_into(hash: &[u8; 32], output: &mut [u8]) -> Option<usize> {
raw_foreign_lookup_into(u64::MAX, hash, output)
}
pub fn foreign_lookup_into(
service_id: ServiceId,
hash: &[u8; 32],
output: &mut [u8],
) -> Option<usize> {
raw_foreign_lookup_into(service_id as _, hash, output)
}
pub fn lookup(hash: &[u8; 32]) -> Option<Vec<u8>> {
raw_foreign_lookup(u64::MAX, hash)
}
pub fn foreign_lookup(service_id: ServiceId, hash: &[u8; 32]) -> Option<Vec<u8>> {
raw_foreign_lookup(service_id as _, hash)
}
#[derive(Debug)]
pub enum LookupRequestStatus {
Unprovided,
Provided {
since: Slot,
},
Unrequested {
provided_since: Slot,
unrequested_since: Slot,
},
Rerequested {
provided_since: Slot,
unrequested_at: Slot,
rerequested_since: Slot,
},
}
#[derive(Debug)]
pub enum ForgetImplication {
Drop,
Unrequest,
Expunge,
NotYetUnrequest {
success_after: Slot,
},
NotYetExpunge {
success_after: Slot,
},
}
impl LookupRequestStatus {
pub fn forget_implication(&self, now: Slot) -> ForgetImplication {
match self {
Self::Unprovided => ForgetImplication::Drop,
Self::Provided { .. } => ForgetImplication::Unrequest,
Self::Unrequested { unrequested_since, .. }
if now > unrequested_since + min_turnaround_period() =>
ForgetImplication::Drop,
Self::Unrequested { unrequested_since, .. } => ForgetImplication::NotYetExpunge {
success_after: unrequested_since + min_turnaround_period(),
},
Self::Rerequested { unrequested_at, .. }
if now > unrequested_at + min_turnaround_period() =>
ForgetImplication::Unrequest,
Self::Rerequested { unrequested_at, .. } => ForgetImplication::NotYetUnrequest {
success_after: unrequested_at + min_turnaround_period(),
},
}
}
}
pub fn query(hash: &[u8; 32], len: usize) -> Option<LookupRequestStatus> {
let (r0, r1): (u64, u64) = unsafe { imports::query(hash.as_ptr(), len as u64) };
let n = r0 as u32;
let x = (r0 >> 32) as Slot;
let y = r1 as Slot;
Some(match n {
0 => LookupRequestStatus::Unprovided,
1 => LookupRequestStatus::Provided { since: x },
2 => LookupRequestStatus::Unrequested { provided_since: x, unrequested_since: y },
3 => LookupRequestStatus::Rerequested {
provided_since: x,
unrequested_at: y,
rerequested_since: (r1 >> 32) as Slot,
},
_ => return None,
})
}
pub fn solicit(hash: &[u8; 32], len: usize) -> Result<(), ApiError> {
unsafe { imports::solicit(hash.as_ptr(), len as u64) }.into_api_result()
}
pub fn forget(hash: &[u8; 32], len: usize) -> Result<(), ApiError> {
unsafe { imports::forget(hash.as_ptr(), len as u64) }.into_api_result()
}
pub fn yield_hash(hash: &[u8; 32]) {
unsafe { imports::yield_hash(hash.as_ptr()) }
.into_api_result()
.expect("Cannot fail except for memory access; we provide a good address; qed")
}
pub fn provide(service_id: ServiceId, preimage: &[u8]) -> Result<(), ApiError> {
unsafe { imports::provide(service_id as u64, preimage.as_ptr(), preimage.len() as _) }
.into_api_result()
}
pub fn get_storage(key: &[u8]) -> Option<Vec<u8>> {
raw_get_foreign_storage(u64::MAX, key)
}
pub fn get_storage_into(key: &[u8], value: &mut [u8]) -> Option<usize> {
raw_get_foreign_storage_into(u64::MAX, key, value)
}
pub fn get_foreign_storage(id: ServiceId, key: &[u8]) -> Option<Vec<u8>> {
raw_get_foreign_storage(id as u64, key)
}
pub fn get_foreign_storage_into(id: ServiceId, key: &[u8], value: &mut [u8]) -> Option<usize> {
raw_get_foreign_storage_into(id as u64, key, value)
}
pub fn get<R: Decode>(key: impl Encode) -> Option<R> {
Decode::decode(&mut &key.using_encoded(get_storage)?[..]).ok()
}
pub fn get_foreign<R: Decode>(id: ServiceId, key: impl Encode) -> Option<R> {
Decode::decode(&mut &key.using_encoded(|k| get_foreign_storage(id, k))?[..]).ok()
}
pub fn set_storage(key: &[u8], data: &[u8]) -> Result<Option<usize>, ApiError> {
unsafe { imports::write(key.as_ptr(), key.len() as u64, data.as_ptr(), data.len() as u64) }
.into_api_result()
}
pub fn remove_storage(key: &[u8]) -> Option<usize> {
unsafe { imports::write(key.as_ptr(), key.len() as u64, ptr::null(), 0) }
.into_api_result()
.expect("Cannot fail except for memory access; we provide a good address; qed")
}
pub fn set(key: impl Encode, value: impl Encode) -> Result<(), ApiError> {
value.using_encoded(|v| key.using_encoded(|k| set_storage(k, v).map(|_| ())))
}
pub fn remove(key: impl Encode) {
let _ = key.using_encoded(remove_storage);
}
pub fn my_info() -> ServiceInfo {
raw_service_info(u64::MAX).expect("Current service must exist; qed")
}
pub fn service_info(id: ServiceId) -> Option<ServiceInfo> {
raw_service_info(id as _)
}
#[doc(hidden)]
pub fn raw_service_info_field<T: Decode, const N: usize>(service: u64, offset: u64) -> Option<T> {
let mut buffer = [0u8; N];
let maybe_ok: Option<()> =
unsafe { imports::info(service as _, buffer.as_mut_ptr(), offset, N as u64) }
.into_api_result()
.expect("Cannot fail except for memory access; we provide a good address; qed");
maybe_ok?;
T::decode(&mut &buffer[..]).ok()
}
#[doc(hidden)]
#[rustfmt::skip]
#[macro_export]
macro_rules! service_info_field_type {
(code_hash) => {::jam_types::CodeHash};
(balance) => {::jam_types::Balance};
(threshold) => {::jam_types::Balance};
(min_item_gas) => {::jam_types::UnsignedGas};
(min_memo_gas) => {::jam_types::UnsignedGas};
(bytes) => {u64};
(items) => {u32};
(deposit_offset) => {::jam_types::Balance};
(creation_slot) => {::jam_types::Slot};
(last_accumulation_slot) => {::jam_types::Slot};
(parent_service) => {::jam_types::ServiceId};
}
#[doc(hidden)]
#[rustfmt::skip]
#[macro_export]
macro_rules! service_info_field_offset {
(code_hash) => {::jam_types::ServiceInfo::CODE_HASH_OFFSET};
(balance) => {::jam_types::ServiceInfo::BALANCE_OFFSET};
(threshold) => {::jam_types::ServiceInfo::THRESHOLD_OFFSET};
(min_item_gas) => {::jam_types::ServiceInfo::MIN_ITEM_GAS_OFFSET};
(min_memo_gas) => {::jam_types::ServiceInfo::MIN_MEMO_GAS_OFFSET};
(bytes) => {::jam_types::ServiceInfo::BYTES_OFFSET};
(items) => {::jam_types::ServiceInfo::ITEMS_OFFSET};
(deposit_offset) => {::jam_types::ServiceInfo::DEPOSIT_OFFSET_OFFSET};
(creation_slot) => {::jam_types::ServiceInfo::CREATION_SLOT_OFFSET};
(last_accumulation_slot) => {::jam_types::ServiceInfo::LAST_ACCUMULATION_SLOT_OFFSET};
(parent_service) => {::jam_types::ServiceInfo::PARENT_SERVICE_OFFSET};
}
#[macro_export]
macro_rules! service_info_field {
($service: expr, $field: ident) => {{
type T = $crate::service_info_field_type!($field);
const OFFSET: usize = $crate::service_info_field_offset!($field);
const LEN: usize = ::core::mem::size_of::<T>();
$crate::internal::raw_service_info_field::<T, LEN>($service, OFFSET as u64)
}};
}
#[macro_export]
macro_rules! my_info_field {
($field: ident) => {
$crate::service_info_field!(u64::MAX, $field).expect("Current service must exist; qed")
};
}
pub fn create_service(
code_hash: &CodeHash,
code_len: usize,
min_item_gas: UnsignedGas,
min_memo_gas: UnsignedGas,
) -> Result<ServiceId, ApiError> {
create_service_ext(code_hash, code_len, min_item_gas, min_memo_gas, None, None)
}
pub fn create_service_ext(
code_hash: &CodeHash,
code_len: usize,
min_item_gas: UnsignedGas,
min_memo_gas: UnsignedGas,
deposit_offset: Option<Balance>,
new_service_id: Option<ServiceId>,
) -> Result<ServiceId, ApiError> {
unsafe {
imports::new(
code_hash.as_ptr(),
code_len as u64,
min_item_gas,
min_memo_gas,
deposit_offset.unwrap_or_default(),
new_service_id.unwrap_or(ServiceId::MAX) as u64,
)
.into_api_result()
}
}
pub fn upgrade(code_hash: &CodeHash, min_item_gas: UnsignedGas, min_memo_gas: UnsignedGas) {
unsafe { imports::upgrade(code_hash.as_ptr(), min_item_gas, min_memo_gas) }
.into_api_result()
.expect("Failure only in case of bad memory; it is good; qed")
}
pub fn zombify(ejector: ServiceId) {
(ejector, [0; 28]).using_encoded(|data| {
unsafe { imports::upgrade(data.as_ptr(), 0, 0) }
.into_api_result()
.expect("Failure only in case of bad memory; it is good; qed")
})
}
pub fn transfer(
destination: ServiceId,
amount: Balance,
gas_limit: UnsignedGas,
memo: &Memo,
) -> Result<(), ApiError> {
unsafe {
imports::transfer(destination as _, amount, gas_limit, memo.as_ref().as_ptr())
.into_api_result()
}
}
pub fn eject(target: ServiceId, code_hash: &CodeHash) -> Result<(), ApiError> {
unsafe { imports::eject(target as _, code_hash.as_ref().as_ptr()) }.into_api_result()
}
pub fn bless<'a>(
manager: ServiceId,
assigner: ServiceId,
designator: ServiceId,
registrar: ServiceId,
always_acc: impl IntoIterator<Item = &'a (ServiceId, UnsignedGas)>,
) {
let mut aa_count = 0;
let aa_data: Vec<u8> = always_acc
.into_iter()
.flat_map(|x| {
aa_count += 1;
x.encode()
})
.collect();
let assigners_data = FixedVec::<ServiceId, CoreCount>::new(assigner).encode();
unsafe {
imports::bless(
manager as _,
assigners_data.as_ptr() as _,
designator as _,
registrar as _,
aa_data.as_ptr(),
aa_count,
)
}
.into_api_result()
.expect("Failure only in case of bad memory or bad service ID; both are good; qed")
}
pub fn assign(
core: CoreIndex,
auth_queue: &AuthQueue,
assigner: ServiceId,
) -> Result<(), ApiError> {
auth_queue
.using_encoded(|d| unsafe { imports::assign(core as _, d.as_ptr(), assigner as _) })
.into_api_result()
}
pub fn designate(keys: &OpaqueValKeysets) -> Result<(), ApiError> {
keys.using_encoded(|d| unsafe { imports::designate(d.as_ptr()) })
.into_api_result()
}
pub fn checkpoint() -> UnsignedGas {
unsafe { imports::checkpoint() }
}
fn raw_foreign_lookup(service_id: u64, hash: &[u8; 32]) -> Option<Vec<u8>> {
let maybe_len: Option<u64> =
unsafe { imports::lookup(service_id, hash.as_ptr(), ptr::null_mut(), 0, 0) }
.into_api_result()
.expect("Cannot fail except for memory access; we provide a good address; qed");
let len = maybe_len?;
let mut incoming = vec![0; len as usize];
unsafe {
imports::lookup(service_id, hash.as_ptr(), incoming.as_mut_ptr(), 0, len);
}
Some(incoming)
}
fn raw_foreign_lookup_into(service_id: u64, hash: &[u8; 32], output: &mut [u8]) -> Option<usize> {
let maybe_len: Option<u64> = unsafe {
imports::lookup(service_id, hash.as_ptr(), output.as_mut_ptr(), 0, output.len() as u64)
}
.into_api_result()
.expect("Cannot fail except for memory access; we provide a good address; qed");
Some(maybe_len? as usize)
}
fn raw_foreign_historical_lookup(service_id: u64, hash: &[u8; 32]) -> Option<Vec<u8>> {
let maybe_len: Option<u64> =
unsafe { imports::historical_lookup(service_id, hash.as_ptr(), ptr::null_mut(), 0, 0) }
.into_api_result()
.expect("Cannot fail except for memory access; we provide a good address; qed");
let len = maybe_len?;
let mut incoming = vec![0; len as usize];
unsafe {
imports::historical_lookup(service_id, hash.as_ptr(), incoming.as_mut_ptr(), 0, len);
}
Some(incoming)
}
fn raw_foreign_historical_lookup_into(
service_id: u64,
hash: &[u8; 32],
output: &mut [u8],
) -> Option<usize> {
let maybe_len: Option<u64> = unsafe {
imports::historical_lookup(
service_id,
hash.as_ptr(),
output.as_mut_ptr(),
0,
output.len() as u64,
)
}
.into_api_result()
.expect("Cannot fail except for memory access; we provide a good address; qed");
Some(maybe_len? as usize)
}
fn raw_service_info(service: u64) -> Option<ServiceInfo> {
let mut buffer = [0u8; ServiceInfo::ENCODED_LEN];
let maybe_ok: Option<()> =
unsafe { imports::info(service as _, buffer.as_mut_ptr(), 0, buffer.len() as u64) }
.into_api_result()
.expect("Cannot fail except for memory access; we provide a good address; qed");
maybe_ok?;
ServiceInfo::decode(&mut &buffer[..]).ok()
}
fn raw_get_foreign_storage(id: u64, key: &[u8]) -> Option<Vec<u8>> {
let maybe_len: Option<u64> =
unsafe { imports::read(id as _, key.as_ptr(), key.len() as u64, ptr::null_mut(), 0, 0) }
.into_api_result()
.expect("Cannot fail except for memory access; we provide a good address; qed");
let len = maybe_len?;
if len == 0 {
Some(vec![])
} else {
let mut incoming = vec![0; len as usize];
unsafe {
imports::read(id as _, key.as_ptr(), key.len() as u64, incoming.as_mut_ptr(), 0, len);
}
Some(incoming)
}
}
fn raw_get_foreign_storage_into(id: u64, key: &[u8], value: &mut [u8]) -> Option<usize> {
let r: ApiResult<Option<u64>> = unsafe {
imports::read(
id as _,
key.as_ptr(),
key.len() as _,
value.as_mut_ptr(),
0,
value.len() as _,
)
}
.into_api_result();
Some(r.expect("Only fail is memory access; address is good; qed")? as usize)
}