Merge pull request #543 from status-im/eip_2315
EIP 2315: implement jumpsub, beginsub, and returnsub opcode
This commit is contained in:
commit
4d52d2fb92
|
@ -144,6 +144,7 @@ proc newComputation*(vmState: BaseVMState, message: Message, salt= 0.u256): Comp
|
|||
result.msg = message
|
||||
result.memory = Memory()
|
||||
result.stack = newStack()
|
||||
result.returnStack = @[]
|
||||
result.gasMeter.init(message.gas)
|
||||
result.touchedAccounts = initHashSet[EthAddress]()
|
||||
result.suicides = initHashSet[EthAddress]()
|
||||
|
|
|
@ -535,6 +535,9 @@ template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) =
|
|||
Msize: fixed GasBase,
|
||||
Gas: fixed GasBase,
|
||||
JumpDest: fixed GasJumpDest,
|
||||
BeginSub: fixed GasBase,
|
||||
ReturnSub: fixed GasLow,
|
||||
JumpSub: fixed GasHigh,
|
||||
|
||||
# 60s & 70s: Push Operations
|
||||
Push1: fixed GasVeryLow,
|
||||
|
|
|
@ -95,6 +95,9 @@ fill_enum_holes:
|
|||
Msize = 0x59, # Get the size of active memory in bytes.
|
||||
Gas = 0x5a, # Get the amount of available gas, including the corresponding reduction for the cost of this instruction.
|
||||
JumpDest = 0x5b, # Mark a valid destination for jumps. This operation has no effect on machine state during execution.
|
||||
BeginSub = 0x5c, # Marks the entry point to a subroutine
|
||||
ReturnSub = 0x5d, # Returns control to the caller of a subroutine.
|
||||
JumpSub = 0x5e, # Transfers control to a subroutine.
|
||||
|
||||
# 60s & 70s: Push Operations.
|
||||
Push1 = 0x60, # Place 1-byte item on stack.
|
||||
|
|
|
@ -542,6 +542,38 @@ op jumpDest, inline = true:
|
|||
## 0x5b, Mark a valid destination for jumps. This operation has no effect on machine state during execution.
|
||||
discard
|
||||
|
||||
op beginSub, inline = true:
|
||||
## 0x5c, Marks the entry point to a subroutine
|
||||
raise newException(OutOfGas, "Abort: Attempt to execute BeginSub opcode")
|
||||
|
||||
op returnSub, inline = true:
|
||||
## 0x5d, Returns control to the caller of a subroutine.
|
||||
if c.returnStack.len == 0:
|
||||
raise newException(OutOfGas, "Abort: invalid returnStack during ReturnSub")
|
||||
# Other than the check that the return stack is not empty, there is no
|
||||
# need to validate the pc from 'returns', since we only ever push valid
|
||||
# values onto it via jumpsub.
|
||||
c.code.pc = c.returnStack.pop()
|
||||
|
||||
op jumpSub, inline = true, jumpTarget:
|
||||
## 0x5e, Transfers control to a subroutine.
|
||||
if jumpTarget >= c.code.len.u256:
|
||||
raise newException(InvalidJumpDestination, "JumpSub destination exceeds code len")
|
||||
|
||||
let returnPC = c.code.pc
|
||||
let jt = jumpTarget.truncate(int)
|
||||
c.code.pc = jt
|
||||
|
||||
let nextOpcode = c.code.peek
|
||||
if nextOpcode != BeginSub:
|
||||
raise newException(InvalidJumpDestination, "Invalid JumpSub destination")
|
||||
|
||||
if c.returnStack.len == 1023:
|
||||
raise newException(FullStack, "Out of returnStack")
|
||||
|
||||
c.returnStack.add returnPC
|
||||
inc c.code.pc
|
||||
|
||||
# ##########################################
|
||||
# 60s & 70s: Push Operations.
|
||||
# 80s: Duplication Operations
|
||||
|
|
|
@ -229,6 +229,14 @@ proc genIstanbulJumpTable(ops: array[Op, NimNode]): array[Op, NimNode] {.compile
|
|||
|
||||
let IstanbulOpDispatch {.compileTime.}: array[Op, NimNode] = genIstanbulJumpTable(PetersburgOpDispatch)
|
||||
|
||||
proc genBerlinJumpTable(ops: array[Op, NimNode]): array[Op, NimNode] {.compileTime.} =
|
||||
result = ops
|
||||
result[BeginSub] = newIdentNode "beginSub"
|
||||
result[ReturnSub] = newIdentNode "returnSub"
|
||||
result[JumpSub] = newIdentNode "jumpSub"
|
||||
|
||||
let BerlinOpDispatch {.compileTime.}: array[Op, NimNode] = genBerlinJumpTable(IstanbulOpDispatch)
|
||||
|
||||
proc opTableToCaseStmt(opTable: array[Op, NimNode], c: NimNode): NimNode =
|
||||
|
||||
let instr = quote do: `c`.instr
|
||||
|
@ -308,6 +316,9 @@ macro genPetersburgDispatch(c: Computation): untyped =
|
|||
macro genIstanbulDispatch(c: Computation): untyped =
|
||||
result = opTableToCaseStmt(IstanbulOpDispatch, c)
|
||||
|
||||
macro genBerlinDispatch(c: Computation): untyped =
|
||||
result = opTableToCaseStmt(BerlinOpDispatch, c)
|
||||
|
||||
proc frontierVM(c: Computation) =
|
||||
genFrontierDispatch(c)
|
||||
|
||||
|
@ -332,6 +343,9 @@ proc petersburgVM(c: Computation) {.gcsafe.} =
|
|||
proc istanbulVM(c: Computation) {.gcsafe.} =
|
||||
genIstanbulDispatch(c)
|
||||
|
||||
proc berlinVM(c: Computation) {.gcsafe.} =
|
||||
genBerlinDispatch(c)
|
||||
|
||||
proc selectVM(c: Computation, fork: Fork) {.gcsafe.} =
|
||||
# TODO: Optimise getting fork and updating opCodeExec only when necessary
|
||||
case fork
|
||||
|
@ -349,8 +363,10 @@ proc selectVM(c: Computation, fork: Fork) {.gcsafe.} =
|
|||
c.constantinopleVM()
|
||||
of FkPetersburg:
|
||||
c.petersburgVM()
|
||||
else:
|
||||
of FkIstanbul:
|
||||
c.istanbulVM()
|
||||
else:
|
||||
c.berlinVM()
|
||||
|
||||
proc executeOpcodes(c: Computation) =
|
||||
let fork = c.fork
|
||||
|
|
|
@ -68,6 +68,7 @@ type
|
|||
msg*: Message
|
||||
memory*: Memory
|
||||
stack*: Stack
|
||||
returnStack*: seq[int]
|
||||
gasMeter*: GasMeter
|
||||
code*: CodeStream
|
||||
output*: seq[byte]
|
||||
|
|
|
@ -172,6 +172,10 @@ proc parseFork(fork: NimNode): Fork =
|
|||
fork[0].expectKind({nnkIdent, nnkStrLit})
|
||||
parseEnum[Fork](strip(fork[0].strVal))
|
||||
|
||||
proc parseGasUsed(gas: NimNode): GasInt =
|
||||
gas[0].expectKind(nnkIntLit)
|
||||
result = gas[0].intVal
|
||||
|
||||
proc generateVMProxy(boa: Assembler): NimNode =
|
||||
let
|
||||
vmProxy = genSym(nskProc, "vmProxy")
|
||||
|
@ -262,10 +266,9 @@ proc initComputation(blockNumber: Uint256, chainDB: BaseChainDB, code, data: seq
|
|||
proc runVM*(blockNumber: Uint256, chainDB: BaseChainDB, boa: Assembler): bool =
|
||||
var computation = initComputation(blockNumber, chainDB, boa.code, boa.data, boa.fork)
|
||||
|
||||
# TODO: support gas comsumption validation
|
||||
# let gas = computation.gasMeter.gasRemaining
|
||||
let gas = computation.gasMeter.gasRemaining
|
||||
execComputation(computation)
|
||||
# let gasUsed = gas - computation.gasMeter.gasRemaining
|
||||
let gasUsed = gas - computation.gasMeter.gasRemaining
|
||||
|
||||
if computation.isSuccess:
|
||||
if boa.success == false:
|
||||
|
@ -276,6 +279,11 @@ proc runVM*(blockNumber: Uint256, chainDB: BaseChainDB, boa: Assembler): bool =
|
|||
error "different success value", expected=boa.success, actual=false
|
||||
return false
|
||||
|
||||
if boa.gasUsed != -1:
|
||||
if boa.gasUsed != gasUsed:
|
||||
error "different gasUsed", expected=boa.gasUsed, actual=gasUsed
|
||||
return false
|
||||
|
||||
if boa.stack.len != computation.stack.values.len:
|
||||
error "different stack len", expected=boa.stack.len, actual=computation.stack.values.len
|
||||
return false
|
||||
|
@ -355,7 +363,7 @@ proc runVM*(blockNumber: Uint256, chainDB: BaseChainDB, boa: Assembler): bool =
|
|||
result = true
|
||||
|
||||
macro assembler*(list: untyped): untyped =
|
||||
var boa = Assembler(success: true, fork: FkFrontier)
|
||||
var boa = Assembler(success: true, fork: FkFrontier, gasUsed: -1)
|
||||
list.expectKind nnkStmtList
|
||||
for callSection in list:
|
||||
callSection.expectKind(nnkCall)
|
||||
|
@ -375,6 +383,7 @@ macro assembler*(list: untyped): untyped =
|
|||
of "data": boa.data = parseData(body)
|
||||
of "output": boa.output = parseData(body)
|
||||
of "fork": boa.fork = parseFork(body)
|
||||
of "gasused": boa.gasUsed = parseGasUsed(body)
|
||||
else: error("unknown section '" & label & "'", callSection[0])
|
||||
result = boa.generateVMProxy()
|
||||
|
||||
|
|
|
@ -175,3 +175,102 @@ proc opMiscMain*() =
|
|||
memory:
|
||||
"0xA0B0C0D0E0F0A1B1C1D1E1F1A2B2C2D2E2F2A3B3C3D3E3F3A4B4C4D4E4F4A1B1"
|
||||
"0x00"
|
||||
|
||||
assembler:
|
||||
title: "Simple routine"
|
||||
code:
|
||||
PUSH1 "0x04"
|
||||
JUMPSUB
|
||||
STOP
|
||||
BEGINSUB
|
||||
RETURNSUB
|
||||
gasUsed: 18
|
||||
fork: Berlin
|
||||
|
||||
assembler:
|
||||
title: "Two levels of subroutines"
|
||||
code:
|
||||
PUSH9 "0x00000000000000000C"
|
||||
JUMPSUB
|
||||
STOP
|
||||
BEGINSUB
|
||||
PUSH1 "0x11"
|
||||
JUMPSUB
|
||||
RETURNSUB
|
||||
BEGINSUB
|
||||
RETURNSUB
|
||||
gasUsed: 36
|
||||
fork: Berlin
|
||||
|
||||
assembler:
|
||||
title: "Failure 1: invalid jump"
|
||||
code:
|
||||
PUSH9 "0x01000000000000000C"
|
||||
JUMPSUB
|
||||
STOP
|
||||
BEGINSUB
|
||||
PUSH1 "0x11"
|
||||
JUMPSUB
|
||||
RETURNSUB
|
||||
BEGINSUB
|
||||
RETURNSUB
|
||||
success: false
|
||||
fork: Berlin
|
||||
|
||||
assembler:
|
||||
title: "Failure 2: shallow return stack"
|
||||
code:
|
||||
RETURNSUB
|
||||
PC
|
||||
PC
|
||||
success: false
|
||||
fork: Berlin
|
||||
|
||||
assembler:
|
||||
title: "Subroutine at end of code"
|
||||
code:
|
||||
PUSH1 "0x05"
|
||||
JUMP
|
||||
BEGINSUB
|
||||
RETURNSUB
|
||||
JUMPDEST
|
||||
PUSH1 "0x03"
|
||||
JUMPSUB
|
||||
gasUsed: 30
|
||||
fork: Berlin
|
||||
|
||||
assembler:
|
||||
title: "Error on 'walk-into-subroutine'"
|
||||
code:
|
||||
BEGINSUB
|
||||
RETURNSUB
|
||||
STOP
|
||||
success: false
|
||||
fork: Berlin
|
||||
|
||||
assembler:
|
||||
title: "sol test"
|
||||
code:
|
||||
PUSH1 "0x02"
|
||||
PUSH1 "0x03"
|
||||
PUSH1 "0x08" # jumpdest
|
||||
JUMPSUB
|
||||
STOP
|
||||
|
||||
# 0x08
|
||||
BEGINSUB
|
||||
PUSH1 "0x0D" # jumpdest
|
||||
JUMPSUB
|
||||
RETURNSUB
|
||||
|
||||
# 0x0D
|
||||
BEGINSUB
|
||||
MUL
|
||||
RETURNSUB
|
||||
gasUsed: 47
|
||||
fork: Berlin
|
||||
stack:
|
||||
"0x06"
|
||||
|
||||
when isMainModule:
|
||||
opMiscMain()
|
||||
|
|
Loading…
Reference in New Issue