summaryrefslogtreecommitdiff
path: root/vm
diff options
context:
space:
mode:
Diffstat (limited to 'vm')
-rw-r--r--vm/go.mod5
-rw-r--r--vm/go.sum2
-rw-r--r--vm/instruction.go147
-rw-r--r--vm/instructions.go3
-rw-r--r--vm/main.go26
-rw-r--r--vm/memory.go39
-rw-r--r--vm/opcodes.go57
-rw-r--r--vm/rom.go19
-rw-r--r--vm/stack.go32
-rw-r--r--vm/vm.go45
10 files changed, 375 insertions, 0 deletions
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)
+}