rust_simple_stack_processor/
lib.rs1use std::convert::TryFrom;
2use thiserror::Error;
3
4#[cfg(test)]
5mod tests;
6
7#[derive(Debug, Clone, PartialEq)]
9pub enum GasLimit {
10 Unlimited,
11 Limited(u64),
12}
13
14#[derive(Error, Debug, Clone, PartialEq)]
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
50#[derive(Debug, Clone, PartialEq)]
52pub enum TrapHandled {
53 Handled,
54 NotHandled,
55}
56
57pub trait HandleTrap {
59 fn handle_trap(
60 &mut self,
61 trap_id: i64,
62 st: &mut StackMachineState,
63 ) -> Result<TrapHandled, StackMachineError>;
64}
65
66pub struct TrapHandler<'a> {
68 handled_trap: i64,
69 to_run: Box<dyn Fn(i64, &mut StackMachineState) -> Result<TrapHandled, StackMachineError> + 'a>,
70}
71
72impl<'a> TrapHandler<'a> {
73 pub fn new<C>(handled_trap: i64, f: C) -> TrapHandler<'a>
75 where
76 C: Fn(i64, &mut StackMachineState) -> Result<TrapHandled, StackMachineError> + 'a,
77 {
78 TrapHandler {
79 handled_trap,
80 to_run: Box::new(f),
81 }
82 }
83}
84
85impl<'a> HandleTrap for TrapHandler<'a> {
86 fn handle_trap(
87 &mut self,
88 trap_number: i64,
89 st: &mut StackMachineState,
90 ) -> Result<TrapHandled, StackMachineError> {
91 if trap_number == self.handled_trap {
92 (self.to_run)(self.handled_trap, st)
93 } else {
94 Ok(TrapHandled::NotHandled)
95 }
96 }
97}
98
99#[derive(Debug, Clone, PartialEq)]
101pub enum Opcode {
102 JMP,
103 JR,
104 JRZ,
105 JRNZ,
106 CALL,
107 CMPZ,
108 CMPNZ,
109 LDI(i64),
110 DROP,
111 SWAP,
112 SWAP2,
113 RET,
114 ADD,
115 SUB,
116 MUL,
117 DIV,
118 NOT,
119 DUP,
120 DUP2,
121 TRAP,
122 NOP,
123 PUSHLP,
124 INCLP,
125 ADDLP,
126 GETLP,
127 GETLP2,
128 DROPLP,
129 CMPLOOP,
130 OVER2,
131 GtR,
132 RGt,
133 RAt,
134 GtR2,
135 RGt2,
136 RAt2,
137 AND,
138 NEWCELLS,
139 MOVETOCELLS,
140 MOVEFROMCELLS,
141}
142
143pub struct StackMachineState {
145 pub number_stack: Vec<i64>,
146 pub scratch_stack: Vec<i64>,
147 return_stack: Vec<usize>,
148 loop_stack: Vec<(i64, i64)>,
150 cells: Vec<i64>,
151 pub opcodes: Vec<Opcode>,
152 pc: usize,
153 gas_used: u64,
154}
155
156impl Default for StackMachineState {
157 fn default() -> Self {
158 StackMachineState {
159 number_stack: Vec::new(),
160 scratch_stack: Vec::new(),
161 return_stack: Vec::new(),
162 loop_stack: Vec::new(),
163 cells: Vec::new(),
164 opcodes: Vec::new(),
165 pc: 0,
166 gas_used: 0,
167 }
168 }
169}
170
171impl StackMachineState {
172 pub fn gas_used(&self) -> u64 {
174 self.gas_used
175 }
176}
177
178pub struct StackMachine {
180 pub st: StackMachineState,
181 pub trap_handlers: Vec<Box<dyn HandleTrap>>,
182}
183
184impl Default for StackMachine {
185 fn default() -> StackMachine {
186 StackMachine {
187 st: StackMachineState::default(),
188 trap_handlers: Vec::new(),
189 }
190 }
191}
192
193
194impl StackMachine {
195 fn pop_number_stack(&mut self) -> Result<i64, StackMachineError> {
196 self.st.number_stack.pop().ok_or(StackMachineError::NumberStackUnderflow)
197 }
198
199 fn push_number_stack(&mut self, value: i64) {
200 self.st.number_stack.push(value);
201 }
202
203 fn pop_scratch_stack(&mut self) -> Result<i64, StackMachineError> {
204 self.st.scratch_stack.pop().ok_or(StackMachineError::ScratchStackUnderflow)
205 }
206
207 fn push_scratch_stack(&mut self, value: i64) {
208 self.st.scratch_stack.push(value);
209 }
210
211 fn peek_scratch_stack(&self) -> Result<i64, StackMachineError> {
212 self.st.scratch_stack.last().copied().ok_or(StackMachineError::ScratchStackUnderflow)
213 }
214
215 fn execute_binary_op<F>(&mut self, op: F, opcode: &Opcode) -> Result<(), StackMachineError>
216 where
217 F: FnOnce(i64, i64) -> Option<i64>,
218 {
219 let second = self.pop_number_stack()?;
220 let first = self.pop_number_stack()?;
221 let result = op(first, second).ok_or(StackMachineError::NumericOverflow {
222 failing_opcode: opcode.clone(),
223 })?;
224 self.push_number_stack(result);
225 Ok(())
226 }
227
228 fn execute_division(&mut self, opcode: &Opcode) -> Result<(), StackMachineError> {
229 let divisor = self.pop_number_stack()?;
230 let dividend = self.pop_number_stack()?;
231 let result = dividend.checked_div(divisor).ok_or(StackMachineError::DivisionByZero {
232 failing_opcode: opcode.clone(),
233 })?;
234 self.push_number_stack(result);
235 Ok(())
236 }
237
238 fn execute_jump_relative(&mut self, condition: Option<bool>) -> Result<bool, StackMachineError> {
239 let offset = self.pop_number_stack()?;
240 let should_jump = if let Some(cond) = condition {
241 let value = self.pop_number_stack()?;
242 (value == 0) == cond
243 } else {
244 true
245 };
246
247 if should_jump {
248 let new_offset = i64::try_from(self.st.pc)? + offset;
249 self.st.pc = usize::try_from(new_offset)?;
250 Ok(true)
251 } else {
252 Ok(false)
253 }
254 }
255 pub fn execute(
266 &mut self,
267 starting_point: usize,
268 gas_limit: GasLimit,
269 ) -> Result<(), StackMachineError> {
270 self.st.gas_used = 0;
271 self.st.pc = starting_point;
272 loop {
273 let mut pc_reset = false;
274 let opcode = self
275 .st
276 .opcodes
277 .get(self.st.pc)
278 .ok_or(StackMachineError::UnknownError)?;
279 match opcode {
280 Opcode::JMP => {
281 let target = usize::try_from(self.pop_number_stack()?)?;
282 self.st.pc = target;
283 pc_reset = true;
284 }
285 Opcode::JR => {
286 pc_reset = self.execute_jump_relative(None)?;
287 }
288 Opcode::CALL => {
289 self.st.return_stack.push(self.st.pc + 1);
290 let target = usize::try_from(self.pop_number_stack()?)?;
291 self.st.pc = target;
292 pc_reset = true;
293 }
294 Opcode::CMPZ => {
295 let value = self.pop_number_stack()?;
296 self.push_number_stack(if value == 0 { -1 } else { 0 });
297 }
298 Opcode::CMPNZ => {
299 let value = self.pop_number_stack()?;
300 self.push_number_stack(if value == 0 { 0 } else { -1 });
301 }
302 Opcode::JRZ => {
303 pc_reset = self.execute_jump_relative(Some(true))?;
304 }
305 Opcode::JRNZ => {
306 pc_reset = self.execute_jump_relative(Some(false))?;
307 }
308 Opcode::LDI(immediate_value) => self.push_number_stack(*immediate_value),
309 Opcode::DROP => {
310 let _ = self.pop_number_stack()?;
311 }
312 Opcode::RET => {
313 if let Some(return_address) = self.st.return_stack.pop() {
314 self.st.pc = return_address;
315 pc_reset = true;
316 } else {
317 return Ok(());
318 }
319 }
320 Opcode::GtR => {
321 let value = self.pop_number_stack()?;
322 self.push_scratch_stack(value);
323 }
324 Opcode::RGt => {
325 let value = self.pop_scratch_stack()?;
326 self.push_number_stack(value);
327 }
328 Opcode::RAt => {
329 let value = self.peek_scratch_stack()?;
330 self.push_number_stack(value);
331 }
332 Opcode::GtR2 => {
333 let x = self.pop_number_stack()?;
334 let y = self.pop_number_stack()?;
335 self.push_scratch_stack( y);
336 self.push_scratch_stack( x);
337 }
338 Opcode::RGt2 => {
339 let x = self.pop_scratch_stack()?;
340 let y = self.pop_scratch_stack()?;
341 self.push_number_stack(y);
342 self.push_number_stack(x);
343 }
344 Opcode::RAt2 => {
345 let x = self.pop_scratch_stack()?;
346 let y = self.pop_scratch_stack()?;
347 self.push_scratch_stack(y);
348 self.push_scratch_stack(x);
349 self.push_number_stack(y);
350 self.push_number_stack(x);
351 }
352 Opcode::ADD => {
353 self.execute_binary_op(|a, b| a.checked_add(b), &opcode.clone())?;
354 }
355 Opcode::SUB => {
356 self.execute_binary_op(|a, b| b.checked_sub(a), &opcode.clone())?;
357 }
358 Opcode::MUL => {
359 self.execute_binary_op(|a, b| a.checked_mul(b), &opcode.clone())?;
360 }
361 Opcode::DIV => {
362 self.execute_division(&opcode.clone())?;
363 }
364 Opcode::NOT => {
365 let x = self.pop_number_stack()?;
366 self.push_number_stack(if x == 0 { 1 } else { 0 });
367 }
368 Opcode::DUP => {
369 let x = self.pop_number_stack()?;
370 self.push_number_stack(x);
371 self.push_number_stack(x);
372 }
373 Opcode::DUP2 => {
374 let x = self.pop_number_stack()?;
375 let y = self.pop_number_stack()?;
376 self.push_number_stack(y);
377 self.push_number_stack(x);
378 self.push_number_stack(y);
379 self.push_number_stack(x);
380 }
381 Opcode::OVER2 => {
382 let x4 = self.pop_number_stack()?;
383 let x3 = self.pop_number_stack()?;
384 let x2 = self.pop_number_stack()?;
385 let x1 = self.pop_number_stack()?;
386 self.push_number_stack(x1);
387 self.push_number_stack(x2);
388 self.push_number_stack(x3);
389 self.push_number_stack(x4);
390 self.push_number_stack(x1);
391 self.push_number_stack(x2);
392 }
393 Opcode::SWAP => {
394 let x = self.pop_number_stack()?;
395 let y = self.pop_number_stack()?;
396 self.push_number_stack(x);
397 self.push_number_stack(y);
398 }
399 Opcode::SWAP2 => {
400 let x4 = self.pop_number_stack()?;
401 let x3 = self.pop_number_stack()?;
402 let x2 = self.pop_number_stack()?;
403 let x1 = self.pop_number_stack()?;
404 self.push_number_stack(x3);
405 self.push_number_stack(x4);
406 self.push_number_stack(x1);
407 self.push_number_stack(x2);
408 }
409 Opcode::TRAP => {
410 let trap_id = self.pop_number_stack()?;
411 for h in self.trap_handlers.iter_mut() {
412 if let TrapHandled::Handled = h.handle_trap(trap_id, &mut self.st)? {
413 return Ok(());
414 }
415 }
416 return Err(StackMachineError::UnhandledTrap {
417 unhandled_trap_id: trap_id,
418 });
419 }
420 Opcode::NOP => {}
421 Opcode::PUSHLP => {
422 let current_index = self.pop_number_stack()?;
423 let max_index = self.pop_number_stack()?;
424 self.st.loop_stack.push((current_index, max_index));
425 }
426 Opcode::INCLP => {
427 if let Some((current_index, _)) = self.st.loop_stack.last_mut() {
428 *current_index += 1;
429 } else {
430 return Err(StackMachineError::LoopStackUnderflow);
431 }
432 }
433 Opcode::ADDLP => {
434 let increment = self.pop_number_stack()?;
435 if let Some((current_index, _)) = self.st.loop_stack.last_mut() {
436 *current_index += increment;
437 } else {
438 return Err(StackMachineError::LoopStackUnderflow);
439 }
440 }
441 Opcode::GETLP => {
442 let (current_index, _) = self
443 .st
444 .loop_stack
445 .last()
446 .ok_or(StackMachineError::LoopStackUnderflow)?;
447 self.st.number_stack.push(*current_index);
448 }
449 Opcode::GETLP2 => {
450 if self.st.loop_stack.len() < 2 {
451 return Err(StackMachineError::LoopStackUnderflow);
452 }
453 let (current_index, _) = self
454 .st
455 .loop_stack
456 .get(self.st.loop_stack.len() - 2)
457 .ok_or(StackMachineError::LoopStackUnderflow)?;
458 self.st.number_stack.push(*current_index);
459 }
460 Opcode::DROPLP => {
461 self.st
462 .loop_stack
463 .pop()
464 .ok_or(StackMachineError::LoopStackUnderflow)?;
465 }
466 Opcode::CMPLOOP => {
467 let (current_index, max_index) = self
468 .st
469 .loop_stack
470 .last()
471 .ok_or(StackMachineError::LoopStackUnderflow)?;
472 self.st
473 .number_stack
474 .push(if *current_index >= *max_index { 1 } else { 0 });
475 }
476 Opcode::AND => {
477 let x = self.pop_number_stack()?;
478 let y = self.pop_number_stack()?;
479 self.push_number_stack(x & y);
480 }
481 Opcode::NEWCELLS => {
482 let num_cells = usize::try_from(self.pop_number_stack()?)
483 .map_err(|_| StackMachineError::InvalidCellOperation)?;
484 let newaddress = self.st.cells.len();
485 self.st
486 .cells
487 .resize_with(newaddress + num_cells, Default::default);
488 }
489 Opcode::MOVETOCELLS => {
490 let num_cells = usize::try_from(self.pop_number_stack()?)
491 .map_err(|_| StackMachineError::InvalidCellOperation)?;
492 let address = usize::try_from(self.pop_number_stack()?)
493 .map_err(|_| StackMachineError::InvalidCellOperation)?;
494 if num_cells < 1 || self.st.cells.len() < address + num_cells {
495 return Err(StackMachineError::InvalidCellOperation);
496 }
497 for i in address..address + num_cells {
498 self.st.cells[i] = self.pop_number_stack()?;
499 }
500 }
501 Opcode::MOVEFROMCELLS => {
502 let num_cells = usize::try_from(self.pop_number_stack()?)
503 .map_err(|_| StackMachineError::InvalidCellOperation)?;
504 let address = usize::try_from(self.pop_number_stack()?)
505 .map_err(|_| StackMachineError::InvalidCellOperation)?;
506 if num_cells < 1 || self.st.cells.len() < address + num_cells {
507 return Err(StackMachineError::InvalidCellOperation);
508 }
509 for i in (address..address + num_cells).rev() {
510 self.push_number_stack(self.st.cells[i]);
511 }
512 }
513 }
514 if !pc_reset {
515 self.st.pc += 1;
516 }
517
518 self.st.gas_used += 1;
519
520 if let GasLimit::Limited(limit) = gas_limit {
521 if self.st.gas_used > limit {
522 return Err(StackMachineError::RanOutOfGas {
523 gas_used: self.st.gas_used,
524 gas_limit,
525 });
526 }
527 }
528 }
529 }
530}