rust_simple_stack_processor/
lib.rs

1use std::convert::TryFrom;
2use thiserror::Error;
3
4#[cfg(test)]
5mod tests;
6
7/// Gas limit for execution: unlimited or limited to a number of steps.
8#[derive(Debug)]
9pub enum GasLimit {
10    Unlimited,
11    Limited(u64),
12}
13
14/// Errors that can occur during stack machine execution.
15#[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
50/// Result of trap handling.
51pub enum TrapHandled {
52    Handled,
53    NotHandled,
54}
55
56/// Trait for trap handlers implementing the Chain of Command pattern.
57pub trait HandleTrap {
58    fn handle_trap(
59        &mut self,
60        trap_id: i64,
61        st: &mut StackMachineState,
62    ) -> Result<TrapHandled, StackMachineError>;
63}
64
65/// A trap handler that handles a specific trap id with a closure.
66pub 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    /// Create a new TrapHandler for a specific trap id.
73    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/// Opcodes supported by the stack machine.
99#[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
142/// Internal state of the stack machine.
143pub struct StackMachineState {
144    pub number_stack: Vec<i64>,
145    pub scratch_stack: Vec<i64>,
146    return_stack: Vec<usize>,
147    // current index, max_index
148    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    /// Returns the amount of gas used so far.
172    pub fn gas_used(&self) -> u64 {
173        self.gas_used
174    }
175}
176
177/// The stack machine itself, holding state and trap handlers.
178pub 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    /// JR(*) is relative from the JR(*) instruction,
236    /// 0 would jump back onto the JR instruction
237    /// -1 Would jump back to the instruction before the JR(*}) instruction
238    /// 1 Would jump to the instruction after the JR(*) instruction
239    ///
240    /// TRAPs always have a numeric code on the number stack to define which TRAP is being called
241    ///
242    /// CMPLOOP
243    /// pushes 1 on the stack if the loop counter is greater than or equal to the max
244    /// pushes 0 on the stack if the loop counter is less than the max
245    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}