|
| 1 | +use crate::cache::Cache; |
| 2 | +use crate::vm::{self, Environment}; |
| 3 | + |
| 4 | +pub use crate::error::Error; |
| 5 | +pub use std::ptr::NonNull; |
| 6 | +use std::sync::Arc; |
| 7 | +use wasmer::Universal; |
| 8 | +use wasmer_middlewares::metering::{get_remaining_points, MeteringPoints}; |
| 9 | + |
| 10 | +use wasmer::Singlepass; |
| 11 | + |
| 12 | +use wasmer::{imports, wasmparser::Operator, CompilerConfig, Function, Store}; |
| 13 | +use wasmer_middlewares::Metering; |
| 14 | + |
| 15 | +// use owasm_crypto::ecvrf; |
| 16 | + |
| 17 | +fn require_mem_range(max_range: usize, require_range: usize) -> Result<(), Error> { |
| 18 | + if max_range < require_range { |
| 19 | + return Err(Error::MemoryOutOfBoundError); |
| 20 | + } |
| 21 | + Ok(()) |
| 22 | +} |
| 23 | + |
| 24 | +fn cost(_operator: &Operator) -> u64 { |
| 25 | + // A flat fee for each operation |
| 26 | + 1 |
| 27 | +} |
| 28 | + |
| 29 | +pub fn run<E>( |
| 30 | + cache: &mut Cache, |
| 31 | + code: &[u8], |
| 32 | + gas: u64, |
| 33 | + is_prepare: bool, |
| 34 | + env: E, |
| 35 | +) -> Result<u64, Error> |
| 36 | +where |
| 37 | + E: vm::Env + 'static, |
| 38 | +{ |
| 39 | + let owasm_env = Environment::new(env); |
| 40 | + |
| 41 | + let mut compiler = Singlepass::new(); |
| 42 | + let metering = Arc::new(Metering::new(0, cost)); |
| 43 | + compiler.push_middleware(metering); |
| 44 | + let engine = Universal::new(compiler).engine(); |
| 45 | + let store = Store::new(&engine); |
| 46 | + |
| 47 | + let import_object = imports! { |
| 48 | + "env" => { |
| 49 | + "gas" => Function::new_native_with_env(&store, owasm_env.clone(), |env: &Environment<E>, _gas: u32| { |
| 50 | + env.decrease_gas_left(1) |
| 51 | + }), |
| 52 | + "get_span_size" => Function::new_native_with_env(&store, owasm_env.clone(), |env: &Environment<E>| { |
| 53 | + env.with_vm(|vm| { |
| 54 | + vm.env.get_span_size() |
| 55 | + }) |
| 56 | + }), |
| 57 | + "read_calldata" => Function::new_native_with_env(&store, owasm_env.clone(), |env: &Environment<E>, ptr: i64| { |
| 58 | + env.with_mut_vm(|vm| -> Result<i64, Error>{ |
| 59 | + let span_size = vm.env.get_span_size(); |
| 60 | + // consume gas equal size of span when read calldata |
| 61 | + env.decrease_gas_left(span_size as u64)?; |
| 62 | + |
| 63 | + let memory = env.memory()?; |
| 64 | + require_mem_range(memory.size().bytes().0, (ptr + span_size) as usize)?; |
| 65 | + |
| 66 | + let data = vm.env.get_calldata()?; |
| 67 | + |
| 68 | + for (idx, byte) in data.iter().enumerate() { |
| 69 | + memory.view()[ptr as usize + idx].set(*byte); |
| 70 | + } |
| 71 | + |
| 72 | + Ok(data.len() as i64) |
| 73 | + }) |
| 74 | + }), |
| 75 | + "set_return_data" => Function::new_native_with_env(&store, owasm_env.clone(), |env: &Environment<E>, ptr: i64, len: i64| { |
| 76 | + env.with_mut_vm(|vm| { |
| 77 | + let span_size = vm.env.get_span_size(); |
| 78 | + |
| 79 | + if len > span_size { |
| 80 | + return Err(Error::SpanTooSmallError); |
| 81 | + } |
| 82 | + |
| 83 | + // consume gas equal size of span when save data to memory |
| 84 | + env.decrease_gas_left(span_size as u64)?; |
| 85 | + |
| 86 | + let memory = env.memory()?; |
| 87 | + require_mem_range(memory.size().bytes().0, (ptr + span_size) as usize)?; |
| 88 | + |
| 89 | + let data: Vec<u8> = memory.view()[ptr as usize..(ptr + len) as usize].iter().map(|cell| cell.get()).collect(); |
| 90 | + vm.env.set_return_data(&data) |
| 91 | + }) |
| 92 | + }), |
| 93 | + "get_ask_count" => Function::new_native_with_env(&store, owasm_env.clone(), |env: &Environment<E>| { |
| 94 | + env.with_vm(|vm| { |
| 95 | + vm.env.get_ask_count() |
| 96 | + }) |
| 97 | + }), |
| 98 | + "get_min_count" => Function::new_native_with_env(&store, owasm_env.clone(), |env: &Environment<E>| { |
| 99 | + env.with_vm(|vm| { |
| 100 | + vm.env.get_min_count() |
| 101 | + }) |
| 102 | + }), |
| 103 | + "get_prepare_time" => Function::new_native_with_env(&store, owasm_env.clone(), |env: &Environment<E>| { |
| 104 | + env.with_vm(|vm| { |
| 105 | + vm.env.get_prepare_time() |
| 106 | + }) |
| 107 | + }), |
| 108 | + "get_execute_time" => Function::new_native_with_env(&store, owasm_env.clone(), |env: &Environment<E>| { |
| 109 | + env.with_vm(|vm| { |
| 110 | + vm.env.get_execute_time() |
| 111 | + }) |
| 112 | + }), |
| 113 | + "get_ans_count" => Function::new_native_with_env(&store, owasm_env.clone(), |env: &Environment<E>| { |
| 114 | + env.with_vm(|vm| { |
| 115 | + vm.env.get_ans_count() |
| 116 | + }) |
| 117 | + }), |
| 118 | + "ask_external_data" => Function::new_native_with_env(&store, owasm_env.clone(), |env: &Environment<E>, eid: i64, did: i64, ptr: i64, len: i64| { |
| 119 | + env.with_mut_vm(|vm| { |
| 120 | + let span_size = vm.env.get_span_size(); |
| 121 | + |
| 122 | + if len > span_size { |
| 123 | + return Err(Error::SpanTooSmallError); |
| 124 | + } |
| 125 | + |
| 126 | + // consume gas equal size of span when write calldata for raw request |
| 127 | + env.decrease_gas_left(span_size as u64)?; |
| 128 | + |
| 129 | + let memory = env.memory()?; |
| 130 | + require_mem_range(memory.size().bytes().0, (ptr + span_size) as usize)?; |
| 131 | + |
| 132 | + let data: Vec<u8> = memory.view()[ptr as usize..(ptr + len) as usize].iter().map(|cell| cell.get()).collect(); |
| 133 | + vm.env.ask_external_data(eid, did, &data) |
| 134 | + }) |
| 135 | + }), |
| 136 | + "get_external_data_status" => Function::new_native_with_env(&store, owasm_env.clone(), |env: &Environment<E>, eid: i64, vid: i64| { |
| 137 | + env.with_vm(|vm| { |
| 138 | + vm.env.get_external_data_status(eid, vid) |
| 139 | + }) |
| 140 | + }), |
| 141 | + "read_external_data" => Function::new_native_with_env(&store, owasm_env.clone(), |env: &Environment<E>, eid: i64, vid: i64, ptr: i64| { |
| 142 | + env.with_mut_vm(|vm| -> Result<i64, Error>{ |
| 143 | + let span_size = vm.env.get_span_size(); |
| 144 | + // consume gas equal size of span when read data from report |
| 145 | + env.decrease_gas_left(span_size as u64)?; |
| 146 | + |
| 147 | + let memory = env.memory()?; |
| 148 | + require_mem_range(memory.size().bytes().0, (ptr + span_size) as usize)?; |
| 149 | + |
| 150 | + let data = vm.env.get_external_data(eid, vid)?; |
| 151 | + |
| 152 | + for (idx, byte) in data.iter().enumerate() { |
| 153 | + memory.view()[ptr as usize + idx].set(*byte); |
| 154 | + } |
| 155 | + |
| 156 | + Ok(data.len() as i64) |
| 157 | + }) |
| 158 | + }), |
| 159 | + // "ecvrf_verify" => Function::new_native_with_env(&store, owasm_env.clone(), |env: &Environment<E>, y_ptr: i64, y_len: i64, pi_ptr: i64, pi_len: i64, alpha_ptr: i64, alpha_len: i64| { |
| 160 | + // env.with_mut_vm(|vm| -> Result<u32, Error>{ |
| 161 | + // // consume gas relatively to the function running time (~12ms) |
| 162 | + // vm.consume_gas(500000)?; |
| 163 | + // env.decrease_gas_left(500000)?; |
| 164 | + |
| 165 | + // let y: Vec<u8> = get_from_mem(env, y_ptr, y_len)?; |
| 166 | + // let pi: Vec<u8>= get_from_mem(env, pi_ptr, pi_len)?; |
| 167 | + // let alpha: Vec<u8> = get_from_mem(env, alpha_ptr, alpha_len)?; |
| 168 | + // Ok(ecvrf::ecvrf_verify(&y, &pi, &alpha) as u32) |
| 169 | + // }) |
| 170 | + // }), |
| 171 | + }, |
| 172 | + }; |
| 173 | + |
| 174 | + let instance = cache.get_instance(code, &store, &import_object)?; |
| 175 | + let instance_ptr = NonNull::from(&instance); |
| 176 | + owasm_env.set_wasmer_instance(Some(instance_ptr)); |
| 177 | + owasm_env.set_gas_left(gas); |
| 178 | + |
| 179 | + // get function and exec |
| 180 | + let entry = if is_prepare { "prepare" } else { "execute" }; |
| 181 | + let function = instance |
| 182 | + .exports |
| 183 | + .get_function(entry) |
| 184 | + .unwrap() |
| 185 | + .native::<(), ()>() |
| 186 | + .map_err(|_| Error::BadEntrySignatureError)?; |
| 187 | + |
| 188 | + function.call().map_err(|runtime_err| { |
| 189 | + if let Ok(err) = runtime_err.downcast::<Error>() { |
| 190 | + return err.clone(); |
| 191 | + } |
| 192 | + |
| 193 | + match get_remaining_points(&instance) { |
| 194 | + MeteringPoints::Remaining(_) => Error::RuntimeError, |
| 195 | + MeteringPoints::Exhausted => Error::OutOfGasError, |
| 196 | + } |
| 197 | + })?; |
| 198 | + |
| 199 | + match get_remaining_points(&instance) { |
| 200 | + MeteringPoints::Remaining(count) => Ok(gas.saturating_sub(count)), |
| 201 | + MeteringPoints::Exhausted => Err(Error::OutOfGasError), |
| 202 | + } |
| 203 | +} |
| 204 | + |
| 205 | +#[cfg(test)] |
| 206 | +mod test { |
| 207 | + use crate::cache::CacheOptions; |
| 208 | + |
| 209 | + use super::*; |
| 210 | + use crate::compile::compile; |
| 211 | + use std::io::{Read, Write}; |
| 212 | + use std::process::Command; |
| 213 | + use tempfile::NamedTempFile; |
| 214 | + |
| 215 | + pub struct MockEnv {} |
| 216 | + |
| 217 | + impl vm::Env for MockEnv { |
| 218 | + fn get_span_size(&self) -> i64 { |
| 219 | + 30000 |
| 220 | + } |
| 221 | + fn get_calldata(&self) -> Result<Vec<u8>, Error> { |
| 222 | + Ok(vec![1]) |
| 223 | + } |
| 224 | + fn set_return_data(&self, _: &[u8]) -> Result<(), Error> { |
| 225 | + Ok(()) |
| 226 | + } |
| 227 | + fn get_ask_count(&self) -> i64 { |
| 228 | + 10 |
| 229 | + } |
| 230 | + fn get_min_count(&self) -> i64 { |
| 231 | + 8 |
| 232 | + } |
| 233 | + fn get_prepare_time(&self) -> i64 { |
| 234 | + 100_000 |
| 235 | + } |
| 236 | + fn get_execute_time(&self) -> Result<i64, Error> { |
| 237 | + Ok(100_000) |
| 238 | + } |
| 239 | + fn get_ans_count(&self) -> Result<i64, Error> { |
| 240 | + Ok(8) |
| 241 | + } |
| 242 | + fn ask_external_data(&self, _: i64, _: i64, _: &[u8]) -> Result<(), Error> { |
| 243 | + Ok(()) |
| 244 | + } |
| 245 | + fn get_external_data_status(&self, _: i64, _: i64) -> Result<i64, Error> { |
| 246 | + Ok(1) |
| 247 | + } |
| 248 | + fn get_external_data(&self, _: i64, _: i64) -> Result<Vec<u8>, Error> { |
| 249 | + Ok(vec![1]) |
| 250 | + } |
| 251 | + } |
| 252 | + |
| 253 | + fn wat2wasm(wat: impl AsRef<[u8]>) -> Vec<u8> { |
| 254 | + let mut input_file = NamedTempFile::new().unwrap(); |
| 255 | + let mut output_file = NamedTempFile::new().unwrap(); |
| 256 | + input_file.write_all(wat.as_ref()).unwrap(); |
| 257 | + Command::new("wat2wasm") |
| 258 | + .args(&[ |
| 259 | + input_file.path().to_str().unwrap(), |
| 260 | + "-o", |
| 261 | + output_file.path().to_str().unwrap(), |
| 262 | + ]) |
| 263 | + .output() |
| 264 | + .unwrap(); |
| 265 | + let mut wasm = Vec::new(); |
| 266 | + output_file.read_to_end(&mut wasm).unwrap(); |
| 267 | + wasm |
| 268 | + } |
| 269 | + |
| 270 | + #[test] |
| 271 | + fn test_simple_gas_used() { |
| 272 | + let wasm = wat2wasm( |
| 273 | + r#"(module |
| 274 | + (type (func (param i64 i64 i64 i64) (result))) |
| 275 | + (func |
| 276 | + (local $idx i32) |
| 277 | + (local.set $idx (i32.const 0)) |
| 278 | + (block |
| 279 | + (loop |
| 280 | + (local.set $idx (local.get $idx) (i32.const 1) (i32.add) ) |
| 281 | + (br_if 0 (i32.lt_u (local.get $idx) (i32.const 100000))) |
| 282 | + ) |
| 283 | + ) |
| 284 | + ) |
| 285 | + (func (;"execute": Resolves with result "beeb";) |
| 286 | + ) |
| 287 | + (memory 17) |
| 288 | + (data (i32.const 1048576) "beeb") (;str = "beeb";) |
| 289 | + (export "prepare" (func 0)) |
| 290 | + (export "execute" (func 1))) |
| 291 | + "#, |
| 292 | + ); |
| 293 | + let code = compile(&wasm).unwrap(); |
| 294 | + let mut cache = Cache::new(CacheOptions { cache_size: 10000 }); |
| 295 | + let env = MockEnv {}; |
| 296 | + let gas_used = run(&mut cache, &code, 4294967290, true, env).unwrap(); |
| 297 | + assert_eq!(gas_used, 800013 as u64); |
| 298 | + } |
| 299 | + |
| 300 | + #[test] |
| 301 | + fn test_ask_count_gas_used() { |
| 302 | + let wasm = wat2wasm( |
| 303 | + r#"(module |
| 304 | + (type (func (param i64 i64 i64 i64) (result))) |
| 305 | + (import "env" "ask_external_data" (func (type 0))) |
| 306 | + (func |
| 307 | + (local $idx i32) |
| 308 | +
|
| 309 | + (i64.const 1) |
| 310 | + (i64.const 1) |
| 311 | + (i64.const 1048576) |
| 312 | + (i64.const 4) |
| 313 | + call 0 |
| 314 | +
|
| 315 | + (local.set $idx (i32.const 0)) |
| 316 | + (block |
| 317 | + (loop |
| 318 | + (local.set $idx (local.get $idx) (i32.const 1) (i32.add) ) |
| 319 | + (br_if 0 (i32.lt_u (local.get $idx) (i32.const 100000))) |
| 320 | + ) |
| 321 | + ) |
| 322 | + ) |
| 323 | + (func (;"execute": Resolves with result "beeb";)) |
| 324 | + (memory (export "memory") 17) |
| 325 | + (data (i32.const 1048576) "beeb") |
| 326 | + (export "prepare" (func 1)) |
| 327 | + (export "execute" (func 2))) |
| 328 | + "#, |
| 329 | + ); |
| 330 | + |
| 331 | + let code = compile(&wasm).unwrap(); |
| 332 | + let mut cache = Cache::new(CacheOptions { cache_size: 10000 }); |
| 333 | + let env = MockEnv {}; |
| 334 | + let gas_used = run(&mut cache, &code, 4294967290, true, env).unwrap(); |
| 335 | + assert_eq!(gas_used, 830018 as u64); |
| 336 | + } |
| 337 | +} |
0 commit comments