use crate::{method::InkMethod, Error, Result};
use inkpad_executor::{Executor, Memory};
use inkpad_sandbox::{RuntimeInterfaces, Sandbox, Transaction};
use inkpad_std::{Rc, String, ToString, Vec};
use inkpad_support::{
convert, traits,
types::{self, Metadata},
};
use core::cell::RefCell;
pub struct Runtime {
pub sandbox: Sandbox,
pub metadata: Metadata,
pub cache: Rc<RefCell<dyn traits::Frame<Memory>>>,
}
impl Runtime {
pub fn contract(contract: &[u8], ri: Option<impl RuntimeInterfaces>) -> Result<Runtime> {
let meta = serde_json::from_str::<Metadata>(&String::from_utf8_lossy(contract))
.map_err(|_| Error::DecodeContractFailed)?;
Self::new(meta, types::Cache::default(), ri)
}
pub fn from_contract(
contract: &[u8],
cache: impl traits::Frame<Memory> + 'static,
ri: Option<impl RuntimeInterfaces>,
) -> Result<Runtime> {
let meta = serde_json::from_slice::<Metadata>(contract)
.map_err(|_| Error::DecodeContractFailed)?;
Self::new(meta, cache, ri)
}
pub fn from_metadata(
meta: Metadata,
cache: impl traits::Frame<Memory> + 'static,
ri: Option<impl RuntimeInterfaces>,
) -> Result<Runtime> {
Self::new(meta, cache, ri)
}
pub fn load(&mut self, b: &[u8]) -> Result<[u8; 32]> {
self.load_metadata(
&serde_json::from_slice::<Metadata>(b).map_err(|_| Error::DecodeContractFailed)?,
)
}
pub fn load_metadata(&mut self, meta: &Metadata) -> Result<[u8; 32]> {
Ok(self.sandbox.load_metadata(meta)?)
}
pub fn new(
metadata: Metadata,
cache: impl traits::Frame<Memory> + 'static,
ri: Option<impl RuntimeInterfaces>,
) -> Result<Runtime> {
let seal_calls = inkpad_seal::pallet_contracts(ri);
let cache = Rc::new(RefCell::new(cache));
let mut sandbox = Sandbox::new(cache.clone(), seal_calls.clone());
let code_hash = sandbox.load_metadata(&metadata)?;
sandbox.prepare(code_hash)?;
Ok(Runtime {
sandbox,
metadata,
cache,
})
}
pub fn deploy(
&mut self,
method: &str,
args: Vec<Vec<u8>>,
tx: Option<Transaction>,
) -> Result<Option<Vec<u8>>> {
self.invoke(InkMethod::Deploy, method, args, tx)
}
pub fn call(
&mut self,
method: &str,
args: Vec<Vec<u8>>,
tx: Option<Transaction>,
) -> Result<Option<Vec<u8>>> {
self.invoke(InkMethod::Call, method, args, tx)
}
pub fn invoke(
&mut self,
method: InkMethod,
inner_method: &str,
args: Vec<Vec<u8>>,
tx: Option<Transaction>,
) -> Result<Option<Vec<u8>>> {
if let Some(tx) = tx {
self.sandbox.tx = tx;
}
self.sandbox.input = Some(method.parse(&self.metadata, inner_method, args)?);
let hash = self
.cache
.borrow()
.active()
.ok_or(inkpad_executor::Error::CodeNotFound)?;
Executor::new(
convert::to_storage_key(&hash[..]).ok_or(inkpad_executor::Error::CodeNotFound)?,
&mut self.sandbox,
)?
.invoke(&method.to_string(), &[], &mut self.sandbox)
.map_err(|error| Error::CallContractFailed { error })?;
self.cache
.borrow_mut()
.flush()
.ok_or(Error::FlushDataFailed)?;
Ok(self.sandbox.ret.take())
}
}