This document aims to specify an Ethereum VM in a way useful to contract writers and VM implementers (mostly for Ewasm). To this end, multiple things are specified:
- The extra state that a VM needs to have around to successfully respond to calls into the EEI.
- The EEI (Ethereum Environment Interface), currently specified loosely here.
EEI: The Ethereum Environment Interface refers to the layer between the Ethereum Client code and the execution engine. TODO: Is EEI the right term to use?
Ethereum Client: Code which can interact with the blockchain (read/validate and sending transactions).
execution engine: The underlying "hardware" of the VM, implementing the basic computational functions.
VM: The VM is the combination of an Ethereum Client and the execution engine.
We are using K Framework notation to specify the EEI, which makes this specification executable.
requires "domains.k"
module EEI
imports INT
imports SET
imports LIST
imports BOOL
imports MAP
imports BYTES
Below both a K rule and a prose description of each state transition is given.
The state is specified using a K configuration.
Each XML-like cell contains a field which is relevant to Ethereum client execution (eg. below the first cell is the <eeiK>
cell).
The default/initial values of the cells are provided along with the declaration of the configuration.
In the texual rules below, we'll refer to cells by accesing subcells with the .
operator.
For example, we would access contents of the statusCode
cell with eei.statusCode
.
For cells that contain elements of K builtin sorts Map
, List
, and Set
, we'll use standard K operators for referring to their contents.
For example, we can access the third element of the returnData
cell's list using eei.returnData[2]
.
For some cells, we have comments following the cell declarations with the name the Yellow Paper gives to that element of the state.
configuration
<eei>
<eeiK> .K </eeiK>
<statusCode> .StatusCode </statusCode>
<returnData> .Bytes </returnData>
The <callState>
sub-configuration can be saved/restored when needed between calls.
When stored, it's stored in the <callStack>
cell as a list.
<callState>
<callDepth> 0 </callDepth>
<acct> 0 </acct> // I_a
<program> .Code </program> // I_b
<caller> 0 </caller> // I_s
<callData> .Bytes </callData> // I_d
<callValue> 0 </callValue> // I_v
<gas> 0 </gas> // \mu_g
</callState>
<callStack> .List </callStack>
The execution <substate>
keeps track of the self-destruct set, the log, and accumulated gas refund.
<substate>
<selfDestruct> .Set </selfDestruct> // A_s
<log> .List </log> // A_l
<refund> 0 </refund> // A_r
</substate>
The <accounts>
sub-configuration stores information about each account on the blockchain.
The multiplicity="*"
allows us to have multiple accounts simultaneously, and type="Map"
allows us to access accounts by using the <id>
as a key.
For example, eei.accounts[0x0001].nonce
would access the nonce of account 0x0001
.
Similar to the <callState>
, the an <account>
state can be saved or restored on the <accountStack>
cell.
<accounts>
<account multiplicity="*" type="Map">
<id> 0 </id>
<balance> 0 </balance>
<code> .Code </code>
<storage> .Map </storage>
<nonce> 0 </nonce>
</account>
</accounts>
<accountsStack> .List </accountsStack>
Transaction state <tx>
:
<tx>
<gasPrice> 0 </gasPrice> // I_p
<origin> 0 </origin> // I_o
</tx>
And finally, block stack <block>
:
<block>
<hashes> .List </hashes>
<coinbase> 0 </coinbase> // H_c
// <logsBloom> .WordStack </logsBloom> // H_b
<difficulty> 0 </difficulty> // H_d
<number> 0 </number> // H_i
<gasLimit> 0 </gasLimit> // H_l
<timestamp> 0 </timestamp> // H_s
</block>
</eei>
An EEIMethods
is a list of commands to be executed via the EEI.
Each EEIMethod
can invoke the execution engine on an input or interact with the client.
syntax EEIMethods ::= List{EEIMethod, ""}
The contracts can contain code which can be executed by an execution engine.
However, the EEI is agnostic to execution engines.
In this specification, we therefore only allow the constant .Code
to represent contract code, and an embedder making use of the EEI can extend the Code
production to include other code.
syntax Code ::= ".Code"
The EVMC status codes are used by the execution engine to indicate to the client how execution ended.
Currently, they are broken into three subsorts, for exceptional, ending, or error statuses.
The extra status code .StatusCode
is used as a default status code when none has been set.
syntax StatusCode ::= ExceptionalStatusCode
| EndStatusCode
| ErrorStatusCode
| ".StatusCode"
The following codes all indicate that the VM ended execution with an exception, but give details about how.
EVMC_FAILURE
is a catch-all for generic execution failure.EVMC_INVALID_INSTRUCTION
indicates reaching the designatedINVALID
opcode.EVMC_UNDEFINED_INSTRUCTION
indicates that an undefined opcode has been reached.EVMC_OUT_OF_GAS
indicates that execution exhausted the gas supply.EVMC_BAD_JUMP_DESTINATION
indicates aJUMP*
to a non-JUMPDEST
location.EVMC_STACK_OVERFLOW
indicates pushing more than 1024 elements onto the wordstack.EVMC_STACK_UNDERFLOW
indicates popping elements off an empty wordstack.EVMC_CALL_DEPTH_EXCEEDED
indicates that we have executed too deeply a nested sequence ofCALL*
orCREATE
opcodes.EVMC_INVALID_MEMORY_ACCESS
indicates that a bad memory access occured. This can happen when accessing local memory withCODECOPY*
orCALLDATACOPY
, or when accessing return data withRETURNDATACOPY
.EVMC_STATIC_MODE_VIOLATION
indicates that aSTATICCALL
tried to change state.EVMC_PRECOMPILE_FAILURE
indicates an error in the precompiled contracts (eg. invalid points handed to elliptic curve functions).
syntax ExceptionalStatusCode ::= "EVMC_FAILURE"
| "EVMC_INVALID_INSTRUCTION"
| "EVMC_UNDEFINED_INSTRUCTION"
| "EVMC_OUT_OF_GAS"
| "EVMC_BAD_JUMP_DESTINATION"
| "EVMC_STACK_OVERFLOW"
| "EVMC_STACK_UNDERFLOW"
| "EVMC_CALL_DEPTH_EXCEEDED"
| "EVMC_INVALID_MEMORY_ACCESS"
| "EVMC_STATIC_MODE_VIOLATION"
| "EVMC_PRECOMPILE_FAILURE"
The following are status codes used to report network state failures to the EVM from the client. These are not present in the [EVM-C API].
EVMC_ACCOUNT_ALREADY_EXISTS
indicates that a newly created account already exists.EVMC_BALANCE_UNDERFLOW
indicates an attempt to create an account which already exists.
syntax ExceptionalStatusCode ::= "EVMC_ACCOUNT_ALREADY_EXISTS"
| "EVMC_BALANCE_UNDERFLOW"
These additional status codes indicate that execution has ended in some non-exceptional way.
EVMC_SUCCESS
indicates successful end of execution.EVMC_REVERT
indicates that the contract calledREVERT
.
syntax EndStatusCode ::= ExceptionalStatusCode
| "EVMC_SUCCESS"
| "EVMC_REVERT"
The following codes indicate other non-execution errors with the execution engine.
EVMC_REJECTED
indicates malformed or wrong-version EVM bytecode.EVMC_INTERNAL_ERROR
indicates some other error that is unrecoverable but not due to the bytecode.
syntax ErrorStatusCode ::= "EVMC_REJECTED"
| "EVMC_INTERNAL_ERROR"
The EEI signals returns results to an outside caller by wrapping it in a #result
.
syntax ResultType ::= Int | Code | Bytes
syntax K ::= "#result" "(" ResultType ")"
// -----------------------------------------
The EEI exports several methods which can be invoked by the VM to interact with the client. Here the syntax and semantics of these methods is defined.
Each section header gives the name of the given EEI method, along with the arguments needed.
For example, EEI.useGas : Int
declares that EEI.useGas
in an EEI method which takes a single integer as input.
The semantics are provided in three forms:
-
a short prose description of purpose,
-
a list of steps that must be taken, and
-
a set K rules specifying the state update that can happen.
These methods are used by the other EEI methods as helpers and intermediates to perform larger more complex tasks. They are for the most part not intended to be exposed to the execution engine or Ethereum client for direct usage.
TODO: {push,pop,drop}Accounts
should be able to take a specific list of accounts to push/pop/drop, making them more efficient.
Saves a copy of the current call state in the <callStack>
.
-
Load the current
CALLSTATE
fromeei.callState
. -
Prepend
CALLSTATE
to theeei.callStack
.
syntax EEIMethod ::= "EEI.pushCallState"
// ----------------------------------------
rule <eeiK> EEI.pushCallState => . </eeiK>
<callState> CALLSTATE </callState>
<callStack> (.List => ListItem(CALLSTATE)) ... </callStack>
Restores the most recently saved <callState>
.
-
Load the new
CALLSTATE
fromeei.callStack[0]
. -
Remove the first element of
eei.callStack
. -
Set
eei.callState
toCALLSTATE
.
syntax EEIMethod ::= "EEI.popCallState"
// ----------------------------------------
rule <eeiK> EEI.popCallState => . </eeiK>
<callState> _ => CALLSTATE </callState>
<callStack> (ListItem(CALLSTATE) => .List) ... </callStack>
Forgets the most recently saved <callState>
as reverting back to it will no longer happen.
- Remove the first element of
eei.callStack
.
syntax EEIMethod ::= "EEI.dropCallState"
// ----------------------------------------
rule <eeiK> EEI.dropCallState => . </eeiK>
<callStack> (ListItem(_) => .List) ... </callStack>
Saves a copy of the <accounts>
state in the <accountStack>
cell.
-
Load the current
ACCTDATA
fromeei.accounts
. -
Prepend
ACCTDATA
to theeei.accountStack
.
syntax EEIMethod ::= "EEI.pushAccounts"
// ---------------------------------------
rule <eeiK> EEI.pushAccounts => . </eeiK>
<accounts> ACCTDATA </accounts>
<accountsStack> (.List => ListItem(ACCTDATA)) ... </accountsStack>
Restores the most recently saved <accounts>
state.
-
Load the new
ACCTDATA
fromeei.accountsStack[0]
. -
Remove the first element of
eei.accountsStack
. -
Set
eei.accounts
toACCTDATA
.
syntax EEIMethod ::= "EEI.popAccounts"
// --------------------------------------
rule <eeiK> EEI.popAccounts => . </eeiK>
<accounts> _ => ACCTDATA </accounts>
<accountsStack> (ListItem(ACCTDATA) => .List) ... </accountsStack>
Forgets the most recently saved <accounts>
state as reverting back to it will no longer happen.
- Remove the first element of
eei.accountsStack
.
syntax EEIMethod ::= "EEI.dropAccounts"
// ---------------------------------------
rule <eeiK> EEI.dropAccounts => . </eeiK>
<accountsStack> (ListItem(_) => .List) ... </accountsStack>
If the statuscode is not exception, execute ETHSIMULATIONGOOD
, otherwise execute ETHSIMULATIONBAD
.
-
Load the
STATUSCODE
fromeei.statusCode
. -
If
STATUSCODE
is not anExceptionalStatusCode
, then:i. Call
ETHSIMULATIONGOOD
.else:
i. Call
ETHSIMULATIONBAD
.
syntax EEIMethod ::= "EEI.ifStatus" EEIMethods EEIMethods
// ---------------------------------------------------------
rule <eeiK> EEI.ifStatus ETHSIMULATIONGOOD ETHSIMULATIONBAD => ETHSIMULATIONGOOD </eeiK>
<statusCode> STATUSCODE </statusCode>
requires notBool isExceptionalStatusCode(STATUSCODE)
rule <eeiK> EEI.ifStatus ETHSIMULATIONGOOD ETHSIMULATIONBAD => ETHSIMULATIONBAD </eeiK>
<statusCode> STATUSCODE </statusCode>
requires isExceptionalStatusCode(STATUSCODE)
Executes the given ETHSIMULATION
if the current status code is not exceptional.
- Call
EEI.ifStatus ETHSIMULATION .EEIMethods
syntax EEIMethod ::= "EEI.onGoodStatus" EEIMethods
// --------------------------------------------------
rule <eeiK> EEI.onGoodStatus ETHSIMULATION => EEI.ifStatus ETHSIMULATION .EEIMethods </eeiK>
Resets the configuration.
syntax EEIMethod ::= "EEI.clearConfig"
// --------------------------------------
rule <eeiK> EEI.clearConfig => . </eeiK>
<statusCode> _ => .StatusCode </statusCode>
<returnData> _ => .Bytes </returnData>
<callState>
<callDepth> _ => 0 </callDepth>
<acct> _ => 0 </acct> // I_a
<program> _ => .Code </program> // I_b
<caller> _ => 0 </caller> // I_s
<callData> _ => .Bytes </callData> // I_d
<callValue> _ => 0 </callValue> // I_v
<gas> _ => 0 </gas> // \mu_g
</callState>
<callStack> _ => .List </callStack>
<substate>
<selfDestruct> _ => .Set </selfDestruct> // A_s
<log> _ => .List </log> // A_l
<refund> _ => 0 </refund> // A_r
</substate>
<accounts> _ => .Bag </accounts>
<accountsStack> _ => .List </accountsStack>
<tx>
<gasPrice> _ => 0 </gasPrice> // I_p
<origin> _ => 0 </origin> // I_o
</tx>
<block>
<hashes> _ => .List </hashes>
<coinbase> _ => 0 </coinbase> // H_c
// <logsBloom> _ => .WordStack </logsBloom> // H_b
<difficulty> _ => 0 </difficulty> // H_d
<number> _ => 0 </number> // H_i
<gasLimit> _ => 0 </gasLimit> // H_l
<timestamp> _ => 0 </timestamp> // H_s
</block>
Many of the methods exported by the EEI simply query for some state of the current block/transaction.
These methods are prefixed with get
, and have largely similar and simple rules.
Get the coinbase of the current block.
- Load and return
eei.block.coinbase
.
syntax EEIMethod ::= "EEI.getBlockCoinbase"
// -------------------------------------------
rule <eeiK> EEI.getBlockCoinbase => #result(CBASE) </eeiK>
<coinbase> CBASE </coinbase>
Get the difficulty of the current block.
- Load and return
eei.block.difficulty
.
syntax EEIMethod ::= "EEI.getBlockDifficulty"
// ---------------------------------------------
rule <eeiK> EEI.getBlockDifficulty => #result(DIFF) </eeiK>
<difficulty> DIFF </difficulty>
Get the gas limit for the current block.
- Load and return
eei.block.gasLimit
.
syntax EEIMethod ::= "EEI.getBlockGasLimit"
// -------------------------------------------
rule <eeiK> EEI.getBlockGasLimit => #result(GLIMIT) </eeiK>
<gasLimit> GLIMIT </gasLimit>
Return the blockhash of one of the N
th most recent complete blocks (as long as N <Int 256
).
If there are not N
blocks yet, return 0
.
TODO: Double-check this logic, esp for off-by-one errors.
-
Load
BLOCKNUM
fromeei.block.number
. -
If
N <Int 256
andN <Int BLOCKNUM
, then:i. Load and return
eei.block.hashes[N]
.else:
i. Return
0
.
syntax EEIMethod ::= "EEI.getBlockHash" Int
// -------------------------------------------
rule <eeiK> EEI.getBlockHash N => #result( {BLKHASHES[N]}:>Int ) </eeiK>
<hashes> BLKHASHES </hashes>
requires N <Int 256
rule <eeiK> EEI.getBlockHash N => #result(0) </eeiK>
requires N >=Int 256
Get the current block number.
- Load and return
eei.block.number
.
syntax EEIMethod ::= "EEI.getBlockNumber"
// -----------------------------------------
rule <eeiK> EEI.getBlockNumber => #result(BLKNUMBER) </eeiK>
<number> BLKNUMBER </number>
Get the timestamp of the last block.
- Load and return
eei.block.timestamp
.
syntax EEIMethod ::= "EEI.getBlockTimestamp"
// --------------------------------------------
rule <eeiK> EEI.getBlockTimestamp => #result(TSTAMP) </eeiK>
<timestamp> TSTAMP </timestamp>
Get the gas price of the current transation.
- Load and return
eei.tx.gasPrice
.
syntax EEIMethod ::= "EEI.getTxGasPrice"
// ----------------------------------------
rule <eeiK> EEI.getTxGasPrice => #result(GPRICE) </eeiK>
<gasPrice> GPRICE </gasPrice>
Get the address which sent this transaction.
- Load and return
eei.tx.origin
.
syntax EEIMethod ::= "EEI.getTxOrigin"
// --------------------------------------
rule <eeiK> EEI.getTxOrigin => #result(ORG) </eeiK>
<origin> ORG </origin>
These methods return information about the current call operation, which may change throughout a given transaction/block.
Return the address of the currently executing account.
- Load and return the value
eei.callState.acct
.
syntax EEIMethod ::= "EEI.getAddress"
// -------------------------------------
rule <eeiK> EEI.getAddress => #result(ADDR) </eeiK>
<acct> ADDR </acct>
Get the account id of the caller into the current execution.
- Load and return
eei.callState.caller
.
syntax EEIMethod ::= "EEI.getCaller"
// ------------------------------------
rule <eeiK> EEI.getCaller => #result(CACCT) </eeiK>
<caller> CACCT </caller>
callDataSize
can be implemented client-side in terms of this opcode.
Returns the calldata associated with this call.
- Load and return
eei.callState.callData
.
syntax EEIMethod ::= "EEI.getCallData"
// --------------------------------------
rule <eeiK> EEI.getCallData => #result(CDATA) </eeiK>
<callData> CDATA </callData>
Get the value transferred for the current call.
- Load and return
eei.callState.callValue
.
syntax EEIMethod ::= "EEI.getCallValue"
// ---------------------------------------
rule <eeiK> EEI.getCallValue => #result(CVALUE) </eeiK>
<callValue> CVALUE </callValue>
Get the gas left available for this execution.
- Load and return
eei.callState.gas
.
syntax EEIMethod ::= "EEI.getGasLeft"
// -------------------------------------
rule <eeiK> EEI.getGasLeft => #result(GAVAIL) </eeiK>
<gas> GAVAIL </gas>
getReturnDataSize
can be implemented in terms of this method.
Get the return data of the last call.
- Load and return
eei.returnData
.
syntax EEIMethod ::= "EEI.getReturnData"
// ----------------------------------------
rule <eeiK> EEI.getReturnData => #result(RETDATA) </eeiK>
<returnData> RETDATA </returnData>
Deduct the specified amount of gas (GDEDUCT
) from the available gas.
-
Load the value
GAVAIL
fromeei.gas
. -
If
GDEDUCT <Int GAVAIL
, then:i. Set
eei.callState.gas
toGAVAIL -Int GDEDUCT
.else:
i. Set
eei.statusCode
toEVMC_OUT_OF_GAS
andeei.callState.gas
to0
.
syntax EEIMethod ::= "EEI.useGas" Int
// -------------------------------------
rule <eeiK> EEI.useGas GDEDUCT => . </eeiK>
<gas> GAVAIL => GAVAIL -Int GDEDUCT </gas>
requires GAVAIL >Int GDEDUCT
rule <eeiK> EEI.useGas GDEDUCT => . </eeiK>
<statusCode> _ => EVMC_OUT_OF_GAS </statusCode>
<gas> GAVAIL </gas>
requires GAVAIL <=Int GDEDUCT
These operators query the world state (eg. account balances).
We prefix those that query about the currently executing account with getAccount
(similarly setAccount
for setting state).
Those that can query about other accounts are prefixed with getExternalAccount
.
Return the balance of the current account (ACCT
).
-
Load the value
ACCT
fromeei.callState.acct
. -
Load and return the value
eei.accounts[ACCT].balance
.
syntax EEIMethod ::= "EEI.getAccountBalance"
// --------------------------------------------
rule <eeiK> EEI.getAccountBalance => #result(BAL) </eeiK>
<acct> ACCT </acct>
<account>
<id> ACCT </id>
<balance> BAL </balance>
...
</account>
Return the code of the current account (ACCT
).
-
Load the value
ACCT
fromeei.callState.acct
. -
Load and return
eei.accounts[ACCT].code
.
syntax EEIMethod ::= "EEI.getAccountCode"
// -----------------------------------------
rule <eeiK> EEI.getAccountCode => #result(ACCTCODE) </eeiK>
<acct> ACCT </acct>
<accounts>
<id> ACCT </id>
<code> ACCTCODE </code>
...
</accounts>
Return the code of the given account ACCT
.
- Load and return
eei.accounts[ACCT].code
.
syntax EEIMethod ::= "EEI.getExternalAccountCode" Int
// -----------------------------------------------------
rule <eeiK> EEI.getExternalAccountCode ACCT => #result(ACCTCODE) </eeiK>
<accounts>
<id> ACCT </id>
<code> ACCTCODE </code>
...
</accounts>
Return the value at the given INDEX
in the current executing accout's storage.
-
Load
ACCT
fromeei.callState.acct
. -
If
eei.accounts[ACCT].storage[INDEX]
exists, then:i. Return
eei.accounts[ACCT].storage[INDEX]
.else:
i. Return
0
.
syntax EEIMethod ::= "EEI.getAccountStorage" Int
// ------------------------------------------------
rule <eeiK> EEI.getAccountStorage INDEX => #result(VALUE) </eeiK>
<acct> ACCT </acct>
<account>
<id> ACCT </id>
<storage> ... INDEX |-> VALUE ... </storage>
...
</account>
rule <eeiK> EEI.getAccountStorage INDEX => #result(0) </eeiK>
<acct> ACCT </acct>
<account>
<id> ACCT </id>
<storage> STORAGE </storage>
...
</account>
requires notBool INDEX in_keys(STORAGE)
At the given INDEX
in the executing accounts storage, stores the given VALUE
.
-
Load
ACCT
fromeei.callState.acct
. -
Set
eei.accounts[ACCT].storage[INDEX]
toVALUE
.
syntax EEIMethod ::= "EEI.setAccountStorage" Int Int
// ----------------------------------------------------
rule <eeiK> EEI.setAccountStorage INDEX VALUE => . </eeiK>
<acct> ACCT </acct>
<account>
<id> ACCT </id>
<storage> STORAGE => STORAGE [ INDEX <- VALUE ] </storage>
...
</account>
Logging places a user-specified lists of integers (BS1
and BS2
) on the blockchain Log for external inspection.
First we define a log-item, which is an account id and two integer lists (in EVM, these come from the wordstack and the local memory).
syntax LogItem ::= "{" Int "|" List "|" List "}"
// ------------------------------------------------
-
Load the current
ACCT
fromeei.callState.acct
. -
Append
{ ACCT | BS1 | BS2 }
to theeei.substate.log
.
syntax EEIMethod ::= "EEI.log" List List
// ----------------------------------------
rule <eeiK> EEI.log BS1 BS2 => . </eeiK>
<acct> ACCT </acct>
<log> ... (.List => ListItem({ ACCT | BS1 | BS2 })) </log>
The remaining methods have more complex interactions with the EEI, often triggering further computation.
Selfdestructing removes the current executing account and transfers the funds of it to the specified target account ACCTTO
.
If the target account is the same as the executing account, the balance of the current account is zeroed immediately.
In any case, the status is set to EVMC_SUCCESS
.
-
Load
ACCT
fromeei.callState.acct
. -
Add
ACCT
to the seteei.substate.selfDestruct
. -
Set
eei.returnData
to.Bytes
(empty). -
Load
BALFROM
fromeei.accounts[ACCT].balance
. -
Set
eei.accounts[ACCT].balance
to0
. -
If
ACCT =/=Int ACCTTO
, then:i. Load
BALTO
fromeei.acounts[ACCTTO].balance
.ii. Set
eei.accounts[ACCTTO].balance
toBALTO +Int BALFROM
.
syntax EEIMethod ::= "EEI.selfDestruct" Int
// -------------------------------------------
rule <eeiK> EEI.selfDestruct ACCTTO => . </eeiK>
<statusCode> _ => EVMC_SUCCESS </statusCode>
<acct> ACCT </acct>
<returnData> _ => .Bytes </returnData>
<selfDestruct> ... (.Set => SetItem(ACCT)) ... </selfDestruct>
<accounts>
<account>
<id> ACCT </id>
<balance> BALFROM => 0 </balance>
...
</account>
<account>
<id> ACCTTO </id>
<balance> BALTO => BALTO +Int BALFROM </balance>
...
</account>
...
</accounts>
requires ACCTTO =/=K ACCT
rule <eeiK> EEI.selfDestruct ACCT => . </eeiK>
<statusCode> _ => EVMC_SUCCESS </statusCode>
<acct> ACCT </acct>
<returnData> _ => .Bytes </returnData>
<selfDestruct> ... (.Set => SetItem(ACCT)) ... </selfDestruct>
<accounts>
<account>
<id> ACCT </id>
<balance> BALFROM => 0 </balance>
...
</account>
...
</accounts>
Set the return data to the given list of RDATA
as well setting the status code to EVMC_SUCCESS
.
-
Set
eei.returnData
toRDATA
. -
Set
eei.statusCode
toEVMC_SUCCESS
.
syntax EEIMethod ::= "EEI.return" Bytes
// ---------------------------------------
rule <eeiK> EEI.return RDATA => . </eeiK>
<statusCode> _ => EVMC_SUCCESS </statusCode>
<returnData> _ => RDATA </returnData>
Set the return data to the given list of RDATA
as well setting the status code to EVMC_REVERT
.
-
Set
eei.returnData
toRDATA
. -
Set
eei.statusCode
toEVMC_REVERT
.
syntax EEIMethod ::= "EEI.revert" Bytes
// ---------------------------------------
rule <eeiK> EEI.revert RDATA => . </eeiK>
<statusCode> _ => EVMC_REVERT </statusCode>
<returnData> _ => RDATA </returnData>
Transfer VALUE
funds into account ACCTTO
.
-
Load
ACCTFROM
fromeei.callState.acct
. -
Load
BALFROM
fromeei.accounts[ACCTFROM].balance
. -
If
VALUE >Int BALFROM
, then:i. Set
eei.statusCode
toEVMC_BALANCE_UNDERFLOW
.else:
i. Set
eei.accounts[ACCTFROM].balance
toBAL -Int VALUE
.ii. Load
BALTO
fromeei.accounts[ACCTTO].balance
.iii. Set
eei.accounts[ACCTTO].balance
toBALTO +Int VALUE
.
syntax EEIMethod ::= "EEI.transfer" Int Int
// -------------------------------------------
rule <eeiK> EEI.transfer ACCTTO VALUE => . </eeiK>
<statusCode> _ => EVMC_BALANCE_UNDERFLOW </statusCode>
<acct> ACCTFROM </acct>
<account>
<id> ACCTFROM </id>
<balance> BALFROM </balance>
...
</account>
requires VALUE >Int BALFROM
rule <eeiK> EEI.transfer ACCTTO VALUE => . </eeiK>
<acct> ACCTFROM </acct>
<account>
<id> ACCTFROM </id>
<balance> BALFROM => BALFROM -Int VALUE </balance>
...
</account>
<account>
<id> ACCTTO </id>
<balance> BALTO => BALTO +Int VALUE </balance>
...
</account>
requires VALUE <=Int BALFROM
Helper for setting up the execution engine to run a specific code as if called by ACCTFROM
into ACCTTO
, with apparent value transfer APPVALUE
, gas allocation GAVAIL
, code CODE
, and arguments ARGS
.
-
Load
CALLDEPTH
fromeei.callState.callDepth
. -
Set
eei.callState.callDepth
toCALLDEPTH +Int 1
. -
Set
eei.callState.caller
toACCTFROM
. -
Set
eei.callState.acct
toACCTTO
. -
Set
eei.callState.callValue
toAPPVALUE
. -
Set
eei.callState.gas
toGAVAIL
. -
Set
eei.callState.program
toCODE
. -
Set
eei.callState.callData
toARGS
. -
Set
eei.returnData
to the empty.Bytes
.
syntax EEIMethod ::= "EEI.callInit" Int Int Int Int Code Bytes
// --------------------------------------------------------------
rule <eeiK> EEI.callInit ACCTFROM ACCTTO APPVALUE GAVAIL CODE ARGS => . </eeiK>
<returnData> _ => .Bytes </returnData>
<callState>
<callDepth> CALLDEPTH => CALLDEPTH +Int 1 </callDepth>
<caller> _ => ACCTFROM </caller>
<acct> _ => ACCTTO </acct>
<callValue> _ => APPVALUE </callValue>
<gas> _ => GAVAIL </gas>
<program> _ => CODE </program>
<callData> _ => ARGS </callData>
</callState>
TODO
syntax EEIMethod ::= "EEI.callFinish"
// -------------------------------------
TODO
syntax EEIMethod ::= "EEI.execute"
// ----------------------------------
TODO: Parameterize the 1024
max call depth.
Call into account ACCTTO
, with gas allocation GAVAIL
, apparent value APPVALUE
, and arguments ARGS
.
-
Load
CALLDEPTH
fromeei.callState.callDepth
. -
If
CALLDEPTH >=Int 1024
, then:i. Set
eei.statusCode
toEVMC_CALL_DEPTH_EXCEEDED
.else:
i. Load
CODE
fromeei.accountss[ACCTTO].code
.ii. Load
ACCTFROM
fromeei.callState.acct
.iii. Call
EEI.pushCallState
.iv. Call
EEI.pushAccounts
.vi. Call
EEI.callInit ACCTFROM ACCTTO APPVALUE GAVAIL CODE ARGS
vii. Call
EEI.execute
.viii. Call
EEI.popCallState
.viii. Call
EEI.ifStatus EEI.dropAccounts EEI.popAccounts
.iv. Call
EEI.execute
.
syntax EEIMethod ::= "EEI.call" Int Int Int Bytes
// -------------------------------------------------
rule <eeiK> EEI.call ACCTTO GAVAIL APPVALUE ARGS => . </eeiK>
<statusCode> _ => EVMC_CALL_DEPTH_EXCEEDED </statusCode>
<callDepth> CALLDEPTH </callDepth>
requires CALLDEPTH >=Int 1024
rule <eeiK> EEI.call ACCTTO GAVAIL APPVALUE ARGS
=> EEI.pushCallState ~> EEI.pushAccounts
~> EEI.callInit ACCTFROM ACCTTO APPVALUE GAVAIL CODE ARGS
~> EEI.execute
~> EEI.callFinish
~> EEI.execute
...
</eeiK>
<acct> ACCTFROM </acct>
<callDepth> CALLDEPTH </callDepth>
<account>
<id> ACCTTO </id>
<code> CODE </code>
...
</account>
requires CALLDEPTH <Int 1024
Call into account ACCTTO
, transfering value VALUE
, with gas allocation GAVAIL
, and arguments ARGS
.
-
Call
EEI.transfer VALUE ACCT
. -
Call
EEI.onGoodStatus (EEI.call ACCTTO VALUE GAVAIL ARGS)
.
syntax EEIMethod ::= "EEI.transferCall" Int Int Int Bytes
// ---------------------------------------------------------
rule <eeiK> EEI.transferCall ACCTTO VALUE GAVAIL ARGS
=> EEI.transfer VALUE ACCTTO
~> EEI.onGoodStatus (EEI.call ACCTTO VALUE GAVAIL ARGS)
...
</eeiK>
EEI.call
TODOEEI.callCode
TODOEEI.callDelegate
TODOEEI.callStatic
TODO
TODO: Implement one abstract-level EEI.call
, akin to #call
in KEVM, which other CALL*
opcodes can be expressed in terms of.
endmodule