1use vanta_core::{Area, VtaError, VtaResult};
11use wasmtime::{Config, Engine, Instance, Module, Store};
12
13pub struct Sandbox {
15 engine: Engine,
16}
17
18impl Sandbox {
19 pub fn new() -> VtaResult<Sandbox> {
21 let mut config = Config::new();
22 config.consume_fuel(true);
23 let engine = Engine::new(&config).map_err(|e| err(format!("engine: {e}")))?;
24 Ok(Sandbox { engine })
25 }
26
27 pub fn run_i32(&self, wasm: &[u8], func: &str, arg: i32, fuel: u64) -> VtaResult<i32> {
32 let module = Module::new(&self.engine, wasm).map_err(|e| err(format!("compile: {e}")))?;
33 let mut store = Store::new(&self.engine, ());
34 store
35 .set_fuel(fuel)
36 .map_err(|e| err(format!("set fuel: {e}")))?;
37 let instance = Instance::new(&mut store, &module, &[])
39 .map_err(|e| err(format!("instantiate (no capabilities granted): {e}")))?;
40 let typed = instance
41 .get_typed_func::<i32, i32>(&mut store, func)
42 .map_err(|e| err(format!("export `{func}`: {e}")))?;
43 typed
44 .call(&mut store, arg)
45 .map_err(|e| err(format!("guest trap: {e}")))
46 }
47}
48
49fn err(msg: String) -> VtaError {
50 VtaError::new(Area::Prov, 1, msg)
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 const DOUBLE: &str = r#"(module
59 (func (export "double") (param i32) (result i32)
60 local.get 0
61 i32.const 2
62 i32.mul))"#;
63
64 const SPIN: &str = r#"(module
66 (func (export "spin") (param i32) (result i32)
67 (loop (br 0))
68 i32.const 0))"#;
69
70 const NEEDS_IMPORT: &str = r#"(module
73 (import "env" "secret" (func $secret (result i32)))
74 (func (export "go") (param i32) (result i32) (call $secret)))"#;
75
76 #[test]
77 fn runs_pure_compute() {
78 let wasm = wat::parse_str(DOUBLE).unwrap();
79 let sb = Sandbox::new().unwrap();
80 assert_eq!(sb.run_i32(&wasm, "double", 21, 1_000_000).unwrap(), 42);
81 }
82
83 #[test]
84 fn fuel_exhaustion_traps_not_hangs() {
85 let wasm = wat::parse_str(SPIN).unwrap();
86 let sb = Sandbox::new().unwrap();
87 let err = sb.run_i32(&wasm, "spin", 0, 10_000).unwrap_err();
88 assert_eq!(err.area, Area::Prov); }
90
91 #[test]
92 fn imports_are_denied() {
93 let wasm = wat::parse_str(NEEDS_IMPORT).unwrap();
94 let sb = Sandbox::new().unwrap();
95 assert!(sb.run_i32(&wasm, "go", 0, 1_000_000).is_err());
97 }
98}