├── .gitignore ├── src ├── lib.rs ├── main.rs ├── instruction.rs ├── execution_context.rs ├── calldata.rs ├── stack.rs ├── memory.rs └── opcodes.rs ├── Cargo.toml ├── .github ├── workflows │ └── tests.yml └── tests.yml ├── LICENSE ├── README.md └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .vscode -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod calldata; 2 | pub mod execution_context; 3 | pub mod instruction; 4 | pub mod memory; 5 | pub mod opcodes; 6 | pub mod stack; 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "smol-evm-rs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | primitive-types = "0.12.2" 10 | bytes = "1.5.0" 11 | lazy_static = "1.4" 12 | hex = "0.4" 13 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | -------------------------------------------------------------------------------- /.github/tests.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | 3 | on: 4 | push: 5 | branches: [ main, part-1 ] 6 | pull_request: 7 | branches: [ main, part-1 ] 8 | 9 | jobs: 10 | build_and_test: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Install Rust 16 | uses: actions-rs/toolchain@v1 17 | with: 18 | toolchain: stable 19 | profile: minimal 20 | components: rustfmt, clippy 21 | 22 | - name: Build 23 | uses: actions-rs/cargo@v1 24 | with: 25 | command: build 26 | 27 | - name: Run tests 28 | uses: actions-rs/cargo@v1 29 | with: 30 | command: test 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 PraneshASP 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use bytes::Bytes; 2 | use hex::{self}; 3 | use smol_evm_rs::{ 4 | execution_context::ExecutionContext, instruction::Instruction, opcodes::Opcodes, 5 | }; 6 | use std::env::args; 7 | 8 | fn main() { 9 | let max_steps = 1000; // Todo: Should replace with gas 10 | Opcodes::register_instructions(); 11 | 12 | let hex_str = args().nth(1).expect("No bytecode passed"); 13 | let byte_array = hex::decode(&hex_str).expect("Invalid hex string"); 14 | let code = Bytes::from(byte_array); 15 | let mut context = ExecutionContext::new(code); 16 | let mut steps = 1; 17 | while !context.stopped { 18 | let pc_before = context.pc.clone(); 19 | let instruction = Instruction::decode_opcode(&mut context).unwrap(); 20 | instruction.executor.execute(&mut context); 21 | steps += 1; 22 | if steps > max_steps { 23 | panic!("Error: Out of gas"); // Todo: Handle gracefully 24 | } 25 | println!("{:?} @ pc={}", instruction.name, pc_before); 26 | println!("Stack: {:?}", context.stack.stack); 27 | println!("Memory: {:?}", context.memory.memory); 28 | println!("---------"); 29 | } 30 | 31 | println!("{}", format!("Output : 0x{:x}", context.returndata)); 32 | } 33 | -------------------------------------------------------------------------------- /src/instruction.rs: -------------------------------------------------------------------------------- 1 | use bytes::Bytes; 2 | use lazy_static::lazy_static; 3 | use std::collections::HashMap; 4 | use std::sync::{Arc, Mutex}; 5 | 6 | use crate::execution_context::ExecutionContext; 7 | use crate::opcodes::OpcodeExecutor; 8 | 9 | #[derive(Debug)] 10 | pub struct Instruction { 11 | pub opcode: usize, 12 | pub name: String, 13 | pub executor: Box, 14 | } 15 | 16 | #[derive(Debug)] 17 | pub enum InstructionError { 18 | InvalidCodeOffset { code: Bytes, pc: usize }, 19 | OpcodeNotFound(usize), 20 | } 21 | 22 | // Note to self: Rust requires the use of a mutex or similar mechanism for mutable statics due to thread safety concerns. 23 | // Mutex and lazy_static approach is one way to handle this 24 | lazy_static! { 25 | static ref INSTRUCTIONS: Mutex>> = Mutex::new(vec![]); 26 | pub static ref INSTRUCTIONS_BY_OPCODE: Mutex>> = 27 | Mutex::new(HashMap::new()); 28 | } 29 | impl Instruction { 30 | pub fn register_instruction(opcode: usize, name: String, executor: Box) { 31 | let instruction = Arc::new(Instruction { 32 | opcode, 33 | name: name.clone(), 34 | executor, 35 | }); 36 | 37 | { 38 | let mut instructions = INSTRUCTIONS.lock().unwrap(); 39 | instructions.push(instruction.clone()); 40 | 41 | let mut instructions_by_opcode = INSTRUCTIONS_BY_OPCODE.lock().unwrap(); 42 | instructions_by_opcode.insert(opcode, instruction); 43 | } 44 | } 45 | 46 | pub fn decode_opcode( 47 | context: &mut ExecutionContext, 48 | ) -> Result, InstructionError> { 49 | let opcode = context.read_code(1); 50 | 51 | let instructions_by_opcode = INSTRUCTIONS_BY_OPCODE.lock().unwrap(); 52 | 53 | if context.pc > context.code.len() { 54 | Ok(instructions_by_opcode.get(&0x00).cloned().unwrap()) // STOP if pc goes over code length 55 | } else { 56 | match instructions_by_opcode.get(&opcode) { 57 | Some(instruction) => Ok(instruction.to_owned()), 58 | None => Err(InstructionError::OpcodeNotFound(opcode)), 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/execution_context.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | calldata::Calldata, 3 | instruction::{Instruction, INSTRUCTIONS_BY_OPCODE}, 4 | memory::Memory, 5 | stack::Stack, 6 | }; 7 | use bytes::Bytes; 8 | 9 | #[derive(Debug)] 10 | pub struct ExecutionContext { 11 | pub code: Bytes, 12 | pub stack: Stack, 13 | pub memory: Memory, 14 | pub pc: usize, 15 | pub stopped: bool, 16 | pub returndata: Bytes, 17 | pub jumpdests: Vec, 18 | pub calldata: Calldata, 19 | } 20 | 21 | impl ExecutionContext { 22 | pub fn new(code: Bytes) -> Self { 23 | Self { 24 | code: code.clone(), 25 | stack: Stack::new(1024), 26 | memory: Memory::new(), 27 | pc: 0, 28 | stopped: false, 29 | returndata: Bytes::new(), 30 | jumpdests: Self::valid_jump_destinations(code.clone()), 31 | calldata: Calldata::new(code), 32 | } 33 | } 34 | 35 | pub fn stop(&mut self) { 36 | self.stopped = true; 37 | } 38 | 39 | /// Returns the next num_bytes from the code buffer as an integer and advances pc by num_bytes. 40 | pub fn read_code(&mut self, num_bytes: usize) -> usize { 41 | let bytes_slice = &self.code[self.pc..self.pc + num_bytes]; 42 | self.pc += num_bytes; 43 | 44 | let mut result: usize = 0; 45 | for &byte in bytes_slice.iter() { 46 | result = (result << 8) | (byte as usize); 47 | } 48 | 49 | result 50 | } 51 | 52 | pub fn set_returndata(&mut self, offset: usize, length: usize) { 53 | self.stopped = true; 54 | self.returndata = self.memory.load_range(offset, length); 55 | } 56 | 57 | pub fn set_pc(&mut self, pc: usize) { 58 | self.pc = pc; 59 | } 60 | 61 | pub fn valid_jump_destinations(code: Bytes) -> Vec { 62 | let mut jumpdests: Vec = Vec::new(); 63 | 64 | let mut i = 0; 65 | while i < code.len() { 66 | let current_op = code[i] as usize; 67 | if current_op == 0x5B { 68 | jumpdests.push(i); 69 | } else if 0x60 <= current_op && current_op <= 0x7F { 70 | i += current_op - 0x60 + 1 71 | } 72 | i += 1; 73 | } 74 | jumpdests 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/calldata.rs: -------------------------------------------------------------------------------- 1 | use bytes::Bytes; 2 | #[derive(Debug)] 3 | pub struct Calldata { 4 | pub data: Bytes, 5 | } 6 | 7 | impl Calldata { 8 | pub fn new(data: Bytes) -> Self { 9 | Self { data } 10 | } 11 | 12 | pub fn read_byte(&self, offset: usize) -> u8 { 13 | if offset < self.data.len() { 14 | self.data[offset] 15 | } else { 16 | 0 17 | } 18 | } 19 | // TODO: Change to handle U256 20 | pub fn read_word(&self, offset: usize) -> u128 { 21 | let mut bytes = [0u8; 16]; 22 | for i in offset..offset + 16 { 23 | bytes[i] = self.read_byte(i) as u8 24 | } 25 | u128::from_be_bytes(bytes) 26 | } 27 | } 28 | 29 | #[cfg(test)] 30 | mod tests { 31 | use super::*; 32 | use hex; 33 | 34 | #[test] 35 | fn test_read_byte() { 36 | // Convert a hexadecimal string to bytes 37 | let hex_data = "01020304"; // Without the "0x" prefix 38 | let data = match hex::decode(hex_data) { 39 | Ok(bytes) => Bytes::from(bytes), 40 | Err(e) => panic!("Failed to decode hex: {}", e), 41 | }; 42 | let calldata = Calldata::new(data); 43 | 44 | assert_eq!(calldata.read_byte(0), 0x01); 45 | assert_eq!(calldata.read_byte(1), 0x02); 46 | assert_eq!(calldata.read_byte(2), 0x03); 47 | 48 | // Offset is outside the data range 49 | assert_eq!(calldata.read_byte(10), 0); 50 | } 51 | #[test] 52 | fn test_read_word() { 53 | // Extended hex_data to 32 hex digits (16 bytes) 54 | let hex_data = "0123456789abcdef0123456789abcdef"; 55 | let data = match hex::decode(&hex_data) { 56 | Ok(bytes) => Bytes::from(bytes), 57 | Err(e) => panic!("Failed to decode hex: {}", e), 58 | }; 59 | let calldata = Calldata::new(data); 60 | 61 | let offset = 0; 62 | 63 | // Calculate the expected u128 value from the 16 bytes starting at the offset 64 | let expected_word = u128::from_be_bytes( 65 | hex::decode(&hex_data[offset * 2..offset * 2 + 32]) 66 | .unwrap() 67 | .try_into() 68 | .unwrap(), 69 | ); 70 | 71 | let word = calldata.read_word(offset); 72 | 73 | assert_eq!( 74 | word, expected_word, 75 | "read_word did not return the expected value at offset {}", 76 | offset 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # smol-evm-rs • ![License](https://img.shields.io/badge/license-MIT-brown.svg) [![CI](https://github.com/PraneshASP/smol-evm-rs/actions/workflows/tests.yml/badge.svg)](https://github.com/PraneshASP/smol-evm-rs/actions/workflows/tests.yml) ![Built Using Rust](https://img.shields.io/badge/Built%20Using-Rust-orange.svg) 2 | --- 3 | 4 | > [!WARNING] 5 | > It is important to note that the code is not optimized and may contain bugs. It is primarily intended for educational purposes. So don't use any code from this repo for production. 6 | 7 | `smol-evm-rs` is a toy implementation of the Ethereum Virtual Machine, inspired by the [smol-evm](https://github.com/karmacoma-eth/smol-evm) project, originally implemented in Python by karmacoma. The primary goal of the project is to increase my Rust proficiency. 8 | - [X] [**Part 1**: The execution context ](https://github.com/PraneshASP/smol-evm-rs/tree/part-1) 9 | - [X] [**Part 2**: Branching instructions](https://github.com/PraneshASP/smol-evm-rs/tree/part-2) 10 | - [X] [**Part 3**: Calldata & comparison instructions](https://github.com/PraneshASP/smol-evm-rs/tree/part-3) 11 | 12 | 13 | ## Getting started: 14 | 15 | ### Prerequisites 16 | 17 | - [Rust](https://doc.rust-lang.org/book/ch01-01-installation.html) 18 | 19 | ### Build Instructions 20 | 21 | 1. Clone the repository: 22 | ```bash 23 | git clone https://github.com/PraneshASP/smol-evm-rs.git 24 | cd smol-evm-rs 25 | ``` 26 | 2. Build the project: 27 | ```bash 28 | cargo build 29 | ``` 30 | 3. Run the project: 31 | ```bash 32 | cargo run 33 | ``` 34 | 35 | ### Example: 36 | Run: `cargo run 60048060005b8160125760005360016000f35b8201906001900390600556` 37 | 38 | > The above bytecode calculates 4.pow(2) (four-squared) 39 | 40 | It should return`0x10` as output along with the memory, stack and program counter values. 41 | 42 | ```bash 43 | 44 | Opcode: 96 45 | "PUSH1" @ pc=0 46 | Stack: [4] 47 | Memory: [] 48 | --------- 49 | Opcode: 128 50 | "DUP1" @ pc=2 51 | Stack: [4, 4] 52 | Memory: [] 53 | --------- 54 | Opcode: 96 55 | "PUSH1" @ pc=3 56 | Stack: [4, 4, 0] 57 | Memory: [] 58 | --------- 59 | Opcode: 3 60 | "SUB" @ pc=25 61 | Stack: [4, 8, 2] 62 | Memory: [] 63 | --------- 64 | Opcode: 144 65 | "SWAP1" @ pc=26 66 | Stack: [4, 2, 8] 67 | Memory: [] 68 | --------- 69 | 70 | ... 71 | 72 | ... 73 | 74 | ... 75 | 76 | --------- 77 | Opcode: 87 78 | "JUMPI" @ pc=9 79 | Stack: [4, 0, 16] 80 | Memory: [] 81 | --------- 82 | Opcode: 96 83 | "PUSH1" @ pc=10 84 | Stack: [4, 0, 16, 0] 85 | Memory: [] 86 | --------- 87 | "MSTORE8" @ pc=12 88 | Stack: [4, 0] 89 | Memory: [16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 90 | --------- 91 | "PUSH1" @ pc=13 92 | Stack: [4, 0, 1] 93 | Memory: [16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 94 | --------- 95 | "PUSH1" @ pc=15 96 | Stack: [4, 0, 1, 0] 97 | Memory: [16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 98 | --------- 99 | "RETURN" @ pc=17 100 | Stack: [4, 0] 101 | Memory: [16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 102 | --------- 103 | Output : 0x1000000000000000 104 | 105 | ``` 106 | 107 | > [!NOTE] 108 | > Supported Opcodes: `ADD`,`SUB`,`MUL`,`PUSH1`, `MSTORE8`, `RETURN`, `STOP`,`JUMP`, `JUMPI`,`JUMPDEST`,`GT`,`LT`,`ISZERO`,`SHR`,`SHL`,`CALLDATALOAD`, `CALLDATASIZE`, `CALLVALUE`,`SWAP[1-16]`, `PUSH[0-32]`and `DUP[1-16]` 109 | 110 | ### Improvement ideas: 111 | - Make word size 32 instead of 16. 112 | - Implement remaining opcodes like MSTORE, MLOAD, CALLDATACOPY 113 | - Implement `gas` calculation. 114 | - Add more tests. 115 | 116 | ## Acknowledgments 117 | 118 | - [karmacoma-eth](https://github.com/karmacoma-eth) for the original Python implementation of `smol-evm`. -------------------------------------------------------------------------------- /src/stack.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub struct Stack { 3 | pub stack: Vec, 4 | pub max_depth: usize, 5 | } 6 | 7 | #[derive(Debug)] 8 | pub enum StackError { 9 | InvalidItem(usize), 10 | StackOverflow, 11 | StackUnderflow, 12 | InvalidIndex, 13 | } 14 | 15 | impl Stack { 16 | pub fn new(max_depth: usize) -> Self { 17 | Self { 18 | stack: Vec::new(), 19 | max_depth, 20 | } 21 | } 22 | 23 | pub fn push(&mut self, item: usize) -> Result<(), StackError> { 24 | if item > usize::MAX { 25 | return Err(StackError::InvalidItem(item)); 26 | } 27 | 28 | if self.stack.len() >= self.max_depth { 29 | return Err(StackError::StackOverflow); 30 | } 31 | 32 | self.stack.push(item); 33 | Ok(()) 34 | } 35 | 36 | pub fn pop(&mut self) -> Result { 37 | match self.stack.pop() { 38 | Some(item) => Ok(item), 39 | None => Err(StackError::StackUnderflow), 40 | } 41 | } 42 | 43 | pub fn peek(&mut self, index: usize) -> Result { 44 | if self.stack.len() <= index { 45 | return Err(StackError::StackUnderflow); 46 | } 47 | Ok(self.stack[self.stack.len() - (index + 1)]) 48 | } 49 | 50 | pub fn swap(&mut self, index: usize) -> Result<(), StackError> { 51 | if index == 0 { 52 | return Err(StackError::InvalidIndex); 53 | } 54 | 55 | let stack_len = self.stack.len(); 56 | 57 | if stack_len <= index { 58 | return Err(StackError::StackUnderflow); 59 | } 60 | 61 | self.stack.swap(stack_len - 1, stack_len - index - 1); 62 | 63 | Ok(()) 64 | } 65 | } 66 | 67 | #[cfg(test)] 68 | mod tests { 69 | use super::*; 70 | #[test] 71 | fn new_stack_is_empty() { 72 | let stack = Stack::new(1024); 73 | assert_eq!(stack.stack.len(), 0); 74 | } 75 | 76 | #[test] 77 | fn push_and_pop_on_stack() { 78 | let mut stack = Stack::new(1024); 79 | stack.push(42).unwrap(); 80 | assert_eq!(stack.stack.len(), 1); 81 | assert_eq!(stack.pop().unwrap(), 42); 82 | assert_eq!(stack.stack.len(), 0); 83 | } 84 | 85 | #[test] 86 | fn test_peek() { 87 | let mut stack = Stack::new(1024); 88 | stack.push(41).unwrap(); 89 | stack.push(42).unwrap(); 90 | stack.push(43).unwrap(); 91 | 92 | assert_eq!(stack.peek(0).unwrap(), 43); 93 | assert_eq!(stack.peek(1).unwrap(), 42); 94 | assert_eq!(stack.peek(2).unwrap(), 41); 95 | 96 | assert!(stack.peek(3).is_err()); 97 | } 98 | 99 | #[test] 100 | fn test_swap() { 101 | let mut stack = Stack::new(1024); 102 | stack.push(41).unwrap(); 103 | stack.push(42).unwrap(); 104 | stack.push(43).unwrap(); 105 | // Before swap: [41,42,43] <- Top 106 | assert_eq!(stack.peek(0).unwrap(), 43); 107 | assert_eq!(stack.peek(1).unwrap(), 42); 108 | assert_eq!(stack.peek(2).unwrap(), 41); 109 | 110 | stack.swap(1).unwrap(); 111 | 112 | // After swap: [41,43,42] 113 | assert_eq!(stack.peek(0).unwrap(), 42); 114 | assert_eq!(stack.peek(1).unwrap(), 43); 115 | assert_eq!(stack.peek(2).unwrap(), 41); 116 | 117 | stack.swap(2).unwrap(); 118 | 119 | // After swap 2: [42,43,41] 120 | assert_eq!(stack.peek(0).unwrap(), 41); 121 | assert_eq!(stack.peek(1).unwrap(), 43); 122 | assert_eq!(stack.peek(2).unwrap(), 42); 123 | 124 | assert!(stack.swap(3).is_err()); 125 | assert!(stack.swap(0).is_err()); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/memory.rs: -------------------------------------------------------------------------------- 1 | use bytes::{BufMut, Bytes, BytesMut}; 2 | 3 | #[derive(Debug)] 4 | pub struct Memory { 5 | pub memory: Vec, 6 | } 7 | 8 | #[derive(Debug)] 9 | pub enum MemoryError { 10 | InvalidMemoryAccess(usize, usize), 11 | InvalidMemoryValue(usize, usize), 12 | InvalidOffset(usize), 13 | } 14 | 15 | impl Memory { 16 | const ZERO_WORD: [usize; 16] = [0; 16]; 17 | pub fn new() -> Self { 18 | Self { memory: Vec::new() } 19 | } 20 | 21 | pub fn store(&mut self, offset: usize, value: usize) -> Result<(), MemoryError> { 22 | if value >= u8::MAX.into() { 23 | return Err(MemoryError::InvalidMemoryValue(offset, value)); 24 | } 25 | 26 | self.expand_if_needed(offset); 27 | self.memory[offset] = value; 28 | Ok(()) 29 | } 30 | 31 | pub fn load(&mut self, offset: usize) -> usize { 32 | if offset > self.memory.len() { 33 | return 0; 34 | } 35 | self.expand_if_needed(offset); 36 | self.memory[offset] 37 | } 38 | 39 | pub fn load_range(&mut self, offset: usize, length: usize) -> Bytes { 40 | self.expand_if_needed(offset); 41 | let mut bytes = BytesMut::new(); 42 | for i in offset..offset + length { 43 | let data = self.load(i).to_ne_bytes(); // Convert usize to [u8; 8] 44 | for byte in &data { 45 | bytes.put_u8(*byte); 46 | } 47 | } 48 | bytes.freeze() 49 | } 50 | 51 | // TODO: 52 | // Currently handles only upto 16 bytes 53 | // pub fn load_word(&mut self, offset: usize) -> u128 { 54 | // let range = self.load_range(offset, 16); // Ensure loading 16 bytes for a u128 55 | // let mut bytes = [0u8; 16]; 56 | // // Copy bytes from 'range' into 'bytes' array 57 | // for (i, &byte) in range.iter().enumerate() { 58 | // bytes[i] = byte; 59 | // } 60 | 61 | // // Convert the bytes array into a u128 62 | // u128::from_be_bytes(bytes) 63 | // } 64 | 65 | pub fn store_word(&mut self, offset: usize, value: usize) { 66 | self.expand_if_needed(offset + 16); 67 | 68 | for i in 0..16 { 69 | self.memory[offset + 16 - i] = value & (255_u128.wrapping_shl((i * 8) as u32)) as usize; 70 | } 71 | } 72 | 73 | pub fn active_words(&self) -> usize { 74 | match self.memory.len().checked_div(16) { 75 | Some(v) => v, 76 | None => todo!(), 77 | } 78 | } 79 | 80 | fn expand_if_needed(&mut self, offset: usize) { 81 | if offset < self.memory.len() { 82 | return; 83 | } 84 | let active_words_after = std::cmp::max(self.active_words(), (offset + 1).div_ceil(16)); 85 | let additional_words = active_words_after.saturating_sub(self.active_words()); 86 | for _ in 0..additional_words { 87 | self.memory.extend_from_slice(&Self::ZERO_WORD); 88 | } 89 | return; 90 | } 91 | } 92 | 93 | #[cfg(test)] 94 | mod tests { 95 | 96 | use super::*; 97 | #[test] 98 | fn new_memory_is_empty() { 99 | let memory = Memory::new(); 100 | assert_eq!(memory.memory.len(), 0); 101 | } 102 | 103 | #[test] 104 | fn store_in_memory() { 105 | let offset = 0; 106 | let value = 10; 107 | let mut memory = Memory::new(); 108 | let _ = memory.store(offset, value); 109 | assert_eq!(memory.memory.len(), 16); 110 | 111 | memory.store(2, value).unwrap(); 112 | assert_eq!(memory.memory.len(), 16); 113 | 114 | memory.store(15, value).unwrap(); 115 | assert_eq!(memory.memory.len(), 16); 116 | 117 | // Activate 2nd word 118 | memory.store(17, value).unwrap(); 119 | assert_eq!(memory.memory.len(), 32); 120 | } 121 | 122 | #[test] 123 | fn test_active_words() { 124 | let offset = 0; 125 | let value = 10; 126 | let mut memory = Memory::new(); 127 | let _ = memory.store(offset, value); 128 | assert_eq!(memory.active_words(), 1); 129 | 130 | memory.store(2, value).unwrap(); 131 | assert_eq!(memory.active_words(), 1); 132 | 133 | memory.store(15, value).unwrap(); 134 | assert_eq!(memory.active_words(), 1); 135 | 136 | // Activate 3rd word 137 | memory.store(35, value).unwrap(); 138 | assert_eq!(memory.active_words(), 3); 139 | 140 | // Activate 5th word 141 | memory.store(65, value).unwrap(); 142 | assert_eq!(memory.active_words(), 5); 143 | } 144 | 145 | #[test] 146 | fn load_from_memory() { 147 | let offset = 0; 148 | let value = 10; 149 | let mut memory = Memory::new(); 150 | let _ = memory.store(offset, value); 151 | let value = memory.load(offset); 152 | assert_eq!(value, 10); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "arrayvec" 7 | version = "0.7.4" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" 10 | 11 | [[package]] 12 | name = "bitvec" 13 | version = "1.0.1" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" 16 | dependencies = [ 17 | "funty", 18 | "radium", 19 | "tap", 20 | "wyz", 21 | ] 22 | 23 | [[package]] 24 | name = "byte-slice-cast" 25 | version = "1.2.2" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" 28 | 29 | [[package]] 30 | name = "byteorder" 31 | version = "1.5.0" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 34 | 35 | [[package]] 36 | name = "bytes" 37 | version = "1.5.0" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" 40 | 41 | [[package]] 42 | name = "cfg-if" 43 | version = "1.0.0" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 46 | 47 | [[package]] 48 | name = "crunchy" 49 | version = "0.2.2" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 52 | 53 | [[package]] 54 | name = "equivalent" 55 | version = "1.0.1" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 58 | 59 | [[package]] 60 | name = "fixed-hash" 61 | version = "0.8.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" 64 | dependencies = [ 65 | "byteorder", 66 | "rand", 67 | "rustc-hex", 68 | "static_assertions", 69 | ] 70 | 71 | [[package]] 72 | name = "funty" 73 | version = "2.0.0" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" 76 | 77 | [[package]] 78 | name = "getrandom" 79 | version = "0.2.11" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" 82 | dependencies = [ 83 | "cfg-if", 84 | "libc", 85 | "wasi", 86 | ] 87 | 88 | [[package]] 89 | name = "hashbrown" 90 | version = "0.14.3" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" 93 | 94 | [[package]] 95 | name = "hex" 96 | version = "0.4.3" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 99 | 100 | [[package]] 101 | name = "impl-codec" 102 | version = "0.6.0" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" 105 | dependencies = [ 106 | "parity-scale-codec", 107 | ] 108 | 109 | [[package]] 110 | name = "impl-trait-for-tuples" 111 | version = "0.2.2" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" 114 | dependencies = [ 115 | "proc-macro2", 116 | "quote", 117 | "syn 1.0.109", 118 | ] 119 | 120 | [[package]] 121 | name = "indexmap" 122 | version = "2.1.0" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" 125 | dependencies = [ 126 | "equivalent", 127 | "hashbrown", 128 | ] 129 | 130 | [[package]] 131 | name = "lazy_static" 132 | version = "1.4.0" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 135 | 136 | [[package]] 137 | name = "libc" 138 | version = "0.2.150" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" 141 | 142 | [[package]] 143 | name = "memchr" 144 | version = "2.6.4" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" 147 | 148 | [[package]] 149 | name = "parity-scale-codec" 150 | version = "3.6.9" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" 153 | dependencies = [ 154 | "arrayvec", 155 | "bitvec", 156 | "byte-slice-cast", 157 | "impl-trait-for-tuples", 158 | "parity-scale-codec-derive", 159 | "serde", 160 | ] 161 | 162 | [[package]] 163 | name = "parity-scale-codec-derive" 164 | version = "3.6.9" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" 167 | dependencies = [ 168 | "proc-macro-crate", 169 | "proc-macro2", 170 | "quote", 171 | "syn 1.0.109", 172 | ] 173 | 174 | [[package]] 175 | name = "ppv-lite86" 176 | version = "0.2.17" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 179 | 180 | [[package]] 181 | name = "primitive-types" 182 | version = "0.12.2" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" 185 | dependencies = [ 186 | "fixed-hash", 187 | "impl-codec", 188 | "uint", 189 | ] 190 | 191 | [[package]] 192 | name = "proc-macro-crate" 193 | version = "2.0.0" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" 196 | dependencies = [ 197 | "toml_edit", 198 | ] 199 | 200 | [[package]] 201 | name = "proc-macro2" 202 | version = "1.0.70" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" 205 | dependencies = [ 206 | "unicode-ident", 207 | ] 208 | 209 | [[package]] 210 | name = "quote" 211 | version = "1.0.33" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 214 | dependencies = [ 215 | "proc-macro2", 216 | ] 217 | 218 | [[package]] 219 | name = "radium" 220 | version = "0.7.0" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" 223 | 224 | [[package]] 225 | name = "rand" 226 | version = "0.8.5" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 229 | dependencies = [ 230 | "libc", 231 | "rand_chacha", 232 | "rand_core", 233 | ] 234 | 235 | [[package]] 236 | name = "rand_chacha" 237 | version = "0.3.1" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 240 | dependencies = [ 241 | "ppv-lite86", 242 | "rand_core", 243 | ] 244 | 245 | [[package]] 246 | name = "rand_core" 247 | version = "0.6.4" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 250 | dependencies = [ 251 | "getrandom", 252 | ] 253 | 254 | [[package]] 255 | name = "rustc-hex" 256 | version = "2.1.0" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" 259 | 260 | [[package]] 261 | name = "serde" 262 | version = "1.0.193" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" 265 | dependencies = [ 266 | "serde_derive", 267 | ] 268 | 269 | [[package]] 270 | name = "serde_derive" 271 | version = "1.0.193" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" 274 | dependencies = [ 275 | "proc-macro2", 276 | "quote", 277 | "syn 2.0.39", 278 | ] 279 | 280 | [[package]] 281 | name = "smol-evm-rs" 282 | version = "0.1.0" 283 | dependencies = [ 284 | "bytes", 285 | "hex", 286 | "lazy_static", 287 | "primitive-types", 288 | ] 289 | 290 | [[package]] 291 | name = "static_assertions" 292 | version = "1.1.0" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 295 | 296 | [[package]] 297 | name = "syn" 298 | version = "1.0.109" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 301 | dependencies = [ 302 | "proc-macro2", 303 | "quote", 304 | "unicode-ident", 305 | ] 306 | 307 | [[package]] 308 | name = "syn" 309 | version = "2.0.39" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" 312 | dependencies = [ 313 | "proc-macro2", 314 | "quote", 315 | "unicode-ident", 316 | ] 317 | 318 | [[package]] 319 | name = "tap" 320 | version = "1.0.1" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" 323 | 324 | [[package]] 325 | name = "toml_datetime" 326 | version = "0.6.5" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" 329 | 330 | [[package]] 331 | name = "toml_edit" 332 | version = "0.20.7" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" 335 | dependencies = [ 336 | "indexmap", 337 | "toml_datetime", 338 | "winnow", 339 | ] 340 | 341 | [[package]] 342 | name = "uint" 343 | version = "0.9.5" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" 346 | dependencies = [ 347 | "byteorder", 348 | "crunchy", 349 | "hex", 350 | "static_assertions", 351 | ] 352 | 353 | [[package]] 354 | name = "unicode-ident" 355 | version = "1.0.12" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 358 | 359 | [[package]] 360 | name = "wasi" 361 | version = "0.11.0+wasi-snapshot-preview1" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 364 | 365 | [[package]] 366 | name = "winnow" 367 | version = "0.5.19" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" 370 | dependencies = [ 371 | "memchr", 372 | ] 373 | 374 | [[package]] 375 | name = "wyz" 376 | version = "0.5.1" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" 379 | dependencies = [ 380 | "tap", 381 | ] 382 | -------------------------------------------------------------------------------- /src/opcodes.rs: -------------------------------------------------------------------------------- 1 | use crate::{execution_context::ExecutionContext, instruction::Instruction}; 2 | use std::fmt::Debug; 3 | 4 | #[derive(Debug)] 5 | pub enum Opcodes { 6 | STOP, 7 | ADD, 8 | MUL, 9 | SUB, 10 | LT, 11 | GT, 12 | EQ, 13 | SHR, 14 | SHL, 15 | ISZERO, 16 | CALLVALUE, 17 | CALLDATALOAD, 18 | CALLDATASIZE, 19 | MSTORE8, 20 | RETURN, 21 | PC, 22 | MSIZE, 23 | PUSH0, 24 | PUSH1, 25 | PUSH2, 26 | PUSH3, 27 | PUSH4, 28 | PUSH5, 29 | PUSH6, 30 | PUSH7, 31 | PUSH8, 32 | PUSH9, 33 | PUSH10, 34 | PUSH11, 35 | PUSH12, 36 | PUSH13, 37 | PUSH14, 38 | PUSH15, 39 | PUSH16, 40 | PUSH17, 41 | PUSH18, 42 | PUSH19, 43 | PUSH20, 44 | PUSH21, 45 | PUSH22, 46 | PUSH23, 47 | PUSH24, 48 | PUSH25, 49 | PUSH26, 50 | PUSH27, 51 | PUSH28, 52 | PUSH29, 53 | PUSH30, 54 | PUSH31, 55 | PUSH32, 56 | 57 | // DUP Opcodes 58 | DUP1, 59 | DUP2, 60 | DUP3, 61 | DUP4, 62 | DUP5, 63 | DUP6, 64 | DUP7, 65 | DUP8, 66 | DUP9, 67 | DUP10, 68 | DUP11, 69 | DUP12, 70 | DUP13, 71 | DUP14, 72 | DUP15, 73 | DUP16, 74 | 75 | // SWAP Opcodes 76 | SWAP1, 77 | SWAP2, 78 | SWAP3, 79 | SWAP4, 80 | SWAP5, 81 | SWAP6, 82 | SWAP7, 83 | SWAP8, 84 | SWAP9, 85 | SWAP10, 86 | SWAP11, 87 | SWAP12, 88 | SWAP13, 89 | SWAP14, 90 | SWAP15, 91 | SWAP16, 92 | 93 | // JUMP Opcodes 94 | JUMP, 95 | JUMPI, 96 | JUMPDEST, 97 | } 98 | 99 | #[derive(Debug)] 100 | pub enum Errors { 101 | InvalidJumpDestination(usize), 102 | } 103 | 104 | impl Opcodes { 105 | pub fn register_instructions() { 106 | Instruction::register_instruction(0x00, "STOP".to_string(), Box::new(Opcodes::STOP)); 107 | Instruction::register_instruction(0x01, "ADD".to_string(), Box::new(Opcodes::ADD)); 108 | Instruction::register_instruction(0x02, "MUL".to_string(), Box::new(Opcodes::MUL)); 109 | Instruction::register_instruction(0x03, "SUB".to_string(), Box::new(Opcodes::SUB)); 110 | 111 | Instruction::register_instruction(0x53, "MSTORE8".to_string(), Box::new(Opcodes::MSTORE8)); 112 | Instruction::register_instruction(0xf3, "RETURN".to_string(), Box::new(Opcodes::RETURN)); 113 | Instruction::register_instruction(0x58, "PC".to_string(), Box::new(Opcodes::PC)); 114 | Instruction::register_instruction(0x59, "MSIZE".to_string(), Box::new(Opcodes::MSIZE)); 115 | 116 | // PUSH Instructions 117 | Instruction::register_instruction(0x5F, "PUSH0".to_string(), Box::new(Opcodes::PUSH0)); 118 | Instruction::register_instruction(0x60, "PUSH1".to_string(), Box::new(Opcodes::PUSH1)); 119 | Instruction::register_instruction(0x61, "PUSH2".to_string(), Box::new(Opcodes::PUSH2)); 120 | Instruction::register_instruction(0x62, "PUSH3".to_string(), Box::new(Opcodes::PUSH3)); 121 | Instruction::register_instruction(0x63, "PUSH4".to_string(), Box::new(Opcodes::PUSH4)); 122 | Instruction::register_instruction(0x64, "PUSH5".to_string(), Box::new(Opcodes::PUSH5)); 123 | Instruction::register_instruction(0x65, "PUSH6".to_string(), Box::new(Opcodes::PUSH6)); 124 | Instruction::register_instruction(0x66, "PUSH7".to_string(), Box::new(Opcodes::PUSH7)); 125 | Instruction::register_instruction(0x67, "PUSH8".to_string(), Box::new(Opcodes::PUSH8)); 126 | Instruction::register_instruction(0x68, "PUSH9".to_string(), Box::new(Opcodes::PUSH9)); 127 | Instruction::register_instruction(0x69, "PUSH10".to_string(), Box::new(Opcodes::PUSH10)); 128 | Instruction::register_instruction(0x6A, "PUSH11".to_string(), Box::new(Opcodes::PUSH11)); 129 | Instruction::register_instruction(0x6B, "PUSH12".to_string(), Box::new(Opcodes::PUSH12)); 130 | Instruction::register_instruction(0x6C, "PUSH13".to_string(), Box::new(Opcodes::PUSH13)); 131 | Instruction::register_instruction(0x6D, "PUSH14".to_string(), Box::new(Opcodes::PUSH14)); 132 | Instruction::register_instruction(0x6E, "PUSH15".to_string(), Box::new(Opcodes::PUSH15)); 133 | Instruction::register_instruction(0x6F, "PUSH16".to_string(), Box::new(Opcodes::PUSH16)); 134 | Instruction::register_instruction(0x70, "PUSH17".to_string(), Box::new(Opcodes::PUSH17)); 135 | Instruction::register_instruction(0x71, "PUSH18".to_string(), Box::new(Opcodes::PUSH18)); 136 | Instruction::register_instruction(0x72, "PUSH19".to_string(), Box::new(Opcodes::PUSH19)); 137 | Instruction::register_instruction(0x73, "PUSH20".to_string(), Box::new(Opcodes::PUSH20)); 138 | Instruction::register_instruction(0x74, "PUSH21".to_string(), Box::new(Opcodes::PUSH21)); 139 | Instruction::register_instruction(0x75, "PUSH22".to_string(), Box::new(Opcodes::PUSH22)); 140 | Instruction::register_instruction(0x76, "PUSH23".to_string(), Box::new(Opcodes::PUSH23)); 141 | Instruction::register_instruction(0x77, "PUSH24".to_string(), Box::new(Opcodes::PUSH24)); 142 | Instruction::register_instruction(0x78, "PUSH25".to_string(), Box::new(Opcodes::PUSH25)); 143 | Instruction::register_instruction(0x79, "PUSH26".to_string(), Box::new(Opcodes::PUSH26)); 144 | Instruction::register_instruction(0x7A, "PUSH27".to_string(), Box::new(Opcodes::PUSH27)); 145 | Instruction::register_instruction(0x7B, "PUSH28".to_string(), Box::new(Opcodes::PUSH28)); 146 | Instruction::register_instruction(0x7C, "PUSH29".to_string(), Box::new(Opcodes::PUSH29)); 147 | Instruction::register_instruction(0x7D, "PUSH30".to_string(), Box::new(Opcodes::PUSH30)); 148 | Instruction::register_instruction(0x7E, "PUSH31".to_string(), Box::new(Opcodes::PUSH31)); 149 | Instruction::register_instruction(0x7F, "PUSH32".to_string(), Box::new(Opcodes::PUSH32)); 150 | 151 | // DUP Instructions 152 | 153 | Instruction::register_instruction(0x80, "DUP1".to_string(), Box::new(Opcodes::DUP1)); 154 | Instruction::register_instruction(0x81, "DUP2".to_string(), Box::new(Opcodes::DUP2)); 155 | Instruction::register_instruction(0x82, "DUP3".to_string(), Box::new(Opcodes::DUP3)); 156 | Instruction::register_instruction(0x83, "DUP4".to_string(), Box::new(Opcodes::DUP4)); 157 | Instruction::register_instruction(0x84, "DUP5".to_string(), Box::new(Opcodes::DUP5)); 158 | Instruction::register_instruction(0x85, "DUP6".to_string(), Box::new(Opcodes::DUP6)); 159 | Instruction::register_instruction(0x86, "DUP7".to_string(), Box::new(Opcodes::DUP7)); 160 | Instruction::register_instruction(0x87, "DUP8".to_string(), Box::new(Opcodes::DUP8)); 161 | Instruction::register_instruction(0x88, "DUP9".to_string(), Box::new(Opcodes::DUP9)); 162 | Instruction::register_instruction(0x89, "DUP10".to_string(), Box::new(Opcodes::DUP10)); 163 | Instruction::register_instruction(0x8A, "DUP11".to_string(), Box::new(Opcodes::DUP11)); 164 | Instruction::register_instruction(0x8B, "DUP12".to_string(), Box::new(Opcodes::DUP12)); 165 | Instruction::register_instruction(0x8C, "DUP13".to_string(), Box::new(Opcodes::DUP13)); 166 | Instruction::register_instruction(0x8D, "DUP14".to_string(), Box::new(Opcodes::DUP14)); 167 | Instruction::register_instruction(0x8E, "DUP15".to_string(), Box::new(Opcodes::DUP15)); 168 | Instruction::register_instruction(0x8F, "DUP16".to_string(), Box::new(Opcodes::DUP16)); 169 | 170 | // SWAP Instructions 171 | Instruction::register_instruction(0x90, "SWAP1".to_string(), Box::new(Opcodes::SWAP1)); 172 | Instruction::register_instruction(0x91, "SWAP2".to_string(), Box::new(Opcodes::SWAP2)); 173 | Instruction::register_instruction(0x92, "SWAP3".to_string(), Box::new(Opcodes::SWAP3)); 174 | Instruction::register_instruction(0x93, "SWAP4".to_string(), Box::new(Opcodes::SWAP4)); 175 | Instruction::register_instruction(0x94, "SWAP5".to_string(), Box::new(Opcodes::SWAP5)); 176 | Instruction::register_instruction(0x95, "SWAP6".to_string(), Box::new(Opcodes::SWAP6)); 177 | Instruction::register_instruction(0x96, "SWAP7".to_string(), Box::new(Opcodes::SWAP7)); 178 | Instruction::register_instruction(0x97, "SWAP8".to_string(), Box::new(Opcodes::SWAP8)); 179 | Instruction::register_instruction(0x98, "SWAP9".to_string(), Box::new(Opcodes::SWAP9)); 180 | Instruction::register_instruction(0x99, "SWAP10".to_string(), Box::new(Opcodes::SWAP10)); 181 | Instruction::register_instruction(0x9A, "SWAP11".to_string(), Box::new(Opcodes::SWAP11)); 182 | Instruction::register_instruction(0x9B, "SWAP12".to_string(), Box::new(Opcodes::SWAP12)); 183 | Instruction::register_instruction(0x9C, "SWAP13".to_string(), Box::new(Opcodes::SWAP13)); 184 | Instruction::register_instruction(0x9D, "SWAP14".to_string(), Box::new(Opcodes::SWAP14)); 185 | Instruction::register_instruction(0x9E, "SWAP15".to_string(), Box::new(Opcodes::SWAP15)); 186 | Instruction::register_instruction(0x9F, "SWAP16".to_string(), Box::new(Opcodes::SWAP16)); 187 | 188 | // JUMP Instructions 189 | Instruction::register_instruction(0x56, "JUMP".to_string(), Box::new(Opcodes::JUMP)); 190 | Instruction::register_instruction(0x57, "JUMPI".to_string(), Box::new(Opcodes::JUMPI)); 191 | Instruction::register_instruction( 192 | 0x5B, 193 | "JUMPDEST".to_string(), 194 | Box::new(Opcodes::JUMPDEST), 195 | ); 196 | 197 | // Compare Instructions 198 | Instruction::register_instruction(0x10, "LT".to_string(), Box::new(Opcodes::LT)); 199 | Instruction::register_instruction(0x11, "GT".to_string(), Box::new(Opcodes::GT)); 200 | Instruction::register_instruction(0x14, "EQ".to_string(), Box::new(Opcodes::EQ)); 201 | Instruction::register_instruction(0x1B, "SHL".to_string(), Box::new(Opcodes::SHL)); 202 | Instruction::register_instruction(0x1C, "SHR".to_string(), Box::new(Opcodes::SHR)); 203 | Instruction::register_instruction(0x15, "ISZERO".to_string(), Box::new(Opcodes::ISZERO)); 204 | 205 | Instruction::register_instruction( 206 | 0x34, 207 | "CALLVALUE".to_string(), 208 | Box::new(Opcodes::CALLVALUE), 209 | ); 210 | Instruction::register_instruction( 211 | 0x35, 212 | "CALLDATALOAD".to_string(), 213 | Box::new(Opcodes::CALLDATALOAD), 214 | ); 215 | Instruction::register_instruction( 216 | 0x36, 217 | "CALLDATASIZE".to_string(), 218 | Box::new(Opcodes::CALLDATASIZE), 219 | ); 220 | } 221 | } 222 | pub trait OpcodeExecutor: Send + Sync + Debug { 223 | fn execute(&self, context: &mut ExecutionContext); 224 | } 225 | 226 | impl OpcodeExecutor for Opcodes { 227 | fn execute(&self, context: &mut ExecutionContext) { 228 | match self { 229 | Opcodes::STOP => { 230 | context.stop(); 231 | } 232 | 233 | Opcodes::ADD => { 234 | let value1 = context.stack.pop().unwrap(); 235 | let value2 = context.stack.pop().unwrap(); 236 | context.stack.push(value1.wrapping_add(value2)).unwrap(); 237 | } 238 | Opcodes::MUL => { 239 | let value1 = context.stack.pop().unwrap(); 240 | let value2 = context.stack.pop().unwrap(); 241 | context.stack.push(value1.wrapping_mul(value2)).unwrap(); 242 | } 243 | Opcodes::SUB => { 244 | let a = context.stack.pop().unwrap(); 245 | let b = context.stack.pop().unwrap(); 246 | context.stack.push(a.checked_sub(b).unwrap()).unwrap(); 247 | } 248 | 249 | Opcodes::MSTORE8 => { 250 | let offset = context.stack.pop().unwrap(); 251 | let value = context.stack.pop().unwrap() % 128; 252 | context.memory.store(offset, value).unwrap(); 253 | } 254 | Opcodes::RETURN => { 255 | let offset = context.stack.pop().unwrap(); 256 | let length = context.stack.pop().unwrap(); 257 | context.set_returndata(offset, length); 258 | } 259 | Opcodes::PC => context.stack.push(context.pc).unwrap(), 260 | Opcodes::MSIZE => context 261 | .stack 262 | .push(16 * (context.memory.active_words())) 263 | .unwrap(), 264 | Opcodes::PUSH0 => { 265 | context.stack.push(0).unwrap(); 266 | } 267 | Opcodes::PUSH1 => { 268 | let value = context.read_code(1); 269 | context.stack.push(value).unwrap(); 270 | } 271 | Opcodes::PUSH2 => { 272 | let value = context.read_code(2); 273 | context.stack.push(value).unwrap(); 274 | } 275 | Opcodes::PUSH3 => { 276 | let value = context.read_code(3); 277 | context.stack.push(value).unwrap(); 278 | } 279 | Opcodes::PUSH4 => { 280 | let value = context.read_code(4); 281 | context.stack.push(value).unwrap(); 282 | } 283 | Opcodes::PUSH5 => { 284 | let value = context.read_code(5); 285 | context.stack.push(value).unwrap(); 286 | } 287 | Opcodes::PUSH6 => { 288 | let value = context.read_code(6); 289 | context.stack.push(value).unwrap(); 290 | } 291 | Opcodes::PUSH7 => { 292 | let value = context.read_code(7); 293 | context.stack.push(value).unwrap(); 294 | } 295 | Opcodes::PUSH8 => { 296 | let value = context.read_code(8); 297 | context.stack.push(value).unwrap(); 298 | } 299 | Opcodes::PUSH9 => { 300 | let value = context.read_code(9); 301 | context.stack.push(value).unwrap(); 302 | } 303 | Opcodes::PUSH10 => { 304 | let value = context.read_code(10); 305 | context.stack.push(value).unwrap(); 306 | } 307 | Opcodes::PUSH11 => { 308 | let value = context.read_code(11); 309 | context.stack.push(value).unwrap(); 310 | } 311 | Opcodes::PUSH12 => { 312 | let value = context.read_code(12); 313 | context.stack.push(value).unwrap(); 314 | } 315 | Opcodes::PUSH13 => { 316 | let value = context.read_code(13); 317 | context.stack.push(value).unwrap(); 318 | } 319 | Opcodes::PUSH14 => { 320 | let value = context.read_code(14); 321 | context.stack.push(value).unwrap(); 322 | } 323 | Opcodes::PUSH15 => { 324 | let value = context.read_code(15); 325 | context.stack.push(value).unwrap(); 326 | } 327 | Opcodes::PUSH16 => { 328 | let value = context.read_code(16); 329 | context.stack.push(value).unwrap(); 330 | } 331 | Opcodes::PUSH17 => { 332 | let value = context.read_code(17); 333 | context.stack.push(value).unwrap(); 334 | } 335 | Opcodes::PUSH18 => { 336 | let value = context.read_code(18); 337 | context.stack.push(value).unwrap(); 338 | } 339 | Opcodes::PUSH19 => { 340 | let value = context.read_code(19); 341 | context.stack.push(value).unwrap(); 342 | } 343 | Opcodes::PUSH20 => { 344 | let value = context.read_code(20); 345 | context.stack.push(value).unwrap(); 346 | } 347 | Opcodes::PUSH21 => { 348 | let value = context.read_code(21); 349 | context.stack.push(value).unwrap(); 350 | } 351 | Opcodes::PUSH22 => { 352 | let value = context.read_code(22); 353 | context.stack.push(value).unwrap(); 354 | } 355 | Opcodes::PUSH23 => { 356 | let value = context.read_code(23); 357 | context.stack.push(value).unwrap(); 358 | } 359 | Opcodes::PUSH24 => { 360 | let value = context.read_code(24); 361 | context.stack.push(value).unwrap(); 362 | } 363 | Opcodes::PUSH25 => { 364 | let value = context.read_code(25); 365 | context.stack.push(value).unwrap(); 366 | } 367 | Opcodes::PUSH26 => { 368 | let value = context.read_code(26); 369 | context.stack.push(value).unwrap(); 370 | } 371 | Opcodes::PUSH27 => { 372 | let value = context.read_code(27); 373 | context.stack.push(value).unwrap(); 374 | } 375 | Opcodes::PUSH28 => { 376 | let value = context.read_code(28); 377 | context.stack.push(value).unwrap(); 378 | } 379 | Opcodes::PUSH29 => { 380 | let value = context.read_code(29); 381 | context.stack.push(value).unwrap(); 382 | } 383 | Opcodes::PUSH30 => { 384 | let value = context.read_code(30); 385 | context.stack.push(value).unwrap(); 386 | } 387 | Opcodes::PUSH31 => { 388 | let value = context.read_code(31); 389 | context.stack.push(value).unwrap(); 390 | } 391 | Opcodes::PUSH32 => { 392 | let value = context.read_code(32); 393 | context.stack.push(value).unwrap(); 394 | } 395 | 396 | // DUP Opcodes 397 | Opcodes::DUP1 => { 398 | let value = context.stack.peek(0).unwrap(); 399 | context.stack.push(value).unwrap(); 400 | } 401 | Opcodes::DUP2 => { 402 | let value = context.stack.peek(1).unwrap(); 403 | context.stack.push(value).unwrap(); 404 | } 405 | Opcodes::DUP3 => { 406 | let value = context.stack.peek(2).unwrap(); 407 | context.stack.push(value).unwrap(); 408 | } 409 | Opcodes::DUP4 => { 410 | let value = context.stack.peek(3).unwrap(); 411 | context.stack.push(value).unwrap(); 412 | } 413 | Opcodes::DUP5 => { 414 | let value = context.stack.peek(4).unwrap(); 415 | context.stack.push(value).unwrap(); 416 | } 417 | Opcodes::DUP6 => { 418 | let value = context.stack.peek(5).unwrap(); 419 | context.stack.push(value).unwrap(); 420 | } 421 | Opcodes::DUP7 => { 422 | let value = context.stack.peek(6).unwrap(); 423 | context.stack.push(value).unwrap(); 424 | } 425 | Opcodes::DUP8 => { 426 | let value = context.stack.peek(7).unwrap(); 427 | context.stack.push(value).unwrap(); 428 | } 429 | Opcodes::DUP9 => { 430 | let value = context.stack.peek(8).unwrap(); 431 | context.stack.push(value).unwrap(); 432 | } 433 | Opcodes::DUP10 => { 434 | let value = context.stack.peek(9).unwrap(); 435 | context.stack.push(value).unwrap(); 436 | } 437 | Opcodes::DUP11 => { 438 | let value = context.stack.peek(10).unwrap(); 439 | context.stack.push(value).unwrap(); 440 | } 441 | Opcodes::DUP12 => { 442 | let value = context.stack.peek(11).unwrap(); 443 | context.stack.push(value).unwrap(); 444 | } 445 | Opcodes::DUP13 => { 446 | let value = context.stack.peek(12).unwrap(); 447 | context.stack.push(value).unwrap(); 448 | } 449 | Opcodes::DUP14 => { 450 | let value = context.stack.peek(13).unwrap(); 451 | context.stack.push(value).unwrap(); 452 | } 453 | Opcodes::DUP15 => { 454 | let value = context.stack.peek(14).unwrap(); 455 | context.stack.push(value).unwrap(); 456 | } 457 | Opcodes::DUP16 => { 458 | let value = context.stack.peek(15).unwrap(); 459 | context.stack.push(value).unwrap(); 460 | } 461 | 462 | // SWAP Opcodes 463 | Opcodes::SWAP1 => { 464 | context.stack.swap(1).unwrap(); 465 | } 466 | Opcodes::SWAP2 => { 467 | context.stack.swap(2).unwrap(); 468 | } 469 | Opcodes::SWAP3 => { 470 | context.stack.swap(3).unwrap(); 471 | } 472 | Opcodes::SWAP4 => { 473 | context.stack.swap(4).unwrap(); 474 | } 475 | Opcodes::SWAP5 => { 476 | context.stack.swap(5).unwrap(); 477 | } 478 | Opcodes::SWAP6 => { 479 | context.stack.swap(6).unwrap(); 480 | } 481 | Opcodes::SWAP7 => { 482 | context.stack.swap(7).unwrap(); 483 | } 484 | Opcodes::SWAP8 => { 485 | context.stack.swap(8).unwrap(); 486 | } 487 | Opcodes::SWAP9 => { 488 | context.stack.swap(9).unwrap(); 489 | } 490 | Opcodes::SWAP10 => { 491 | context.stack.swap(10).unwrap(); 492 | } 493 | Opcodes::SWAP11 => { 494 | context.stack.swap(11).unwrap(); 495 | } 496 | Opcodes::SWAP12 => { 497 | context.stack.swap(12).unwrap(); 498 | } 499 | Opcodes::SWAP13 => { 500 | context.stack.swap(13).unwrap(); 501 | } 502 | Opcodes::SWAP14 => { 503 | context.stack.swap(14).unwrap(); 504 | } 505 | Opcodes::SWAP15 => { 506 | context.stack.swap(15).unwrap(); 507 | } 508 | Opcodes::SWAP16 => { 509 | context.stack.swap(16).unwrap(); 510 | } 511 | 512 | // JUMP Instructions 513 | Opcodes::JUMP => { 514 | let target_pc = context.stack.pop().unwrap(); 515 | if context.jumpdests.contains(&target_pc) { 516 | context.set_pc(target_pc); 517 | } else { 518 | panic!("Invalid JumpDestination"); //TODO: Handle gracefully 519 | } 520 | } 521 | Opcodes::JUMPI => { 522 | let target_pc = context.stack.pop().unwrap(); 523 | let condition = context.stack.pop().unwrap(); 524 | if condition != 0 { 525 | if context.jumpdests.contains(&target_pc) { 526 | context.set_pc(target_pc); 527 | } else { 528 | panic!("Invalid JumpDestination"); //TODO: Handle gracefully 529 | } 530 | } 531 | } 532 | Opcodes::JUMPDEST => {} 533 | 534 | Opcodes::LT => { 535 | let a = context.stack.pop().unwrap(); 536 | let b = context.stack.pop().unwrap(); 537 | 538 | if a < b { 539 | context.stack.push(1).unwrap(); 540 | } else { 541 | context.stack.push(0).unwrap(); 542 | } 543 | } 544 | Opcodes::GT => { 545 | let a = context.stack.pop().unwrap(); 546 | let b = context.stack.pop().unwrap(); 547 | 548 | if a > b { 549 | context.stack.push(1).unwrap(); 550 | } else { 551 | context.stack.push(0).unwrap(); 552 | } 553 | } 554 | Opcodes::EQ => { 555 | let a = context.stack.pop().unwrap(); 556 | let b = context.stack.pop().unwrap(); 557 | 558 | if a == b { 559 | context.stack.push(1).unwrap(); 560 | } else { 561 | context.stack.push(0).unwrap(); 562 | } 563 | } 564 | Opcodes::ISZERO => { 565 | let a = context.stack.pop().unwrap(); 566 | 567 | if a == 0 { 568 | context.stack.push(1).unwrap(); 569 | } else { 570 | context.stack.push(0).unwrap(); 571 | } 572 | } 573 | Opcodes::SHL => { 574 | let a = context.stack.pop().unwrap(); 575 | let b = context.stack.pop().unwrap(); 576 | 577 | context.stack.push(b << a).unwrap(); 578 | } 579 | Opcodes::SHR => { 580 | let a = context.stack.pop().unwrap(); 581 | let b = context.stack.pop().unwrap(); 582 | 583 | context.stack.push(b >> a).unwrap(); 584 | } 585 | Opcodes::CALLVALUE => { 586 | context.stack.push(0).unwrap(); 587 | } 588 | Opcodes::CALLDATALOAD => { 589 | let offset = context.stack.pop().unwrap(); 590 | let value = context.calldata.read_word(offset) as usize; 591 | context.stack.push(value).unwrap(); 592 | } 593 | Opcodes::CALLDATASIZE => { 594 | context.stack.push(context.calldata.data.len()).unwrap(); 595 | } 596 | } 597 | } 598 | } 599 | --------------------------------------------------------------------------------