From ba2b9c8a1bb1876b6eb4c9783fde798b19de4418 Mon Sep 17 00:00:00 2001 From: stefan Date: Wed, 24 May 2023 08:32:08 -0400 Subject: init --- README.md | 10 ++++ as/as | Bin 0 -> 2088979 bytes as/as.go | 32 ++++++++++++ as/go.mod | 5 ++ as/go.sum | 2 + as/main.go | 59 +++++++++++++++++++++ as/opcodes.go | 101 ++++++++++++++++++++++++++++++++++++ node/go.mod | 5 -- node/go.sum | 2 - node/node.go | 15 ------ node/stack.go | 32 ------------ test/add.easm | 4 ++ 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 ++++++++++++++++ 22 files changed, 588 insertions(+), 54 deletions(-) create mode 100644 README.md create mode 100755 as/as create mode 100644 as/as.go create mode 100644 as/go.mod create mode 100644 as/go.sum create mode 100644 as/main.go create mode 100644 as/opcodes.go delete mode 100644 node/go.mod delete mode 100644 node/go.sum delete mode 100644 node/node.go delete mode 100644 node/stack.go create mode 100644 test/add.easm 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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..ab60e06 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +evm +=== + +building and running +-------------------- + +```sh +cd vm & go build +./evm +``` diff --git a/as/as b/as/as new file mode 100755 index 0000000..3c80b92 Binary files /dev/null and b/as/as differ diff --git a/as/as.go b/as/as.go new file mode 100644 index 0000000..bcc7cf8 --- /dev/null +++ b/as/as.go @@ -0,0 +1,32 @@ +package main + +import ( + "fmt" + "strings" + "github.com/holiman/uint256" + +) + +func Assemble(code string) []byte { + bytecode := make([]byte, 0) + + t := strings.Fields(code) + + for i := range t { + x, found := OpcodeStrings[t[i]] + + if found { + bytecode = append(bytecode, x) + } else { + var n *uint256.Int = nil + if len(t[i]) > 1 && t[i][1] == 'x' { + n, _ = uint256.FromHex(t[i]) + } else { + n, _ = uint256.FromDecimal(t[i]) + } + b := n.Bytes32() + bytecode = append(bytecode, b[:]...) + } + } + return bytecode +} diff --git a/as/go.mod b/as/go.mod new file mode 100644 index 0000000..6f40dd8 --- /dev/null +++ b/as/go.mod @@ -0,0 +1,5 @@ +module s00.xyz/as + +go 1.20 + +require github.com/holiman/uint256 v1.2.2 // indirect diff --git a/as/go.sum b/as/go.sum new file mode 100644 index 0000000..e0f03a0 --- /dev/null +++ b/as/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/as/main.go b/as/main.go new file mode 100644 index 0000000..95aff46 --- /dev/null +++ b/as/main.go @@ -0,0 +1,59 @@ +package main + +import ( + "os" + "fmt" +) + +func usage(arg0 string) { + fmt.Fprintf(os.Stderr, "usage: %s [-o file] file.easm\n", os.Args[0]) + os.Exit(1) +} + +func main() { + if (len(os.Args) <= 2) { + usage(os.Args[0]) + } + + + var aout int = 0 + var ain int = 0 + + for i := 0; i < len(os.Args); i++ { + fmt.Println(os.Args[i]) + switch os.Args[i] { + case "-o": + i = i + 1 + aout = i + default: + ain = i + } + } + + if (ain == 0) { + usage(os.Args[0]) + } + + var outf string = "out.bin" + if (aout != 0) { + outf = os.Args[aout] + } + + b, errin := os.ReadFile(os.Args[ain]) + out, errout := os.Create(outf) + + if errin != nil { + fmt.Fprintf(os.Stderr, "failed to read file: %s\n", os.Args[ain]) + os.Exit(1) + } + + if errout != nil { + fmt.Fprintf(os.Stderr, "failed to create file for output: %s\n", outf) + os.Exit(1) + } + + s := string(b) + c := Assemble(s) + + out.Write(c) +} diff --git a/as/opcodes.go b/as/opcodes.go new file mode 100644 index 0000000..94c2e2b --- /dev/null +++ b/as/opcodes.go @@ -0,0 +1,101 @@ +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 +) + +var OpcodeStrings = map[string]byte { + "STOP": STOP, + "ADD": ADD, + "MUL": MUL, + "SUB": SUB, + "DIV": DIV, + "SDIV": SDIV, + "MOD": MOD, + "SMOD": SMOD, + "ADDMOD": ADDMOD, + "MULMOD": MULMOD, + "POP": POP, + "MLOAD": MLOAD, + "MSTORE": MSTORE, + "MSTORE8": MSTORE8, + "PUSH1": PUSH1, + "PUSH2": PUSH2, + "PUSH3": PUSH3, + "PUSH4": PUSH4, + "PUSH5": PUSH5, + "PUSH6": PUSH6, + "PUSH7": PUSH7, + "PUSH8": PUSH8, + "PUSH9": PUSH9, + "PUSH10": PUSH10, + "PUSH11": PUSH11, + "PUSH12": PUSH12, + "PUSH13": PUSH13, + "PUSH14": PUSH14, + "PUSH15": PUSH15, + "PUSH16": PUSH16, + "PUSH17": PUSH17, + "PUSH18": PUSH18, + "PUSH19": PUSH19, + "PUSH20": PUSH20, + "PUSH21": PUSH21, + "PUSH22": PUSH22, + "PUSH23": PUSH23, + "PUSH24": PUSH24, + "PUSH25": PUSH25, + "PUSH26": PUSH26, + "PUSH27": PUSH27, + "PUSH28": PUSH28, + "PUSH29": PUSH29, + "PUSH30": PUSH30, + "PUSH31": PUSH31, + "PUSH32": PUSH32, +} diff --git a/node/go.mod b/node/go.mod deleted file mode 100644 index 725da6d..0000000 --- a/node/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module s00.xyz/evm - -go 1.20 - -require github.com/holiman/uint256 v1.2.2 // indirect diff --git a/node/go.sum b/node/go.sum deleted file mode 100644 index e0f03a0..0000000 --- a/node/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -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/node/node.go b/node/node.go deleted file mode 100644 index 18640e9..0000000 --- a/node/node.go +++ /dev/null @@ -1,15 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/holiman/uint256" -) - -func main() { - s := NewStack() - x, _ := uint256.FromHex("0xfeedface") - s.Push(x) - y := s.Pop() - fmt.Println(y.String()) -} diff --git a/node/stack.go b/node/stack.go deleted file mode 100644 index a5460a7..0000000 --- a/node/stack.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "log" - - "github.com/holiman/uint256" -) - -const STACK_CAP = (1 << 10) - -type Stack []uint256.Int - -func NewStack() *Stack { - return &Stack{} -} - -func (s *Stack) Push(x *uint256.Int) { - *s = append(*s, *x) - if len(*s) > 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 -} diff --git a/test/add.easm b/test/add.easm new file mode 100644 index 0000000..4876b0b --- /dev/null +++ b/test/add.easm @@ -0,0 +1,4 @@ +PUSH 9 +PUSH 4 +ADD +STOP 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