Skip to content

Commit

Permalink
refactor vm
Browse files Browse the repository at this point in the history
  • Loading branch information
cdump committed Dec 15, 2023
1 parent c9010d2 commit 800d178
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 65 deletions.
59 changes: 28 additions & 31 deletions evmole/evm/vm.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Any

from .opcodes import Op, OpCode
from .opcodes import Op, OpCode, name as opcode2name
from .stack import Stack
from .memory import Memory

Expand Down Expand Up @@ -34,7 +34,7 @@ def __str__(self):
return '\n'.join(
(
f'Vm ({id(self):x}):',
f' .pc = {hex(self.pc)} | {self.current_op()}',
f' .pc = {hex(self.pc)} | {opcode2name(self.current_op())}',
f' .stack = {self.stack}',
f' .memory = {self.memory}',
)
Expand All @@ -54,28 +54,25 @@ def current_op(self) -> OpCode:
return OpCode(self.code[self.pc])

def step(self) -> tuple[OpCode, int, *tuple[Any, ...]]:
ret = self._exec_next_opcode()
op, gas_used = ret[0], ret[1]
assert gas_used != -1, f'Op {op} with unset gas_used'
op = self.current_op()
if op in self.blacklisted_ops:
raise BlacklistedOpError(op)
ret = self._exec_opcode(op)
if op not in {Op.JUMP, Op.JUMPI}:
self.pc += 1

if self.pc >= len(self.code):
self.stopped = True
return ret
return (op, *ret)

def _exec_next_opcode(self) -> tuple[OpCode, int, *tuple[Any, ...]]:
op = self.current_op()
def _exec_opcode(self, op: OpCode) -> tuple[int, *tuple[Any, ...]]:
match op:
case op if op in self.blacklisted_ops:
raise BlacklistedOpError(op)

case op if op >= Op.PUSH0 and op <= Op.PUSH32:
n = op - Op.PUSH0
args = self.code[(self.pc + 1) : (self.pc + 1 + n)].rjust(32, b'\x00')
self.stack.push(args)
self.pc += n
return (op, 2 if n == 0 else 3)
return (2 if n == 0 else 3,)

case op if op in {Op.JUMP, Op.JUMPI}:
s0 = self.stack.pop_uint()
Expand All @@ -85,22 +82,22 @@ def _exec_next_opcode(self) -> tuple[OpCode, int, *tuple[Any, ...]]:
s1 = self.stack.pop_uint()
if s1 == 0:
self.pc += 1
return (op, 10)
return (10,)
self.pc = s0
return (op, 8 if op == Op.JUMP else 10)
return (8 if op == Op.JUMP else 10,)

case op if op >= Op.DUP1 and op <= Op.DUP16:
self.stack.dup(op - Op.DUP1 + 1)
return (op, 3)
return (3,)

case Op.JUMPDEST:
return (op, 1)
return (1,)

case Op.REVERT:
self.stack.pop()
self.stack.pop()
self.stopped = True
return (op, 4)
return (4,)

case op if op in {
Op.EQ,
Expand Down Expand Up @@ -161,7 +158,7 @@ def _exec_next_opcode(self) -> tuple[OpCode, int, *tuple[Any, ...]]:
raise Exception(f'BUG: op {op} not handled in match')

self.stack.push_uint(res)
return (op, gas_used, raws0, raws1)
return (gas_used, raws0, raws1)

case op if op in {Op.SLT, Op.SGT}:
raws0 = self.stack.pop()
Expand All @@ -174,53 +171,53 @@ def _exec_next_opcode(self) -> tuple[OpCode, int, *tuple[Any, ...]]:
else:
res = 1 if s0 > s1 else 0
self.stack.push_uint(res)
return (op, 3)
return (3,)

case Op.ISZERO:
raws0 = self.stack.pop()
s0 = int.from_bytes(raws0, 'big', signed=False)
res = 0 if s0 else 1
self.stack.push_uint(res)
return (op, 3, raws0)
return (3, raws0)

case Op.POP:
self.stack.pop()
return (op, 2)
return (2,)

case Op.CALLVALUE:
self.stack.push_uint(0) # msg.value == 0
return (op, 2)
return (2,)

case Op.CALLDATALOAD:
raws0 = self.stack.pop()
offset = int.from_bytes(raws0, 'big', signed=False)
self.stack.push(self.calldata.load(offset))
return (op, 3, raws0)
return (3, raws0)

case Op.CALLDATASIZE:
self.stack.push_uint(len(self.calldata))
return (op, 2)
return (2,)

case op if op >= Op.SWAP1 and op <= Op.SWAP16:
self.stack.swap(op - Op.SWAP1 + 1)
return (op, 3)
return (3,)

case Op.MSTORE:
offset = self.stack.pop_uint()
value = self.stack.pop()
self.memory.store(offset, value)
return (op, 3)
return (3,)

case Op.MLOAD:
offset = self.stack.pop_uint()
val, used = self.memory.load(offset)
self.stack.push(val)
return (op, 4, used)
return (4, used)

case Op.NOT:
s0 = self.stack.pop_uint()
self.stack.push_uint(E256M1 - s0)
return (op, 3)
return (3,)

case Op.SIGNEXTEND:
s0 = self.stack.pop_uint()
Expand All @@ -236,19 +233,19 @@ def _exec_next_opcode(self) -> tuple[OpCode, int, *tuple[Any, ...]]:
res = s1

self.stack.push_uint(res)
return (op, 5, s0, raws1)
return (5, s0, raws1)

case Op.ADDRESS:
self.stack.push_uint(1)
return (op, 2)
return (2,)

case Op.CALLDATACOPY:
mem_off = self.stack.pop_uint()
src_off = self.stack.pop_uint()
size = self.stack.pop_uint()
value = self.calldata.load(src_off, size)
self.memory.store(mem_off, value)
return (op, 4)
return (4,)

case _:
raise UnsupportedOpError(op)
1 change: 1 addition & 0 deletions js/src/arguments.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export function functionArguments(
}
} catch (e) {
if (e instanceof BadJumpDestError || e instanceof UnsupportedOpError) {
// console.log(e)
break
} else {
throw e
Expand Down
63 changes: 29 additions & 34 deletions js/src/evm/vm.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class Vm {

toString() {
let r = 'Vm:\n'
r += ` .pc = 0x${this.pc.toString(16)} | ${this.current_op().name}\n`
r += ` .pc = 0x${this.pc.toString(16)} | ${Op.name(this.current_op())}\n`
r += ` .stack = ${this.stack}\n`
r += ` .memory = ${this.memory}\n`
return r
Expand All @@ -54,26 +54,21 @@ export class Vm {
}

step() {
const ret = this.#exec_next_opcode()
const op = ret[0]
if (ret[1] == -1) {
throw `Op ${op.name} with unset gas_used`
const op = this.current_op()
if (this.blacklisted_ops.has(op)) {
throw new BlacklistedOpError(op)
}
const ret = this.#exec_opcode(op)
if (op != Op.JUMP && op != Op.JUMPI) {
this.pc += 1
}
if (this.pc >= this.code.length) {
this.stopped = true
}
return ret
return [op, ...ret]
}

#exec_next_opcode() {
const op = this.current_op()
if (this.blacklisted_ops.has(op)) {
throw new BlacklistedOpError(op.name)
}

#exec_opcode(op) {
if (op >= Op.PUSH0 && op <= Op.PUSH32) {
const n = op - Op.PUSH0
if (n != 0) {
Expand All @@ -82,19 +77,19 @@ export class Vm {
v.set(args, v.length - args.length)
this.stack.push(v)
this.pc += n
return [op, 3]
return [3]
} else {
this.stack.push_uint(0n)
return [op, 2]
return [2]
}
}
if (op >= Op.DUP1 && op <= Op.DUP16) {
this.stack.dup(op - Op.DUP1 + 1)
return [op, 3]
return [3]
}
if (op >= Op.SWAP1 && op <= Op.SWAP16) {
this.stack.swap(op - Op.SWAP1 + 1)
return [op, 3]
return [3]
}

switch (op) {
Expand All @@ -108,21 +103,21 @@ export class Vm {
const s1 = this.stack.pop_uint()
if (s1 == 0n) {
this.pc += 1
return [op, 10]
return [10]
}
}
this.pc = Number(s0)
return [op, op === Op.JUMP ? 8 : 10]
return [op === Op.JUMP ? 8 : 10]
}

case Op.JUMPDEST:
return [op, 1]
return [1]

case Op.REVERT:
this.stack.pop()
this.stack.pop()
this.stopped = true
return [op, 4]
return [4]

case Op.EQ:
case Op.LT:
Expand Down Expand Up @@ -194,7 +189,7 @@ export class Vm {
break
}
this.stack.push_uint(res)
return [op, gas_used, raws0, raws1]
return [gas_used, raws0, raws1]
}

case Op.SLT:
Expand All @@ -212,53 +207,53 @@ export class Vm {
res = s0 > s1 ? 1n : 0n
}
this.stack.push_uint(res)
return [op, 3]
return [3]
}

case Op.ISZERO: {
const raw = this.stack.pop()
const v = toBigInt(raw)
this.stack.push_uint(v === 0n ? 1n : 0n)
return [op, 3, raw]
return [3, raw]
}

case Op.POP:
this.stack.pop()
return [op, 2]
return [2]

case Op.CALLVALUE:
this.stack.push_uint(0n) // msg.value == 0
return [op, 2]
return [2]

case Op.CALLDATALOAD: {
const raws0 = this.stack.pop()
const offset = Number(toBigInt(raws0))
this.stack.push(this.calldata.load(offset))
return [op, 3, raws0]
return [3, raws0]
}

case Op.CALLDATASIZE:
this.stack.push_uint(BigInt(this.calldata.length))
return [op, 2]
return [2]

case Op.MSTORE: {
const offset = Number(this.stack.pop_uint())
const v = this.stack.pop()
this.memory.store(offset, v)
return [op, 3]
return [3]
}

case Op.MLOAD: {
const offset = Number(this.stack.pop_uint())
const [val, used] = this.memory.load(offset)
this.stack.push(val)
return [op, 4, used]
return [4, used]
}

case Op.NOT: {
const s0 = this.stack.pop_uint()
this.stack.push_uint(E256M1 - s0)
return [op, 3]
return [3]
}

case Op.SIGNEXTEND: {
Expand All @@ -275,12 +270,12 @@ export class Vm {
}
}
this.stack.push_uint(res)
return [op, 5, s0, raws1]
return [5, s0, raws1]
}

case Op.ADDRESS: {
this.stack.push_uint(1n)
return [op, 2]
return [2]
}

case Op.CALLDATACOPY: {
Expand All @@ -289,11 +284,11 @@ export class Vm {
const size = Number(this.stack.pop_uint())
const value = this.calldata.load(src_off, size)
this.memory.store(mem_off, value)
return [op, 4]
return [4]
}

default:
throw new UnsupportedOpError(op.name)
throw new UnsupportedOpError(op)
}
}
}

0 comments on commit 800d178

Please sign in to comment.