1use crate::{ExecAction, ExecError, ExecOutcome, MemoryT, Virt, VirtT};
19
20const GAS_MAX: i64 = i64::MAX;
21
22pub fn run(program: &[u8]) {
33 counter_start_at_0(program);
34 counter_start_at_7(program);
35 counter_multiple_calls(program);
36 panic_works(program);
37 exit_works(program);
38 run_out_of_gas_works(program);
39 gas_consumption_works(program);
40 memory_reset_on_instantiate(program);
41 memory_persistent(program);
42 counter_in_subcall(program);
43}
44
45enum RunResult {
47 Ok,
49 Exit,
51 Err(ExecError),
53}
54
55fn run_loop(
60 virt: &mut Virt,
61 function: &str,
62 gas_left: &mut i64,
63 mut handler: impl FnMut(u32, u64, u64, u64, u64, u64, u64) -> Result<u64, ()>,
64) -> RunResult {
65 let mut action = ExecAction::Execute(function);
66 loop {
67 let outcome = match virt.run(*gas_left, action) {
68 Ok(outcome) => outcome,
69 Err(ExecError::OutOfGas) => {
70 *gas_left = 0;
71 return RunResult::Err(ExecError::OutOfGas);
72 },
73 Err(err) => return RunResult::Err(err),
74 };
75 match outcome {
76 ExecOutcome::Finished { gas_left: g } => {
77 *gas_left = g;
78 return RunResult::Ok;
79 },
80 ExecOutcome::Syscall { gas_left: g, syscall_no, a0, a1, a2, a3, a4, a5 } => {
81 *gas_left = g;
82 match handler(syscall_no, a0, a1, a2, a3, a4, a5) {
83 Ok(result) => action = ExecAction::Resume(result),
84 Err(()) => return RunResult::Exit,
85 }
86 },
87 }
88 }
89}
90
91fn make_handler<'a>(
95 counter: &'a mut u64,
96 memory: &'a mut <Virt as VirtT>::Memory,
97) -> impl FnMut(u32, u64, u64, u64, u64, u64, u64) -> Result<u64, ()> + 'a {
98 move |syscall_no, a0, _a1, _a2, _a3, _a4, _a5| match syscall_no {
99 1 => {
101 let buf = counter.to_le_bytes();
102 memory.write(a0 as u32, buf.as_ref()).unwrap();
103 Ok(syscall_no.into())
104 },
105 2 => {
107 let mut buf = [0u8; 8];
108 memory.read(a0 as u32, buf.as_mut()).unwrap();
109 *counter += u64::from_le_bytes(buf);
110 Ok(u64::from(syscall_no) << 56)
111 },
112 3 => Err(()),
114 _ => panic!("unknown syscall: {:?}", syscall_no),
115 }
116}
117
118fn counter_start_at_0(program: &[u8]) {
120 let mut instance = Virt::instantiate(program).unwrap();
121 let mut gas_left = GAS_MAX;
122 let mut counter: u64 = 0;
123 let mut memory = instance.memory();
124 let result =
125 run_loop(&mut instance, "counter", &mut gas_left, make_handler(&mut counter, &mut memory));
126 assert!(matches!(result, RunResult::Ok));
127 assert_eq!(counter, 8);
128}
129
130fn counter_start_at_7(program: &[u8]) {
132 let mut instance = Virt::instantiate(program).unwrap();
133 let mut gas_left = GAS_MAX;
134 let mut counter: u64 = 7;
135 let mut memory = instance.memory();
136 let result =
137 run_loop(&mut instance, "counter", &mut gas_left, make_handler(&mut counter, &mut memory));
138 assert!(matches!(result, RunResult::Ok));
139 assert_eq!(counter, 15);
140}
141
142fn counter_multiple_calls(program: &[u8]) {
144 let mut instance = Virt::instantiate(program).unwrap();
145 let mut gas_left = GAS_MAX;
146 let mut counter: u64 = 7;
147 let mut memory = instance.memory();
148
149 let result =
150 run_loop(&mut instance, "counter", &mut gas_left, make_handler(&mut counter, &mut memory));
151 assert!(matches!(result, RunResult::Ok));
152 assert_eq!(counter, 15);
153
154 let result =
155 run_loop(&mut instance, "counter", &mut gas_left, make_handler(&mut counter, &mut memory));
156 assert!(matches!(result, RunResult::Ok));
157 assert_eq!(counter, 23);
158}
159
160fn panic_works(program: &[u8]) {
162 let mut instance = Virt::instantiate(program).unwrap();
163 let mut gas_left = GAS_MAX;
164 let mut counter: u64 = 0;
165 let mut memory = instance.memory();
166 let result =
167 run_loop(&mut instance, "do_panic", &mut gas_left, make_handler(&mut counter, &mut memory));
168 assert!(matches!(result, RunResult::Err(ExecError::Trap)));
169 assert_eq!(counter, 0);
170}
171
172fn exit_works(program: &[u8]) {
174 let mut instance = Virt::instantiate(program).unwrap();
175 let mut gas_left = GAS_MAX;
176 let mut counter: u64 = 0;
177 let mut memory = instance.memory();
178 let result =
179 run_loop(&mut instance, "do_exit", &mut gas_left, make_handler(&mut counter, &mut memory));
180 assert!(matches!(result, RunResult::Exit));
181 assert_eq!(counter, 0);
182}
183
184fn run_out_of_gas_works(program: &[u8]) {
186 let mut instance = Virt::instantiate(program).unwrap();
187 let mut gas_left: i64 = 100_000;
188 let mut counter: u64 = 0;
189 let mut memory = instance.memory();
190 let result = run_loop(
191 &mut instance,
192 "increment_forever",
193 &mut gas_left,
194 make_handler(&mut counter, &mut memory),
195 );
196 assert!(matches!(result, RunResult::Err(ExecError::OutOfGas)));
197 assert_eq!(counter, 14_285);
198 assert_eq!(gas_left, 0);
199}
200
201fn gas_consumption_works(program: &[u8]) {
203 let gas_limit_0 = GAS_MAX;
204 let gas_limit_1 = gas_limit_0 / 2;
205
206 let mut instance = Virt::instantiate(program).unwrap();
207 let mut gas_left = gas_limit_0;
208 let mut counter: u64 = 0;
209 let mut memory = instance.memory();
210 let result =
211 run_loop(&mut instance, "counter", &mut gas_left, make_handler(&mut counter, &mut memory));
212 assert!(matches!(result, RunResult::Ok));
213 let gas_consumed = gas_limit_0 - gas_left;
214
215 let mut instance = Virt::instantiate(program).unwrap();
216 let mut gas_left = gas_limit_1;
217 let mut counter: u64 = 0;
218 let mut memory = instance.memory();
219 let result =
220 run_loop(&mut instance, "counter", &mut gas_left, make_handler(&mut counter, &mut memory));
221 assert!(matches!(result, RunResult::Ok));
222 assert_eq!(gas_consumed, gas_limit_1 - gas_left);
223}
224
225fn memory_reset_on_instantiate(program: &[u8]) {
227 let mut instance = Virt::instantiate(program).unwrap();
228 let mut gas_left = GAS_MAX;
229 let mut counter: u64 = 0;
230 let mut memory = instance.memory();
231 let result =
232 run_loop(&mut instance, "offset", &mut gas_left, make_handler(&mut counter, &mut memory));
233 assert!(matches!(result, RunResult::Ok));
234 assert_eq!(counter, 3);
235
236 let mut instance = Virt::instantiate(program).unwrap();
237 let mut memory = instance.memory();
238 let result =
239 run_loop(&mut instance, "offset", &mut gas_left, make_handler(&mut counter, &mut memory));
240 assert!(matches!(result, RunResult::Ok));
241 assert_eq!(counter, 6);
242}
243
244fn memory_persistent(program: &[u8]) {
246 let mut instance = Virt::instantiate(program).unwrap();
247 let mut gas_left = GAS_MAX;
248 let mut counter: u64 = 0;
249 let mut memory = instance.memory();
250
251 let result =
252 run_loop(&mut instance, "offset", &mut gas_left, make_handler(&mut counter, &mut memory));
253 assert!(matches!(result, RunResult::Ok));
254 assert_eq!(counter, 3);
255
256 let result =
257 run_loop(&mut instance, "offset", &mut gas_left, make_handler(&mut counter, &mut memory));
258 assert!(matches!(result, RunResult::Ok));
259 assert_eq!(counter, 7);
260}
261
262fn counter_in_subcall(program: &[u8]) {
264 let mut instance = Virt::instantiate(program).unwrap();
265 let mut gas_left = GAS_MAX;
266 let mut counter: u64 = 0;
267 let mut memory = instance.memory();
268 let program = program.to_vec();
269 let result = run_loop(
270 &mut instance,
271 "do_subcall",
272 &mut gas_left,
273 |syscall_no, a0, a1, a2, a3, a4, a5| {
274 match syscall_no {
275 1..=3 => {
276 make_handler(&mut counter, &mut memory)(syscall_no, a0, a1, a2, a3, a4, a5)
277 },
278 4 => {
280 let mut sub_instance = Virt::instantiate(program.as_ref()).unwrap();
281 let mut sub_gas = GAS_MAX;
282 let mut sub_counter: u64 = 0;
283 let mut sub_memory = sub_instance.memory();
284 let result = run_loop(
285 &mut sub_instance,
286 "counter",
287 &mut sub_gas,
288 make_handler(&mut sub_counter, &mut sub_memory),
289 );
290 assert!(matches!(result, RunResult::Ok));
291 assert_eq!(sub_counter, 8);
292 Ok(0)
293 },
294 _ => panic!("unknown syscall: {:?}", syscall_no),
295 }
296 },
297 );
298 assert!(matches!(result, RunResult::Ok));
299 assert_eq!(counter, 0);
301}
302
303#[cfg(test)]
304#[test]
305fn tests() {
306 sp_tracing::try_init_simple();
307 run(sp_virtualization_test_fixture::binary());
308}