Skip to content

Commit

Permalink
zig evm starting points, basic opcodes
Browse files Browse the repository at this point in the history
  • Loading branch information
rauljordan committed Apr 2, 2023
0 parents commit 1e55871
Show file tree
Hide file tree
Showing 13 changed files with 605 additions and 0 deletions.
44 changes: 44 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const std = @import("std");

// Although this function looks imperative, note that its job is to
// declaratively construct a build graph that will be executed by an external
// runner.
pub fn build(b: *std.Build) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});

// Standard optimization options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
// set a preferred release mode, allowing the user to decide how to optimize.
const optimize = b.standardOptimizeOption(.{});

const lib = b.addStaticLibrary(.{
.name = "zevm",
// In this case the main source file is merely a path, however, in more
// complicated build scripts, this could be a generated file.
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = optimize,
});

// This declares intent for the library to be installed into the standard
// location when the user invokes the "install" step (the default step when
// running `zig build`).
lib.install();

// Creates a step for unit testing.
const main_tests = b.addTest(.{
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = optimize,
});

// This creates a build step. It will be visible in the `zig build --help` menu,
// and can be selected like this: `zig build test`
// This will evaluate the `test` step rather than the default, which is "install".
const test_step = b.step("test", "Run library tests");
test_step.dependOn(&main_tests.step);
}
2 changes: 2 additions & 0 deletions src/gas.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub const VERYLOW: u64 = 3;
pub const LOW: u64 = 5;
164 changes: 164 additions & 0 deletions src/main.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
const std = @import("std");
const testing = std.testing;
const opcode = @import("opcode.zig");
const gas = @import("gas.zig");

pub const Status = enum { Break, Continue, OutOfGas };

pub const StackErr = error{Overflow};

fn Stack(comptime T: type) type {
const STACK_LIMIT: usize = 1024;
return struct {
const This = @This();
inner: std.ArrayList(T),
ac: std.mem.Allocator,
fn init(ac: std.mem.Allocator) !This {
var inner = try std.ArrayList(T).initCapacity(ac, STACK_LIMIT);
return .{
.ac = ac,
.inner = inner,
};
}
fn deinit(self: *This) void {
self.inner.deinit();
}
fn get(self: This, idx: usize) *T {
return &self.inner.items[idx];
}
fn push(self: *This, x: T) !void {
return try self.inner.append(x);
}
fn pop(self: *This) T {
return self.inner.pop();
}
fn dup(self: *This, idx: usize) void {
const item = self.inner.items[idx];
_ = item;
return;
}
fn print(self: This) void {
std.debug.print("{any}\n", .{self.inner.items});
}
};
}

pub const GasTracker = struct {
limit: u64,
total_used: u64,
no_mem_used: u64,
mem_used: u64,
refunded: i64,
pub fn init(gas_limit: u64) GasTracker {
return .{
.limit = gas_limit,
.total_used = 0,
.no_mem_used = 0,
.mem_used = 0,
.refunded = 0,
};
}
inline fn recordGasCost(self: *GasTracker, cost: u64) bool {
// Check if we overflow.
const max_u64 = (1 << 64) - 1;
if (self.total_used >= max_u64 - cost) {
return false;
}
const all_used = self.total_used + cost;
if (all_used >= self.limit) {
return false;
}
self.no_mem_used += cost;
self.total_used = all_used;
return true;
}
};

