rust_simple_stack_processor/
lib.rs1use std::convert::TryFrom;
2use thiserror::Error;
3
4#[cfg(test)]
5mod tests;
6
7#[derive(Debug)]
9pub enum GasLimit {
10 Unlimited,
11 Limited(u64),
12}
13
14#[derive(Error, Debug)]
16pub enum StackMachineError {
17 #[error("Division by zero")]
18 DivisionByZero { failing_opcode: Opcode },
19
20 #[error("Numeric overflow")]
21 NumericOverflow { failing_opcode: Opcode },
22
23 #[error("TryFromInt error")]
24 TryFromIntError(#[from] std::num::TryFromIntError),
25
26 #[error("The internal number stack has underflowed (do you have too many POPs?)")]
27 NumberStackUnderflow,
28
29 #[error("The internal loop stack has underflowed (are you missing a loop start opcode?)")]
30 LoopStackUnderflow,
31
32 #[error(
33 "The internal scratch stack has underflowed (do you have too many scratch stack POPs?)"
34 )]
35 ScratchStackUnderflow,
36
37 #[error("Invalid Cell Operation (perhaps your parameters are incorrect?)")]
38 InvalidCellOperation,
39
40 #[error("Unhandled trap id: {unhandled_trap_id}")]
41 UnhandledTrap { unhandled_trap_id: i64 },
42
43 #[error("You used too much gas during execution (used {gas_used:?}, gas_limit {gas_limit:?}")]
44 RanOutOfGas { gas_used: u64, gas_limit: GasLimit },
45
46 #[error("Unknown StackMachineError")]
47 UnknownError,
48}
49
50pub enum TrapHandled {
52 Handled,
53 NotHandled,
54}
55
56pub trait HandleTrap {
58 fn handle_trap(
59 &mut self,
60 trap_id: i64,
61 st: &mut StackMachineState,
62 ) -> Result<TrapHandled, StackMachineError>;
63}
64
65pub struct TrapHandler<'a> {
67 handled_trap: i64,
68 to_run: Box<dyn Fn(i64, &mut StackMachineState) -> Result<TrapHandled, StackMachineError> + 'a>,
69}
70
71impl<'a> TrapHandler<'a> {
72 pub fn new<C>(handled_trap: i64, f: C) -> TrapHandler<'a>
74 where
75 C: Fn(i64, &mut StackMachineState) -> Result<TrapHandled, StackMachineError> + 'a,
76 {
77 TrapHandler {
78 handled_trap,
79 to_run: Box::new(f),
80 }
81 }
82}
83
84impl<'a> HandleTrap for TrapHandler<'a> {
85 fn handle_trap(
86 &mut self,
87 trap_number: i64,
88 st: &mut StackMachineState,
89 ) -> Result<TrapHandled, StackMachineError> {
90 if trap_number == self.handled_trap {
91 (self.to_run)(self.handled_trap, st)
92 } else {
93 Ok(TrapHandled::NotHandled)
94 }
95 }
96}
97
98#[derive(Debug, Clone, PartialEq)]
100pub enum Opcode {
101 JMP,
102 JR,
103 JRZ,
104 JRNZ,
105 CALL,
106 CMPZ,
107 CMPNZ,
108 LDI(i64),
109 DROP,
110 SWAP,
111 SWAP2,
112 RET,
113 ADD,
114 SUB,
115 MUL,
116 DIV,
117 NOT,
118 DUP,
119 DUP2,
120 TRAP,
121 NOP,
122 PUSHLP,
123 INCLP,
124 ADDLP,
125 GETLP,
126 GETLP2,
127 DROPLP,
128 CMPLOOP,
129 OVER2,
130 GtR,
131 RGt,
132 RAt,
133 GtR2,
134 RGt2,
135 RAt2,
136 AND,
137 NEWCELLS,
138 MOVETOCELLS,
139 MOVEFROMCELLS,
140}
141
142pub struct StackMachineState {
144 pub number_stack: Vec<i64>,
145 pub scratch_stack: Vec<i64>,
146 return_stack: Vec<usize>,
147 loop_stack: Vec<(i64, i64)>,
149 cells: Vec<i64>,
150 pub opcodes: Vec<Opcode>,
151 pc: usize,
152 gas_used: u64,
153}
154
155impl Default for StackMachineState {
156 fn default() -> Self {
157 StackMachineState {
158 number_stack: Vec::new(),
159 scratch_stack: Vec::new(),
160 return_stack: Vec::new(),
161 loop_stack: Vec::new(),
162 cells: Vec::new(),
163 opcodes: Vec::new(),
164 pc: 0,
165 gas_used: 0,
166 }
167 }
168}
169
170impl StackMachineState {
171 pub fn gas_used(&self) -> u64 {
173 self.gas_used
174 }
175}
176
177pub struct StackMachine {
179 pub st: StackMachineState,
180 pub trap_handlers: Vec<Box<dyn HandleTrap>>,
181}
182
183impl Default for StackMachine {
184 fn default() -> StackMachine {
185 StackMachine {
186 st: StackMachineState::default(),
187 trap_handlers: Vec::new(),
188 }
189 }
190}
191
192macro_rules! pop_number_stack {
193 ($variable:ident) => {
194 $variable
195 .st
196 .number_stack
197 .pop()
198 .ok_or(StackMachineError::NumberStackUnderflow)?
199 };
200}
201
202macro_rules! push_number_stack {
203 ($variable:ident,$expr:expr) => {
204 $variable.st.number_stack.push($expr)
205 };
206}
207
208macro_rules! pop_scratch_stack {
209 ($variable:ident) => {
210 $variable
211 .st
212 .scratch_stack
213 .pop()
214 .ok_or(StackMachineError::ScratchStackUnderflow)?
215 };
216}
217
218macro_rules! push_scratch_stack {
219 ($variable:ident,$expr:expr) => {
220 $variable.st.scratch_stack.push($expr);
221 };
222}
223
224macro_rules! last_scratch_stack {
225 ($variable:ident) => {
226 $variable
227 .st
228 .scratch_stack
229 .last()
230 .ok_or(StackMachineError::ScratchStackUnderflow)?
231 };
232}
233
234impl StackMachine {
235 pub fn execute(
246 &mut self,
247 starting_point: usize,
248 gas_limit: GasLimit,
249 ) -> Result<(), StackMachineError> {
250 self.st.gas_used = 0;
251 self.st.pc = starting_point;
252 loop {
253 let mut pc_reset = false;
254 let opcode = self
255 .st
256 .opcodes
257 .get(self.st.pc)
258 .ok_or(StackMachineError::UnknownError)?;
259 match opcode {
260 Opcode::JMP => {
261 let target = usize::try_from(pop_number_stack!(self))?;
262 self.st.pc = target;
263 pc_reset = true;
264 }
265 Opcode::JR => {
266 let offset = pop_number_stack!(self);
267 let new_offset = i64::try_from(self.st.pc)? + offset;
268 self.st.pc = usize::try_from(new_offset)?;
269 pc_reset = true;
270 }
271 Opcode::CALL => {
272 self.st.return_stack.push(self.st.pc + 1);
273 let target = usize::try_from(pop_number_stack!(self))?;
274 self.st.pc = target;
275 pc_reset = true;
276 }
277 Opcode::CMPZ => {
278 let x = pop_number_stack!(self);
279 self.st.number_stack.push(if x == 0 { -1 } else { 0 });
280 }
281 Opcode::CMPNZ => {
282 let x = pop_number_stack!(self);
283 self.st.number_stack.push(if x == 0 { 0 } else { -1 });
284 }
285 Opcode::JRZ => {
286 let offset = pop_number_stack!(self);
287 let new_offset = i64::try_from(self.st.pc)? + offset;
288 let x = pop_number_stack!(self);
289 if x == 0 {
290 self.st.pc = usize::try_from(new_offset)?;
291 pc_reset = true;
292 }
293 }
294 Opcode::JRNZ => {
295 let offset = pop_number_stack!(self);
296 let new_offset = i64::try_from(self.st.pc)? + offset;
297 let x = pop_number_stack!(self);
298 if x != 0 {
299 self.st.pc = usize::try_from(new_offset)?;
300 pc_reset = true;
301 }
302 }
303 Opcode::LDI(x) => push_number_stack!(self, *x),
304 Opcode::DROP => {
305 let _ = pop_number_stack!(self);
306 }
307 Opcode::RET => {
308 if let Some(oldpc) = self.st.return_stack.pop() {
309 self.st.pc = oldpc;
310 pc_reset = true;
311 } else {
312 return Ok(());
313 }
314 }
315 Opcode::GtR => {
316 let x = pop_number_stack!(self);
317 push_scratch_stack!(self, x);
318 }
319 Opcode::RGt => {
320 let x = pop_scratch_stack!(self);
321 push_number_stack!(self, x);
322 }
323 Opcode::RAt => {
324 let x = last_scratch_stack!(self);
325 push_number_stack!(self, *x);
326 }
327 Opcode::GtR2 => {
328 let x = pop_number_stack!(self);
329 let y = pop_number_stack!(self);
330 push_scratch_stack!(self, y);
331 push_scratch_stack!(self, x);
332 }
333 Opcode::RGt2 => {
334 let x = pop_scratch_stack!(self);
335 let y = pop_scratch_stack!(self);
336 push_number_stack!(self, y);
337 push_number_stack!(self, x);
338 }
339 Opcode::RAt2 => {
340 let x = pop_scratch_stack!(self);
341 let y = pop_scratch_stack!(self);
342 push_scratch_stack!(self, y);
343 push_scratch_stack!(self, x);
344 push_number_stack!(self, y);
345 push_number_stack!(self, x);
346 }
347 Opcode::ADD => {
348 let x = pop_number_stack!(self);
349 let y = pop_number_stack!(self);
350 push_number_stack!(
351 self,
352 x.checked_add(y).ok_or(StackMachineError::NumericOverflow {
353 failing_opcode: opcode.clone()
354 })?
355 );
356 }
357 Opcode::SUB => {
358 let x = pop_number_stack!(self);
359 let y = pop_number_stack!(self);
360 push_number_stack!(
361 self,
362 x.checked_sub(y).ok_or(StackMachineError::NumericOverflow {
363 failing_opcode: opcode.clone()
364 })?
365 );
366 }
367 Opcode::MUL => {
368 let x = pop_number_stack!(self);
369 let y = pop_number_stack!(self);
370 push_number_stack!(
371 self,
372 x.checked_mul(y).ok_or(StackMachineError::NumericOverflow {
373 failing_opcode: opcode.clone()
374 })?
375 );
376 }
377 Opcode::DIV => {
378 let x = pop_number_stack!(self);
379 let y = pop_number_stack!(self);
380 push_number_stack!(
381 self,
382 y.checked_div(x).ok_or(StackMachineError::DivisionByZero {
383 failing_opcode: opcode.clone()
384 })?
385 );
386 }
387 Opcode::NOT => {
388 let x = pop_number_stack!(self);
389 push_number_stack!(self, if x == 0 { 1 } else { 0 });
390 }
391 Opcode::DUP => {
392 let x = pop_number_stack!(self);
393 push_number_stack!(self, x);
394 push_number_stack!(self, x);
395 }
396 Opcode::DUP2 => {
397 let x = pop_number_stack!(self);
398 let y = pop_number_stack!(self);
399 push_number_stack!(self, y);
400 push_number_stack!(self, x);
401 push_number_stack!(self, y);
402 push_number_stack!(self, x);
403 }
404 Opcode::OVER2 => {
405 let x4 = pop_number_stack!(self);
406 let x3 = pop_number_stack!(self);
407 let x2 = pop_number_stack!(self);
408 let x1 = pop_number_stack!(self);
409 push_number_stack!(self, x1);
410 push_number_stack!(self, x2);
411 push_number_stack!(self, x3);
412 push_number_stack!(self, x4);
413 push_number_stack!(self, x1);
414 push_number_stack!(self, x2);
415 }
416 Opcode::SWAP => {
417 let x = pop_number_stack!(self);
418 let y = pop_number_stack!(self);
419 push_number_stack!(self, x);
420 push_number_stack!(self, y);
421 }
422 Opcode::SWAP2 => {
423 let x4 = pop_number_stack!(self);
424 let x3 = pop_number_stack!(self);
425 let x2 = pop_number_stack!(self);
426 let x1 = pop_number_stack!(self);
427 push_number_stack!(self, x3);
428 push_number_stack!(self, x4);
429 push_number_stack!(self, x1);
430 push_number_stack!(self, x2);
431 }
432 Opcode::TRAP => {
433 let trap_id = pop_number_stack!(self);
434 for h in self.trap_handlers.iter_mut() {
435 if let TrapHandled::Handled = h.handle_trap(trap_id, &mut self.st)? {
436 return Ok(());
437 }
438 }
439 return Err(StackMachineError::UnhandledTrap {
440 unhandled_trap_id: trap_id,
441 });
442 }
443 Opcode::NOP => {}
444 Opcode::PUSHLP => {
445 let current_index = pop_number_stack!(self);
446 let max_index = pop_number_stack!(self);
447 self.st.loop_stack.push((current_index, max_index));
448 }
449 Opcode::INCLP => {
450 if let Some((current_index, _)) = self.st.loop_stack.last_mut() {
451 *current_index += 1;
452 } else {
453 return Err(StackMachineError::LoopStackUnderflow);
454 }
455 }
456 Opcode::ADDLP => {
457 let increment = pop_number_stack!(self);
458 if let Some((current_index, _)) = self.st.loop_stack.last_mut() {
459 *current_index += increment;
460 } else {
461 return Err(StackMachineError::LoopStackUnderflow);
462 }
463 }
464 Opcode::GETLP => {
465 let (current_index, _) = self
466 .st
467 .loop_stack
468 .last()
469 .ok_or(StackMachineError::LoopStackUnderflow)?;
470 self.st.number_stack.push(*current_index);
471 }
472 Opcode::GETLP2 => {
473 if self.st.loop_stack.len() < 2 {
474 return Err(StackMachineError::LoopStackUnderflow);
475 }
476 let (current_index, _) = self
477 .st
478 .loop_stack
479 .get(self.st.loop_stack.len() - 2)
480 .ok_or(StackMachineError::LoopStackUnderflow)?;
481 self.st.number_stack.push(*current_index);
482 }
483 Opcode::DROPLP => {
484 self.st
485 .loop_stack
486 .pop()
487 .ok_or(StackMachineError::LoopStackUnderflow)?;
488 }
489 Opcode::CMPLOOP => {
490 let (current_index, max_index) = self
491 .st
492 .loop_stack
493 .last()
494 .ok_or(StackMachineError::LoopStackUnderflow)?;
495 self.st
496 .number_stack
497 .push(if *current_index >= *max_index { 1 } else { 0 });
498 }
499 Opcode::AND => {
500 let x = pop_number_stack!(self);
501 let y = pop_number_stack!(self);
502 push_number_stack!(self, x & y);
503 }
504 Opcode::NEWCELLS => {
505 let num_cells = usize::try_from(pop_number_stack!(self))
506 .map_err(|_| StackMachineError::InvalidCellOperation)?;
507 let newaddress = self.st.cells.len();
508 self.st
509 .cells
510 .resize_with(newaddress + num_cells, Default::default);
511 }
512 Opcode::MOVETOCELLS => {
513 let num_cells = usize::try_from(pop_number_stack!(self))
514 .map_err(|_| StackMachineError::InvalidCellOperation)?;
515 let address = usize::try_from(pop_number_stack!(self))
516 .map_err(|_| StackMachineError::InvalidCellOperation)?;
517 if num_cells < 1 || self.st.cells.len() < address + num_cells {
518 return Err(StackMachineError::InvalidCellOperation);
519 }
520 for i in address..address + num_cells {
521 self.st.cells[i] = pop_number_stack!(self);
522 }
523 }
524 Opcode::MOVEFROMCELLS => {
525 let num_cells = usize::try_from(pop_number_stack!(self))
526 .map_err(|_| StackMachineError::InvalidCellOperation)?;
527 let address = usize::try_from(pop_number_stack!(self))
528 .map_err(|_| StackMachineError::InvalidCellOperation)?;
529 if num_cells < 1 || self.st.cells.len() < address + num_cells {
530 return Err(StackMachineError::InvalidCellOperation);
531 }
532 for i in (address..address + num_cells).rev() {
533 push_number_stack!(self, self.st.cells[i]);
534 }
535 }
536 }
537 if !pc_reset {
538 self.st.pc += 1;
539 }
540
541 self.st.gas_used += 1;
542
543 if let GasLimit::Limited(limit) = gas_limit {
544 if self.st.gas_used > limit {
545 return Err(StackMachineError::RanOutOfGas {
546 gas_used: self.st.gas_used,
547 gas_limit,
548 });
549 }
550 }
551 }
552 }
553}