1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

mod abstract_evm;
mod abstract_stack;
mod concrete_evm;
mod concrete_stack;
mod disassembler;
pub mod opcode;

pub use crate::evm::abstract_evm::*;
pub use crate::evm::abstract_stack::*;
pub use crate::evm::concrete_evm::*;
pub use crate::evm::concrete_stack::*;
pub use crate::evm::disassembler::*;

use crate::util::{w256, Interval};

/// Represents the fundamental unit of computation within the EVM,
/// namely a word.  This is intentially left abstract, so that it
/// could be reused across both _concrete_ and _abstract_ semantics.
pub trait Word: Sized + Copy + From<w256> + PartialEq + std::ops::Add<Output = Self> {}

/// Default implementations for `w256`
impl Word for w256 {}
impl Word for Interval<w256> {}

// ===================================================================
// Stack
// ===================================================================

/// Represents an EVM stack of some form.  This could a _concrete_
/// stack (i.e. useful for execution) or an _abstract_ stack
/// (i.e. useful for analysis).
pub trait Stack: Default + PartialEq {
    /// Underlying machine word this stack operates on.  In a concrete EVM, this
    /// would be `w256` or some other concrete implementation.
    type Word: Word;

    /// Peek `nth` item from stack (where `n==0` is top element).
    fn peek(&self, n: usize) -> Self::Word;

    /// Determine number of items on stack.
    fn len(&self) -> Self::Word;

    /// Push an item onto the stack.
    fn push(&mut self, item: Self::Word);

    /// Pop an item from the stack.
    fn pop(&mut self, n: usize);

    /// Set ith item on stack (where `n==0` is top element)
    fn set(&mut self, n: usize, item: Self::Word);
}

// ===================================================================
// EVM
// ===================================================================

/// Represents an EVM of some form.  This could be a _concrete_ EVM (i.e. useful for actually
/// executing bytecodes), or an _abstract_ EVM (i.e. useful for some kind of dataflow analysis).
#[derive(Clone, Debug, PartialEq)]
pub struct Evm<'a, S: Stack> {
    /// Program Counter
    pub pc: usize,
    /// Bytecode being executed
    pub code: &'a [u8],
    // Stack
    pub stack: S,
}

impl<'a, S: Stack> Evm<'a, S> {
    /// Construct a new EVM.
    pub fn new(code: &'a [u8]) -> Self {
        // Create default stack
        let stack = S::default();
        // Create EVM!
        Evm { pc: 0, code, stack }
    }

    pub const fn new_const(code: &'a [u8], stack: S) -> Self {
        // Create EVM!
        Evm { pc: 0, code, stack }
    }

    /// Peek 'n'th item on the stack.
    pub fn peek(&self, n: usize) -> S::Word {
        self.stack.peek(n)
    }

    /// Pop `n` items of the stack.
    pub fn pop(mut self, n: usize) -> Self {
        self.stack.pop(n);
        self
    }

    /// Push a word onto the stack.
    pub fn push(mut self, word: S::Word) -> Self {
        self.stack.push(word);
        self
    }

    pub fn set(mut self, n: usize, word: S::Word) -> Self {
        self.stack.set(n, word);
        self
    }

    /// Shift the `pc` by `n` bytes.
    pub fn next(mut self, n: usize) -> Self {
        self.pc = self.pc + n;
        self
    }

    /// Update `pc` to a given location.
    pub fn goto(mut self, n: usize) -> Self {
        self.pc = n;
        self
    }
}

/// A stepper is a trait for describing a single execution step of the
/// EVM.  This is subtle because it can be abstract or concrete.
pub trait Stepper {
    type Result;

    /// Take a single step of the EVM producing a result of some kind
    /// (e.g. an updated EVM state).
    fn step(self) -> Self::Result;
}