use crate::{
wasm::{Runtime, RuntimeCosts},
Error,
};
use alloc::vec::Vec;
use codec::{Decode, MaxEncodedLen};
use core::marker::PhantomData;
use frame_support::weights::Weight;
use sp_runtime::DispatchError;
pub use crate::{exec::Ext, gas::ChargedAmount, storage::meter::Diff, Config};
pub use frame_system::Config as SysConfig;
pub use pallet_contracts_uapi::ReturnFlags;
pub type Result<T> = core::result::Result<T, DispatchError>;
pub trait ChainExtension<C: Config> {
fn call<E: Ext<T = C>>(&mut self, env: Environment<E, InitState>) -> Result<RetVal>;
fn enabled() -> bool {
true
}
}
pub trait RegisteredChainExtension<C: Config>: ChainExtension<C> {
const ID: u16;
}
#[impl_trait_for_tuples::impl_for_tuples(10)]
#[tuple_types_custom_trait_bound(RegisteredChainExtension<C>)]
impl<C: Config> ChainExtension<C> for Tuple {
fn call<E: Ext<T = C>>(&mut self, mut env: Environment<E, InitState>) -> Result<RetVal> {
for_tuples!(
#(
if (Tuple::ID == env.ext_id()) && Tuple::enabled() {
return Tuple.call(env);
}
)*
);
Err(Error::<E::T>::NoChainExtension.into())
}
fn enabled() -> bool {
for_tuples!(
#(
if Tuple::enabled() {
return true;
}
)*
);
false
}
}
pub enum RetVal {
Converging(u32),
Diverging { flags: ReturnFlags, data: Vec<u8> },
}
pub struct Environment<'a, 'b, E: Ext, S: State> {
inner: Inner<'a, 'b, E>,
phantom: PhantomData<S>,
}
impl<'a, 'b, E: Ext, S: State> Environment<'a, 'b, E, S> {
pub fn func_id(&self) -> u16 {
(self.inner.id & 0x0000FFFF) as u16
}
pub fn ext_id(&self) -> u16 {
(self.inner.id >> 16) as u16
}
pub fn charge_weight(&mut self, amount: Weight) -> Result<ChargedAmount> {
self.inner.runtime.charge_gas(RuntimeCosts::ChainExtension(amount))
}
pub fn adjust_weight(&mut self, charged: ChargedAmount, actual_weight: Weight) {
self.inner
.runtime
.adjust_gas(charged, RuntimeCosts::ChainExtension(actual_weight))
}
pub fn ext(&mut self) -> &mut E {
self.inner.runtime.ext()
}
}
impl<'a, 'b, E: Ext> Environment<'a, 'b, E, InitState> {
pub(crate) fn new(
runtime: &'a mut Runtime<'b, E>,
memory: &'a mut [u8],
id: u32,
input_ptr: u32,
input_len: u32,
output_ptr: u32,
output_len_ptr: u32,
) -> Self {
Environment {
inner: Inner { runtime, memory, id, input_ptr, input_len, output_ptr, output_len_ptr },
phantom: PhantomData,
}
}
pub fn only_in(self) -> Environment<'a, 'b, E, OnlyInState> {
Environment { inner: self.inner, phantom: PhantomData }
}
pub fn prim_in_buf_out(self) -> Environment<'a, 'b, E, PrimInBufOutState> {
Environment { inner: self.inner, phantom: PhantomData }
}
pub fn buf_in_buf_out(self) -> Environment<'a, 'b, E, BufInBufOutState> {
Environment { inner: self.inner, phantom: PhantomData }
}
}
impl<'a, 'b, E: Ext, S: 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: 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: BufIn> Environment<'a, 'b, E, S> {
pub fn read(&self, max_len: u32) -> Result<Vec<u8>> {
self.inner.runtime.read_sandbox_memory(
self.inner.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.memory,
self.inner.input_ptr,
sliced,
)?;
*buffer = sliced;
Ok(())
}
pub fn read_as<T: Decode + MaxEncodedLen>(&mut self) -> Result<T> {
self.inner
.runtime
.read_sandbox_memory_as(self.inner.memory, self.inner.input_ptr)
}
pub fn read_as_unbounded<T: Decode>(&mut self, len: u32) -> Result<T> {
self.inner.runtime.read_sandbox_memory_as_unbounded(
self.inner.memory,
self.inner.input_ptr,
len,
)
}
pub fn in_len(&self) -> u32 {
self.inner.input_len
}
}
impl<'a, 'b, E: Ext, S: BufOut> Environment<'a, 'b, E, S> {
pub fn write(
&mut self,
buffer: &[u8],
allow_skip: bool,
weight_per_byte: Option<Weight>,
) -> Result<()> {
self.inner.runtime.write_sandbox_output(
self.inner.memory,
self.inner.output_ptr,
self.inner.output_len_ptr,
buffer,
allow_skip,
|len| {
weight_per_byte.map(|w| RuntimeCosts::ChainExtension(w.saturating_mul(len.into())))
},
)
}
}
struct Inner<'a, 'b, E: Ext> {
runtime: &'a mut Runtime<'b, E>,
memory: &'a mut [u8],
id: u32,
input_ptr: u32,
input_len: u32,
output_ptr: u32,
output_len_ptr: u32,
}
pub trait State: sealed::Sealed {}
pub trait PrimIn: State {}
pub trait PrimOut: State {}
pub trait BufIn: State {}
pub trait BufOut: State {}
pub enum InitState {}
pub enum OnlyInState {}
pub enum PrimInBufOutState {}
pub enum BufInBufOutState {}
mod sealed {
use super::*;
pub trait Sealed {}
impl Sealed for InitState {}
impl Sealed for OnlyInState {}
impl Sealed for PrimInBufOutState {}
impl Sealed for BufInBufOutState {}
impl State for InitState {}
impl State for OnlyInState {}
impl State for PrimInBufOutState {}
impl State for BufInBufOutState {}
impl PrimIn for OnlyInState {}
impl PrimOut for OnlyInState {}
impl PrimIn for PrimInBufOutState {}
impl BufOut for PrimInBufOutState {}
impl BufIn for BufInBufOutState {}
impl BufOut for BufInBufOutState {}
}