pub const Interpreter = struct {
const This = @This();
ac: std.mem.Allocator,
inst: [*]u8,
gas_tracker: GasTracker,
bytecode: []u8,
stack: Stack(u8),
inst_result: Status,
fn eval(self: *This, op: u8) !void {
switch (op) {
opcode.ADD => {
if (!self.gas_tracker.recordGasCost(gas.VERYLOW)) {
self.inst_result = Status.OutOfGas;
}
const a = self.stack.pop();
const b = self.stack.pop();
// TODO: Modulo add.
try self.stack.push(a + b);
self.stack.print();
self.inst_result = Status.Break;
},
opcode.PUSH1 => {
const start = @ptrCast(*u8, self.inst + 1);
try self.stack.push(start.*);
std.debug.print("push1 = {x}\n", .{start.*});
self.inst += 1;
},
opcode.DUP1 => {
if (!self.gas_tracker.recordGasCost(gas.LOW)) {
self.inst_result = Status.OutOfGas;
}
const item = self.stack.get(0);
try self.stack.push(item.*);
self.stack.print();
},
else => {
std.debug.print("Unhandled opcode 0x{x}\n", .{op});
self.inst_result = Status.Break;
},
}
}
fn init(
alloc: std.mem.Allocator,
bytecode: []u8,
) !This {
return .{
.ac = alloc,
.inst = bytecode.ptr,
.bytecode = bytecode,
.stack = try Stack(u8).init(alloc),
.gas_tracker = GasTracker.init(100),
.inst_result = Status.Continue,
};
}
fn deinit(self: *This) void {
self.stack.deinit();
}
fn programCounter(self: This) usize {
// Subtraction of pointers is safe here
return @ptrToInt(self.bytecode.ptr - self.inst);
}
fn runLoop(self: *This) !void {
while (self.inst_result == Status.Continue) {
const op = @ptrCast(*u8, self.inst);
std.debug.print("Running 0x{x}\n", .{op.*});
try self.eval(op.*);
self.inst = self.inst + 1;
}
}
};

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
var ac = gpa.allocator();
defer _ = gpa.deinit();

var bytecode = try ac.alloc(u8, 4);
defer ac.free(bytecode);

bytecode = try std.fmt.hexToBytes(bytecode, "60038001");
std.debug.print("input bytecode 0x{x}\n", .{
std.fmt.fmtSliceHexLower(bytecode),
});
var interpreter = try Interpreter.init(ac, bytecode);
defer interpreter.deinit();
try interpreter.runLoop();
std.debug.print("Finished, result {}\n", .{interpreter.inst_result});
}
147 changes: 147 additions & 0 deletions src/opcode.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
pub const STOP: u8 = 0x00;
pub const ADD: u8 = 0x01;
pub const MUL: u8 = 0x02;
pub const SUB: u8 = 0x03;
pub const DIV: u8 = 0x04;
pub const SDIV: u8 = 0x05;
pub const MOD: u8 = 0x06;
pub const SMOD: u8 = 0x07;
pub const ADDMOD: u8 = 0x08;
pub const MULMOD: u8 = 0x09;
pub const EXP: u8 = 0x0a;
pub const SIGNEXTEND: u8 = 0x0b;

pub const LT: u8 = 0x10;
pub const GT: u8 = 0x11;
pub const SLT: u8 = 0x12;
pub const SGT: u8 = 0x13;
pub const EQ: u8 = 0x14;
pub const ISZERO: u8 = 0x15;
pub const AND: u8 = 0x16;
pub const OR: u8 = 0x17;
pub const XOR: u8 = 0x18;
pub const NOT: u8 = 0x19;
pub const BYTE: u8 = 0x1a;

pub const CALLDATALOAD: u8 = 0x35;
pub const CALLDATASIZE: u8 = 0x36;
pub const CALLDATACOPY: u8 = 0x37;
pub const CODESIZE: u8 = 0x38;
pub const CODECOPY: u8 = 0x39;

