calimero_runtime/
lib.rs

1use calimero_node_primitives::client::NodeClient;
2use calimero_primitives::context::ContextId;
3use calimero_primitives::identity::PublicKey;
4use wasmer::{CompileError, DeserializeError, Instance, NativeEngineExt, SerializeError, Store};
5
6mod constants;
7mod constraint;
8pub mod errors;
9pub mod logic;
10mod memory;
11pub mod store;
12
13pub use constraint::Constraint;
14use errors::{FunctionCallError, VMRuntimeError};
15use logic::{Outcome, VMContext, VMLimits, VMLogic, VMLogicError};
16use memory::WasmerTunables;
17use store::Storage;
18
19pub type RuntimeResult<T, E = VMRuntimeError> = Result<T, E>;
20
21#[derive(Clone, Debug)]
22pub struct Engine {
23    limits: VMLimits,
24    engine: wasmer::Engine,
25}
26
27impl Default for Engine {
28    fn default() -> Self {
29        let limits = VMLimits::default();
30
31        let engine = wasmer::Engine::default();
32
33        Self::new(engine, limits)
34    }
35}
36
37impl Engine {
38    pub fn new(mut engine: wasmer::Engine, limits: VMLimits) -> Self {
39        engine.set_tunables(WasmerTunables::new(&limits));
40
41        Self { limits, engine }
42    }
43
44    pub fn headless() -> Self {
45        let limits = VMLimits::default();
46
47        let engine = wasmer::Engine::headless();
48
49        Self::new(engine, limits)
50    }
51
52    pub fn compile(&self, bytes: &[u8]) -> Result<Module, CompileError> {
53        // todo! apply a prepare step
54        // todo! - parse the wasm blob, validate and apply transformations
55        // todo!   - validations:
56        // todo!     - there is no memory import
57        // todo!     - there is no _start function
58        // todo!   - transformations:
59        // todo!     - remove memory export
60        // todo!     - remove memory section
61        // todo! cache the compiled module in storage for later
62
63        let module = wasmer::Module::new(&self.engine, bytes)?;
64
65        Ok(Module {
66            limits: self.limits.clone(),
67            engine: self.engine.clone(),
68            module,
69        })
70    }
71
72    pub unsafe fn from_precompiled(&self, bytes: &[u8]) -> Result<Module, DeserializeError> {
73        let module = wasmer::Module::deserialize(&self.engine, bytes)?;
74
75        Ok(Module {
76            limits: self.limits.clone(),
77            engine: self.engine.clone(),
78            module,
79        })
80    }
81}
82
83#[derive(Debug)]
84pub struct Module {
85    limits: VMLimits,
86    engine: wasmer::Engine,
87    module: wasmer::Module,
88}
89
90impl Module {
91    pub fn to_bytes(&self) -> Result<Box<[u8]>, SerializeError> {
92        let bytes = self.module.serialize()?;
93
94        Ok(Vec::into_boxed_slice(bytes.into()))
95    }
96
97    pub fn run(
98        &self,
99        context: ContextId,
100        executor: PublicKey,
101        method: &str,
102        input: &[u8],
103        storage: &mut dyn Storage,
104        node_client: Option<NodeClient>,
105    ) -> RuntimeResult<Outcome> {
106        let context = VMContext::new(input.into(), *context, *executor);
107
108        let mut logic = VMLogic::new(storage, context, &self.limits, node_client);
109
110        let mut store = Store::new(self.engine.clone());
111
112        let imports = logic.imports(&mut store);
113
114        let instance = match Instance::new(&mut store, &self.module, &imports) {
115            Ok(instance) => instance,
116            Err(err) => return Ok(logic.finish(Some(err.into()))),
117        };
118
119        let _ = match instance.exports.get_memory("memory") {
120            Ok(memory) => logic.with_memory(memory.clone()),
121            // todo! test memory returns MethodNotFound
122            Err(err) => return Ok(logic.finish(Some(err.into()))),
123        };
124
125        let function = match instance.exports.get_function(method) {
126            Ok(function) => function,
127            Err(err) => return Ok(logic.finish(Some(err.into()))),
128        };
129
130        let signature = function.ty(&store);
131
132        if !(signature.params().is_empty() && signature.results().is_empty()) {
133            return Ok(logic.finish(Some(FunctionCallError::MethodResolutionError(
134                errors::MethodResolutionError::InvalidSignature {
135                    name: method.to_owned(),
136                },
137            ))));
138        }
139
140        if let Err(err) = function.call(&mut store, &[]) {
141            return match err.downcast::<VMLogicError>() {
142                Ok(err) => Ok(logic.finish(Some(err.try_into()?))),
143                Err(err) => Ok(logic.finish(Some(err.into()))),
144            };
145        }
146
147        Ok(logic.finish(None))
148    }
149}
150
151#[cfg(test)]
152mod integration_tests_package_usage {
153    use {eyre as _, owo_colors as _, rand as _};
154}