essential_vm/
total_control_flow.rs

1use crate::{
2    error::{OpError, OpResult, StackError, TotalControlFlowError},
3    Gas, Stack,
4};
5use essential_types::convert::bool_from_word;
6
7#[cfg(test)]
8mod tests;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
11/// Update the program to a new position or halt it.
12pub enum ProgramControlFlow {
13    /// New program counter position.
14    Pc(usize),
15    /// Halt the program.
16    Halt,
17    /// End the compute program.
18    ComputeEnd,
19    /// Resulting program counter, total gas spent during compute
20    /// and whether top-level VM should halt.
21    ComputeResult((usize, Gas, bool)),
22}
23
24pub fn jump_if(stack: &mut Stack, pc: usize) -> OpResult<Option<ProgramControlFlow>> {
25    let [dist, cond] = stack.pop2()?;
26    let cond = bool_from_word(cond).ok_or(TotalControlFlowError::InvalidJumpForwardIfCondition)?;
27    if cond {
28        let neg = dist < 0;
29        let dist = usize::try_from(dist.abs()).map_err(|_| StackError::IndexOutOfBounds)?;
30        if dist == 0 {
31            return Err(TotalControlFlowError::JumpedToSelf.into());
32        }
33        if neg {
34            let pc = pc.checked_sub(dist).ok_or(OpError::PcOverflow)?;
35            Ok(Some(ProgramControlFlow::Pc(pc)))
36        } else {
37            let pc = pc.checked_add(dist).ok_or(OpError::PcOverflow)?;
38            Ok(Some(ProgramControlFlow::Pc(pc)))
39        }
40    } else {
41        Ok(None)
42    }
43}
44
45pub fn halt_if(stack: &mut Stack) -> OpResult<Option<ProgramControlFlow>> {
46    let cond = stack.pop()?;
47    let cond = bool_from_word(cond).ok_or(TotalControlFlowError::InvalidHaltIfCondition)?;
48    if cond {
49        Ok(Some(ProgramControlFlow::Halt))
50    } else {
51        Ok(None)
52    }
53}
54
55/// Implementation of the `PanicIf` operation.
56pub fn panic_if(stack: &mut Stack) -> OpResult<()> {
57    let cond = stack.pop()?;
58    let cond = bool_from_word(cond).ok_or(TotalControlFlowError::InvalidPanicIfCondition)?;
59    if cond {
60        let stack = stack.iter().copied().collect();
61        Err(TotalControlFlowError::Panic(stack).into())
62    } else {
63        Ok(())
64    }
65}