vapcore_wasm/
lib.rs

1// Copyright 2015-2020 Parity Technologies (UK) Ltd.
2// This file is part of Tetsy Vapory.
3
4// Tetsy Vapory is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Tetsy Vapory is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Tetsy Vapory.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Wasm Interpreter
18
19extern crate byteorder;
20extern crate vapory_types;
21#[macro_use] extern crate log;
22extern crate libc;
23extern crate tetsy_wasm;
24extern crate tetsy_vm;
25extern crate twasm_utils as wasm_utils;
26extern crate twasmi;
27
28#[cfg(test)]
29extern crate env_logger;
30
31mod env;
32mod panic_payload;
33mod parser;
34mod runtime;
35
36#[cfg(test)]
37mod tests;
38
39
40use tetsy_vm::{GasLeft, ReturnData, ActionParams};
41use twasmi::{Error as InterpreterError, Trap};
42
43use runtime::{Runtime, RuntimeContext};
44
45use vapory_types::U256;
46
47/// Wrapped interpreter error
48#[derive(Debug)]
49pub enum Error {
50	Interpreter(InterpreterError),
51	Trap(Trap),
52}
53
54impl From<InterpreterError> for Error {
55	fn from(e: InterpreterError) -> Self {
56		Error::Interpreter(e)
57	}
58}
59
60impl From<Trap> for Error {
61	fn from(e: Trap) -> Self {
62		Error::Trap(e)
63	}
64}
65
66impl From<Error> for tetsy_vm::Error {
67	fn from(e: Error) -> Self {
68		match e {
69			Error::Interpreter(e) => tetsy_vm::Error::Wasm(format!("Wasm runtime error: {:?}", e)),
70			Error::Trap(e) => tetsy_vm::Error::Wasm(format!("Wasm contract trap: {:?}", e)),
71		}
72	}
73}
74
75/// Wasm interpreter instance
76pub struct WasmInterpreter {
77	params: ActionParams,
78}
79
80impl WasmInterpreter {
81	pub fn new(params: ActionParams) -> Self {
82		WasmInterpreter { params }
83	}
84}
85
86impl From<runtime::Error> for tetsy_vm::Error {
87	fn from(e: runtime::Error) -> Self {
88		tetsy_vm::Error::Wasm(format!("Wasm runtime error: {:?}", e))
89	}
90}
91
92enum ExecutionOutcome {
93	Suicide,
94	Return,
95	NotSpecial,
96}
97
98impl WasmInterpreter {
99	pub fn run(self: Box<WasmInterpreter>, ext: &mut dyn tetsy_vm::Ext) -> tetsy_vm::Result<GasLeft> {
100		let (module, data) = parser::payload(&self.params, ext.schedule().wasm())?;
101
102		let loaded_module = twasmi::Module::from_tetsy_wasm_module(module).map_err(Error::Interpreter)?;
103
104		let instantiation_resolver = env::ImportResolver::with_limit(16, ext.schedule().wasm());
105
106		let module_instance = twasmi::ModuleInstance::new(
107			&loaded_module,
108			&twasmi::ImportsBuilder::new().with_resolver("env", &instantiation_resolver)
109		).map_err(Error::Interpreter)?;
110
111		let adjusted_gas = self.params.gas * U256::from(ext.schedule().wasm().opcodes_div) /
112			U256::from(ext.schedule().wasm().opcodes_mul);
113
114		if adjusted_gas > ::std::u64::MAX.into()
115		{
116			return Err(tetsy_vm::Error::Wasm("Wasm interpreter cannot run contracts with gas (wasm adjusted) >= 2^64".to_owned()));
117		}
118
119		let initial_memory = instantiation_resolver.memory_size().map_err(Error::Interpreter)?;
120		trace!(target: "wasm", "Contract requested {:?} pages of initial memory", initial_memory);
121
122		let (gas_left, result) = {
123			let mut runtime = Runtime::with_params(
124				ext,
125				instantiation_resolver.memory_ref(),
126				// cannot overflow, checked above
127				adjusted_gas.low_u64(),
128				data.to_vec(),
129				RuntimeContext {
130					address: self.params.address,
131					sender: self.params.sender,
132					origin: self.params.origin,
133					code_address: self.params.code_address,
134					code_version: self.params.code_version,
135					value: self.params.value.value(),
136				},
137			);
138
139			// cannot overflow if static_region < 2^16,
140			// initial_memory ∈ [0..2^32)
141			// total_charge <- static_region * 2^32 * 2^16
142			// total_charge ∈ [0..2^64) if static_region ∈ [0..2^16)
143			// qed
144			assert!(runtime.schedule().wasm().initial_mem < 1 << 16);
145			runtime.charge(|s| initial_memory as u64 * s.wasm().initial_mem as u64)?;
146
147			let module_instance = module_instance.run_start(&mut runtime).map_err(Error::Trap)?;
148
149			let invoke_result = module_instance.invoke_export("call", &[], &mut runtime);
150
151			let mut execution_outcome = ExecutionOutcome::NotSpecial;
152			if let Err(InterpreterError::Trap(ref trap)) = invoke_result {
153				if let twasmi::TrapKind::Host(ref boxed) = *trap.kind() {
154					let ref runtime_err = boxed.downcast_ref::<runtime::Error>()
155						.expect("Host errors other than runtime::Error never produced; qed");
156
157					match **runtime_err {
158						runtime::Error::Suicide => { execution_outcome = ExecutionOutcome::Suicide; },
159						runtime::Error::Return => { execution_outcome = ExecutionOutcome::Return; },
160						_ => {}
161					}
162				}
163			}
164
165			if let (ExecutionOutcome::NotSpecial, Err(e)) = (execution_outcome, invoke_result) {
166				trace!(target: "wasm", "Error executing contract: {:?}", e);
167				return Err(tetsy_vm::Error::from(Error::from(e)));
168			}
169
170			(
171				runtime.gas_left().expect("Cannot fail since it was not updated since last charge"),
172				runtime.into_result(),
173			)
174		};
175
176		let gas_left =
177			U256::from(gas_left) * U256::from(ext.schedule().wasm().opcodes_mul)
178				/ U256::from(ext.schedule().wasm().opcodes_div);
179
180		if result.is_empty() {
181			trace!(target: "wasm", "Contract execution result is empty.");
182			Ok(GasLeft::Known(gas_left))
183		} else {
184			let len = result.len();
185			Ok(GasLeft::NeedsReturn {
186				gas_left: gas_left,
187				data: ReturnData::new(
188					result,
189					0,
190					len,
191				),
192				apply_state: true,
193			})
194		}
195	}
196}
197
198impl tetsy_vm::Exec for WasmInterpreter {
199	fn exec(self: Box<WasmInterpreter>, ext: &mut dyn tetsy_vm::Ext) -> tetsy_vm::ExecTrapResult<GasLeft> {
200		Ok(self.run(ext))
201	}
202}