1use std::any::Any;
8use std::borrow::Cow;
9use std::collections::BTreeMap;
10use std::fmt::{self, Debug, Formatter};
11use std::path::Path;
12use std::sync::Arc;
13use std::thread;
14
15use dusk_wasmtime::{
16 Config, Engine, ModuleVersionStrategy, OperatorCost, OptLevel, Strategy,
17 WasmBacktraceDetails,
18};
19use tempfile::tempdir;
20
21use crate::config::BYTE_STORE_COST;
22use crate::session::{Session, SessionData};
23use crate::store::ContractStore;
24use crate::Error::{self, PersistenceError};
25
26fn config() -> Config {
27 let mut config = Config::new();
28
29 config.wasm_backtrace(false);
31 config.wasm_backtrace_details(WasmBacktraceDetails::Disable);
32
33 config.native_unwind_info(false);
34
35 config.max_wasm_stack(0x80000);
37 config.consume_fuel(true);
38
39 config.strategy(Strategy::Cranelift);
40 config.cranelift_opt_level(OptLevel::SpeedAndSize);
41 config.cranelift_nan_canonicalization(true);
43
44 config.static_memory_forced(true);
48 config.static_memory_guard_size(0);
49 config.dynamic_memory_guard_size(0);
50 config.guard_before_linear_memory(false);
51 config.memory_init_cow(false);
52
53 config
54 .module_version(ModuleVersionStrategy::Custom(String::from("piecrust")))
55 .expect("Module version should be valid");
56 config.generate_address_map(false);
57 config.macos_use_mach_ports(false);
58
59 config.wasm_memory64(true);
61
62 const BYTE4_STORE_COST: i64 = 4 * BYTE_STORE_COST;
63 const BYTE8_STORE_COST: i64 = 8 * BYTE_STORE_COST;
64 const BYTE16_STORE_COST: i64 = 16 * BYTE_STORE_COST;
65
66 config.operator_cost(OperatorCost {
67 I32Store: BYTE4_STORE_COST,
68 F32Store: BYTE4_STORE_COST,
69 I32Store8: BYTE4_STORE_COST,
70 I32Store16: BYTE4_STORE_COST,
71 I32AtomicStore: BYTE4_STORE_COST,
72 I32AtomicStore8: BYTE4_STORE_COST,
73 I32AtomicStore16: BYTE4_STORE_COST,
74
75 I64Store: BYTE8_STORE_COST,
76 F64Store: BYTE8_STORE_COST,
77 I64Store8: BYTE8_STORE_COST,
78 I64Store16: BYTE8_STORE_COST,
79 I64Store32: BYTE8_STORE_COST,
80 I64AtomicStore: BYTE8_STORE_COST,
81 I64AtomicStore8: BYTE8_STORE_COST,
82 I64AtomicStore16: BYTE8_STORE_COST,
83 I64AtomicStore32: BYTE8_STORE_COST,
84
85 V128Store: BYTE16_STORE_COST,
86 V128Store8Lane: BYTE16_STORE_COST,
87 V128Store16Lane: BYTE16_STORE_COST,
88 V128Store32Lane: BYTE16_STORE_COST,
89 V128Store64Lane: BYTE16_STORE_COST,
90
91 ..Default::default()
92 });
93
94 config
95}
96
97pub struct VM {
116 engine: Engine,
117 host_queries: HostQueries,
118 store: ContractStore,
119}
120
121impl Debug for VM {
122 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
123 f.debug_struct("VM")
124 .field("config", self.engine.config())
125 .field("host_queries", &self.host_queries)
126 .field("store", &self.store)
127 .finish()
128 }
129}
130
131impl VM {
132 pub fn new<P: AsRef<Path>>(root_dir: P) -> Result<Self, Error> {
141 tracing::trace!("vm::new");
142 let config = config();
143
144 let engine = Engine::new(&config).expect(
145 "Configuration should be valid since its set at compile time",
146 );
147
148 tracing::trace!("before ContractStore::new");
149 let mut store = ContractStore::new(engine.clone(), root_dir)
150 .map_err(|err| PersistenceError(Arc::new(err)))?;
151 tracing::trace!("before ContractStore::finish_new");
152 store
153 .finish_new()
154 .map_err(|err| PersistenceError(Arc::new(err)))?;
155 tracing::trace!("after ContractStore::finish_new");
156
157 Ok(Self {
158 engine,
159 host_queries: HostQueries::default(),
160 store,
161 })
162 }
163
164 pub fn ephemeral() -> Result<Self, Error> {
172 let tmp = tempdir().map_err(|err| PersistenceError(Arc::new(err)))?;
173 let tmp = tmp.path().to_path_buf();
174
175 let config = config();
176
177 let engine = Engine::new(&config).expect(
178 "Configuration should be valid since its set at compile time",
179 );
180
181 let mut store = ContractStore::new(engine.clone(), tmp)
182 .map_err(|err| PersistenceError(Arc::new(err)))?;
183 store
184 .finish_new()
185 .map_err(|err| PersistenceError(Arc::new(err)))?;
186
187 Ok(Self {
188 engine,
189 host_queries: HostQueries::default(),
190 store,
191 })
192 }
193
194 pub fn register_host_query<Q, S>(&mut self, name: S, query: Q)
201 where
202 Q: 'static + HostQuery,
203 S: Into<Cow<'static, str>>,
204 {
205 self.host_queries.insert(name, query);
206 }
207
208 pub fn session(
215 &self,
216 data: impl Into<SessionData>,
217 ) -> Result<Session, Error> {
218 let data = data.into();
219 let contract_session = match data.base {
220 Some(base) => self
221 .store
222 .session(base.into())
223 .map_err(|err| PersistenceError(Arc::new(err)))?,
224 _ => self.store.genesis_session(),
225 };
226 Ok(Session::new(
227 self.engine.clone(),
228 contract_session,
229 self.host_queries.clone(),
230 data,
231 ))
232 }
233
234 pub fn commits(&self) -> Vec<[u8; 32]> {
236 self.store.commits().into_iter().map(Into::into).collect()
237 }
238
239 pub fn delete_commit(&self, root: [u8; 32]) -> Result<(), Error> {
241 self.store
242 .delete_commit(root.into())
243 .map_err(|err| PersistenceError(Arc::new(err)))
244 }
245
246 pub fn finalize_commit(&self, root: [u8; 32]) -> Result<(), Error> {
248 self.store
249 .finalize_commit(root.into())
250 .map_err(|err| PersistenceError(Arc::new(err)))
251 }
252
253 pub fn root_dir(&self) -> &Path {
261 self.store.root_dir()
262 }
263
264 pub fn sync_thread(&self) -> &thread::Thread {
266 self.store.sync_loop()
267 }
268}
269
270#[derive(Default, Clone)]
271pub struct HostQueries {
272 map: BTreeMap<Cow<'static, str>, Arc<dyn HostQuery>>,
273}
274
275impl Debug for HostQueries {
276 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
277 f.debug_list().entries(self.map.keys()).finish()
278 }
279}
280
281impl HostQueries {
282 pub fn insert<Q, S>(&mut self, name: S, query: Q)
283 where
284 Q: 'static + HostQuery,
285 S: Into<Cow<'static, str>>,
286 {
287 self.map.insert(name.into(), Arc::new(query));
288 }
289
290 pub fn get(&self, name: &str) -> Option<&dyn HostQuery> {
291 self.map.get(name).map(|q| q.as_ref())
292 }
293}
294
295pub trait HostQuery: Send + Sync {
306 fn deserialize_and_price(
317 &self,
318 arg_buf: &[u8],
319 arg: &mut Box<dyn Any>,
320 ) -> u64;
321
322 fn execute(&self, arg: &Box<dyn Any>, arg_buf: &mut [u8]) -> u32;
330}
331
332impl<F> HostQuery for F
335where
336 F: Send + Sync + Fn(&mut [u8], u32) -> u32,
337{
338 fn deserialize_and_price(
339 &self,
340 arg_buf: &[u8],
341 arg: &mut Box<dyn Any>,
342 ) -> u64 {
343 let len = Box::new(arg_buf.len() as u32);
344 *arg = len;
345 0
346 }
347
348 fn execute(&self, arg: &Box<dyn Any>, arg_buf: &mut [u8]) -> u32 {
349 let arg_len = *arg.downcast_ref::<u32>().unwrap();
350 self(arg_buf, arg_len)
351 }
352}