use crate::{
Error,
wasm::{Runtime, RuntimeToken},
};
use codec::Decode;
use fabric_support::weights::Weight;
use tp_runtime::DispatchError;
use tetcore_std::{
marker::PhantomData,
vec::Vec,
};
pub use fabric_system::Config as SysConfig;
pub use noble_contracts_primitives::ReturnFlags;
pub use tet_core::crypto::UncheckedFrom;
pub use crate::exec::Ext;
pub use state::Init as InitState;
pub type Result<T> = tetcore_std::result::Result<T, DispatchError>;
pub trait ChainExtension {
fn call<E: Ext>(func_id: u32, env: Environment<E, InitState>) -> Result<RetVal>
where
<E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>;
fn enabled() -> bool {
true
}
}
impl ChainExtension for () {
fn call<E: Ext>(_func_id: u32, mut _env: Environment<E, InitState>) -> Result<RetVal>
where
<E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>,
{
Err(Error::<E::T>::NoChainExtension.into())
}
fn enabled() -> bool {
false
}
}
pub enum RetVal {
Converging(u32),
Diverging{flags: ReturnFlags, data: Vec<u8>},
}
pub struct Environment<'a, 'b, E: Ext, S: state::State> {
inner: Inner<'a, 'b, E>,
phantom: PhantomData<S>,
}
impl<'a, 'b, E: Ext, S: state::State> Environment<'a, 'b, E, S>
where
<E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>,
{
pub fn charge_weight(&mut self, amount: Weight) -> Result<()> {
self.inner.runtime.charge_gas(RuntimeToken::ChainExtension(amount)).map(|_| ())
}
pub fn ext(&mut self) -> &mut E {
self.inner.runtime.ext()
}
}
impl<'a, 'b, E: Ext> Environment<'a, 'b, E, state::Init> {
pub(crate) fn new(
runtime: &'a mut Runtime::<'b, E>,
input_ptr: u32,
input_len: u32,
output_ptr: u32,
output_len_ptr: u32,
) -> Self {
Environment {
inner: Inner {
runtime,
input_ptr,
input_len,
output_ptr,
output_len_ptr,
},
phantom: PhantomData,
}
}
pub fn only_in(self) -> Environment<'a, 'b, E, state::OnlyIn> {
Environment {
inner: self.inner,
phantom: PhantomData,
}
}
pub fn prim_in_buf_out(self) -> Environment<'a, 'b, E, state::PrimInBufOut> {
Environment {
inner: self.inner,
phantom: PhantomData,
}
}
pub fn buf_in_buf_out(self) -> Environment<'a, 'b, E, state::BufInBufOut> {
Environment {
inner: self.inner,
phantom: PhantomData,
}
}
}
impl<'a, 'b, E: Ext, S: state::PrimIn> Environment<'a, 'b, E, S> {
pub fn val0(&self) -> u32 {
self.inner.input_ptr
}
pub fn val1(&self) -> u32 {
self.inner.input_len
}
}
impl<'a, 'b, E: Ext, S: state::PrimOut> Environment<'a, 'b, E, S> {
pub fn val2(&self) -> u32 {
self.inner.output_ptr
}
pub fn val3(&self) -> u32 {
self.inner.output_len_ptr
}
}
impl<'a, 'b, E: Ext, S: state::BufIn> Environment<'a, 'b, E, S>
where
<E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>,
{
pub fn read(&self, max_len: u32) -> Result<Vec<u8>> {
self.inner.runtime.read_sandbox_memory(
self.inner.input_ptr,
self.inner.input_len.min(max_len),
)
}
pub fn read_into(&self, buffer: &mut &mut [u8]) -> Result<()> {
let len = buffer.len();
let sliced = {
let buffer = core::mem::take(buffer);
&mut buffer[..len.min(self.inner.input_len as usize)]
};
self.inner.runtime.read_sandbox_memory_into_buf(
self.inner.input_ptr,
sliced,
)?;
*buffer = sliced;
Ok(())
}
pub fn read_as<T: Decode>(&mut self) -> Result<T> {
self.inner.runtime.read_sandbox_memory_as(
self.inner.input_ptr,
self.inner.input_len,
)
}
pub fn in_len(&self) -> u32 {
self.inner.input_len
}
}
impl<'a, 'b, E: Ext, S: state::BufOut> Environment<'a, 'b, E, S>
where
<E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>,
{
pub fn write(
&mut self,
buffer: &[u8],
allow_skip: bool,
weight_per_byte: Option<Weight>,
) -> Result<()> {
self.inner.runtime.write_sandbox_output(
self.inner.output_ptr,
self.inner.output_len_ptr,
buffer,
allow_skip,
|len| {
weight_per_byte.map(|w| RuntimeToken::ChainExtension(w.saturating_mul(len.into())))
},
)
}
}
struct Inner<'a, 'b, E: Ext> {
runtime: &'a mut Runtime::<'b, E>,
input_ptr: u32,
input_len: u32,
output_ptr: u32,
output_len_ptr: u32,
}
mod state {
pub trait State {}
pub trait PrimIn: State {}
pub trait PrimOut: State {}
pub trait BufIn: State {}
pub trait BufOut: State {}
pub enum Init {}
pub enum OnlyIn {}
pub enum PrimInBufOut {}
pub enum BufInBufOut {}
impl State for Init {}
impl State for OnlyIn {}
impl State for PrimInBufOut {}
impl State for BufInBufOut {}
impl PrimIn for OnlyIn {}
impl PrimOut for OnlyIn {}
impl PrimIn for PrimInBufOut {}
impl BufOut for PrimInBufOut {}
impl BufIn for BufInBufOut {}
impl BufOut for BufInBufOut {}
}