From ba2b9c8a1bb1876b6eb4c9783fde798b19de4418 Mon Sep 17 00:00:00 2001 From: stefan Date: Wed, 24 May 2023 08:32:08 -0400 Subject: init --- vm/go.mod | 5 ++ vm/go.sum | 2 + vm/instruction.go | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++++ vm/instructions.go | 3 ++ vm/main.go | 26 ++++++++++ vm/memory.go | 39 ++++++++++++++ vm/opcodes.go | 57 +++++++++++++++++++++ vm/rom.go | 19 +++++++ vm/stack.go | 32 ++++++++++++ vm/vm.go | 45 ++++++++++++++++ 10 files changed, 375 insertions(+) create mode 100644 vm/go.mod create mode 100644 vm/go.sum create mode 100644 vm/instruction.go create mode 100644 vm/instructions.go create mode 100644 vm/main.go create mode 100644 vm/memory.go create mode 100644 vm/opcodes.go create mode 100644 vm/rom.go create mode 100644 vm/stack.go create mode 100644 vm/vm.go (limited to 'vm') diff --git a/vm/go.mod b/vm/go.mod new file mode 100644 index 0000000..725da6d --- /dev/null +++ b/vm/go.mod @@ -0,0 +1,5 @@ +module s00.xyz/evm + +go 1.20 + +require github.com/holiman/uint256 v1.2.2 // indirect diff --git a/vm/go.sum b/vm/go.sum new file mode 100644 index 0000000..e0f03a0 --- /dev/null +++ b/vm/go.sum @@ -0,0 +1,2 @@ +github.com/holiman/uint256 v1.2.2 h1:TXKcSGc2WaxPD2+bmzAsVthL4+pEN0YwXcL5qED83vk= +github.com/holiman/uint256 v1.2.2/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= diff --git a/vm/instruction.go b/vm/instruction.go new file mode 100644 index 0000000..5cd56e6 --- /dev/null +++ b/vm/instruction.go @@ -0,0 +1,147 @@ +package main + +import ( + "github.com/holiman/uint256" +) + +type Handler func(vm *Evm) + +type Instruction struct { + name string + n int // number of params + handler Handler +} + +type Inst Instruction // shorthand + +// i miss c macros :( +var Instructions = [256]Instruction{ + STOP: { + name: "STOP", + n: 0, + handler: func(vm *Evm) { vm.stopped = true }, + } , + ADD: { + name: "ADD", + n: 2, + handler: func(vm *Evm) { + o := uint256.NewInt(0) + vm.stack.Push(o.Add(vm.stack.Pop(), vm.stack.Pop())) + }, + }, + MUL: { + n: 2, + name: "MUL", + handler: func(vm *Evm) { + o := uint256.NewInt(0) + vm.stack.Push(o.Mul(vm.stack.Pop(), vm.stack.Pop())) + }, + }, + SUB: { + n: 2, + name: "SUB", + handler: func(vm *Evm) { + o := uint256.NewInt(0) + vm.stack.Push(o.Sub(vm.stack.Pop(), vm.stack.Pop())) + }, + }, + DIV: { + n: 2, + name: "DIV", + handler: func(vm *Evm) { + o := uint256.NewInt(0) + vm.stack.Push(o.Div(vm.stack.Pop(), vm.stack.Pop())) + }, + }, + SDIV: { + n: 2, + name: "SDIV", + handler: func(vm *Evm) { + o := uint256.NewInt(0) + vm.stack.Push(o.SDiv(vm.stack.Pop(), vm.stack.Pop())) + }, + }, + MOD: { + n: 2, + name: "MOD", + handler: func(vm *Evm) { + o := uint256.NewInt(0) + vm.stack.Push(o.Mod(vm.stack.Pop(), vm.stack.Pop())) + }, + }, + SMOD: { + n: 2, + name: "SMOD", + handler: func(vm *Evm) { + o := uint256.NewInt(0) + vm.stack.Push(o.SMod(vm.stack.Pop(), vm.stack.Pop())) + }, + }, + ADDMOD: { + n: 3, + name: "ADDMOD", + handler: func(vm *Evm) { + o := uint256.NewInt(0) + vm.stack.Push(o.AddMod(vm.stack.Pop(), vm.stack.Pop(), vm.stack.Pop())) + }, + }, + MULMOD: { + n: 3, + name: "MULMOD", + handler: func(vm *Evm) { + o := uint256.NewInt(0) + vm.stack.Push(o.MulMod(vm.stack.Pop(), vm.stack.Pop(), vm.stack.Pop())) + }, + }, + POP: { + n: 1, + name: "POP", + handler: func(vm *Evm) { + vm.stack.Pop() + }, + }, + MLOAD: { + n: 1, + name: "MLOAD", + }, + MSTORE: { + n: 2, + name: "MSTORE", + }, + MSTORE8: { + n: 2, + name: "MSTORE8", + }, + PUSH1: { name: "PUSH1" }, + PUSH2: { name: "PUSH2" }, + PUSH3: { name: "PUSH3" }, + PUSH4: { name: "PUSH4" }, + PUSH5: { name: "PUSH5" }, + PUSH6: { name: "PUSH6" }, + PUSH7: { name: "PUSH7" }, + PUSH8: { name: "PUSH8" }, + PUSH9: { name: "PUSH9" }, + PUSH10: { name: "PUSH0" }, + PUSH11: { name: "PUSH11" }, + PUSH12: { name: "PUSH12" }, + PUSH13: { name: "PUSH13" }, + PUSH14: { name: "PUSH14" }, + PUSH15: { name: "PUSH15" }, + PUSH16: { name: "PUSH16" }, + PUSH17: { name: "PUSH17" }, + PUSH18: { name: "PUSH18" }, + PUSH19: { name: "PUSH19" }, + PUSH20: { name: "PUSH20" }, + PUSH21: { name: "PUSH21" }, + PUSH22: { name: "PUSH22" }, + PUSH23: { name: "PUSH23" }, + PUSH24: { name: "PUSH24" }, + PUSH25: { name: "PUSH25" }, + PUSH26: { name: "PUSH26" }, + PUSH27: { name: "PUSH27" }, + PUSH28: { name: "PUSH28" }, + PUSH29: { name: "PUSH29" }, + PUSH30: { name: "PUSH30" }, + PUSH31: { name: "PUSH31" }, + PUSH32: { name: "PUSH32" }, +} diff --git a/vm/instructions.go b/vm/instructions.go new file mode 100644 index 0000000..5a1220b --- /dev/null +++ b/vm/instructions.go @@ -0,0 +1,3 @@ +package main + +type InstructionHandler func (vm *Evm) diff --git a/vm/main.go b/vm/main.go new file mode 100644 index 0000000..61aeebb --- /dev/null +++ b/vm/main.go @@ -0,0 +1,26 @@ +package main + +import ( + "fmt" + "os" +) + +func main() { + + if (len(os.Args) != 2) { + fmt.Fprintf(os.Stderr, "usage: %s bytecode.bin\n") + os.Exit(1) + } + + b, err := os.ReadFile(os.Args[1]) + + if err != nil { + fmt.Fprintf(os.Stderr, "failed to open file: %s\n", os.Args[1]) + os.Exit(1) + } + + vm := NewEvm(b) + + vm.Start() +} + diff --git a/vm/memory.go b/vm/memory.go new file mode 100644 index 0000000..0a9dd2c --- /dev/null +++ b/vm/memory.go @@ -0,0 +1,39 @@ +package main + +import ( + "github.com/holiman/uint256" + "log" +) + +type Memory []byte + +// store 256b word at offset in memory +func (m *Memory) sw(ost uint64, val *uint256.Int) { + if ost + 32 > uint64(len(*m)) { + *m = append(*m, make([]byte, ost + 32 - uint64(len(*m)))...) + } + + bytes := val.Bytes32() + copy((*m)[ost:], bytes[:]) +} + +// store byte at offset in memory +func (m *Memory) sb(ost uint64, val byte) { + if ost + 1 > uint64(len(*m)) { + *m = append(*m, make([]byte, ost + 1 - uint64(len(*m)))...) + } + + (*m)[ost] = val +} + +// loads 256b word fron offset in memory +func (m *Memory) ld(ost uint64) []byte { + if ost + 32 > uint64(len(*m)) { + log.Fatal("trying to load out of memory") + } + + r := make([]byte, 32) + copy(r, (*m)[ost:ost+32]) + return r +} + diff --git a/vm/opcodes.go b/vm/opcodes.go new file mode 100644 index 0000000..b3d4229 --- /dev/null +++ b/vm/opcodes.go @@ -0,0 +1,57 @@ +package main + +type Opcode byte + +const ( + STOP = iota + ADD + MUL + SUB + DIV + SDIV + MOD + SMOD + ADDMOD + MULMOD + POP = 0x50 + MLOAD = 0x51 + MSTORE = 0x52 + MSTORE8 = 0x53 + PUSH1 = 0x60 + PUSH2 = 0x61 + PUSH3 = 0x62 + PUSH4 = 0x63 + PUSH5 = 0x64 + PUSH6 = 0x65 + PUSH7 = 0x66 + PUSH8 = 0x67 + PUSH9 = 0x68 + PUSH10 = 0x69 + PUSH11 = 0x6a + PUSH12 = 0x6b + PUSH13 = 0x6c + PUSH14 = 0x6d + PUSH15 = 0x6e + PUSH16 = 0x6f + PUSH17 = 0x70 + PUSH18 = 0x71 + PUSH19 = 0x72 + PUSH20 = 0x73 + PUSH21 = 0x74 + PUSH22 = 0x75 + PUSH23 = 0x76 + PUSH24 = 0x77 + PUSH25 = 0x78 + PUSH26 = 0x79 + PUSH27 = 0x7a + PUSH28 = 0x7b + PUSH29 = 0x7c + PUSH30 = 0x7d + PUSH31 = 0x7e + PUSH32 = 0x7f + //... +) + +func (op *Opcode) String() string { + return Instructions[*op].name +} diff --git a/vm/rom.go b/vm/rom.go new file mode 100644 index 0000000..958e70e --- /dev/null +++ b/vm/rom.go @@ -0,0 +1,19 @@ +package main + +import ( + "os" + "fmt" +) + +type Rom []byte + +func (r *Rom) Fetch(pc *uint64, n uint64) []byte { + if (*pc + n > uint64(len(*r)) - 1) { + fmt.Fprintf(os.Stderr, "error: trying to read outside of rom\n") + os.Exit(1) + } + x := make([]byte, n) + copy(x, (*r)[*pc:*pc + n]) + *pc += n + return x +} diff --git a/vm/stack.go b/vm/stack.go new file mode 100644 index 0000000..fc1cbd4 --- /dev/null +++ b/vm/stack.go @@ -0,0 +1,32 @@ +package main + +import ( + "log" + + "github.com/holiman/uint256" +) + +const STACK_CAP = (1 << 10) + +type Stack []uint256.Int + +func (s *Stack) Push(x *uint256.Int) { + *s = append(*s, *x) + if len(*s) + 1 > STACK_CAP { + log.Fatal("stack overflow") + } +} + +func (s *Stack) Pop() *uint256.Int { + if len(*s) <= 0 { + log.Fatal("stack underflow") + } + + r := (*s)[len(*s)-1] + *s = (*s)[:len(*s)-1] + return &r +} + +func (s *Stack) Peek() *uint256.Int { + return &(*s)[len(*s)-1] +} diff --git a/vm/vm.go b/vm/vm.go new file mode 100644 index 0000000..209ab71 --- /dev/null +++ b/vm/vm.go @@ -0,0 +1,45 @@ +package main + +import ( + "fmt" + "github.com/holiman/uint256" +) + +type Evm struct { + code Rom + stack Stack + memory Memory + pc uint64 + stopped bool +} + +func NewEvm(_code []byte) *Evm { + return &Evm{ + pc: 0, + stopped: true, + stack : Stack{}, + code: _code, + } +} + +func (vm *Evm) Start() { + vm.stopped = false + for !(vm.stopped) { + op := vm.code.Fetch(&(vm.pc), 1)[0] + fmt.Printf("pc: %d | opcode: %x -> string: %s\n", vm.pc, op, Instructions[op].name) + if op >= PUSH1 && op <= PUSH32 { + nb := op - PUSH1 + 1 + fmt.Printf("pushing %d byte value to the stack!\n", vm.pc, nb) + b := vm.code.Fetch(&(vm.pc), uint64(nb)) + x := uint256.NewInt(0) + x = x.SetBytes(b) + vm.stack.Push(x) + } else { + vm.Execute(op) + } + } +} + +func (vm *Evm) Execute(op byte) { + Instructions[op].handler(vm) +} -- cgit v1.2.3