EVM

Contents

%%capture
%run 02_stack.ipynb
%run 03_memory.ipynb
%run 04_storage.ipynb
%run 05_opcodes.ipynb
%run 06_stop.ipynb
%run 07_math.ipynb
%run push.ipynb
---------------------------------------------------------------------------
OSError                                   Traceback (most recent call last)
File ~/.local/share/mise/installs/python/3.13.3/lib/python3.13/site-packages/IPython/core/magics/execution.py:727, in ExecutionMagics.run(self, parameter_s, runner, file_finder)
    726     fpath = arg_lst[0]
--> 727     filename = file_finder(fpath)
    728 except IndexError as e:

File ~/.local/share/mise/installs/python/3.13.3/lib/python3.13/site-packages/IPython/utils/path.py:90, in get_py_filename(name)
     89         return py_name
---> 90 raise IOError("File `%r` not found." % name)

OSError: File `'02_stack.ipynb'` not found.

The above exception was the direct cause of the following exception:

Exception                                 Traceback (most recent call last)
Cell In[1], line 1
----> 1 get_ipython().run_line_magic('run', '02_stack.ipynb')
      2 get_ipython().run_line_magic('run', '03_memory.ipynb')
      3 get_ipython().run_line_magic('run', '04_storage.ipynb')

File ~/.local/share/mise/installs/python/3.13.3/lib/python3.13/site-packages/IPython/core/interactiveshell.py:2486, in InteractiveShell.run_line_magic(self, magic_name, line, _stack_depth)
   2484     kwargs['local_ns'] = self.get_local_scope(stack_depth)
   2485 with self.builtin_trap:
-> 2486     result = fn(*args, **kwargs)
   2488 # The code below prevents the output from being displayed
   2489 # when using magics with decorator @output_can_be_silenced
   2490 # when the last Python token in the expression is a ';'.
   2491 if getattr(fn, magic.MAGIC_OUTPUT_CAN_BE_SILENCED, False):

File ~/.local/share/mise/installs/python/3.13.3/lib/python3.13/site-packages/IPython/core/magics/execution.py:738, in ExecutionMagics.run(self, parameter_s, runner, file_finder)
    736     if os.name == 'nt' and re.match(r"^'.*'$",fpath):
    737         warn('For Windows, use double quotes to wrap a filename: %run "mypath\\myfile.py"')
--> 738     raise Exception(msg) from e
    739 except TypeError:
    740     if fpath in sys.meta_path:

Exception: File `'02_stack.ipynb'` not found.
---------------------------------------------------------------------------
TokenError                                Traceback (most recent call last)
Cell In[1], line 1
----> 1 get_ipython().run_cell_magic('capture', '', '%run 02_stack.ipynb\n%run 03_memory.ipynb\n%run 04_storage.ipynb\n%run 05_opcodes.ipynb\n%run 06_stop.ipynb\n%run 07_math.ipynb\n%run push.ipynb\n')

File ~/.local/share/mise/installs/python/3.13.3/lib/python3.13/site-packages/IPython/core/interactiveshell.py:2547, in InteractiveShell.run_cell_magic(self, magic_name, line, cell)
   2545 with self.builtin_trap:
   2546     args = (magic_arg_s, cell)
-> 2547     result = fn(*args, **kwargs)
   2549 # The code below prevents the output from being displayed
   2550 # when using magics with decorator @output_can_be_silenced
   2551 # when the last Python token in the expression is a ';'.
   2552 if getattr(fn, magic.MAGIC_OUTPUT_CAN_BE_SILENCED, False):

File ~/.local/share/mise/installs/python/3.13.3/lib/python3.13/site-packages/IPython/core/magics/execution.py:1549, in ExecutionMagics.capture(self, line, cell)
   1547 with capture_output(out, err, disp) as io:
   1548     self.shell.run_cell(cell)
-> 1549 if DisplayHook.semicolon_at_end_of_expression(cell):
   1550     if args.output in self.shell.user_ns:
   1551         del self.shell.user_ns[args.output]

File ~/.local/share/mise/installs/python/3.13.3/lib/python3.13/site-packages/IPython/core/displayhook.py:104, in DisplayHook.semicolon_at_end_of_expression(expression)
    101 """Parse Python expression and detects whether last token is ';'"""
    103 sio = _io.StringIO(expression)
--> 104 tokens = list(tokenize.generate_tokens(sio.readline))
    106 for token in reversed(tokens):
    107     if token[0] in (tokenize.ENDMARKER, tokenize.NL, tokenize.NEWLINE, tokenize.COMMENT):

File ~/.local/share/mise/installs/python/3.13.3/lib/python3.13/tokenize.py:588, in _generate_tokens_from_c_tokenizer(source, encoding, extra_tokens)
    586     raise e from None
    587 msg = _transform_msg(e.msg)
--> 588 raise TokenError(msg, (e.lineno, e.offset)) from None

TokenError: ('invalid decimal literal', (1, 8))

EVM#

Now we are going to put everything together.

Program Counter#

The program counter indexes into the bytecode. It tells the EVM which opcode to execute next

class EVM:
    def __init__(self,
                 program,
                 gas,
                 value,
                 calldata=[]):
        self.pc      = 0
        self.stack   = Stack()
        self.memory  = Memory()
        self.storage = Storage()
        
        self.program  = program
        self.gas      = gas
        self.value    = value
        self.calldata = calldata
        
        self.stop_flag   = False
        self.revert_flag = False
        
        self.returndata = []
        self.logs       = []
        
    def peek(self): return self.program[self.pc]
    
    def gas_dec(self, amount):
        if self.gas - amount < 0: 
            raise Exception("out of gas")
        self.gas -= amount

    
    def should_execute_next_opcode(self):
        if self.pc > len(self.program)-1: return False
        if self.stop_flag               : return False
        if self.revert_flag             : return False
        
        return True
    
    def run(self):
        while self.should_execute_next_opcode():
            op = self.program[self.pc]
            
            if op == STOP: stop(self)
                
            if op == ADD: add(self)

                
            if op == PUSH1: _push(self, 1)
        
    def reset(self):
        self.pc      = 0
        self.stack   = Stack()
        self.memory  = Memory()
        self.storage = Storage()