1use anyhow::{Result, bail, ensure};
2use charms_data::{App, B32, Data, Transaction, is_simple_transfer, util};
3use serde::{Deserialize, Serialize};
4use sha2::{Digest, Sha256};
5use std::{
6 collections::BTreeMap,
7 io::Write,
8 sync::{Arc, Mutex},
9};
10use wasmi::{Caller, Config, Engine, Extern, Linker, Memory, Module, Store};
11
12#[derive(Clone, Debug, Serialize, Deserialize)]
13pub struct AppProverInput {
14 pub app_binaries: BTreeMap<B32, Vec<u8>>,
15 pub tx: Transaction,
16 pub app_public_inputs: BTreeMap<App, Data>,
17 pub app_private_inputs: BTreeMap<App, Data>,
18}
19
20#[derive(Clone, Debug, Serialize, Deserialize)]
21pub struct AppProverOutput {
22 pub tx: Transaction,
23 pub app_public_inputs: BTreeMap<App, Data>,
24 pub cycles: Vec<u64>,
25}
26
27#[derive(Clone)]
28pub struct AppRunner {
29 pub count_cycles: bool,
30 pub engine: Engine,
31}
32
33#[derive(Clone)]
34struct HostState {
35 stdin: Arc<Mutex<Vec<u8>>>, stderr: Arc<Mutex<dyn Write>>, }
38
39fn read_i32(memory: &Memory, caller: &mut Caller<'_, HostState>, ptr: i32) -> Result<i32> {
41 let data = read_memory(memory, caller, ptr as usize, 4)?;
42 Ok(i32::from_le_bytes(data.try_into().unwrap()))
43}
44
45fn write_i32(
46 memory: &Memory,
47 caller: &mut Caller<'_, HostState>,
48 ptr: i32,
49 value: i32,
50) -> Result<()> {
51 let data = value.to_le_bytes();
52 write_memory(memory, caller, ptr as usize, &data)
53}
54
55fn read_memory(
56 memory: &Memory,
57 caller: &mut Caller<'_, HostState>,
58 ptr: usize,
59 len: usize,
60) -> Result<Vec<u8>> {
61 let mut buffer = vec![0; len];
62 memory.read(caller, ptr, &mut buffer)?;
63 Ok(buffer)
64}
65
66fn write_memory(
67 memory: &Memory,
68 caller: &mut Caller<'_, HostState>,
69 ptr: usize,
70 data: &[u8],
71) -> Result<()> {
72 memory.write(caller, ptr, data)?;
73 Ok(())
74}
75
76fn fd_read_impl(
77 mut caller: Caller<'_, HostState>,
78 fd: i32,
79 iovs: i32,
80 iovs_len: i32,
81 nread: i32,
82) -> Result<i32> {
83 if fd != 0 {
84 return Ok(-1); }
86
87 let memory = caller
88 .get_export("memory")
89 .and_then(Extern::into_memory)
90 .ok_or_else(|| anyhow::anyhow!("No memory export"))?;
91
92 let iov_size = 8;
94 let mut iov_info = Vec::new();
95 for i in 0..iovs_len {
96 let iov_addr = iovs + i * iov_size;
97 let buf_ptr = read_i32(&memory, &mut caller, iov_addr).unwrap() as usize;
98 let buf_len = read_i32(&memory, &mut caller, iov_addr + 4).unwrap() as usize;
99 iov_info.push((buf_ptr, buf_len));
100 }
101
102 let stdin_data = {
104 let state = caller.data();
105 let mut stdin = state.stdin.lock().unwrap();
106
107 let mut total_read = 0;
108 let mut operations = Vec::new();
109
110 for (buf_ptr, buf_len) in iov_info {
111 let to_read = buf_len.min(stdin.len());
113 if to_read == 0 {
114 break; }
116 let data = stdin.drain(..to_read).collect::<Vec<_>>();
117 operations.push((buf_ptr, data));
118 total_read += to_read;
119 }
120
121 (operations, total_read)
122 };
123
124 for (buf_ptr, data) in stdin_data.0 {
126 write_memory(&memory, &mut caller, buf_ptr, &data).unwrap();
127 }
128
129 write_i32(&memory, &mut caller, nread, stdin_data.1 as i32)?;
131
132 Ok(0) }
134
135fn fd_write_impl(
136 mut caller: Caller<'_, HostState>,
137 fd: i32,
138 iovs: i32,
139 iovs_len: i32,
140 nwritten: i32,
141) -> Result<i32> {
142 if fd != 2 {
143 bail!("can only write to stderr"); }
145
146 let memory = caller
147 .get_export("memory")
148 .and_then(Extern::into_memory)
149 .ok_or_else(|| anyhow::anyhow!("No memory export"))?;
150
151 let iov_size = 8; let mut total_written = 0;
154 let mut all_data = Vec::new();
155
156 for i in 0..iovs_len {
157 let iov_addr = iovs + i * iov_size;
158 let buf_ptr = read_i32(&memory, &mut caller, iov_addr)? as usize;
160 let buf_len = read_i32(&memory, &mut caller, iov_addr + 4)? as usize;
161
162 let data = read_memory(&memory, &mut caller, buf_ptr, buf_len)?;
164 all_data.extend_from_slice(&data);
165 total_written += buf_len;
166 }
167
168 {
170 let state = caller.data_mut();
171 let mut stderr = state.stderr.lock().unwrap();
172 stderr.write_all(&all_data)?;
173 }
174
175 write_i32(&memory, &mut caller, nwritten, total_written as i32)?;
177
178 Ok(0) }
180
181fn fd_write(
182 caller: Caller<'_, HostState>,
183 fd: i32,
184 iovs: i32,
185 iovs_len: i32,
186 nwritten: i32,
187) -> i32 {
188 let result = fd_write_impl(caller, fd, iovs, iovs_len, nwritten);
189 result.unwrap_or_else(|e| {
190 eprintln!("error: {}", e);
191 -1
192 })
193}
194
195fn fd_read(caller: Caller<'_, HostState>, fd: i32, iovs: i32, iovs_len: i32, nread: i32) -> i32 {
196 fd_read_impl(caller, fd, iovs, iovs_len, nread).unwrap_or_else(|e| {
197 eprintln!("error: {}", e);
198 -1
199 })
200}
201
202fn environ_sizes_get_impl(
203 mut caller: Caller<'_, HostState>,
204 environc_ptr: i32,
205 environ_buf_size_ptr: i32,
206) -> Result<i32> {
207 let memory = caller
208 .get_export("memory")
209 .and_then(Extern::into_memory)
210 .ok_or_else(|| anyhow::anyhow!("No memory export"))?;
211
212 write_i32(&memory, &mut caller, environc_ptr, 0)?;
214 write_i32(&memory, &mut caller, environ_buf_size_ptr, 0)?;
216
217 Ok(0) }
219
220fn environ_get_impl(
221 _caller: Caller<'_, HostState>,
222 _environ_ptr: i32,
223 _environ_buf_ptr: i32,
224) -> Result<i32> {
225 Ok(0) }
228
229fn environ_sizes_get(
230 caller: Caller<'_, HostState>,
231 environc_ptr: i32,
232 environ_buf_size_ptr: i32,
233) -> i32 {
234 environ_sizes_get_impl(caller, environc_ptr, environ_buf_size_ptr).unwrap_or_else(|e| {
235 eprintln!("error: {}", e);
236 -1
237 })
238}
239
240fn environ_get(caller: Caller<'_, HostState>, environ_ptr: i32, environ_buf_ptr: i32) -> i32 {
241 environ_get_impl(caller, environ_ptr, environ_buf_ptr).unwrap_or_else(|e| {
242 eprintln!("error: {}", e);
243 -1
244 })
245}
246const MAX_FUEL_PER_RUN: u64 = 1000000000;
247
248impl AppRunner {
249 pub fn new(count_cycles: bool) -> Self {
250 let mut config = Config::default();
251 if count_cycles {
252 config.consume_fuel(true);
253 }
254 Self {
255 count_cycles,
256 engine: Engine::new(&config),
257 }
258 }
259
260 pub fn vk(&self, binary: &[u8]) -> B32 {
261 let hash = Sha256::digest(binary);
262 B32(hash.into())
263 }
264
265 pub fn run(
266 &self,
267 app_binary: &[u8],
268 app: &App,
269 tx: &Transaction,
270 x: &Data,
271 w: &Data,
272 ) -> Result<u64> {
273 let vk = self.vk(app_binary);
274 ensure!(app.vk == vk, "app.vk mismatch");
275
276 let stdin_content = util::write(&(app, tx, x, w))?;
277
278 let state = HostState {
279 stdin: Arc::new(Mutex::new(stdin_content)),
280 stderr: Arc::new(Mutex::new(std::io::stderr())),
281 };
282
283 let mut store = Store::new(&self.engine, state.clone());
284 if self.count_cycles {
285 store.set_fuel(MAX_FUEL_PER_RUN)?;
286 }
287 let mut linker = Linker::new(&self.engine);
288
289 linker.func_wrap("wasi_snapshot_preview1", "fd_write", fd_write)?;
290 linker.func_wrap("wasi_snapshot_preview1", "fd_read", fd_read)?;
291 linker.func_wrap("wasi_snapshot_preview1", "environ_get", environ_get)?;
292 linker.func_wrap(
293 "wasi_snapshot_preview1",
294 "environ_sizes_get",
295 environ_sizes_get,
296 )?;
297 linker.func_wrap(
298 "wasi_snapshot_preview1",
299 "proc_exit",
300 |_: Caller<'_, HostState>, _: i32| {},
301 )?;
302
303 let module = Module::new(&self.engine, app_binary)?;
304
305 let instance = linker.instantiate_and_start(&mut store, &module)?;
306
307 let Some(main_func) = instance.get_func(&store, "_start") else {
308 unreachable!("we should have a main function")
309 };
310 let result = main_func.typed::<(), ()>(&store)?.call(&mut store, ());
311
312 state.stderr.lock().unwrap().flush()?;
313
314 result.map_err(|e| anyhow::anyhow!("error running wasm: {:?}", e))?;
315
316 let cycles = match self.count_cycles {
317 true => MAX_FUEL_PER_RUN - store.get_fuel()?,
318 false => 0,
319 };
320 Ok(cycles)
321 }
322
323 pub fn run_all(
324 &self,
325 app_binaries: &BTreeMap<B32, Vec<u8>>,
326 tx: &Transaction,
327 app_public_inputs: &BTreeMap<App, Data>,
328 app_private_inputs: &BTreeMap<App, Data>,
329 ) -> Result<Vec<u64>> {
330 let empty = Data::empty();
331 let app_cycles = app_public_inputs
332 .iter()
333 .map(|(app, x)| {
334 let w = app_private_inputs.get(app).unwrap_or(&empty);
335 match app_binaries.get(&app.vk) {
336 Some(app_binary) => {
337 let cycles = self.run(app_binary, app, tx, x, w)?;
338 eprintln!("✅ app contract satisfied: {}", app);
339 Ok(cycles)
340 }
341 None => {
342 ensure!(is_simple_transfer(app, tx));
343 eprintln!("✅ simple transfer ok: {}", app);
344 Ok(0)
345 }
346 }
347 })
348 .collect::<Result<_>>()?;
349
350 Ok(app_cycles)
351 }
352}