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 `__. .. code-block:: k requires "verification.k" .. code-block:: k requires "evm.k" requires "analysis.k" module ETHEREUM-SIMULATION imports EVM imports EVM-ANALYSIS .. code-block:: k imports VERIFICATION .. code-block:: k 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). .. code-block:: k syntax EthereumSimulation ::= ".EthereumSimulation" | EthereumCommand EthereumSimulation // ---------------------------------------------------------------- rule .EthereumSimulation => . rule ETC:EthereumCommand ETS:EthereumSimulation => ETC ~> ETS syntax EthereumSimulation ::= JSON // ---------------------------------- rule JSONINPUT:JSON => run JSONINPUT success .EthereumSimulation 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. .. code-block:: k 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 (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 ```` cell so that execution of the loaded state begin. - ``flush`` places ``#finalize`` on the ```` cell once it sees ``#end`` in the ```` cell, clearing any exceptions it finds. .. code-block:: k syntax EthereumCommand ::= "start" // ---------------------------------- rule NORMAL start => #execute ... rule VMTESTS start => #execute ... rule GASANALYZE start => #gasAnalyze ... syntax EthereumCommand ::= "flush" // ---------------------------------- rule #end ~> flush => #finalizeTx(false) ... rule #exception ~> flush => #finalizeTx(false) ~> #exception ... - ``startTx`` computes the sender of the transaction, and places loadTx on the ``k`` 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. .. code-block:: k syntax EthereumCommand ::= "startTx" // ------------------------------------ rule startTx => #finalizeBlock ... .List rule startTx => loadTx(#sender(TN, TP, TG, TT, TV, #unparseByteStack(DATA), TW, TR, TS)) ... ListItem(TXID:Int) ... TXID TN TP TG TT TV TW TR TS DATA syntax EthereumCommand ::= loadTx ( Int ) // ----------------------------------------- rule loadTx(ACCTFROM) => #create ACCTFROM #newAddr(ACCTFROM, NONCE) (GLIMIT -Int G0(SCHED, CODE, true)) VALUE CODE ~> #execute ~> #finishTx ~> #finalizeTx(false) ~> startTx ... SCHED _ => GPRICE _ => ACCTFROM _ => -1 ListItem(TXID:Int) ... TXID GPRICE GLIMIT .Account VALUE CODE ... ACCTFROM BAL => BAL -Int (GLIMIT *Int GPRICE) NONCE => NONCE +Int 1 ... ... ACCTFROM |-> (_ => false) ... rule loadTx(ACCTFROM) => #call ACCTFROM ACCTTO ACCTTO (GLIMIT -Int G0(SCHED, DATA, false)) VALUE VALUE DATA ~> #execute ~> #finishTx ~> #finalizeTx(false) ~> startTx ... SCHED _ => GPRICE _ => ACCTFROM _ => -1 ListItem(TXID:Int) ... TXID GPRICE GLIMIT ACCTTO VALUE DATA ... ACCTFROM BAL => BAL -Int (GLIMIT *Int GPRICE) NONCE => NONCE +Int 1 ... ... ACCTFROM |-> (_ => false) ... requires ACCTTO =/=K .Account syntax EthereumCommand ::= "#finishTx" // -------------------------------------- rule #exception ~> #finishTx => #popCallStack ~> #popWorldState ~> #popSubstate ... rule #end ~> #finishTx => #mkCodeDeposit ACCT ... ACCT ListItem(TXID:Int) ... TXID .Account ... rule #end ~> #finishTx => #popCallStack ~> #dropWorldState ~> #dropSubstate ~> #refund GAVAIL ... ACCT GAVAIL ListItem(TXID:Int) ... TXID TT ... 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. .. code-block:: k syntax EthereumCommand ::= "#finalizeBlock" | #rewardOmmers ( JSONList ) // ------------------------------------------------------------------------ rule #finalizeBlock => #rewardOmmers(OMMERS) ... [ OMMERS ] MINER MINER MINBAL => MINBAL +Int Rb ... ... MINER |-> (_ => false) ... rule (.K => #newAccount MINER) ~> #finalizeBlock ... MINER ACCTS requires notBool MINER in_keys(ACCTS) rule #rewardOmmers(.JSONList) => . ... rule #rewardOmmers([ _ , _ , OMMER , _ , _ , _ , _ , _ , OMMNUM , _ ] , REST) => #rewardOmmers(REST) ... MINER CURNUM MINER MINBAL => MINBAL +Int Rb /Int 32 ... OMMER OMMBAL => OMMBAL +Int Rb +Int (OMMNUM -Int CURNUM) *Int (Rb /Int 8) ... ... MINER |-> (_ => false) OMMER |-> (_ => false) ... syntax Int ::= "Rb" [function] // ------------------------------ rule Rb => 5 *Int (10 ^Int 18) - ``exception`` only clears from the ```` cell if there is an exception preceding it. - ``failure_`` holds the name of a test that failed if a test does fail. .. code-block:: k syntax EthereumCommand ::= "exception" | "failure" String | "success" // --------------------------------------------------------------------- rule #exception ~> exception => . ... rule success => . ... _ => 0 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"``. .. code-block:: k 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. .. code-block:: k 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). .. code-block:: k 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. .. code-block:: k 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. .. code-block:: k 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 substate ``X``, for ``TX``, ``BLOCK``, and ``NETWORK``. .. code-block:: k syntax EthereumCommand ::= "clear" // ---------------------------------- rule clear => clearTX ~> clearBLOCK ~> clearNETWORK ... _ => .Map syntax EthreumCommand ::= "clearTX" // ----------------------------------- rule clearTX => . ... _ => .WordStack _ => 0 _ => 0 _ => .List _ => .Set _ => .Map _ => .WordStack _ => 0 _ => 0 _ => .WordStack _ => 0 _ => .WordStack _ => .Map _ => 0 _ => 0 _ => 0 _ => .Set _ => .List _ => 0 _ => 0 _ => 0 syntax EthreumCommand ::= "clearBLOCK" // -------------------------------------- rule clearBLOCK => . ... _ => 0 _ => 0 _ => 0 _ => 0 _ => 0 _ => 0 _ => .WordStack _ => 0 _ => 0 _ => 0 _ => 0 _ => 0 _ => .WordStack _ => 0 _ => 0 _ => [ .JSONList ] _ => .List syntax EthreumCommand ::= "clearNETWORK" // ---------------------------------------- rule clearNETWORK => . ... _ => .Map _ => .Bag _ => .Bag _ => DEFAULT Loading State ~~~~~~~~~~~~~ - ``mkAcct_`` creates an account with the supplied ID (assuming it's already been chopped to 160 bits). .. code-block:: k syntax EthereumCommand ::= "mkAcct" Int // --------------------------------------- rule mkAcct ACCT => #newAccount ACCT ... - ``load`` loads an account or transaction into the world state. .. code-block:: k 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. .. code-block:: k 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. .. code-block:: k rule load "account" : { ACCT : { "balance" : (BAL:Int) } } => . ... ACCT _ => BAL ... ... ACCT |-> (EMPTY => #if BAL =/=Int 0 #then false #else EMPTY #fi) ... rule load "account" : { ACCT : { "code" : (CODE:WordStack) } } => . ... ACCT _ => CODE ... ... ACCT |-> (EMPTY => #if CODE =/=K .WordStack #then false #else EMPTY #fi) ... rule load "account" : { ACCT : { "nonce" : (NONCE:Int) } } => . ... ACCT _ => NONCE ... ... ACCT |-> (EMPTY => #if NONCE =/=Int 0 #then false #else EMPTY #fi) ... rule load "account" : { ACCT : { "storage" : (STORAGE:Map) } } => . ... ACCT _ => STORAGE ... Here we load the environmental information. .. code-block:: k 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 load "env" : { "currentCoinbase" : (CB:Int) } => . ... _ => CB rule load "env" : { "currentDifficulty" : (DIFF:Int) } => . ... _ => DIFF rule load "env" : { "currentGasLimit" : (GLIMIT:Int) } => . ... _ => GLIMIT rule load "env" : { "currentNumber" : (NUM:Int) } => . ... _ => NUM rule load "env" : { "previousHash" : (HASH:Int) } => . ... _ => HASH rule load "env" : { "currentTimestamp" : (TS:Int) } => . ... _ => TS 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 load "exec" : { "gasPrice" : (GPRICE:Int) } => . ... _ => GPRICE rule load "exec" : { "gas" : (GAVAIL:Int) } => . ... _ => GAVAIL rule load "exec" : { "address" : (ACCTTO:Int) } => . ... _ => ACCTTO rule load "exec" : { "caller" : (ACCTFROM:Int) } => . ... _ => ACCTFROM rule load "exec" : { "gas" : (GAVAIL:Int) } => . ... _ => GAVAIL rule load "exec" : { "value" : (VALUE:Int) } => . ... _ => VALUE rule load "exec" : { "origin" : (ORIG:Int) } => . ... _ => ORIG rule load "exec" : { "code" : ((CODE:String) => #parseByteStack(CODE)) } ... rule load "exec" : { "data" : ((DATA:String) => #parseByteStack(DATA)) } // ------------------------------------------------------------------------ rule load "exec" : { "data" : (DATA:WordStack) } => . ... _ => DATA rule load "exec" : { "code" : (CODE:WordStack) } => . ... _ => #asMapOpCodes(#dasmOpCodes(CODE, SCHED)) _ => CODE SCHED The ``"network"`` key allows setting the fee schedule inside the test. .. code-block:: k rule load "network" : SCHEDSTRING => . ... _ => #asScheduleString(SCHEDSTRING) 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. .. code-block:: k rule load "rlp" : (VAL:String => #rlpDecode(#unparseByteStack(#parseByteStack(VAL)))) rule load "genesisRLP" : (VAL:String => #rlpDecode(#unparseByteStack(#parseByteStack(VAL)))) // -------------------------------------------------------------------------------------------- rule load "rlp" : [ [ HP , HO , HC , HR , HT , HE , HB , HD , HI , HL , HG , HS , HX , HM , HN , .JSONList ] , BT , BU , .JSONList ] => load "transaction" : BT ... _ => #asWord(#parseByteStackRaw(HP)) _ => #asWord(#parseByteStackRaw(HO)) _ => #asWord(#parseByteStackRaw(HC)) _ => #asWord(#parseByteStackRaw(HR)) _ => #asWord(#parseByteStackRaw(HT)) _ => #asWord(#parseByteStackRaw(HE)) _ => #parseByteStackRaw(HB) _ => #asWord(#parseByteStackRaw(HD)) _ => #asWord(#parseByteStackRaw(HI)) _ => #asWord(#parseByteStackRaw(HL)) _ => #asWord(#parseByteStackRaw(HG)) _ => #asWord(#parseByteStackRaw(HS)) _ => #parseByteStackRaw(HX) _ => #asWord(#parseByteStackRaw(HM)) _ => #asWord(#parseByteStackRaw(HN)) _ => BU rule load "genesisRLP": [ [ HP, HO, HC, HR, HT, HE:String, HB, HD, HI, HL, HG, HS, HX, HM, HN, .JSONList ], _, _, .JSONList ] => .K ... .List => ListItem(#blockHeaderHash(HP, HO, HC, HR, HT, HE, HB, HD, HI, HL, HG, HS, HX, HM, HN)) ListItem(#asWord(#parseByteStackRaw(HP))) ... rule load "transaction" : [ [ TN , TP , TG , TT , TV , TI , TW , TR , TS ] , REST => REST ] ... ... .List => ListItem(!ID) ... .List => ListItem(!ID) ( .Bag => !ID:Int #asWord(#parseByteStackRaw(TN)) #asWord(#parseByteStackRaw(TP)) #asWord(#parseByteStackRaw(TG)) #asAccount(#parseByteStackRaw(TT)) #asWord(#parseByteStackRaw(TV)) #asWord(#parseByteStackRaw(TW)) #padToWidth(32, #parseByteStackRaw(TR)) #padToWidth(32, #parseByteStackRaw(TS)) #parseByteStackRaw(TI) ) ... Checking State ~~~~~~~~~~~~~~ - ``check_`` checks if an account/transaction appears in the world-state as stated. .. code-block:: k 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 check "account" : { ACCT : { "balance" : (BAL:Int) } } => . ... ACCT BAL ... rule check "account" : { ACCT : { "nonce" : (NONCE:Int) } } => . ... ACCT NONCE ... rule check "account" : { ACCT : { "storage" : (STORAGE:Map) } } => . ... ACCT ACCTSTORAGE ... requires #removeZeros(ACCTSTORAGE) ==K STORAGE rule check "account" : { ACCT : { "code" : (CODE:WordStack) } } => . ... ACCT CODE ... Here we check the other post-conditions associated with an EVM test. .. code-block:: k rule check TESTID : { "out" : OUT } => check "out" : OUT ~> failure TESTID // -------------------------------------------------------------------------- rule check "out" : ((OUT:String) => #parseByteStack(OUT)) rule check "out" : OUT => . ... OUT rule check TESTID : { "logs" : LOGS } => check "logs" : LOGS ~> failure TESTID // ------------------------------------------------------------------------------ rule check "logs" : HASH:String => . ... SL 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 check "gas" : GLEFT => . ... GLEFT 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 check "callcreates" : C:Call => . ... CL 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 check "blockHeader" : { "bloom" : VALUE } => . ... VALUE rule check "blockHeader" : { "coinbase" : VALUE } => . ... VALUE rule check "blockHeader" : { "difficulty" : VALUE } => . ... VALUE rule check "blockHeader" : { "extraData" : VALUE } => . ... VALUE rule check "blockHeader" : { "gasLimit" : VALUE } => . ... VALUE rule check "blockHeader" : { "gasUsed" : VALUE } => . ... VALUE rule check "blockHeader" : { "mixHash" : VALUE } => . ... VALUE rule check "blockHeader" : { "nonce" : VALUE } => . ... VALUE rule check "blockHeader" : { "number" : VALUE } => . ... VALUE rule check "blockHeader" : { "parentHash" : VALUE } => . ... VALUE rule check "blockHeader" : { "receiptTrie" : VALUE } => . ... VALUE rule check "blockHeader" : { "stateRoot" : VALUE } => . ... VALUE rule check "blockHeader" : { "timestamp" : VALUE } => . ... VALUE rule check "blockHeader" : { "transactionsTrie" : VALUE } => . ... VALUE rule check "blockHeader" : { "uncleHash" : VALUE } => . ... VALUE rule check "blockHeader" : { "hash": HASH:WordStack } => . ... HP HO HC HR HT HE HB HD HI HL HG HS HX HM HN 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 check "genesisBlockHeader" : { "hash": HASH } => . ... ... ListItem(HASH) ListItem(_) rule check TESTID : { "transactions" : TRANSACTIONS } => check "transactions" : TRANSACTIONS ~> failure TESTID // -------------------------------------------------------------------------------------------------------------- rule check "transactions" : [ .JSONList ] => . ... .List 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 check "transactions" : { .JSONList } => . ... ListItem(_) => .List ... 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) #asAccount(VALUE))) rule check "transactions" : (KEY : (VALUE:WordStack => #asWord(VALUE))) requires KEY in ( SetItem("gasLimit") SetItem("gasPrice") SetItem("nonce") SetItem("v") SetItem("value") ) rule check "transactions" : ("data" : VALUE) => . ... ListItem(TXID) ... TXID VALUE ... rule check "transactions" : ("gasLimit" : VALUE) => . ... ListItem(TXID) ... TXID VALUE ... rule check "transactions" : ("gasPrice" : VALUE) => . ... ListItem(TXID) ... TXID VALUE ... rule check "transactions" : ("nonce" : VALUE) => . ... ListItem(TXID) ... TXID VALUE ... rule check "transactions" : ("r" : VALUE) => . ... ListItem(TXID) ... TXID VALUE ... rule check "transactions" : ("s" : VALUE) => . ... ListItem(TXID) ... TXID VALUE ... rule check "transactions" : ("to" : VALUE) => . ... ListItem(TXID) ... TXID VALUE ... rule check "transactions" : ("v" : VALUE) => . ... ListItem(TXID) ... TXID VALUE ... rule check "transactions" : ("value" : VALUE) => . ... ListItem(TXID) ... TXID VALUE ... TODO: case with nonzero ommers. .. code-block:: k rule check TESTID : { "uncleHeaders" : OMMERS } => check "ommerHeaders" : OMMERS ~> failure TESTID // -------------------------------------------------------------------------------------------------- rule check "ommerHeaders" : [ .JSONList ] => . ... [ .JSONList ] endmodule