2021-04-14 16:34:24 +00:00
|
|
|
# Nimbus
|
|
|
|
# Copyright (c) 2018 Status Research & Development GmbH
|
|
|
|
# Licensed under either of
|
|
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0)
|
|
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
|
|
|
# http://opensource.org/licenses/MIT)
|
|
|
|
# at your option. This file may not be copied, modified, or distributed except
|
|
|
|
# according to those terms.
|
|
|
|
|
|
|
|
## EVM Opcode Handlers: Stack, Memory, Storage And Flow Operations
|
|
|
|
## ===============================================================
|
|
|
|
##
|
|
|
|
|
|
|
|
import
|
|
|
|
../../../errors,
|
2023-03-10 22:16:42 +00:00
|
|
|
../../async/operations,
|
2021-04-21 17:04:54 +00:00
|
|
|
../../code_stream,
|
2021-04-26 16:00:46 +00:00
|
|
|
../../computation,
|
2021-04-22 16:05:58 +00:00
|
|
|
../../memory,
|
2021-04-21 17:04:54 +00:00
|
|
|
../../stack,
|
2021-04-22 16:05:58 +00:00
|
|
|
../../types,
|
2021-04-21 17:04:54 +00:00
|
|
|
../gas_costs,
|
|
|
|
../gas_meter,
|
|
|
|
../op_codes,
|
2021-04-22 16:05:58 +00:00
|
|
|
../utils/utils_numeric,
|
2021-04-14 16:34:24 +00:00
|
|
|
./oph_defs,
|
2021-04-15 13:40:31 +00:00
|
|
|
./oph_helpers,
|
2021-04-21 17:04:54 +00:00
|
|
|
eth/common,
|
|
|
|
stint,
|
|
|
|
strformat
|
2021-04-14 16:34:24 +00:00
|
|
|
|
2023-02-14 14:37:21 +00:00
|
|
|
{.push raises: [CatchableError].} # basically the annotation type of a `Vm2OpFn`
|
|
|
|
|
2022-09-28 06:09:33 +00:00
|
|
|
when not defined(evmc_enabled):
|
|
|
|
import
|
|
|
|
../../state,
|
|
|
|
../../../db/accounts_cache
|
|
|
|
|
2021-04-14 16:34:24 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Private helpers
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
2022-09-26 04:56:54 +00:00
|
|
|
when evmc_enabled:
|
|
|
|
proc sstoreEvmc(c: Computation, slot, newValue: UInt256) =
|
|
|
|
let
|
|
|
|
currentValue = c.getStorage(slot)
|
|
|
|
status = c.host.setStorage(c.msg.contractAddress, slot, newValue)
|
|
|
|
gasParam = GasParams(kind: Op.Sstore, s_status: status)
|
|
|
|
gasCost = c.gasCosts[Sstore].c_handler(newValue, gasParam)[0]
|
|
|
|
|
|
|
|
c.gasMeter.consumeGas(
|
|
|
|
gasCost, &"SSTORE: {c.msg.contractAddress}[{slot}] " &
|
|
|
|
&"-> {newValue} ({currentValue})")
|
2021-04-14 16:34:24 +00:00
|
|
|
|
2022-09-26 04:56:54 +00:00
|
|
|
else:
|
|
|
|
proc sstoreImpl(c: Computation, slot, newValue: UInt256) =
|
|
|
|
let
|
|
|
|
currentValue = c.getStorage(slot)
|
|
|
|
gasParam = GasParams(
|
|
|
|
kind: Op.Sstore,
|
|
|
|
s_currentValue: currentValue)
|
2021-04-14 16:34:24 +00:00
|
|
|
|
2022-09-26 04:56:54 +00:00
|
|
|
(gasCost, gasRefund) =
|
|
|
|
c.gasCosts[Sstore].c_handler(newValue, gasParam)
|
2021-04-14 16:34:24 +00:00
|
|
|
|
2022-09-26 04:56:54 +00:00
|
|
|
c.gasMeter.consumeGas(
|
|
|
|
gasCost, &"SSTORE: {c.msg.contractAddress}[{slot}] " &
|
|
|
|
&"-> {newValue} ({currentValue})")
|
|
|
|
if gasRefund > 0:
|
|
|
|
c.gasMeter.refundGas(gasRefund)
|
|
|
|
|
|
|
|
c.vmState.mutateStateDB:
|
|
|
|
db.setStorage(c.msg.contractAddress, slot, newValue)
|
|
|
|
|
|
|
|
|
|
|
|
proc sstoreNetGasMeteringImpl(c: Computation; slot, newValue: UInt256) =
|
|
|
|
let
|
|
|
|
stateDB = c.vmState.readOnlyStateDB
|
|
|
|
currentValue = c.getStorage(slot)
|
|
|
|
|
|
|
|
gasParam = GasParams(
|
|
|
|
kind: Op.Sstore,
|
|
|
|
s_currentValue: currentValue,
|
|
|
|
s_originalValue: stateDB.getCommittedStorage(c.msg.contractAddress, slot))
|
2021-04-14 16:34:24 +00:00
|
|
|
|
2022-09-26 04:56:54 +00:00
|
|
|
(gasCost, gasRefund) = c.gasCosts[Sstore].c_handler(newValue, gasParam)
|
2021-04-14 16:34:24 +00:00
|
|
|
|
2022-09-26 04:56:54 +00:00
|
|
|
c.gasMeter.consumeGas(
|
|
|
|
gasCost, &"SSTORE EIP2200: {c.msg.contractAddress}[{slot}]" &
|
|
|
|
&" -> {newValue} ({currentValue})")
|
|
|
|
|
|
|
|
if gasRefund != 0:
|
|
|
|
c.gasMeter.refundGas(gasRefund)
|
|
|
|
|
|
|
|
c.vmState.mutateStateDB:
|
|
|
|
db.setStorage(c.msg.contractAddress, slot, newValue)
|
2021-04-14 16:34:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
proc jumpImpl(c: Computation; jumpTarget: UInt256) =
|
|
|
|
if jumpTarget >= c.code.len.u256:
|
|
|
|
raise newException(
|
|
|
|
InvalidJumpDestination, "Invalid Jump Destination")
|
|
|
|
|
|
|
|
let jt = jumpTarget.truncate(int)
|
|
|
|
c.code.pc = jt
|
|
|
|
|
|
|
|
let nextOpcode = c.code.peek
|
2022-04-08 04:54:11 +00:00
|
|
|
if nextOpcode != JumpDest:
|
2021-04-14 16:34:24 +00:00
|
|
|
raise newException(InvalidJumpDestination, "Invalid Jump Destination")
|
|
|
|
|
|
|
|
# TODO: next check seems redundant
|
|
|
|
if not c.code.isValidOpcode(jt):
|
|
|
|
raise newException(
|
|
|
|
InvalidInstruction, "Jump resulted in invalid instruction")
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Private, op handlers implementation
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
const
|
2021-04-20 12:07:01 +00:00
|
|
|
popOp: Vm2OpFn = proc (k: var Vm2Ctx) =
|
2021-04-14 16:34:24 +00:00
|
|
|
## 0x50, Remove item from stack.
|
|
|
|
discard k.cpt.stack.popInt
|
|
|
|
|
2021-04-20 12:07:01 +00:00
|
|
|
mloadOp: Vm2OpFn = proc (k: var Vm2Ctx) =
|
2021-04-14 16:34:24 +00:00
|
|
|
## 0x51, Load word from memory
|
2023-04-24 20:59:38 +00:00
|
|
|
let (memStartPos) = k.cpt.stack.popInt(1)
|
2021-04-14 16:34:24 +00:00
|
|
|
|
|
|
|
let memPos = memStartPos.cleanMemRef
|
2023-04-24 20:59:38 +00:00
|
|
|
k.cpt.gasMeter.consumeGas(
|
|
|
|
k.cpt.gasCosts[Mload].m_handler(k.cpt.memory.len, memPos, 32),
|
2021-04-14 16:34:24 +00:00
|
|
|
reason = "MLOAD: GasVeryLow + memory expansion")
|
|
|
|
|
2023-04-24 20:59:38 +00:00
|
|
|
k.cpt.memory.extend(memPos, 32)
|
|
|
|
k.cpt.stack.push:
|
|
|
|
k.cpt.memory.read(memPos, 32)
|
2021-04-14 16:34:24 +00:00
|
|
|
|
|
|
|
|
2021-04-20 12:07:01 +00:00
|
|
|
mstoreOp: Vm2OpFn = proc (k: var Vm2Ctx) =
|
2021-04-14 16:34:24 +00:00
|
|
|
## 0x52, Save word to memory
|
|
|
|
let (memStartPos, value) = k.cpt.stack.popInt(2)
|
|
|
|
|
|
|
|
let memPos = memStartPos.cleanMemRef
|
|
|
|
k.cpt.gasMeter.consumeGas(
|
2022-04-08 04:54:11 +00:00
|
|
|
k.cpt.gasCosts[Mstore].m_handler(k.cpt.memory.len, memPos, 32),
|
2021-04-14 16:34:24 +00:00
|
|
|
reason = "MSTORE: GasVeryLow + memory expansion")
|
|
|
|
|
|
|
|
k.cpt.memory.extend(memPos, 32)
|
2023-04-24 20:59:38 +00:00
|
|
|
k.cpt.memory.write(memPos, value.toByteArrayBE)
|
2021-04-14 16:34:24 +00:00
|
|
|
|
|
|
|
|
2021-04-20 12:07:01 +00:00
|
|
|
mstore8Op: Vm2OpFn = proc (k: var Vm2Ctx) =
|
2021-04-14 16:34:24 +00:00
|
|
|
## 0x53, Save byte to memory
|
|
|
|
let (memStartPos, value) = k.cpt.stack.popInt(2)
|
|
|
|
|
|
|
|
let memPos = memStartPos.cleanMemRef
|
|
|
|
k.cpt.gasMeter.consumeGas(
|
2022-04-08 04:54:11 +00:00
|
|
|
k.cpt.gasCosts[Mstore].m_handler(k.cpt.memory.len, memPos, 1),
|
2021-04-14 16:34:24 +00:00
|
|
|
reason = "MSTORE8: GasVeryLow + memory expansion")
|
|
|
|
|
|
|
|
k.cpt.memory.extend(memPos, 1)
|
2023-04-24 20:59:38 +00:00
|
|
|
k.cpt.memory.write(memPos, [value.toByteArrayBE[31]])
|
2021-04-14 16:34:24 +00:00
|
|
|
|
2021-04-15 16:42:19 +00:00
|
|
|
# -------
|
2021-04-14 16:34:24 +00:00
|
|
|
|
2021-04-20 12:07:01 +00:00
|
|
|
sloadOp: Vm2OpFn = proc (k: var Vm2Ctx) =
|
2021-04-14 16:34:24 +00:00
|
|
|
## 0x54, Load word from storage.
|
Added basic async capabilities for vm2. (#1260)
* Added basic async capabilities for vm2.
This is a whole new Git branch, not the same one as last time
(https://github.com/status-im/nimbus-eth1/pull/1250) - there wasn't
much worth salvaging. Main differences:
I didn't do the "each opcode has to specify an async handler" junk
that I put in last time. Instead, in oph_memory.nim you can see
sloadOp calling asyncChainTo and passing in an async operation.
That async operation is then run by the execCallOrCreate (or
asyncExecCallOrCreate) code in interpreter_dispatch.nim.
In the test code, the (previously existing) macro called "assembler"
now allows you to add a section called "initialStorage", specifying
fake data to be used by the EVM computation run by that test. (In
the long run we'll obviously want to write tests that for-real use
the JSON-RPC API to asynchronously fetch data; for now, this was
just an expedient way to write a basic unit test that exercises the
async-EVM code pathway.)
There's also a new macro called "concurrentAssemblers" that allows
you to write a test that runs multiple assemblers concurrently (and
then waits for them all to finish). There's one example test using
this, in test_op_memory_lazy.nim, though you can't actually see it
doing so unless you uncomment some echo statements in
async_operations.nim (in which case you can see the two concurrently
running EVM computations each printing out what they're doing, and
you'll see that they interleave).
A question: is it possible to make EVMC work asynchronously? (For
now, this code compiles and "make test" passes even if ENABLE_EVMC
is turned on, but it doesn't actually work asynchronously, it just
falls back on doing the usual synchronous EVMC thing. See
FIXME-asyncAndEvmc.)
* Moved the AsyncOperationFactory to the BaseVMState object.
* Made the AsyncOperationFactory into a table of fn pointers.
Also ditched the plain-data Vm2AsyncOperation type; it wasn't
really serving much purpose. Instead, the pendingAsyncOperation
field directly contains the Future.
* Removed the hasStorage idea.
It's not the right solution to the "how do we know whether we
still need to fetch the storage value or not?" problem. I
haven't implemented the right solution yet, but at least
we're better off not putting in a wrong one.
* Added/modified/removed some comments.
(Based on feedback on the PR.)
* Removed the waitFor from execCallOrCreate.
There was some back-and-forth in the PR regarding whether nested
waitFor calls are acceptable:
https://github.com/status-im/nimbus-eth1/pull/1260#discussion_r998587449
The eventual decision was to just change the waitFor to a doAssert
(since we probably won't want this extra functionality when running
synchronously anyway) to make sure that the Future is already
finished.
2022-11-01 15:35:46 +00:00
|
|
|
let cpt = k.cpt # so it can safely be captured by the asyncChainTo closure below
|
|
|
|
let (slot) = cpt.stack.popInt(1)
|
2023-03-10 22:16:42 +00:00
|
|
|
cpt.asyncChainTo(ifNecessaryGetSlot(cpt.vmState, cpt.msg.contractAddress, slot)):
|
Added basic async capabilities for vm2. (#1260)
* Added basic async capabilities for vm2.
This is a whole new Git branch, not the same one as last time
(https://github.com/status-im/nimbus-eth1/pull/1250) - there wasn't
much worth salvaging. Main differences:
I didn't do the "each opcode has to specify an async handler" junk
that I put in last time. Instead, in oph_memory.nim you can see
sloadOp calling asyncChainTo and passing in an async operation.
That async operation is then run by the execCallOrCreate (or
asyncExecCallOrCreate) code in interpreter_dispatch.nim.
In the test code, the (previously existing) macro called "assembler"
now allows you to add a section called "initialStorage", specifying
fake data to be used by the EVM computation run by that test. (In
the long run we'll obviously want to write tests that for-real use
the JSON-RPC API to asynchronously fetch data; for now, this was
just an expedient way to write a basic unit test that exercises the
async-EVM code pathway.)
There's also a new macro called "concurrentAssemblers" that allows
you to write a test that runs multiple assemblers concurrently (and
then waits for them all to finish). There's one example test using
this, in test_op_memory_lazy.nim, though you can't actually see it
doing so unless you uncomment some echo statements in
async_operations.nim (in which case you can see the two concurrently
running EVM computations each printing out what they're doing, and
you'll see that they interleave).
A question: is it possible to make EVMC work asynchronously? (For
now, this code compiles and "make test" passes even if ENABLE_EVMC
is turned on, but it doesn't actually work asynchronously, it just
falls back on doing the usual synchronous EVMC thing. See
FIXME-asyncAndEvmc.)
* Moved the AsyncOperationFactory to the BaseVMState object.
* Made the AsyncOperationFactory into a table of fn pointers.
Also ditched the plain-data Vm2AsyncOperation type; it wasn't
really serving much purpose. Instead, the pendingAsyncOperation
field directly contains the Future.
* Removed the hasStorage idea.
It's not the right solution to the "how do we know whether we
still need to fetch the storage value or not?" problem. I
haven't implemented the right solution yet, but at least
we're better off not putting in a wrong one.
* Added/modified/removed some comments.
(Based on feedback on the PR.)
* Removed the waitFor from execCallOrCreate.
There was some back-and-forth in the PR regarding whether nested
waitFor calls are acceptable:
https://github.com/status-im/nimbus-eth1/pull/1260#discussion_r998587449
The eventual decision was to just change the waitFor to a doAssert
(since we probably won't want this extra functionality when running
synchronously anyway) to make sure that the Future is already
finished.
2022-11-01 15:35:46 +00:00
|
|
|
cpt.stack.push:
|
|
|
|
cpt.getStorage(slot)
|
2022-09-26 04:56:54 +00:00
|
|
|
|
2021-04-20 12:07:01 +00:00
|
|
|
sloadEIP2929Op: Vm2OpFn = proc (k: var Vm2Ctx) =
|
2021-04-15 16:42:19 +00:00
|
|
|
## 0x54, EIP2929: Load word from storage for Berlin and later
|
2023-04-12 12:39:11 +00:00
|
|
|
let cpt = k.cpt
|
|
|
|
let (slot) = cpt.stack.popInt(1)
|
|
|
|
|
|
|
|
cpt.asyncChainTo(ifNecessaryGetSlot(cpt.vmState, cpt.msg.contractAddress, slot)):
|
|
|
|
when evmc_enabled:
|
|
|
|
let gasCost = if cpt.host.accessStorage(cpt.msg.contractAddress, slot) == EVMC_ACCESS_COLD:
|
2022-09-26 04:56:54 +00:00
|
|
|
ColdSloadCost
|
|
|
|
else:
|
|
|
|
WarmStorageReadCost
|
2023-04-12 12:39:11 +00:00
|
|
|
cpt.gasMeter.consumeGas(gasCost, reason = "sloadEIP2929")
|
|
|
|
else:
|
|
|
|
cpt.vmState.mutateStateDB:
|
|
|
|
let gasCost = if not db.inAccessList(cpt.msg.contractAddress, slot):
|
|
|
|
db.accessList(cpt.msg.contractAddress, slot)
|
|
|
|
ColdSloadCost
|
|
|
|
else:
|
|
|
|
WarmStorageReadCost
|
|
|
|
cpt.gasMeter.consumeGas(gasCost, reason = "sloadEIP2929")
|
|
|
|
cpt.stack.push:
|
|
|
|
cpt.getStorage(slot)
|
2021-04-15 16:42:19 +00:00
|
|
|
|
|
|
|
# -------
|
|
|
|
|
2021-04-20 12:07:01 +00:00
|
|
|
sstoreOp: Vm2OpFn = proc (k: var Vm2Ctx) =
|
2021-04-14 16:34:24 +00:00
|
|
|
## 0x55, Save word to storage.
|
2023-04-12 12:39:11 +00:00
|
|
|
let cpt = k.cpt
|
|
|
|
let (slot, newValue) = cpt.stack.popInt(2)
|
2021-04-14 16:34:24 +00:00
|
|
|
|
2023-04-12 12:39:11 +00:00
|
|
|
checkInStaticContext(cpt)
|
|
|
|
cpt.asyncChainTo(ifNecessaryGetSlot(cpt.vmState, cpt.msg.contractAddress, slot)):
|
|
|
|
when evmc_enabled:
|
|
|
|
sstoreEvmc(cpt, slot, newValue)
|
|
|
|
else:
|
|
|
|
sstoreImpl(cpt, slot, newValue)
|
2021-04-14 16:34:24 +00:00
|
|
|
|
|
|
|
|
2021-04-20 12:07:01 +00:00
|
|
|
sstoreEIP1283Op: Vm2OpFn = proc (k: var Vm2Ctx) =
|
2021-04-14 16:34:24 +00:00
|
|
|
## 0x55, EIP1283: sstore for Constantinople and later
|
2023-04-12 12:39:11 +00:00
|
|
|
let cpt = k.cpt
|
|
|
|
let (slot, newValue) = cpt.stack.popInt(2)
|
2021-04-14 16:34:24 +00:00
|
|
|
|
2023-04-12 12:39:11 +00:00
|
|
|
checkInStaticContext(cpt)
|
|
|
|
cpt.asyncChainTo(ifNecessaryGetSlot(cpt.vmState, cpt.msg.contractAddress, slot)):
|
|
|
|
when evmc_enabled:
|
|
|
|
sstoreEvmc(cpt, slot, newValue)
|
|
|
|
else:
|
|
|
|
sstoreNetGasMeteringImpl(cpt, slot, newValue)
|
2021-04-14 16:34:24 +00:00
|
|
|
|
|
|
|
|
2021-04-20 12:07:01 +00:00
|
|
|
sstoreEIP2200Op: Vm2OpFn = proc (k: var Vm2Ctx) =
|
2021-04-14 16:34:24 +00:00
|
|
|
## 0x55, EIP2200: sstore for Istanbul and later
|
2023-04-12 12:39:11 +00:00
|
|
|
let cpt = k.cpt
|
|
|
|
let (slot, newValue) = cpt.stack.popInt(2)
|
2021-04-14 16:34:24 +00:00
|
|
|
|
2023-04-12 12:39:11 +00:00
|
|
|
checkInStaticContext(cpt)
|
2021-04-14 16:34:24 +00:00
|
|
|
const SentryGasEIP2200 = 2300
|
|
|
|
|
2023-04-12 12:39:11 +00:00
|
|
|
if cpt.gasMeter.gasRemaining <= SentryGasEIP2200:
|
2021-04-14 16:34:24 +00:00
|
|
|
raise newException(
|
|
|
|
OutOfGas,
|
|
|
|
"Gas not enough to perform EIP2200 SSTORE")
|
|
|
|
|
2023-04-12 12:39:11 +00:00
|
|
|
cpt.asyncChainTo(ifNecessaryGetSlot(cpt.vmState, cpt.msg.contractAddress, slot)):
|
|
|
|
when evmc_enabled:
|
|
|
|
sstoreEvmc(cpt, slot, newValue)
|
|
|
|
else:
|
|
|
|
sstoreNetGasMeteringImpl(cpt, slot, newValue)
|
2021-04-14 16:34:24 +00:00
|
|
|
|
|
|
|
|
2021-04-20 12:07:01 +00:00
|
|
|
sstoreEIP2929Op: Vm2OpFn = proc (k: var Vm2Ctx) =
|
2021-04-15 16:42:19 +00:00
|
|
|
## 0x55, EIP2929: sstore for Berlin and later
|
2023-04-12 12:39:11 +00:00
|
|
|
let cpt = k.cpt
|
|
|
|
let (slot, newValue) = cpt.stack.popInt(2)
|
|
|
|
checkInStaticContext(cpt)
|
2021-04-15 16:42:19 +00:00
|
|
|
|
|
|
|
# Minimum gas required to be present for an SSTORE call, not consumed
|
|
|
|
const SentryGasEIP2200 = 2300
|
|
|
|
|
2023-04-12 12:39:11 +00:00
|
|
|
if cpt.gasMeter.gasRemaining <= SentryGasEIP2200:
|
2021-04-15 16:42:19 +00:00
|
|
|
raise newException(OutOfGas, "Gas not enough to perform EIP2200 SSTORE")
|
|
|
|
|
2023-04-12 12:39:11 +00:00
|
|
|
cpt.asyncChainTo(ifNecessaryGetSlot(cpt.vmState, cpt.msg.contractAddress, slot)):
|
|
|
|
when evmc_enabled:
|
|
|
|
if cpt.host.accessStorage(cpt.msg.contractAddress, slot) == EVMC_ACCESS_COLD:
|
|
|
|
cpt.gasMeter.consumeGas(ColdSloadCost, reason = "sstoreEIP2929")
|
|
|
|
else:
|
|
|
|
cpt.vmState.mutateStateDB:
|
|
|
|
if not db.inAccessList(cpt.msg.contractAddress, slot):
|
|
|
|
db.accessList(cpt.msg.contractAddress, slot)
|
|
|
|
cpt.gasMeter.consumeGas(ColdSloadCost, reason = "sstoreEIP2929")
|
|
|
|
|
|
|
|
when evmc_enabled:
|
|
|
|
sstoreEvmc(cpt, slot, newValue)
|
|
|
|
else:
|
|
|
|
sstoreNetGasMeteringImpl(cpt, slot, newValue)
|
2021-04-15 16:42:19 +00:00
|
|
|
|
|
|
|
# -------
|
|
|
|
|
2021-04-20 12:07:01 +00:00
|
|
|
jumpOp: Vm2OpFn = proc (k: var Vm2Ctx) =
|
2021-04-14 16:34:24 +00:00
|
|
|
## 0x56, Alter the program counter
|
|
|
|
let (jumpTarget) = k.cpt.stack.popInt(1)
|
|
|
|
jumpImpl(k.cpt, jumpTarget)
|
|
|
|
|
2021-04-20 12:07:01 +00:00
|
|
|
jumpIOp: Vm2OpFn = proc (k: var Vm2Ctx) =
|
2021-04-14 16:34:24 +00:00
|
|
|
## 0x57, Conditionally alter the program counter.
|
|
|
|
let (jumpTarget, testedValue) = k.cpt.stack.popInt(2)
|
|
|
|
if testedValue != 0:
|
|
|
|
jumpImpl(k.cpt, jumpTarget)
|
|
|
|
|
2021-04-20 12:07:01 +00:00
|
|
|
pcOp: Vm2OpFn = proc (k: var Vm2Ctx) =
|
2021-04-14 16:34:24 +00:00
|
|
|
## 0x58, Get the value of the program counter prior to the increment
|
|
|
|
## corresponding to this instruction.
|
|
|
|
k.cpt.stack.push:
|
|
|
|
max(k.cpt.code.pc - 1, 0)
|
|
|
|
|
2021-04-20 12:07:01 +00:00
|
|
|
msizeOp: Vm2OpFn = proc (k: var Vm2Ctx) =
|
2021-04-14 16:34:24 +00:00
|
|
|
## 0x59, Get the size of active memory in bytes.
|
|
|
|
k.cpt.stack.push:
|
|
|
|
k.cpt.memory.len
|
|
|
|
|
2021-04-20 12:07:01 +00:00
|
|
|
gasOp: Vm2OpFn = proc (k: var Vm2Ctx) =
|
2021-04-14 16:34:24 +00:00
|
|
|
## 0x5a, Get the amount of available gas, including the corresponding
|
|
|
|
## reduction for the cost of this instruction.
|
|
|
|
k.cpt.stack.push:
|
|
|
|
k.cpt.gasMeter.gasRemaining
|
|
|
|
|
2021-04-20 12:07:01 +00:00
|
|
|
jumpDestOp: Vm2OpFn = proc (k: var Vm2Ctx) =
|
2021-04-14 16:34:24 +00:00
|
|
|
## 0x5b, Mark a valid destination for jumps. This operation has no effect
|
|
|
|
## on machine state during execution.
|
|
|
|
discard
|
|
|
|
|
2023-03-21 13:27:12 +00:00
|
|
|
tloadOp: Vm2OpFn = proc (k: var Vm2Ctx) =
|
|
|
|
## 0xb3, Load word from transient storage.
|
|
|
|
let
|
|
|
|
slot = k.cpt.stack.peek()
|
|
|
|
val = k.cpt.getTransientStorage(slot)
|
|
|
|
k.cpt.stack.top(val)
|
|
|
|
|
|
|
|
tstoreOp: Vm2OpFn = proc (k: var Vm2Ctx) =
|
|
|
|
## 0xb4, Save word to transient storage.
|
|
|
|
checkInStaticContext(k.cpt)
|
|
|
|
|
|
|
|
let
|
|
|
|
slot = k.cpt.stack.popInt()
|
|
|
|
val = k.cpt.stack.popInt()
|
|
|
|
k.cpt.setTransientStorage(slot, val)
|
|
|
|
|
2022-09-28 06:09:33 +00:00
|
|
|
#[
|
|
|
|
EIP-2315: temporary disabled
|
|
|
|
Reason : not included in berlin hard fork
|
2021-04-20 12:07:01 +00:00
|
|
|
beginSubOp: Vm2OpFn = proc (k: var Vm2Ctx) =
|
2021-04-14 16:34:24 +00:00
|
|
|
## 0x5c, Marks the entry point to a subroutine
|
|
|
|
raise newException(
|
|
|
|
OutOfGas,
|
|
|
|
"Abort: Attempt to execute BeginSub opcode")
|
|
|
|
|
|
|
|
|
2021-04-20 12:07:01 +00:00
|
|
|
returnSubOp: Vm2OpFn = proc (k: var Vm2Ctx) =
|
2021-04-14 16:34:24 +00:00
|
|
|
## 0x5d, Returns control to the caller of a subroutine.
|
|
|
|
if k.cpt.returnStack.len == 0:
|
|
|
|
raise newException(
|
|
|
|
OutOfGas,
|
|
|
|
"Abort: invalid returnStack during ReturnSub")
|
|
|
|
k.cpt.code.pc = k.cpt.returnStack.pop()
|
|
|
|
|
|
|
|
|
2021-04-20 12:07:01 +00:00
|
|
|
jumpSubOp: Vm2OpFn = proc (k: var Vm2Ctx) =
|
2021-04-14 16:34:24 +00:00
|
|
|
## 0x5e, Transfers control to a subroutine.
|
|
|
|
let (jumpTarget) = k.cpt.stack.popInt(1)
|
|
|
|
|
|
|
|
if jumpTarget >= k.cpt.code.len.u256:
|
|
|
|
raise newException(
|
|
|
|
InvalidJumpDestination, "JumpSub destination exceeds code len")
|
|
|
|
|
|
|
|
let returnPC = k.cpt.code.pc
|
|
|
|
let jt = jumpTarget.truncate(int)
|
|
|
|
k.cpt.code.pc = jt
|
|
|
|
|
|
|
|
let nextOpcode = k.cpt.code.peek
|
|
|
|
if nextOpcode != BeginSub:
|
|
|
|
raise newException(
|
|
|
|
InvalidJumpDestination, "Invalid JumpSub destination")
|
|
|
|
|
|
|
|
if k.cpt.returnStack.len == 1023:
|
|
|
|
raise newException(
|
|
|
|
FullStack, "Out of returnStack")
|
|
|
|
|
|
|
|
k.cpt.returnStack.add returnPC
|
|
|
|
inc k.cpt.code.pc
|
2022-09-28 06:09:33 +00:00
|
|
|
]#
|
2021-04-14 16:34:24 +00:00
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public, op exec table entries
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
const
|
|
|
|
vm2OpExecMemory*: seq[Vm2OpExec] = @[
|
|
|
|
|
|
|
|
(opCode: Pop, ## x50, Remove item from stack
|
|
|
|
forks: Vm2OpAllForks,
|
2021-04-19 09:15:35 +00:00
|
|
|
name: "pop",
|
2021-04-14 16:34:24 +00:00
|
|
|
info: "Remove item from stack",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: popOp,
|
|
|
|
post: vm2OpIgnore)),
|
|
|
|
|
|
|
|
(opCode: Mload, ## 0x51, Load word from memory
|
|
|
|
forks: Vm2OpAllForks,
|
2021-04-19 09:15:35 +00:00
|
|
|
name: "mload",
|
2021-04-14 16:34:24 +00:00
|
|
|
info: "Load word from memory",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: mloadOp,
|
|
|
|
post: vm2OpIgnore)),
|
|
|
|
|
|
|
|
(opCode: Mstore, ## 0x52, Save word to memory
|
|
|
|
forks: Vm2OpAllForks,
|
2021-04-19 09:15:35 +00:00
|
|
|
name: "mstore",
|
2021-04-14 16:34:24 +00:00
|
|
|
info: "Save word to memory",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: mstoreOp,
|
|
|
|
post: vm2OpIgnore)),
|
|
|
|
|
|
|
|
(opCode: Mstore8, ## 0x53, Save byte to memory
|
|
|
|
forks: Vm2OpAllForks,
|
2021-04-19 09:15:35 +00:00
|
|
|
name: "mstore8",
|
2021-04-14 16:34:24 +00:00
|
|
|
info: "Save byte to memory",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: mstore8Op,
|
|
|
|
post: vm2OpIgnore)),
|
|
|
|
|
|
|
|
(opCode: Sload, ## 0x54, Load word from storage
|
2021-04-15 16:42:19 +00:00
|
|
|
forks: Vm2OpAllForks - Vm2OpBerlinAndLater,
|
2021-04-19 09:15:35 +00:00
|
|
|
name: "sload",
|
2021-04-14 16:34:24 +00:00
|
|
|
info: "Load word from storage",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: sloadOp,
|
|
|
|
post: vm2OpIgnore)),
|
|
|
|
|
2021-04-15 16:42:19 +00:00
|
|
|
(opCode: Sload, ## 0x54, sload for Berlin and later
|
|
|
|
forks: Vm2OpBerlinAndLater,
|
2021-04-19 09:15:35 +00:00
|
|
|
name: "sloadEIP2929",
|
2021-04-15 16:42:19 +00:00
|
|
|
info: "EIP2929: sload for Berlin and later",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: sloadEIP2929Op,
|
|
|
|
post: vm2OpIgnore)),
|
|
|
|
|
2021-04-14 16:34:24 +00:00
|
|
|
(opCode: Sstore, ## 0x55, Save word
|
|
|
|
forks: Vm2OpAllForks - Vm2OpConstantinopleAndLater,
|
2021-04-19 09:15:35 +00:00
|
|
|
name: "sstore",
|
2021-04-14 16:34:24 +00:00
|
|
|
info: "Save word to storage",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: sstoreOp,
|
|
|
|
post: vm2OpIgnore)),
|
|
|
|
|
|
|
|
(opCode: Sstore, ## 0x55, sstore for Constantinople and later
|
2021-04-19 09:15:35 +00:00
|
|
|
forks: Vm2OpConstantinopleAndLater - Vm2OpPetersburgAndLater,
|
|
|
|
name: "sstoreEIP1283",
|
2021-04-14 16:34:24 +00:00
|
|
|
info: "EIP1283: sstore for Constantinople and later",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: sstoreEIP1283Op,
|
|
|
|
post: vm2OpIgnore)),
|
|
|
|
|
2021-04-19 09:15:35 +00:00
|
|
|
(opCode: Sstore, ## 0x55, sstore for Petersburg and later
|
|
|
|
forks: Vm2OpPetersburgAndLater - Vm2OpIstanbulAndLater,
|
|
|
|
name: "sstore",
|
|
|
|
info: "sstore for Constantinople and later",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: sstoreOp,
|
|
|
|
post: vm2OpIgnore)),
|
|
|
|
|
2021-04-14 16:34:24 +00:00
|
|
|
(opCode: Sstore, ## 0x55, sstore for Istanbul and later
|
2021-04-15 16:42:19 +00:00
|
|
|
forks: Vm2OpIstanbulAndLater - Vm2OpBerlinAndLater,
|
2021-04-19 09:15:35 +00:00
|
|
|
name: "sstoreEIP2200",
|
2021-04-14 16:34:24 +00:00
|
|
|
info: "EIP2200: sstore for Istanbul and later",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: sstoreEIP2200Op,
|
|
|
|
post: vm2OpIgnore)),
|
|
|
|
|
2021-04-15 16:42:19 +00:00
|
|
|
(opCode: Sstore, ## 0x55, sstore for Berlin and later
|
|
|
|
forks: Vm2OpBerlinAndLater,
|
2021-04-19 09:15:35 +00:00
|
|
|
name: "sstoreEIP2929",
|
2021-04-15 16:42:19 +00:00
|
|
|
info: "EIP2929: sstore for Istanbul and later",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: sstoreEIP2929Op,
|
|
|
|
post: vm2OpIgnore)),
|
|
|
|
|
2021-04-14 16:34:24 +00:00
|
|
|
(opCode: Jump, ## 0x56, Jump
|
2021-04-19 09:15:35 +00:00
|
|
|
forks: Vm2OpAllForks,
|
|
|
|
name: "jump",
|
2021-04-14 16:34:24 +00:00
|
|
|
info: "Alter the program counter",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: jumpOp,
|
|
|
|
post: vm2OpIgnore)),
|
|
|
|
|
|
|
|
(opCode: JumpI, ## 0x57, Conditional jump
|
|
|
|
forks: Vm2OpAllForks,
|
2021-04-19 09:15:35 +00:00
|
|
|
name: "jumpI",
|
2021-04-14 16:34:24 +00:00
|
|
|
info: "Conditionally alter the program counter",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: jumpIOp,
|
|
|
|
post: vm2OpIgnore)),
|
|
|
|
|
|
|
|
(opCode: Pc, ## 0x58, Program counter prior to instruction
|
|
|
|
forks: Vm2OpAllForks,
|
2021-04-19 09:15:35 +00:00
|
|
|
name: "pc",
|
2021-04-14 16:34:24 +00:00
|
|
|
info: "Get the value of the program counter prior to the increment "&
|
|
|
|
"corresponding to this instruction",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: pcOp,
|
|
|
|
post: vm2OpIgnore)),
|
|
|
|
|
|
|
|
(opCode: Msize, ## 0x59, Memory size
|
|
|
|
forks: Vm2OpAllForks,
|
2021-04-19 09:15:35 +00:00
|
|
|
name: "msize",
|
2021-04-14 16:34:24 +00:00
|
|
|
info: "Get the size of active memory in bytes",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: msizeOp,
|
|
|
|
post: vm2OpIgnore)),
|
|
|
|
|
|
|
|
(opCode: Gas, ## 0x5a, Get available gas
|
|
|
|
forks: Vm2OpAllForks,
|
2021-04-19 09:15:35 +00:00
|
|
|
name: "gas",
|
2021-04-14 16:34:24 +00:00
|
|
|
info: "Get the amount of available gas, including the corresponding "&
|
|
|
|
"reduction for the cost of this instruction",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: gasOp,
|
|
|
|
post: vm2OpIgnore)),
|
|
|
|
|
|
|
|
(opCode: JumpDest, ## 0x5b, Mark jump target. This operation has no effect
|
|
|
|
## on machine state during execution
|
|
|
|
forks: Vm2OpAllForks,
|
2021-04-19 09:15:35 +00:00
|
|
|
name: "jumpDest",
|
2021-04-14 16:34:24 +00:00
|
|
|
info: "Mark a valid destination for jumps",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: jumpDestOp,
|
2023-03-21 13:27:12 +00:00
|
|
|
post: vm2OpIgnore)),
|
|
|
|
|
|
|
|
(opCode: Tload, ## 0xb3, Load word from transient storage.
|
|
|
|
forks: Vm2Op1153AndLater,
|
|
|
|
name: "tLoad",
|
|
|
|
info: "Load word from transient storage",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: tloadOp,
|
|
|
|
post: vm2OpIgnore)),
|
|
|
|
|
|
|
|
(opCode: Tstore, ## 0xb4, Save word to transient storage.
|
|
|
|
forks: Vm2Op1153AndLater,
|
|
|
|
name: "tStore",
|
|
|
|
info: "Save word to transient storage",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: tstoreOp,
|
2021-05-16 12:31:13 +00:00
|
|
|
post: vm2OpIgnore))]
|
2021-04-14 16:34:24 +00:00
|
|
|
|
2022-04-08 04:54:11 +00:00
|
|
|
#[
|
|
|
|
EIP-2315: temporary disabled
|
2021-05-16 12:31:13 +00:00
|
|
|
Reason : not included in berlin hard fork
|
2021-04-14 16:34:24 +00:00
|
|
|
(opCode: BeginSub, ## 0x5c, Begin subroutine
|
2021-04-19 09:15:35 +00:00
|
|
|
forks: Vm2OpBerlinAndLater,
|
|
|
|
name: "beginSub",
|
2021-04-14 16:34:24 +00:00
|
|
|
info: " Marks the entry point to a subroutine",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: beginSubOp,
|
|
|
|
post: vm2OpIgnore)),
|
|
|
|
|
|
|
|
(opCode: ReturnSub, ## 0x5d, Return
|
2021-04-19 09:15:35 +00:00
|
|
|
forks: Vm2OpBerlinAndLater,
|
|
|
|
name: "returnSub",
|
2021-04-14 16:34:24 +00:00
|
|
|
info: "Returns control to the caller of a subroutine",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: returnSubOp,
|
|
|
|
post: vm2OpIgnore)),
|
|
|
|
|
|
|
|
(opCode: JumpSub, ## 0x5e, Call subroutine
|
2021-04-19 09:15:35 +00:00
|
|
|
forks: Vm2OpBerlinAndLater,
|
|
|
|
name: "jumpSub",
|
2021-04-14 16:34:24 +00:00
|
|
|
info: "Transfers control to a subroutine",
|
|
|
|
exec: (prep: vm2OpIgnore,
|
|
|
|
run: jumpSubOp,
|
|
|
|
post: vm2OpIgnore))]
|
2021-05-16 12:31:13 +00:00
|
|
|
]#
|
2021-04-14 16:34:24 +00:00
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# End
|
|
|
|
# ------------------------------------------------------------------------------
|