Ethereum Simulations¶
Ethereum is using the EVM to drive updates over the world state. Actual execution of the EVM is defined in the EVM file.
requires "verification.k"
requires "evm.k"
requires "analysis.k"
module ETHEREUM-SIMULATION
imports EVM
imports EVM-ANALYSIS
imports VERIFICATION
imports K-REFLECTION
An Ethereum simulation is a list of Ethereum commands. Some Ethereum commands take an Ethereum specification (eg. for an account or transaction).
syntax EthereumSimulation ::= ".EthereumSimulation"
| EthereumCommand EthereumSimulation
// ----------------------------------------------------------------
rule .EthereumSimulation => .
rule ETC:EthereumCommand ETS:EthereumSimulation => ETC ~> ETS
syntax EthereumSimulation ::= JSON
// ----------------------------------
rule <k> JSONINPUT:JSON => run JSONINPUT success .EthereumSimulation </k>
For verification purposes, it’s much easier to specify a program in
terms of its op-codes and not the hex-encoding that the tests use. To do
so, we’ll extend sort JSON
with some EVM specific syntax, and
provide a “pretti-fication” to the nicer input form.
syntax JSON ::= Int | WordStack | OpCodes | Map | Call | SubstateLogEntry | Account
// -----------------------------------------------------------------------------------
syntax JSONList ::= #sortJSONList ( JSONList ) [function]
| #sortJSONList ( JSONList , JSONList ) [function, klabel(#sortJSONListAux)]
// ----------------------------------------------------------------------------------------------
rule #sortJSONList(JS) => #sortJSONList(JS, .JSONList)
rule #sortJSONList(.JSONList, LS) => LS
rule #sortJSONList(((KEY : VAL) , REST), LS) => #insertJSONKey((KEY : VAL), #sortJSONList(REST, LS))
syntax JSONList ::= #insertJSONKey ( JSON , JSONList ) [function]
// -----------------------------------------------------------------
rule #insertJSONKey( JS , .JSONList ) => JS , .JSONList
rule #insertJSONKey( (KEY : VAL) , ((KEY' : VAL') , REST) ) => (KEY : VAL) , (KEY' : VAL') , REST requires KEY <String KEY'
rule #insertJSONKey( (KEY : VAL) , ((KEY' : VAL') , REST) ) => (KEY' : VAL') , #insertJSONKey((KEY : VAL) , REST) requires KEY >=String KEY'
syntax Bool ::= #isSorted ( JSONList ) [function]
// -------------------------------------------------
rule #isSorted( .JSONList ) => true
rule #isSorted( KEY : _ ) => true
rule #isSorted( (KEY : _) , (KEY' : VAL) , REST ) => KEY <=String KEY' andThenBool #isSorted((KEY' : VAL) , REST)
Driving Execution¶
start
places#next
on the<k>
cell so that execution of the loaded state begin.flush
places#finalize
on the<k>
cell once it sees#end
in the<k>
cell, clearing any exceptions it finds.
syntax EthereumCommand ::= "start"
// ----------------------------------
rule <mode> NORMAL </mode> <k> start => #execute ... </k>
rule <mode> VMTESTS </mode> <k> start => #execute ... </k>
rule <mode> GASANALYZE </mode> <k> start => #gasAnalyze ... </k>
syntax EthereumCommand ::= "flush"
// ----------------------------------
rule <k> #end ~> flush => #finalizeTx(false) ... </k>
rule <k> #exception ~> flush => #finalizeTx(false) ~> #exception ... </k>
startTx
computes the sender of the transaction, and places loadTx on thek
cell.loadTx(_)
loads the next transaction to be executed into the current state.finishTx
is a place-holder for performing necessary cleanup after a transaction.
syntax EthereumCommand ::= "startTx"
// ------------------------------------
rule <k> startTx => #finalizeBlock ... </k>
<txPending> .List </txPending>
rule <k> startTx => loadTx(#sender(TN, TP, TG, TT, TV, #unparseByteStack(DATA), TW, TR, TS)) ... </k>
<txPending> ListItem(TXID:Int) ... </txPending>
<message>
<msgID> TXID </msgID>
<txNonce> TN </txNonce>
<txGasPrice> TP </txGasPrice>
<txGasLimit> TG </txGasLimit>
<to> TT </to>
<value> TV </value>
<v> TW </v>
<r> TR </r>
<s> TS </s>
<data> DATA </data>
</message>
syntax EthereumCommand ::= loadTx ( Int )
// -----------------------------------------
rule <k> loadTx(ACCTFROM)
=> #create ACCTFROM #newAddr(ACCTFROM, NONCE) (GLIMIT -Int G0(SCHED, CODE, true)) VALUE CODE
~> #execute ~> #finishTx ~> #finalizeTx(false) ~> startTx
...
</k>
<schedule> SCHED </schedule>
<gasPrice> _ => GPRICE </gasPrice>
<origin> _ => ACCTFROM </origin>
<callDepth> _ => -1 </callDepth>
<txPending> ListItem(TXID:Int) ... </txPending>
<message>
<msgID> TXID </msgID>
<txGasPrice> GPRICE </txGasPrice>
<txGasLimit> GLIMIT </txGasLimit>
<to> .Account </to>
<value> VALUE </value>
<data> CODE </data>
...
</message>
<account>
<acctID> ACCTFROM </acctID>
<balance> BAL => BAL -Int (GLIMIT *Int GPRICE) </balance>
<nonce> NONCE => NONCE +Int 1 </nonce>
...
</account>
<activeAccounts> ... ACCTFROM |-> (_ => false) ... </activeAccounts>
rule <k> loadTx(ACCTFROM)
=> #call ACCTFROM ACCTTO ACCTTO (GLIMIT -Int G0(SCHED, DATA, false)) VALUE VALUE DATA
~> #execute ~> #finishTx ~> #finalizeTx(false) ~> startTx
...
</k>
<schedule> SCHED </schedule>
<gasPrice> _ => GPRICE </gasPrice>
<origin> _ => ACCTFROM </origin>
<callDepth> _ => -1 </callDepth>
<txPending> ListItem(TXID:Int) ... </txPending>
<message>
<msgID> TXID </msgID>
<txGasPrice> GPRICE </txGasPrice>
<txGasLimit> GLIMIT </txGasLimit>
<to> ACCTTO </to>
<value> VALUE </value>
<data> DATA </data>
...
</message>
<account>
<acctID> ACCTFROM </acctID>
<balance> BAL => BAL -Int (GLIMIT *Int GPRICE) </balance>
<nonce> NONCE => NONCE +Int 1 </nonce>
...
</account>
<activeAccounts> ... ACCTFROM |-> (_ => false) ... </activeAccounts>
requires ACCTTO =/=K .Account
syntax EthereumCommand ::= "#finishTx"
// --------------------------------------
rule <k> #exception ~> #finishTx => #popCallStack ~> #popWorldState ~> #popSubstate ... </k>
rule <k> #end ~> #finishTx => #mkCodeDeposit ACCT ... </k>
<id> ACCT </id>
<txPending> ListItem(TXID:Int) ... </txPending>
<message>
<msgID> TXID </msgID>
<to> .Account </to>
...
</message>
rule <k> #end ~> #finishTx => #popCallStack ~> #dropWorldState ~> #dropSubstate ~> #refund GAVAIL ... </k>
<id> ACCT </id>
<gas> GAVAIL </gas>
<txPending> ListItem(TXID:Int) ... </txPending>
<message>
<msgID> TXID </msgID>
<to> TT </to>
...
</message>
requires TT =/=K .Account
#finalizeBlock
is used to signal that block finalization procedures should take place (after transactions have executed).#rewardOmmers(_)
pays out the reward to uncle blocks so that blocks are orphaned less often in Ethereum.
syntax EthereumCommand ::= "#finalizeBlock" | #rewardOmmers ( JSONList )
// ------------------------------------------------------------------------
rule <k> #finalizeBlock => #rewardOmmers(OMMERS) ... </k>
<ommerBlockHeaders> [ OMMERS ] </ommerBlockHeaders>
<coinbase> MINER </coinbase>
<account>
<acctID> MINER </acctID>
<balance> MINBAL => MINBAL +Int Rb </balance>
...
</account>
<activeAccounts> ... MINER |-> (_ => false) ... </activeAccounts>
rule <k> (.K => #newAccount MINER) ~> #finalizeBlock ... </k>
<coinbase> MINER </coinbase>
<activeAccounts> ACCTS </activeAccounts>
requires notBool MINER in_keys(ACCTS)
rule <k> #rewardOmmers(.JSONList) => . ... </k>
rule <k> #rewardOmmers([ _ , _ , OMMER , _ , _ , _ , _ , _ , OMMNUM , _ ] , REST) => #rewardOmmers(REST) ... </k>
<coinbase> MINER </coinbase>
<number> CURNUM </number>
<account>
<acctID> MINER </acctID>
<balance> MINBAL => MINBAL +Int Rb /Int 32 </balance>
...
</account>
<account>
<acctID> OMMER </acctID>
<balance> OMMBAL => OMMBAL +Int Rb +Int (OMMNUM -Int CURNUM) *Int (Rb /Int 8) </balance>
...
</account>
<activeAccounts> ... MINER |-> (_ => false) OMMER |-> (_ => false) ... </activeAccounts>
syntax Int ::= "Rb" [function]
// ------------------------------
rule Rb => 5 *Int (10 ^Int 18)
exception
only clears from the<k>
cell if there is an exception preceding it.failure_
holds the name of a test that failed if a test does fail.
syntax EthereumCommand ::= "exception" | "failure" String | "success"
// ---------------------------------------------------------------------
rule <k> #exception ~> exception => . ... </k>
rule <k> success => . ... </k> <exit-code> _ => 0 </exit-code>
rule failure _ => .
Running Tests¶
run
runs a given set of Ethereum tests (from the test-set).
Note that TEST
is sorted here so that key "network"
comes before
key "pre"
.
syntax EthereumCommand ::= "run" JSON
// -------------------------------------
rule run { .JSONList } => .
rule run { TESTID : { TEST:JSONList } , TESTS }
=> run ( TESTID : { #sortJSONList(TEST) } )
~> #if #hasPost?( { TEST } ) #then .K #else exception #fi
~> clear
~> run { TESTS }
syntax Bool ::= "#hasPost?" "(" JSON ")" [function]
// ---------------------------------------------------
rule #hasPost? ({ .JSONList }) => false
rule #hasPost? ({ (KEY:String) : _ , REST }) => (KEY in #postKeys) orBool #hasPost? ({ REST })
#loadKeys
are all the JSON nodes which should be considered as loads before execution.
syntax Set ::= "#loadKeys" [function]
// -------------------------------------
rule #loadKeys => ( SetItem("env") SetItem("pre") SetItem("rlp") SetItem("network") SetItem("genesisRLP") )
rule run TESTID : { KEY : (VAL:JSON) , REST } => load KEY : VAL ~> run TESTID : { REST } requires KEY in #loadKeys
rule run TESTID : { "blocks" : [ { KEY : VAL , REST1 => REST1 }, .JSONList ] , ( REST2 => KEY : VAL , REST2 ) }
rule run TESTID : { "blocks" : [ { .JSONList }, .JSONList ] , REST } => run TESTID : { REST }
#execKeys
are all the JSON nodes which should be considered for execution (between loading and checking).
syntax Set ::= "#execKeys" [function]
// -------------------------------------
rule #execKeys => ( SetItem("exec") SetItem("lastblockhash") )
rule run TESTID : { KEY : (VAL:JSON) , NEXT , REST } => run TESTID : { NEXT , KEY : VAL , REST } requires KEY in #execKeys
rule run TESTID : { "exec" : (EXEC:JSON) } => load "exec" : EXEC ~> start ~> flush
rule run TESTID : { "lastblockhash" : (HASH:String) } => startTx
#postKeys
are a subset of#checkKeys
which correspond to post-state account checks.#checkKeys
are all the JSON nodes which should be considered as checks after execution.
syntax Set ::= "#postKeys" [function] | "#allPostKeys" [function] | "#checkKeys" [function]
// -------------------------------------------------------------------------------------------
rule #postKeys => ( SetItem("post") SetItem("postState") )
rule #allPostKeys => ( #postKeys SetItem("expect") SetItem("export") SetItem("expet") )
rule #checkKeys => ( #allPostKeys SetItem("logs") SetItem("callcreates") SetItem("out") SetItem("gas")
SetItem("blockHeader") SetItem("transactions") SetItem("uncleHeaders") SetItem("genesisBlockHeader")
)
rule run TESTID : { KEY : (VAL:JSON) , REST } => run TESTID : { REST } ~> check TESTID : { "post" : VAL } requires KEY in #allPostKeys
rule run TESTID : { KEY : (VAL:JSON) , REST } => run TESTID : { REST } ~> check TESTID : { KEY : VAL } requires KEY in #checkKeys andBool notBool KEY in #allPostKeys
#discardKeys
are all the JSON nodes in the tests which should just be ignored.
syntax Set ::= "#discardKeys" [function]
// ----------------------------------------
rule #discardKeys => ( SetItem("//") SetItem("_info") )
rule run TESTID : { KEY : _ , REST } => run TESTID : { REST } requires KEY in #discardKeys
State Manipulation¶
Clearing State¶
clear
clears all the execution state of the machine.clearX
clears the substateX
, forTX
,BLOCK
, andNETWORK
.
syntax EthereumCommand ::= "clear"
// ----------------------------------
rule <k> clear => clearTX ~> clearBLOCK ~> clearNETWORK ... </k>
<analysis> _ => .Map </analysis>
syntax EthreumCommand ::= "clearTX"
// -----------------------------------
rule <k> clearTX => . ... </k>
<output> _ => .WordStack </output>
<memoryUsed> _ => 0 </memoryUsed>
<callDepth> _ => 0 </callDepth>
<callStack> _ => .List </callStack>
<callLog> _ => .Set </callLog>
<program> _ => .Map </program>
<programBytes> _ => .WordStack </programBytes>
<id> _ => 0 </id>
<caller> _ => 0 </caller>
<callData> _ => .WordStack </callData>
<callValue> _ => 0 </callValue>
<wordStack> _ => .WordStack </wordStack>
<localMem> _ => .Map </localMem>
<pc> _ => 0 </pc>
<gas> _ => 0 </gas>
<previousGas> _ => 0 </previousGas>
<selfDestruct> _ => .Set </selfDestruct>
<log> _ => .List </log>
<refund> _ => 0 </refund>
<gasPrice> _ => 0 </gasPrice>
<origin> _ => 0 </origin>
syntax EthreumCommand ::= "clearBLOCK"
// --------------------------------------
rule <k> clearBLOCK => . ... </k>
<previousHash> _ => 0 </previousHash>
<ommersHash> _ => 0 </ommersHash>
<coinbase> _ => 0 </coinbase>
<stateRoot> _ => 0 </stateRoot>
<transactionsRoot> _ => 0 </transactionsRoot>
<receiptsRoot> _ => 0 </receiptsRoot>
<logsBloom> _ => .WordStack </logsBloom>
<difficulty> _ => 0 </difficulty>
<number> _ => 0 </number>
<gasLimit> _ => 0 </gasLimit>
<gasUsed> _ => 0 </gasUsed>
<timestamp> _ => 0 </timestamp>
<extraData> _ => .WordStack </extraData>
<mixHash> _ => 0 </mixHash>
<blockNonce> _ => 0 </blockNonce>
<ommerBlockHeaders> _ => [ .JSONList ] </ommerBlockHeaders>
<blockhash> _ => .List </blockhash>
syntax EthreumCommand ::= "clearNETWORK"
// ----------------------------------------
rule <k> clearNETWORK => . ... </k>
<activeAccounts> _ => .Map </activeAccounts>
<accounts> _ => .Bag </accounts>
<messages> _ => .Bag </messages>
<schedule> _ => DEFAULT </schedule>
Loading State¶
mkAcct_
creates an account with the supplied ID (assuming it’s already been chopped to 160 bits).
syntax EthereumCommand ::= "mkAcct" Int
// ---------------------------------------
rule <k> mkAcct ACCT => #newAccount ACCT ... </k>
load
loads an account or transaction into the world state.
syntax EthereumCommand ::= "load" JSON
// --------------------------------------
rule load DATA : { .JSONList } => .
rule load DATA : { KEY : VALUE , REST } => load DATA : { KEY : VALUE } ~> load DATA : { REST }
requires REST =/=K .JSONList andBool DATA =/=String "transaction"
rule load DATA : [ .JSONList ] => .
rule load DATA : [ { TEST } , REST ] => load DATA : { TEST } ~> load DATA : [ REST ]
Here we perform pre-proccesing on account data which allows “pretty” specification of input.
rule load "pre" : { (ACCTID:String) : ACCT } => mkAcct #parseAddr(ACCTID) ~> load "account" : { ACCTID : ACCT }
rule load "account" : { ACCTID: { KEY : VALUE , REST } } => load "account" : { ACCTID : { KEY : VALUE } } ~> load "account" : { ACCTID : { REST } } requires REST =/=K .JSONList
rule load "account" : { ((ACCTID:String) => #parseAddr(ACCTID)) : ACCT }
rule load "account" : { (ACCT:Int) : { "balance" : ((VAL:String) => #parseWord(VAL)) } }
rule load "account" : { (ACCT:Int) : { "nonce" : ((VAL:String) => #parseWord(VAL)) } }
rule load "account" : { (ACCT:Int) : { "code" : ((CODE:String) => #parseByteStack(CODE)) } }
rule load "account" : { (ACCT:Int) : { "storage" : ({ STORAGE:JSONList } => #parseMap({ STORAGE })) } }
The individual fields of the accounts are dealt with here.
rule <k> load "account" : { ACCT : { "balance" : (BAL:Int) } } => . ... </k>
<account>
<acctID> ACCT </acctID>
<balance> _ => BAL </balance>
...
</account>
<activeAccounts> ... ACCT |-> (EMPTY => #if BAL =/=Int 0 #then false #else EMPTY #fi) ... </activeAccounts>
rule <k> load "account" : { ACCT : { "code" : (CODE:WordStack) } } => . ... </k>
<account>
<acctID> ACCT </acctID>
<code> _ => CODE </code>
...
</account>
<activeAccounts> ... ACCT |-> (EMPTY => #if CODE =/=K .WordStack #then false #else EMPTY #fi) ... </activeAccounts>
rule <k> load "account" : { ACCT : { "nonce" : (NONCE:Int) } } => . ... </k>
<account>
<acctID> ACCT </acctID>
<nonce> _ => NONCE </nonce>
...
</account>
<activeAccounts> ... ACCT |-> (EMPTY => #if NONCE =/=Int 0 #then false #else EMPTY #fi) ... </activeAccounts>
rule <k> load "account" : { ACCT : { "storage" : (STORAGE:Map) } } => . ... </k>
<account>
<acctID> ACCT </acctID>
<storage> _ => STORAGE </storage>
...
</account>
Here we load the environmental information.
rule load "env" : { KEY : ((VAL:String) => #parseWord(VAL)) }
requires KEY in (SetItem("currentTimestamp") SetItem("currentGasLimit") SetItem("currentNumber") SetItem("currentDifficulty"))
rule load "env" : { KEY : ((VAL:String) => #parseHexWord(VAL)) }
requires KEY in (SetItem("currentCoinbase") SetItem("previousHash"))
// ----------------------------------------------------------------------
rule <k> load "env" : { "currentCoinbase" : (CB:Int) } => . ... </k> <coinbase> _ => CB </coinbase>
rule <k> load "env" : { "currentDifficulty" : (DIFF:Int) } => . ... </k> <difficulty> _ => DIFF </difficulty>
rule <k> load "env" : { "currentGasLimit" : (GLIMIT:Int) } => . ... </k> <gasLimit> _ => GLIMIT </gasLimit>
rule <k> load "env" : { "currentNumber" : (NUM:Int) } => . ... </k> <number> _ => NUM </number>
rule <k> load "env" : { "previousHash" : (HASH:Int) } => . ... </k> <previousHash> _ => HASH </previousHash>
rule <k> load "env" : { "currentTimestamp" : (TS:Int) } => . ... </k> <timestamp> _ => TS </timestamp>
rule load "exec" : { KEY : ((VAL:String) => #parseWord(VAL)) }
requires KEY in (SetItem("gas") SetItem("gasPrice") SetItem("value"))
rule load "exec" : { KEY : ((VAL:String) => #parseHexWord(VAL)) }
requires KEY in (SetItem("address") SetItem("caller") SetItem("origin"))
// --------------------------------------------------------------------------
rule <k> load "exec" : { "gasPrice" : (GPRICE:Int) } => . ... </k> <gasPrice> _ => GPRICE </gasPrice>
rule <k> load "exec" : { "gas" : (GAVAIL:Int) } => . ... </k> <gas> _ => GAVAIL </gas>
rule <k> load "exec" : { "address" : (ACCTTO:Int) } => . ... </k> <id> _ => ACCTTO </id>
rule <k> load "exec" : { "caller" : (ACCTFROM:Int) } => . ... </k> <caller> _ => ACCTFROM </caller>
rule <k> load "exec" : { "gas" : (GAVAIL:Int) } => . ... </k> <gas> _ => GAVAIL </gas>
rule <k> load "exec" : { "value" : (VALUE:Int) } => . ... </k> <callValue> _ => VALUE </callValue>
rule <k> load "exec" : { "origin" : (ORIG:Int) } => . ... </k> <origin> _ => ORIG </origin>
rule <k> load "exec" : { "code" : ((CODE:String) => #parseByteStack(CODE)) } ... </k>
rule load "exec" : { "data" : ((DATA:String) => #parseByteStack(DATA)) }
// ------------------------------------------------------------------------
rule <k> load "exec" : { "data" : (DATA:WordStack) } => . ... </k> <callData> _ => DATA </callData>
rule <k> load "exec" : { "code" : (CODE:WordStack) } => . ... </k> <program> _ => #asMapOpCodes(#dasmOpCodes(CODE, SCHED)) </program> <programBytes> _ => CODE </programBytes> <schedule> SCHED </schedule>
The "network"
key allows setting the fee schedule inside the test.
rule <k> load "network" : SCHEDSTRING => . ... </k>
<schedule> _ => #asScheduleString(SCHEDSTRING) </schedule>
syntax Schedule ::= #asScheduleString ( String ) [function]
// -----------------------------------------------------------
rule #asScheduleString("EIP150") => EIP150
rule #asScheduleString("EIP158") => EIP158
rule #asScheduleString("Frontier") => FRONTIER
rule #asScheduleString("Homestead") => HOMESTEAD
rule #asScheduleString("Byzantium") => BYZANTIUM
rule #asScheduleString("Constantinople") => CONSTANTINOPLE
The "rlp"
key loads the block information.
rule load "rlp" : (VAL:String => #rlpDecode(#unparseByteStack(#parseByteStack(VAL))))
rule load "genesisRLP" : (VAL:String => #rlpDecode(#unparseByteStack(#parseByteStack(VAL))))
// --------------------------------------------------------------------------------------------
rule <k> load "rlp" : [ [ HP , HO , HC , HR , HT , HE , HB , HD , HI , HL , HG , HS , HX , HM , HN , .JSONList ] , BT , BU , .JSONList ]
=> load "transaction" : BT
...
</k>
<previousHash> _ => #asWord(#parseByteStackRaw(HP)) </previousHash>
<ommersHash> _ => #asWord(#parseByteStackRaw(HO)) </ommersHash>
<coinbase> _ => #asWord(#parseByteStackRaw(HC)) </coinbase>
<stateRoot> _ => #asWord(#parseByteStackRaw(HR)) </stateRoot>
<transactionsRoot> _ => #asWord(#parseByteStackRaw(HT)) </transactionsRoot>
<receiptsRoot> _ => #asWord(#parseByteStackRaw(HE)) </receiptsRoot>
<logsBloom> _ => #parseByteStackRaw(HB) </logsBloom>
<difficulty> _ => #asWord(#parseByteStackRaw(HD)) </difficulty>
<number> _ => #asWord(#parseByteStackRaw(HI)) </number>
<gasLimit> _ => #asWord(#parseByteStackRaw(HL)) </gasLimit>
<gasUsed> _ => #asWord(#parseByteStackRaw(HG)) </gasUsed>
<timestamp> _ => #asWord(#parseByteStackRaw(HS)) </timestamp>
<extraData> _ => #parseByteStackRaw(HX) </extraData>
<mixHash> _ => #asWord(#parseByteStackRaw(HM)) </mixHash>
<blockNonce> _ => #asWord(#parseByteStackRaw(HN)) </blockNonce>
<ommerBlockHeaders> _ => BU </ommerBlockHeaders>
rule <k> load "genesisRLP": [ [ HP, HO, HC, HR, HT, HE:String, HB, HD, HI, HL, HG, HS, HX, HM, HN, .JSONList ], _, _, .JSONList ] => .K ... </k>
<blockhash> .List => ListItem(#blockHeaderHash(HP, HO, HC, HR, HT, HE, HB, HD, HI, HL, HG, HS, HX, HM, HN)) ListItem(#asWord(#parseByteStackRaw(HP))) ... </blockhash>
rule <k> load "transaction" : [ [ TN , TP , TG , TT , TV , TI , TW , TR , TS ] , REST => REST ] ... </k>
<txOrder> ... .List => ListItem(!ID) </txOrder>
<txPending> ... .List => ListItem(!ID) </txPending>
<messages>
( .Bag
=> <message>
<msgID> !ID:Int </msgID>
<txNonce> #asWord(#parseByteStackRaw(TN)) </txNonce>
<txGasPrice> #asWord(#parseByteStackRaw(TP)) </txGasPrice>
<txGasLimit> #asWord(#parseByteStackRaw(TG)) </txGasLimit>
<to> #asAccount(#parseByteStackRaw(TT)) </to>
<value> #asWord(#parseByteStackRaw(TV)) </value>
<v> #asWord(#parseByteStackRaw(TW)) </v>
<r> #padToWidth(32, #parseByteStackRaw(TR)) </r>
<s> #padToWidth(32, #parseByteStackRaw(TS)) </s>
<data> #parseByteStackRaw(TI) </data>
</message>
)
...
</messages>
Checking State¶
check_
checks if an account/transaction appears in the world-state as stated.
syntax EthereumCommand ::= "check" JSON
// ---------------------------------------
rule #exception ~> check J:JSON => check J ~> #exception
rule check DATA : { .JSONList } => . requires DATA =/=String "transactions"
rule check DATA : { (KEY:String) : VALUE , REST } => check DATA : { KEY : VALUE } ~> check DATA : { REST }
requires REST =/=K .JSONList andBool notBool DATA in (SetItem("callcreates") SetItem("transactions"))
rule check DATA : [ .JSONList ] => . requires DATA =/=String "ommerHeaders"
rule check DATA : [ { TEST } , REST ] => check DATA : { TEST } ~> check DATA : [ REST ] requires DATA =/=String "transactions"
rule check (KEY:String) : { JS:JSONList => #sortJSONList(JS) }
requires KEY in (SetItem("callcreates")) andBool notBool #isSorted(JS)
rule check TESTID : { "post" : POST } => check "account" : POST ~> failure TESTID
rule check "account" : { ACCTID: { KEY : VALUE , REST } } => check "account" : { ACCTID : { KEY : VALUE } } ~> check "account" : { ACCTID : { REST } } requires REST =/=K .JSONList
// -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
rule check "account" : { ((ACCTID:String) => #parseAddr(ACCTID)) : ACCT }
rule check "account" : { (ACCT:Int) : { "balance" : ((VAL:String) => #parseWord(VAL)) } }
rule check "account" : { (ACCT:Int) : { "nonce" : ((VAL:String) => #parseWord(VAL)) } }
rule check "account" : { (ACCT:Int) : { "code" : ((CODE:String) => #parseByteStack(CODE)) } }
rule check "account" : { (ACCT:Int) : { "storage" : ({ STORAGE:JSONList } => #parseMap({ STORAGE })) } }
rule <k> check "account" : { ACCT : { "balance" : (BAL:Int) } } => . ... </k>
<account>
<acctID> ACCT </acctID>
<balance> BAL </balance>
...
</account>
rule <k> check "account" : { ACCT : { "nonce" : (NONCE:Int) } } => . ... </k>
<account>
<acctID> ACCT </acctID>
<nonce> NONCE </nonce>
...
</account>
rule <k> check "account" : { ACCT : { "storage" : (STORAGE:Map) } } => . ... </k>
<account>
<acctID> ACCT </acctID>
<storage> ACCTSTORAGE </storage>
...
</account>
requires #removeZeros(ACCTSTORAGE) ==K STORAGE
rule <k> check "account" : { ACCT : { "code" : (CODE:WordStack) } } => . ... </k>
<account>
<acctID> ACCT </acctID>
<code> CODE </code>
...
</account>
Here we check the other post-conditions associated with an EVM test.
rule check TESTID : { "out" : OUT } => check "out" : OUT ~> failure TESTID
// --------------------------------------------------------------------------
rule check "out" : ((OUT:String) => #parseByteStack(OUT))
rule <k> check "out" : OUT => . ... </k> <output> OUT </output>
rule check TESTID : { "logs" : LOGS } => check "logs" : LOGS ~> failure TESTID
// ------------------------------------------------------------------------------
rule <k> check "logs" : HASH:String => . ... </k> <log> SL </log> requires #parseHexBytes(Keccak256(#rlpEncodeLogs(SL))) ==K #parseByteStack(HASH)
syntax String ::= #rlpEncodeLogs(List) [function]
| #rlpEncodeLogsAux(List) [function]
| #rlpEncodeTopics(WordStack) [function]
// --------------------------------------------------------
rule #rlpEncodeLogs(SL) => #rlpEncodeLength(#rlpEncodeLogsAux(SL), 192)
rule #rlpEncodeLogsAux(ListItem({ ACCT | TOPICS | DATA }) SL) => #rlpEncodeLength(#rlpEncodeBytes(ACCT, 20) +String #rlpEncodeLength(#rlpEncodeTopics(TOPICS), 192) +String #rlpEncodeString(#unparseByteStack(DATA)), 192) +String #rlpEncodeLogsAux(SL)
rule #rlpEncodeLogsAux(.List) => ""
rule #rlpEncodeTopics(TOPIC : TOPICS) => #rlpEncodeBytes(TOPIC, 32) +String #rlpEncodeTopics(TOPICS)
rule #rlpEncodeTopics(.WordStack) => ""
rule check TESTID : { "gas" : GLEFT } => check "gas" : GLEFT ~> failure TESTID
// ------------------------------------------------------------------------------
rule check "gas" : ((GLEFT:String) => #parseWord(GLEFT))
rule <k> check "gas" : GLEFT => . ... </k> <gas> GLEFT </gas>
rule check TESTID : { "callcreates" : CCREATES } => check "callcreates" : CCREATES ~> failure TESTID
// ----------------------------------------------------------------------------------------------------
rule check "callcreates" : { ("data" : (DATA:String)) , ("destination" : (ACCTTO:String)) , ("gasLimit" : (GLIMIT:String)) , ("value" : (VAL:String)) , .JSONList }
=> check "callcreates" : { #parseAddr(ACCTTO) | #parseWord(GLIMIT) | #parseWord(VAL) | #parseByteStack(DATA) }
rule <k> check "callcreates" : C:Call => . ... </k> <callLog> CL </callLog> requires C in CL
rule check TESTID : { "blockHeader" : BLOCKHEADER } => check "blockHeader" : BLOCKHEADER ~> failure TESTID
// ----------------------------------------------------------------------------------------------------------
rule check "blockHeader" : { KEY : VALUE , REST } => check "blockHeader" : { KEY : VALUE } ~> check "blockHeader" : { REST } requires REST =/=K .JSONList
rule check "blockHeader" : { KEY : (VALUE:String => #parseByteStack(VALUE)) }
rule check "blockHeader" : { KEY : (VALUE:WordStack => #asWord(VALUE)) }
requires KEY in ( SetItem("coinbase") SetItem("difficulty") SetItem("gasLimit") SetItem("gasUsed")
SetItem("mixHash") SetItem("nonce") SetItem("number") SetItem("parentHash")
SetItem("receiptTrie") SetItem("stateRoot") SetItem("timestamp")
SetItem("transactionsTrie") SetItem("uncleHash")
)
rule <k> check "blockHeader" : { "bloom" : VALUE } => . ... </k> <logsBloom> VALUE </logsBloom>
rule <k> check "blockHeader" : { "coinbase" : VALUE } => . ... </k> <coinbase> VALUE </coinbase>
rule <k> check "blockHeader" : { "difficulty" : VALUE } => . ... </k> <difficulty> VALUE </difficulty>
rule <k> check "blockHeader" : { "extraData" : VALUE } => . ... </k> <extraData> VALUE </extraData>
rule <k> check "blockHeader" : { "gasLimit" : VALUE } => . ... </k> <gasLimit> VALUE </gasLimit>
rule <k> check "blockHeader" : { "gasUsed" : VALUE } => . ... </k> <gasUsed> VALUE </gasUsed>
rule <k> check "blockHeader" : { "mixHash" : VALUE } => . ... </k> <mixHash> VALUE </mixHash>
rule <k> check "blockHeader" : { "nonce" : VALUE } => . ... </k> <blockNonce> VALUE </blockNonce>
rule <k> check "blockHeader" : { "number" : VALUE } => . ... </k> <number> VALUE </number>
rule <k> check "blockHeader" : { "parentHash" : VALUE } => . ... </k> <previousHash> VALUE </previousHash>
rule <k> check "blockHeader" : { "receiptTrie" : VALUE } => . ... </k> <receiptsRoot> VALUE </receiptsRoot>
rule <k> check "blockHeader" : { "stateRoot" : VALUE } => . ... </k> <stateRoot> VALUE </stateRoot>
rule <k> check "blockHeader" : { "timestamp" : VALUE } => . ... </k> <timestamp> VALUE </timestamp>
rule <k> check "blockHeader" : { "transactionsTrie" : VALUE } => . ... </k> <transactionsRoot> VALUE </transactionsRoot>
rule <k> check "blockHeader" : { "uncleHash" : VALUE } => . ... </k> <ommersHash> VALUE </ommersHash>
rule <k> check "blockHeader" : { "hash": HASH:WordStack } => . ...</k>
<previousHash> HP </previousHash>
<ommersHash> HO </ommersHash>
<coinbase> HC </coinbase>
<stateRoot> HR </stateRoot>
<transactionsRoot> HT </transactionsRoot>
<receiptsRoot> HE </receiptsRoot>
<logsBloom> HB </logsBloom>
<difficulty> HD </difficulty>
<number> HI </number>
<gasLimit> HL </gasLimit>
<gasUsed> HG </gasUsed>
<timestamp> HS </timestamp>
<extraData> HX </extraData>
<mixHash> HM </mixHash>
<blockNonce> HN </blockNonce>
requires #blockHeaderHash(HP, HO, HC, HR, HT, HE, HB, HD, HI, HL, HG, HS, HX, HM, HN) ==Int #asWord(HASH)
rule check TESTID : { "genesisBlockHeader" : BLOCKHEADER } => check "genesisBlockHeader" : BLOCKHEADER ~> failure TESTID
// ------------------------------------------------------------------------------------------------------------------------
rule check "genesisBlockHeader" : { KEY : VALUE , REST } => check "genesisBlockHeader" : { KEY : VALUE } ~> check "genesisBlockHeader" : { REST } requires REST =/=K .JSONList
rule check "genesisBlockHeader" : { KEY : VALUE } => .K requires KEY =/=String "hash"
rule check "genesisBlockHeader" : { "hash": (HASH:String => #asWord(#parseByteStack(HASH))) }
rule <k> check "genesisBlockHeader" : { "hash": HASH } => . ... </k>
<blockhash> ... ListItem(HASH) ListItem(_) </blockhash>
rule check TESTID : { "transactions" : TRANSACTIONS } => check "transactions" : TRANSACTIONS ~> failure TESTID
// --------------------------------------------------------------------------------------------------------------
rule <k> check "transactions" : [ .JSONList ] => . ... </k> <txOrder> .List </txOrder>
rule check "transactions" : [ TRANSACTION , REST ] => check "transactions" : TRANSACTION ~> check "transactions" : [ REST ]
rule check "transactions" : { KEY : VALUE , REST } => check "transactions" : (KEY : VALUE) ~> check "transactions" : { REST }
rule <k> check "transactions" : { .JSONList } => . ... </k> <txOrder> ListItem(_) => .List ... </txOrder>
rule check "transactions" : (KEY : (VALUE:String => #parseByteStack(VALUE)))
rule check "transactions" : (KEY : (VALUE:WordStack => #padToWidth(32, VALUE))) requires (KEY ==String "r" orBool KEY ==String "s") andBool #sizeWordStack(VALUE) <Int 32
rule check "transactions" : ("to" : (VALUE:WordStack => #asAccount(VALUE)))
rule check "transactions" : (KEY : (VALUE:WordStack => #asWord(VALUE)))
requires KEY in ( SetItem("gasLimit") SetItem("gasPrice") SetItem("nonce")
SetItem("v") SetItem("value")
)
rule <k> check "transactions" : ("data" : VALUE) => . ... </k> <txOrder> ListItem(TXID) ... </txOrder> <message> <msgID> TXID </msgID> <data> VALUE </data> ... </message>
rule <k> check "transactions" : ("gasLimit" : VALUE) => . ... </k> <txOrder> ListItem(TXID) ... </txOrder> <message> <msgID> TXID </msgID> <txGasLimit> VALUE </txGasLimit> ... </message>
rule <k> check "transactions" : ("gasPrice" : VALUE) => . ... </k> <txOrder> ListItem(TXID) ... </txOrder> <message> <msgID> TXID </msgID> <txGasPrice> VALUE </txGasPrice> ... </message>
rule <k> check "transactions" : ("nonce" : VALUE) => . ... </k> <txOrder> ListItem(TXID) ... </txOrder> <message> <msgID> TXID </msgID> <txNonce> VALUE </txNonce> ... </message>
rule <k> check "transactions" : ("r" : VALUE) => . ... </k> <txOrder> ListItem(TXID) ... </txOrder> <message> <msgID> TXID </msgID> <r> VALUE </r> ... </message>
rule <k> check "transactions" : ("s" : VALUE) => . ... </k> <txOrder> ListItem(TXID) ... </txOrder> <message> <msgID> TXID </msgID> <s> VALUE </s> ... </message>
rule <k> check "transactions" : ("to" : VALUE) => . ... </k> <txOrder> ListItem(TXID) ... </txOrder> <message> <msgID> TXID </msgID> <to> VALUE </to> ... </message>
rule <k> check "transactions" : ("v" : VALUE) => . ... </k> <txOrder> ListItem(TXID) ... </txOrder> <message> <msgID> TXID </msgID> <v> VALUE </v> ... </message>
rule <k> check "transactions" : ("value" : VALUE) => . ... </k> <txOrder> ListItem(TXID) ... </txOrder> <message> <msgID> TXID </msgID> <value> VALUE </value> ... </message>
TODO: case with nonzero ommers.
rule check TESTID : { "uncleHeaders" : OMMERS } => check "ommerHeaders" : OMMERS ~> failure TESTID
// --------------------------------------------------------------------------------------------------
rule <k> check "ommerHeaders" : [ .JSONList ] => . ... </k> <ommerBlockHeaders> [ .JSONList ] </ommerBlockHeaders>
endmodule