inkpad_runtime/
runtime.rs

1//! Inkpad Runtime
2use crate::{method::InkMethod, Error, Result};
3use inkpad_executor::{Executor, Memory};
4use inkpad_sandbox::{RuntimeInterfaces, Sandbox, Transaction};
5use inkpad_std::{Rc, String, ToString, Vec};
6use inkpad_support::{
7    convert, traits,
8    types::{self, Metadata},
9};
10use core::cell::RefCell;
11
12/// Inkpad Runtime
13pub struct Runtime {
14    pub sandbox: Sandbox,
15    pub metadata: Metadata,
16    pub cache: Rc<RefCell<dyn traits::Frame<Memory>>>,
17}
18
19impl Runtime {
20    /// Create runtime from contract
21    pub fn contract(contract: &[u8], ri: Option<impl RuntimeInterfaces>) -> Result<Runtime> {
22        let meta = serde_json::from_str::<Metadata>(&String::from_utf8_lossy(contract))
23            .map_err(|_| Error::DecodeContractFailed)?;
24
25        Self::new(meta, types::Cache::default(), ri)
26    }
27
28    /// Create runtime from contract
29    pub fn from_contract(
30        contract: &[u8],
31        cache: impl traits::Frame<Memory> + 'static,
32        ri: Option<impl RuntimeInterfaces>,
33    ) -> Result<Runtime> {
34        let meta = serde_json::from_slice::<Metadata>(contract)
35            .map_err(|_| Error::DecodeContractFailed)?;
36
37        Self::new(meta, cache, ri)
38    }
39
40    /// Create runtime from metadata and storage
41    pub fn from_metadata(
42        meta: Metadata,
43        cache: impl traits::Frame<Memory> + 'static,
44        ri: Option<impl RuntimeInterfaces>,
45    ) -> Result<Runtime> {
46        Self::new(meta, cache, ri)
47    }
48
49    /// Load contract to cache
50    pub fn load(&mut self, b: &[u8]) -> Result<[u8; 32]> {
51        self.load_metadata(
52            &serde_json::from_slice::<Metadata>(b).map_err(|_| Error::DecodeContractFailed)?,
53        )
54    }
55
56    /// Load metadata to cache
57    pub fn load_metadata(&mut self, meta: &Metadata) -> Result<[u8; 32]> {
58        Ok(self.sandbox.load_metadata(meta)?)
59    }
60
61    /// New runtime
62    pub fn new(
63        metadata: Metadata,
64        cache: impl traits::Frame<Memory> + 'static,
65        ri: Option<impl RuntimeInterfaces>,
66    ) -> Result<Runtime> {
67        // generate seal calls
68        let seal_calls = inkpad_seal::pallet_contracts(ri);
69
70        // wrap cache
71        let cache = Rc::new(RefCell::new(cache));
72
73        // Create Sandbox and Builder
74        let mut sandbox = Sandbox::new(cache.clone(), seal_calls.clone());
75        let code_hash = sandbox.load_metadata(&metadata)?;
76        sandbox.prepare(code_hash)?;
77
78        // Construct runtime
79        Ok(Runtime {
80            sandbox,
81            metadata,
82            cache,
83        })
84    }
85
86    /// Deploy contract
87    pub fn deploy(
88        &mut self,
89        method: &str,
90        args: Vec<Vec<u8>>,
91        tx: Option<Transaction>,
92    ) -> Result<Option<Vec<u8>>> {
93        self.invoke(InkMethod::Deploy, method, args, tx)
94    }
95
96    /// Call contract
97    pub fn call(
98        &mut self,
99        method: &str,
100        args: Vec<Vec<u8>>,
101        tx: Option<Transaction>,
102    ) -> Result<Option<Vec<u8>>> {
103        self.invoke(InkMethod::Call, method, args, tx)
104    }
105
106    // Invoke (ink) method
107    pub fn invoke(
108        &mut self,
109        method: InkMethod,
110        inner_method: &str,
111        args: Vec<Vec<u8>>,
112        tx: Option<Transaction>,
113    ) -> Result<Option<Vec<u8>>> {
114        // construct transaction
115        if let Some(tx) = tx {
116            self.sandbox.tx = tx;
117        }
118
119        // set input
120        self.sandbox.input = Some(method.parse(&self.metadata, inner_method, args)?);
121
122        // execute
123        let hash = self
124            .cache
125            .borrow()
126            .active()
127            .ok_or(inkpad_executor::Error::CodeNotFound)?;
128        Executor::new(
129            convert::to_storage_key(&hash[..]).ok_or(inkpad_executor::Error::CodeNotFound)?,
130            &mut self.sandbox,
131        )?
132        .invoke(&method.to_string(), &[], &mut self.sandbox)
133        .map_err(|error| Error::CallContractFailed { error })?;
134
135        // flush data
136        self.cache
137            .borrow_mut()
138            .flush()
139            .ok_or(Error::FlushDataFailed)?;
140        Ok(self.sandbox.ret.take())
141    }
142}