1extern 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#[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
75pub 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 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 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}