pub const SHL: u8 = 0x1b;
pub const SHR: u8 = 0x1c;
pub const SAR: u8 = 0x1d;
pub const SHA3: u8 = 0x20;
pub const POP: u8 = 0x50;
pub const MLOAD: u8 = 0x51;
pub const MSTORE: u8 = 0x52;
pub const MSTORE8: u8 = 0x53;
pub const JUMP: u8 = 0x56;
pub const JUMPI: u8 = 0x57;
pub const PC: u8 = 0x58;
pub const MSIZE: u8 = 0x59;
pub const JUMPDEST: u8 = 0x5b;
pub const PUSH0: u8 = 0x5f;
pub const PUSH1: u8 = 0x60;
pub const PUSH2: u8 = 0x61;
pub const PUSH3: u8 = 0x62;
pub const PUSH4: u8 = 0x63;
pub const PUSH5: u8 = 0x64;
pub const PUSH6: u8 = 0x65;
pub const PUSH7: u8 = 0x66;
pub const PUSH8: u8 = 0x67;
pub const PUSH9: u8 = 0x68;
pub const PUSH10: u8 = 0x69;
pub const PUSH11: u8 = 0x6a;
pub const PUSH12: u8 = 0x6b;
pub const PUSH13: u8 = 0x6c;
pub const PUSH14: u8 = 0x6d;
pub const PUSH15: u8 = 0x6e;
pub const PUSH16: u8 = 0x6f;
pub const PUSH17: u8 = 0x70;
pub const PUSH18: u8 = 0x71;
pub const PUSH19: u8 = 0x72;
pub const PUSH20: u8 = 0x73;
pub const PUSH21: u8 = 0x74;
pub const PUSH22: u8 = 0x75;
pub const PUSH23: u8 = 0x76;
pub const PUSH24: u8 = 0x77;
pub const PUSH25: u8 = 0x78;
pub const PUSH26: u8 = 0x79;
pub const PUSH27: u8 = 0x7a;
pub const PUSH28: u8 = 0x7b;
pub const PUSH29: u8 = 0x7c;
pub const PUSH30: u8 = 0x7d;
pub const PUSH31: u8 = 0x7e;
pub const PUSH32: u8 = 0x7f;
pub const DUP1: u8 = 0x80;
pub const DUP2: u8 = 0x81;
pub const DUP3: u8 = 0x82;
pub const DUP4: u8 = 0x83;
pub const DUP5: u8 = 0x84;
pub const DUP6: u8 = 0x85;
pub const DUP7: u8 = 0x86;
pub const DUP8: u8 = 0x87;
pub const DUP9: u8 = 0x88;
pub const DUP10: u8 = 0x89;
pub const DUP11: u8 = 0x8a;
pub const DUP12: u8 = 0x8b;
pub const DUP13: u8 = 0x8c;
pub const DUP14: u8 = 0x8d;
pub const DUP15: u8 = 0x8e;
pub const DUP16: u8 = 0x8f;
pub const SWAP1: u8 = 0x90;
pub const SWAP2: u8 = 0x91;
pub const SWAP3: u8 = 0x92;
pub const SWAP4: u8 = 0x93;
pub const SWAP5: u8 = 0x94;
pub const SWAP6: u8 = 0x95;
pub const SWAP7: u8 = 0x96;
pub const SWAP8: u8 = 0x97;
pub const SWAP9: u8 = 0x98;
pub const SWAP10: u8 = 0x99;
pub const SWAP11: u8 = 0x9a;
pub const SWAP12: u8 = 0x9b;
pub const SWAP13: u8 = 0x9c;
pub const SWAP14: u8 = 0x9d;
pub const SWAP15: u8 = 0x9e;
pub const SWAP16: u8 = 0x9f;
pub const RETURN: u8 = 0xf3;
pub const REVERT: u8 = 0xfd;
pub const INVALID: u8 = 0xfe;
pub const ADDRESS: u8 = 0x30;
pub const BALANCE: u8 = 0x31;
pub const BASEFEE: u8 = 0x48;
pub const ORIGIN: u8 = 0x32;
pub const CALLER: u8 = 0x33;
pub const CALLVALUE: u8 = 0x34;
pub const GASPRICE: u8 = 0x3a;
pub const EXTCODESIZE: u8 = 0x3b;
pub const EXTCODECOPY: u8 = 0x3c;
pub const EXTCODEHASH: u8 = 0x3f;
pub const RETURNDATASIZE: u8 = 0x3d;
pub const RETURNDATACOPY: u8 = 0x3e;
pub const BLOCKHASH: u8 = 0x40;
pub const COINBASE: u8 = 0x41;
pub const TIMESTAMP: u8 = 0x42;
pub const NUMBER: u8 = 0x43;
pub const DIFFICULTY: u8 = 0x44;
pub const GASLIMIT: u8 = 0x45;
pub const SELFBALANCE: u8 = 0x47;
pub const SLOAD: u8 = 0x54;
pub const SSTORE: u8 = 0x55;
pub const GAS: u8 = 0x5a;
pub const LOG0: u8 = 0xa0;
pub const LOG1: u8 = 0xa1;
pub const LOG2: u8 = 0xa2;
pub const LOG3: u8 = 0xa3;
pub const LOG4: u8 = 0xa4;
pub const CREATE: u8 = 0xf0;
pub const CREATE2: u8 = 0xf5;
pub const CALL: u8 = 0xf1;
pub const CALLCODE: u8 = 0xf2;
pub const DELEGATECALL: u8 = 0xf4;
pub const STATICCALL: u8 = 0xfa;
pub const SELFDESTRUCT: u8 = 0xff;
pub const CHAINID: u8 = 0x46;
Empty file.
Empty file added zig-cache/h/timestamp
Empty file.
Loading

0 comments on commit 1e55871

Please sign in to comment.