rusk_vm/
lib.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7//! #Rusk-VM
8//!
9//! The main engine for executing WASM on the network state
10#![warn(missing_docs)]
11#![allow(unreachable_code)]
12
13use std::collections::HashMap;
14use std::{fmt, io};
15
16use canonical::CanonError;
17
18mod call_context;
19mod compiler;
20mod compiler_config;
21mod contract;
22mod env;
23mod gas;
24mod memory;
25mod modules;
26mod ops;
27mod resolver;
28mod state;
29
30pub use dusk_abi;
31
32pub use contract::{Contract, ContractId};
33pub use gas::{Gas, GasMeter};
34pub use state::NetworkState;
35
36#[cfg(feature = "persistence")]
37pub use state::persist::NetworkStateId;
38
39use thiserror::Error;
40use wasmer_vm::TrapCode;
41
42#[derive(Error)]
43/// The errors that can happen while executing the VM
44pub enum VMError {
45    /// Invalid arguments in host call
46    InvalidArguments,
47    /// The contract panicked with message in `String`
48    ContractPanic(String),
49    /// Could not find WASM memory
50    MemoryNotFound,
51    /// Error during the instrumentalization
52    InstrumentationError(modules::InstrumentationError),
53    /// Invalid ABI Call
54    InvalidABICall,
55    /// Invalid Utf8
56    InvalidUtf8,
57    /// Invalid Public key
58    InvalidEd25519PublicKey,
59    /// Invalid Signature
60    InvalidEd25519Signature,
61    /// Contract returned, not an error per se, this is how contracts return.
62    ContractReturn(i32, i32),
63    /// Contract execution ran out of gas
64    OutOfGas,
65    /// Not enough funds for call
66    NotEnoughFunds,
67    /// Contract could not be found in the state
68    UnknownContract,
69    /// WASM threw an error
70    WASMError(failure::Error),
71    /// Input output error
72    IOError(io::Error),
73    /// Invalid WASM Module
74    InvalidWASMModule,
75    /// Error propagated from underlying store
76    StoreError(CanonError),
77    /// Serialization error from the state persistence mechanism
78    PersistenceSerializationError(CanonError),
79    /// Other error from the state persistence mechanism
80    PersistenceError(String),
81    /// WASMER export error
82    WasmerExportError(wasmer::ExportError),
83    /// WASMER runtime error
84    WasmerRuntimeError(wasmer::RuntimeError),
85    /// WASMER compile error
86    WasmerCompileError(wasmer::CompileError),
87    /// WASMER trap
88    WasmerTrap(TrapCode),
89    /// WASMER instantiation error
90    WasmerInstantiationError(wasmer::InstantiationError),
91}
92
93impl From<io::Error> for VMError {
94    fn from(e: io::Error) -> Self {
95        VMError::IOError(e)
96    }
97}
98
99impl From<modules::InstrumentationError> for VMError {
100    fn from(e: modules::InstrumentationError) -> Self {
101        VMError::InstrumentationError(e)
102    }
103}
104
105impl From<gas::GasError> for VMError {
106    fn from(_: gas::GasError) -> Self {
107        // Currently the only gas error is `GasLimitExceeded`
108        VMError::OutOfGas
109    }
110}
111
112impl From<wasmer::InstantiationError> for VMError {
113    fn from(e: wasmer::InstantiationError) -> Self {
114        VMError::WasmerInstantiationError(e)
115    }
116}
117
118impl From<wasmer::ExportError> for VMError {
119    fn from(e: wasmer::ExportError) -> Self {
120        VMError::WasmerExportError(e)
121    }
122}
123
124impl From<wasmer::CompileError> for VMError {
125    fn from(e: wasmer::CompileError) -> Self {
126        VMError::WasmerCompileError(e)
127    }
128}
129
130impl From<wasmer::RuntimeError> for VMError {
131    fn from(e: wasmer::RuntimeError) -> Self {
132        let runtime_error = e.clone();
133        match e.to_trap() {
134            Some(trap_code) => VMError::WasmerTrap(trap_code),
135            _ => VMError::WasmerRuntimeError(runtime_error),
136        }
137    }
138}
139
140// The generic From<CanonError> is not specific enough and conflicts with
141// From<Self>.
142impl VMError {
143    /// Create a VMError from the associated stores
144    pub fn from_store_error(err: CanonError) -> Self {
145        VMError::StoreError(err)
146    }
147}
148
149impl fmt::Display for VMError {
150    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
151        match self {
152            VMError::InvalidArguments => write!(f, "Invalid arguments")?,
153            VMError::ContractPanic(string) => {
154                write!(f, "Contract panic \"{}\"", string)?
155            }
156            VMError::InvalidUtf8 => write!(f, "Invalid UTF-8")?,
157            VMError::InvalidEd25519PublicKey => {
158                write!(f, "Invalid Ed25519 Public Key")?
159            }
160            VMError::InvalidEd25519Signature => {
161                write!(f, "Invalid Ed25519 Signature")?
162            }
163            VMError::ContractReturn(_, _) => write!(f, "Contract Return")?,
164            VMError::OutOfGas => write!(f, "Out of Gas error")?,
165            VMError::NotEnoughFunds => write!(f, "Not enough funds error")?,
166            VMError::WASMError(e) => write!(f, "WASM Error ({:?})", e)?,
167            VMError::MemoryNotFound => write!(f, "Memory not found")?,
168            VMError::InvalidABICall => write!(f, "Invalid ABI Call")?,
169            VMError::IOError(e) => write!(f, "Input/Output Error ({:?})", e)?,
170            VMError::UnknownContract => write!(f, "Unknown Contract")?,
171            VMError::InvalidWASMModule => write!(f, "Invalid WASM module")?,
172            VMError::StoreError(e) => write!(f, "Store error {:?}", e)?,
173            VMError::InstrumentationError(e) => {
174                write!(f, "Instrumentalization error {:?}", e)?
175            }
176            VMError::PersistenceSerializationError(e) => {
177                write!(f, "Persistence serialization error {:?}", e)?
178            }
179            VMError::PersistenceError(string) => {
180                write!(f, "Persistence error \"{}\"", string)?
181            }
182            VMError::WasmerExportError(e) => match e {
183                wasmer::ExportError::IncompatibleType => {
184                    write!(f, "WASMER Export Error - incompatible export type")?
185                }
186                wasmer::ExportError::Missing(s) => {
187                    write!(f, "WASMER Export Error - missing: \"{}\"", s)?
188                }
189            },
190            VMError::WasmerRuntimeError(e) => {
191                write!(f, "WASMER Runtime Error {:?}", e)?
192            }
193            VMError::WasmerTrap(e) => write!(f, "WASMER Trap ({:?})", e)?,
194            VMError::WasmerInstantiationError(e) => {
195                write!(f, "WASMER Instantiation Error ({:?})", e)?
196            }
197            VMError::WasmerCompileError(e) => {
198                write!(f, "WASMER Compile Error {:?}", e)?
199            }
200        }
201        Ok(())
202    }
203}
204
205impl fmt::Debug for VMError {
206    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
207        write!(f, "{}", self)
208    }
209}
210
211/// Definition of the cost schedule and other parameterizations for wasm vm.
212#[derive(Clone, PartialEq, Eq)]
213pub struct Schedule {
214    /// Version of the schedule.
215    pub version: u32,
216
217    /// Gas cost of a regular operation.
218    pub regular_op_cost: Gas,
219
220    /// Gas cost of a growing memory by single page.
221    pub grow_mem_cost: Gas,
222
223    /// Maximum allowed stack height.
224    ///
225    /// See `<https://wiki.parity.io/WebAssembly-StackHeight>` to find out
226    /// how the stack frame cost is calculated.
227    pub max_stack_height: u32,
228
229    /// Maximum allowed size of a declared table.
230    pub max_table_size: u32,
231
232    /// Maximum number of memory pages.
233    pub max_memory_pages: u32,
234
235    /// Floats are forbidden
236    pub has_forbidden_floats: bool,
237
238    /// Cost of memory growth
239    pub has_grow_cost: bool,
240
241    /// Is metering on
242    pub has_metering: bool,
243
244    /// Is table size limit on
245    pub has_table_size_limit: bool,
246
247    /// Op cost bit
248    pub per_type_op_cost: HashMap<String, u32>,
249}
250
251impl Default for Schedule {
252    fn default() -> Schedule {
253        let per_type_op_cost: HashMap<String, u32> = [
254            ("bit", 1),
255            ("add", 1),
256            ("mul", 1),
257            ("div", 1),
258            ("load", 1),
259            ("store", 1),
260            ("const", 1),
261            ("local", 1),
262            ("global", 1),
263            ("flow", 1),
264            ("integer_comp", 1),
265            ("float_comp", 1),
266            ("float", 1),
267            ("conversion", 1),
268            ("float_conversion", 1),
269            ("reinterpret", 1),
270            ("unreachable", 1),
271            ("nop", 1),
272            ("current_mem", 1),
273            ("grow_mem", 1),
274        ]
275        .iter()
276        .map(|(s, c)| (s.to_string(), *c))
277        .collect();
278        Schedule {
279            version: 0,
280            regular_op_cost: 1,
281            grow_mem_cost: 1,
282            max_stack_height: 65536,
283            max_table_size: 16384,
284            max_memory_pages: 16384,
285            has_forbidden_floats: true,
286            has_grow_cost: true,
287            has_metering: true,
288            has_table_size_limit: true,
289            per_type_op_cost,
290        }
291    }
292}
293
294impl Schedule {
295    /// Create schedule with version
296    pub fn with_version(version: u32) -> Self {
297        Self {
298            version,
299            ..Self::default()
300        }
301    }
302}