1use crate::cache::Cache;
2use crate::error::Error;
3use crate::imports::create_import_object;
4use crate::store::make_store;
5use crate::vm::{Environment, Querier};
6
7use std::ptr::NonNull;
8use wasmer_middlewares::metering::{get_remaining_points, MeteringPoints};
9
10pub fn run<Q>(
11 cache: &mut Cache,
12 code: &[u8],
13 gas_limit: u64,
14 is_prepare: bool,
15 querier: Q,
16) -> Result<u64, Error>
17where
18 Q: Querier + 'static,
19{
20 let owasm_env = Environment::new(querier);
21 let store = make_store();
22 let import_object = create_import_object(&store, owasm_env.clone());
23
24 let (instance, _) = cache.get_instance(code, &store, &import_object)?;
25 let instance_ptr = NonNull::from(&instance);
26 owasm_env.set_wasmer_instance(Some(instance_ptr));
27 owasm_env.set_gas_left(gas_limit);
28
29 let entry = if is_prepare { "prepare" } else { "execute" };
31 let function = instance
32 .exports
33 .get_function(entry)
34 .unwrap()
35 .native::<(), ()>()
36 .map_err(|_| Error::BadEntrySignatureError)?;
37
38 function.call().map_err(|runtime_err| {
39 if let Ok(err) = runtime_err.downcast::<Error>() {
40 return err;
41 }
42
43 match get_remaining_points(&instance) {
44 MeteringPoints::Remaining(_) => Error::RuntimeError,
45 MeteringPoints::Exhausted => Error::OutOfGasError,
46 }
47 })?;
48
49 match get_remaining_points(&instance) {
50 MeteringPoints::Remaining(count) => Ok(gas_limit.saturating_sub(count)),
51 MeteringPoints::Exhausted => Err(Error::OutOfGasError),
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use crate::cache::CacheOptions;
58
59 use super::*;
60 use crate::compile::compile;
61 use std::io::{Read, Write};
62 use std::process::Command;
63 use tempfile::NamedTempFile;
64
65 pub struct MockQuerier {}
66
67 impl Querier for MockQuerier {
68 fn get_span_size(&self) -> i64 {
69 300
70 }
71 fn get_calldata(&self) -> Result<Vec<u8>, Error> {
72 Ok(vec![1])
73 }
74 fn set_return_data(&self, _: &[u8]) -> Result<(), Error> {
75 Ok(())
76 }
77 fn get_ask_count(&self) -> i64 {
78 10
79 }
80 fn get_min_count(&self) -> i64 {
81 8
82 }
83 fn get_prepare_time(&self) -> i64 {
84 100_000
85 }
86 fn get_execute_time(&self) -> Result<i64, Error> {
87 Ok(100_000)
88 }
89 fn get_ans_count(&self) -> Result<i64, Error> {
90 Ok(8)
91 }
92 fn ask_external_data(&self, _: i64, _: i64, _: &[u8]) -> Result<(), Error> {
93 Ok(())
94 }
95 fn get_external_data_status(&self, _: i64, _: i64) -> Result<i64, Error> {
96 Ok(1)
97 }
98 fn get_external_data(&self, _: i64, _: i64) -> Result<Vec<u8>, Error> {
99 Ok(vec![1])
100 }
101 }
102
103 fn wat2wasm(wat: impl AsRef<[u8]>) -> Vec<u8> {
104 let mut input_file = NamedTempFile::new().unwrap();
105 let mut output_file = NamedTempFile::new().unwrap();
106 input_file.write_all(wat.as_ref()).unwrap();
107 Command::new("wat2wasm")
108 .args(&[
109 input_file.path().to_str().unwrap(),
110 "-o",
111 output_file.path().to_str().unwrap(),
112 ])
113 .output()
114 .unwrap();
115 let mut wasm = Vec::new();
116 output_file.read_to_end(&mut wasm).unwrap();
117 wasm
118 }
119
120 #[test]
121 fn test_simple_gas_used() {
122 let wasm = wat2wasm(
123 r#"(module
124 (type (func (param i64 i64 i64 i64) (result)))
125 (func
126 (local $idx i32)
127 (local.set $idx (i32.const 0))
128 (block
129 (loop
130 (local.set $idx (local.get $idx) (i32.const 1) (i32.add) )
131 (br_if 0 (i32.lt_u (local.get $idx) (i32.const 100000)))
132 )
133 )
134 )
135 (func (;"execute": Resolves with result "beeb";)
136 )
137 (memory 17)
138 (data (i32.const 1048576) "beeb") (;str = "beeb";)
139 (export "prepare" (func 0))
140 (export "execute" (func 1)))
141 "#,
142 );
143 let code = compile(&wasm).unwrap();
144 let mut cache = Cache::new(CacheOptions { cache_size: 10000 });
145 let querier = MockQuerier {};
146 let gas_used = run(&mut cache, &code, u64::MAX, true, querier).unwrap();
147 assert_eq!(gas_used, 705019550000 as u64);
148 }
149
150 #[test]
151 fn test_ask_external_data_gas_used() {
152 let wasm = wat2wasm(
153 r#"(module
154 (type (func (param i64 i64 i64 i64) (result)))
155 (import "env" "ask_external_data" (func (type 0)))
156 (func
157 (local $idx i32)
158
159 (i64.const 1)
160 (i64.const 1)
161 (i64.const 1048576)
162 (i64.const 4)
163 call 0
164
165 (local.set $idx (i32.const 0))
166 (block
167 (loop
168 (local.set $idx (local.get $idx) (i32.const 1) (i32.add) )
169 (br_if 0 (i32.lt_u (local.get $idx) (i32.const 100000)))
170 )
171 )
172 )
173 (func (;"execute": Resolves with result "beeb";))
174 (memory (export "memory") 17)
175 (data (i32.const 1048576) "beeb")
176 (export "prepare" (func 1))
177 (export "execute" (func 2)))
178 "#,
179 );
180
181 let code = compile(&wasm).unwrap();
182 let mut cache = Cache::new(CacheOptions { cache_size: 10000 });
183 let querier = MockQuerier {};
184 let gas_used = run(&mut cache, &code, u64::MAX, true, querier).unwrap();
185 assert_eq!(gas_used, 706780650000 as u64);
186 }
187
188 #[test]
189 #[cfg(not(tarpaulin))]
190 fn test_out_of_gas() {
191 let wasm = wat2wasm(
192 r#"(module
193 (type (func (param i64 i64 i64 i64) (result)))
194 (func
195 (local $idx i32)
196 (local.set $idx (i32.const 0))
197 (block
198 (loop
199 (local.set $idx (local.get $idx) (i32.const 1) (i32.add) )
200 (br_if 0 (i32.lt_u (local.get $idx) (i32.const 100000)))
201 )
202 )
203 )
204 (func (;"execute": Resolves with result "beeb";)
205 )
206 (memory 17)
207 (data (i32.const 1048576) "beeb") (;str = "beeb";)
208 (export "prepare" (func 0))
209 (export "execute" (func 1)))
210 "#,
211 );
212 let code = compile(&wasm).unwrap();
213 let mut cache = Cache::new(CacheOptions { cache_size: 10000 });
214 let querier = MockQuerier {};
215 let out_of_gas_err = run(&mut cache, &code, 10, true, querier).unwrap_err();
216 assert_eq!(out_of_gas_err, Error::OutOfGasError);
217 }
218}