use crate::{
CompileStatus, DestroyError, ExecError, InstanceId, InstantiateError, MemoryError, ModuleError,
ModuleId, SyscallSymbol,
};
use core::mem;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use sp_runtime_interface::{
pass_by::{
ConvertAndReturnAs, PassAs, PassFatPointerAndRead, PassFatPointerAndReadOption,
PassFatPointerAndWrite, PassPointerAndWrite,
},
runtime_interface,
};
#[derive(Debug, Default)]
#[repr(C)]
pub struct ExecBuffer {
pub gas_left: i64,
pub syscall_symbol: SyscallSymbol,
pub a0: u64,
pub a1: u64,
pub a2: u64,
pub a3: u64,
pub a4: u64,
pub a5: u64,
}
impl AsRef<[u8]> for ExecBuffer {
fn as_ref(&self) -> &[u8] {
unsafe {
core::slice::from_raw_parts(self as *const Self as *const u8, mem::size_of::<Self>())
}
}
}
impl AsMut<[u8]> for ExecBuffer {
fn as_mut(&mut self) -> &mut [u8] {
unsafe {
core::slice::from_raw_parts_mut(self as *mut Self as *mut u8, mem::size_of::<Self>())
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum ExecStatus {
Finished = 0,
Syscall = 1,
}
macro_rules! impl_ri_error_encoding {
($($t:ty),+ $(,)?) => {$(
impl From<$t> for i64 {
fn from(error: $t) -> Self {
i32::from(error) as i64
}
}
impl TryFrom<i64> for $t {
type Error = ();
fn try_from(value: i64) -> Result<Self, Self::Error> {
let v = i32::try_from(value).map_err(|_| ())?;
Self::try_from(v).map_err(|_| ())
}
}
)+};
}
impl_ri_error_encoding!(ModuleError, InstantiateError, ExecError, DestroyError, MemoryError);
impl From<u32> for ModuleId {
fn from(id: u32) -> Self {
Self(id)
}
}
impl From<ModuleId> for u32 {
fn from(id: ModuleId) -> Self {
id.0
}
}
impl IntoI64 for ModuleId {
const MAX: i64 = u32::MAX as i64;
}
impl From<ModuleId> for i64 {
fn from(id: ModuleId) -> Self {
u32::from(id) as i64
}
}
impl TryFrom<i64> for ModuleId {
type Error = ();
fn try_from(value: i64) -> Result<Self, Self::Error> {
u32::try_from(value).map(ModuleId::from).map_err(|_| ())
}
}
pub struct CompiledModule {
pub id: ModuleId,
pub status: CompileStatus,
}
impl IntoI64 for CompiledModule {
const MAX: i64 = (1i64 << 40) - 1;
}
impl From<CompiledModule> for i64 {
fn from(m: CompiledModule) -> Self {
let status = u8::from(m.status) as i64;
(status << 32) | (u32::from(m.id) as i64)
}
}
impl TryFrom<i64> for CompiledModule {
type Error = ();
fn try_from(value: i64) -> Result<Self, Self::Error> {
let id = ModuleId::from(value as u32);
let status = CompileStatus::try_from((value >> 32) as u8).map_err(|_| ())?;
Ok(Self { id, status })
}
}
impl From<u32> for InstanceId {
fn from(id: u32) -> Self {
Self(id)
}
}
impl From<InstanceId> for u32 {
fn from(id: InstanceId) -> Self {
id.0
}
}
impl IntoI64 for InstanceId {
const MAX: i64 = u32::MAX as i64;
}
impl From<InstanceId> for i64 {
fn from(id: InstanceId) -> Self {
u32::from(id) as i64
}
}
impl TryFrom<i64> for InstanceId {
type Error = ();
fn try_from(value: i64) -> Result<Self, Self::Error> {
u32::try_from(value).map(InstanceId::from).map_err(|_| ())
}
}
pub enum RIIntResult<R, E> {
Ok(R),
Err(E),
}
impl<R, E, OR, OE> From<Result<OR, OE>> for RIIntResult<R, E>
where
R: From<OR>,
E: From<OE>,
{
fn from(result: Result<OR, OE>) -> Self {
match result {
Ok(value) => Self::Ok(value.into()),
Err(error) => Self::Err(error.into()),
}
}
}
impl<R, E, OR, OE> From<RIIntResult<R, E>> for Result<OR, OE>
where
OR: From<R>,
OE: From<E>,
{
fn from(result: RIIntResult<R, E>) -> Self {
match result {
RIIntResult::Ok(value) => Ok(value.into()),
RIIntResult::Err(error) => Err(error.into()),
}
}
}
trait IntoI64: Into<i64> {
const MAX: i64;
}
impl IntoI64 for u32 {
const MAX: i64 = u32::MAX as i64;
}
impl<R: Into<i64> + IntoI64, E: Into<i64> + strum::EnumCount> From<RIIntResult<R, E>> for i64 {
fn from(result: RIIntResult<R, E>) -> Self {
match result {
RIIntResult::Ok(value) => value.into(),
RIIntResult::Err(e) => {
let error_code: i64 = e.into();
assert!(
error_code < 0 && error_code >= -(E::COUNT as i64),
"Error variant index out of bounds"
);
error_code
},
}
}
}
impl<R: TryFrom<i64> + IntoI64, E: TryFrom<i64> + strum::EnumCount> TryFrom<i64>
for RIIntResult<R, E>
{
type Error = ();
fn try_from(value: i64) -> Result<Self, Self::Error> {
if value >= 0 && value <= R::MAX.into() {
Ok(RIIntResult::Ok(value.try_into().map_err(|_| ())?))
} else if value < 0 && value >= -(E::COUNT as i64) {
Ok(RIIntResult::Err(value.try_into().map_err(|_| ())?))
} else {
Err(())
}
}
}
pub struct VoidResult;
impl IntoI64 for VoidResult {
const MAX: i64 = 0;
}
impl From<()> for VoidResult {
fn from(_: ()) -> Self {
VoidResult
}
}
impl From<VoidResult> for () {
fn from(_: VoidResult) -> Self {
()
}
}
impl From<VoidResult> for i64 {
fn from(_: VoidResult) -> Self {
0
}
}
impl TryFrom<i64> for VoidResult {
type Error = ();
fn try_from(value: i64) -> Result<Self, Self::Error> {
if value == 0 {
Ok(VoidResult)
} else {
Err(())
}
}
}
#[runtime_interface]
pub trait Virtualization {
fn compile_from_bytes(
&mut self,
program: PassFatPointerAndRead<&[u8]>,
identifier: PassFatPointerAndReadOption<&[u8]>,
) -> ConvertAndReturnAs<
Result<CompiledModule, ModuleError>,
RIIntResult<CompiledModule, ModuleError>,
i64,
> {
use sp_externalities::ExternalitiesExt as _;
use std::sync::Once;
static WARN_ONCE: Once = Once::new();
WARN_ONCE.call_once(|| {
log::warn!(
target: crate::LOG_TARGET,
"Virtualization host functions are UNSTABLE and subject to breaking changes. \
They are NOT available on Polkadot and using them in production will cause breakage. \
Only use for testing and experimentation.",
);
});
if let Some(identifier) = identifier {
let cache_result = self
.extension::<crate::VirtManagerExt>()
.expect("VirtManagerExt not registered in externalities")
.lookup(identifier);
match cache_result {
Ok(id) => return Ok(CompiledModule { id, status: CompileStatus::Cached }),
Err(ModuleError::NotCached) => {},
Err(err) => return Err(err),
}
}
let id = self
.extension::<crate::VirtManagerExt>()
.expect("VirtManagerExt not registered in externalities")
.compile_from_bytes(program, identifier)?;
Ok(CompiledModule { id, status: CompileStatus::Compiled })
}
fn lookup(
&mut self,
identifier: PassFatPointerAndRead<&[u8]>,
) -> ConvertAndReturnAs<Result<ModuleId, ModuleError>, RIIntResult<ModuleId, ModuleError>, i64>
{
use sp_externalities::ExternalitiesExt as _;
self.extension::<crate::VirtManagerExt>()
.expect("VirtManagerExt not registered in externalities")
.lookup(identifier)
}
fn compile_from_storage_key(
&mut self,
storage_key: PassFatPointerAndRead<&[u8]>,
child_trie: PassFatPointerAndRead<&[u8]>,
) -> ConvertAndReturnAs<
Result<CompiledModule, ModuleError>,
RIIntResult<CompiledModule, ModuleError>,
i64,
> {
use sp_externalities::ExternalitiesExt as _;
let cache_result = self
.extension::<crate::VirtManagerExt>()
.expect("VirtManagerExt not registered in externalities")
.lookup(storage_key);
match cache_result {
Ok(id) => return Ok(CompiledModule { id, status: CompileStatus::Cached }),
Err(ModuleError::NotCached) => {},
Err(err) => return Err(err),
}
let code = if child_trie.is_empty() {
self.storage(storage_key)
} else {
let child_info = sp_storage::ChildInfo::new_default(child_trie);
self.child_storage(&child_info, storage_key)
};
let code = match code {
Some(code) => code,
None => return Err(ModuleError::NotFound),
};
let id = self
.extension::<crate::VirtManagerExt>()
.expect("VirtManagerExt not registered in externalities")
.compile_from_bytes(&code, Some(storage_key))?;
Ok(CompiledModule { id, status: CompileStatus::Compiled })
}
fn instantiate(
&mut self,
module_id: PassAs<ModuleId, u32>,
) -> ConvertAndReturnAs<
Result<InstanceId, InstantiateError>,
RIIntResult<InstanceId, InstantiateError>,
i64,
> {
use sp_externalities::ExternalitiesExt as _;
self.extension::<crate::VirtManagerExt>()
.expect("VirtManagerExt not registered in externalities")
.instantiate(module_id)
}
fn prepare(
&mut self,
instance_id: PassAs<InstanceId, u32>,
function: PassFatPointerAndRead<&[u8]>,
) -> ConvertAndReturnAs<Result<(), ExecError>, RIIntResult<VoidResult, ExecError>, i64> {
use sp_externalities::ExternalitiesExt as _;
self.extension::<crate::VirtManagerExt>()
.expect("VirtManagerExt not registered in externalities")
.prepare(instance_id, function)
}
fn run(
&mut self,
instance_id: PassAs<InstanceId, u32>,
gas_left: i64,
a0: u64,
exec_buffer: PassPointerAndWrite<&mut ExecBuffer, { mem::size_of::<ExecBuffer>() }>,
) -> ConvertAndReturnAs<Result<u32, ExecError>, RIIntResult<u32, ExecError>, i64> {
use sp_externalities::ExternalitiesExt as _;
self.extension::<crate::VirtManagerExt>()
.expect("VirtManagerExt not registered in externalities")
.run(instance_id, gas_left, a0)
.map(|(status, buf)| {
*exec_buffer = buf;
u32::from(status)
})
}
fn destroy(
&mut self,
instance_id: PassAs<InstanceId, u32>,
) -> ConvertAndReturnAs<Result<(), DestroyError>, RIIntResult<VoidResult, DestroyError>, i64> {
use sp_externalities::ExternalitiesExt as _;
self.extension::<crate::VirtManagerExt>()
.expect("VirtManagerExt not registered in externalities")
.destroy(instance_id)
}
fn read_memory(
&mut self,
instance_id: PassAs<InstanceId, u32>,
offset: u32,
dest: PassFatPointerAndWrite<&mut [u8]>,
) -> ConvertAndReturnAs<Result<(), MemoryError>, RIIntResult<VoidResult, MemoryError>, i64> {
use sp_externalities::ExternalitiesExt as _;
self.extension::<crate::VirtManagerExt>()
.expect("VirtManagerExt not registered in externalities")
.read_memory(instance_id, offset, dest)
}
fn write_memory(
&mut self,
instance_id: PassAs<InstanceId, u32>,
offset: u32,
src: PassFatPointerAndRead<&[u8]>,
) -> ConvertAndReturnAs<Result<(), MemoryError>, RIIntResult<VoidResult, MemoryError>, i64> {
use sp_externalities::ExternalitiesExt as _;
self.extension::<crate::VirtManagerExt>()
.expect("VirtManagerExt not registered in externalities")
.write_memory(instance_id, offset, src)
}
}
#[cfg(not(substrate_runtime))]
pub trait VirtManagerBackend: Send + 'static {
fn compile_from_bytes(
&mut self,
program: &[u8],
identifier: Option<&[u8]>,
) -> Result<ModuleId, ModuleError>;
fn lookup(&mut self, identifier: &[u8]) -> Result<ModuleId, ModuleError>;
fn instantiate(&mut self, module_id: ModuleId) -> Result<InstanceId, InstantiateError>;
fn prepare(&mut self, instance_id: InstanceId, function: &[u8]) -> Result<(), ExecError>;
fn run(
&mut self,
instance_id: InstanceId,
gas_left: i64,
a0: u64,
) -> Result<(ExecStatus, ExecBuffer), ExecError>;
fn destroy(&mut self, instance_id: InstanceId) -> Result<(), DestroyError>;
fn read_memory(
&mut self,
instance_id: InstanceId,
offset: u32,
dest: &mut [u8],
) -> Result<(), MemoryError>;
fn write_memory(
&mut self,
instance_id: InstanceId,
offset: u32,
src: &[u8],
) -> Result<(), MemoryError>;
}
#[cfg(not(substrate_runtime))]
sp_externalities::decl_extension! {
pub struct VirtManagerExt(Box<dyn VirtManagerBackend>);
}
#[cfg(not(substrate_runtime))]
impl VirtManagerExt {
pub fn new<B: VirtManagerBackend>(backend: B) -> Self {
Self(Box::new(backend))
}
}