owasm_vm/
calls.rs

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    // get function and exec
30    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}