diff --git a/tests/web3/firefly_getCoverageData0.expected.json b/tests/web3/firefly_getCoverageData0.expected.json new file mode 100644 index 0000000000..b58e545cbd --- /dev/null +++ b/tests/web3/firefly_getCoverageData0.expected.json @@ -0,0 +1 @@ +[{"jsonrpc":"2.0","id":1,"result":true},{"jsonrpc":"2.0","id":2,"result":true},{"jsonrpc":"2.0","id":3,"result":"0x0000000000000000000000000000000000000000000000000000000000ffffff"},{"jsonrpc":"2.0","id":3,"result":{"coverages":[{"93758614234106549757282766802448384239213770766359592192750614820519184033004":{"RUNTIME":100}}],"coveredOpcodes":[{"93758614234106549757282766802448384239213770766359592192750614820519184033004":{"RUNTIME":[0,4,6,7,9,11]}}],"programs":[{"93758614234106549757282766802448384239213770766359592192750614820519184033004":{"RUNTIME":[0,4,6,7,9,11]}}]}}] \ No newline at end of file diff --git a/tests/web3/firefly_getCoverageData0.in.json b/tests/web3/firefly_getCoverageData0.in.json new file mode 100644 index 0000000000..187542c6f1 --- /dev/null +++ b/tests/web3/firefly_getCoverageData0.in.json @@ -0,0 +1,42 @@ +[ + { + "jsonrpc": "2.0", + "id": 1, + "method": "firefly_addAccount", + "params": [ + { + "key": "0x5fffe36d0218b8b8d45d4e70788096a5e0c289cf89c907f97d1cdecdccaa8b6b", + "balance": "0x56BC75E2D63100000" + } + ] + }, + { + "jsonrpc": "2.0", + "id": 2, + "method": "firefly_addAccount", + "params": [ + { + "address": "0xdf1db454e4dc226da2b0f8d471976180555355e2", + "code": "0x62FFFFFF60005260206000F3" + } + ] + }, + { + "jsonrpc": "2.0", + "id": 3, + "method": "eth_call", + "params": [ + { + "from": "0xee70809B448816B3D2D40C1DB8B44aACf50BAD3a", + "to": "0xdf1db454e4dc226da2b0f8d471976180555355e2" + }, + "latest" + ] + }, + { + "jsonrpc": "2.0", + "id": 3, + "method": "firefly_getCoverageData", + "params": [] + } +] \ No newline at end of file diff --git a/web3.md b/web3.md index af2b6bd659..51923dd356 100644 --- a/web3.md +++ b/web3.md @@ -315,6 +315,8 @@ WEB3 JSON RPC "eth_call" rule #runRPCCall => #eth_estimateGas ... "eth_estimateGas" + rule #runRPCCall => #firefly_getCoverageData ... + "firefly_getCoverageData" rule #runRPCCall => #sendResponse( "error": {"code": -32601, "message": "Method not found"} ) ... [owise] @@ -1072,6 +1074,10 @@ Collecting Coverage Data - `` cell is a map similar to `` which stores instead a list containing all the `OpcodeItem`s of the executed bytecode for each contract. - `OpcodeItem` is a tuple which contains the Program Counter and the Opcode name. +**TODO**: instead of having both `#serializeCoverage` and `#serializePrograms` we could keep only the first rule as `#serializeCoverageMap` if `` would store `Sets` instead of `Lists`. +**TODO**: compute coverage percentages in `Float` instead of `Int` +**TODO**: `Set2List` won't return `ListItems` in order, causing tests to fail. + ```k syntax Phase ::= ".Phase" | "CONSTRUCTOR" @@ -1126,5 +1132,69 @@ Collecting Coverage Data requires notBool PCOUNT in PCS [priority(25)] + syntax KItem ::= "#firefly_getCoverageData" + // ------------------------------------------- + rule #firefly_getCoverageData => #sendResponse ("result": #makeCoverageReport(COVERAGE, PGMS)) ... + COVERAGE + PGMS + + syntax JSON ::= #makeCoverageReport ( Map, Map ) [function] + // ----------------------------------------------------------- + rule #makeCoverageReport (COVERAGE, PGMS) => { + "coverages": [#coveragePercentages(keys_list(PGMS),COVERAGE,PGMS)], + "coveredOpcodes": [#serializeCoverage(keys_list(COVERAGE),COVERAGE)], + "programs": [#serializePrograms(keys_list(PGMS),PGMS)] + } + + syntax JSONList ::= #serializeCoverage ( List, Map ) [function] + // --------------------------------------------------------------- + rule #serializeCoverage (.List, _ ) => .JSONList + rule #serializeCoverage ((ListItem({ CODEHASH | EPHASE } #as KEY) KEYS), KEY |-> X:Set COVERAGE:Map ) => { Int2String(CODEHASH):{ Phase2String(EPHASE): [IntList2JSONList(qsort(Set2List(X)))] }}, #serializeCoverage(KEYS, COVERAGE) + + syntax JSONList ::= #serializePrograms ( List, Map ) [function] + // --------------------------------------------------------------- + rule #serializePrograms (.List, _ ) => .JSONList + rule #serializePrograms ((ListItem({ CODEHASH | EPHASE } #as KEY) KEYS), KEY |-> X:List PGMS:Map ) => { Int2String(CODEHASH):{ Phase2String(EPHASE): [CoverageIDList2JSONList(X)] }}, #serializePrograms(KEYS, PGMS) + + syntax String ::= Phase2String ( Phase ) [function] + // ---------------------------------------------------- + rule Phase2String (CONSTRUCTOR) => "CONSTRUCTOR" + rule Phase2String (RUNTIME) => "RUNTIME" + + syntax JSONList ::= CoverageIDList2JSONList ( List ) [function] + // --------------------------------------------------------------- + rule CoverageIDList2JSONList (.List) => .JSONList + rule CoverageIDList2JSONList (ListItem({I:Int | _:OpCode }) L) => I, CoverageIDList2JSONList(L) + + syntax JSONList ::= IntList2JSONList ( List ) [function] + // -------------------------------------------------------- + rule IntList2JSONList (.List) => .JSONList + rule IntList2JSONList (ListItem(I:Int) L) => I, IntList2JSONList(L) + + syntax List ::= getIntElementsSmallerThan ( Int, List, List ) [function] + // ------------------------------------------------------------------------ + rule getIntElementsSmallerThan (_, .List, RESULTS) => RESULTS + rule getIntElementsSmallerThan (X, (ListItem(I:Int) L), RESULTS) => getIntElementsSmallerThan (X, L, ListItem(I) RESULTS) requires I getIntElementsSmallerThan (X, L, RESULTS) requires I >=Int X + + syntax List ::= getIntElementsGreaterThan ( Int, List, List ) [function] + // ------------------------------------------------------------------------ + rule getIntElementsGreaterThan (_, .List , RESULTS) => RESULTS + rule getIntElementsGreaterThan (X, (ListItem(I:Int) L), RESULTS) => getIntElementsGreaterThan (X, L, ListItem(I) RESULTS) requires I >Int X + rule getIntElementsGreaterThan (X, (ListItem(I:Int) L), RESULTS) => getIntElementsGreaterThan (X, L, RESULTS) requires I <=Int X + + syntax List ::= qsort ( List ) [function] + // ----------------------------------------- + rule qsort ( .List ) => .List + rule qsort (ListItem(I:Int) L) => qsort(getIntElementsSmallerThan(I, L, .List)) ListItem(I) qsort(getIntElementsGreaterThan(I, L, .List)) + + syntax JSONList ::= #coveragePercentages ( List, Map, Map) [function] + // --------------------------------------------------------------------- + rule #coveragePercentages (.List, _, _) => .JSONList + rule #coveragePercentages ((ListItem({ CODEHASH | EPHASE } #as KEY) KEYS), KEY |-> X:Set COVERAGE:Map, KEY |-> Y:List PGMS:Map) => { Int2String(CODEHASH):{ Phase2String(EPHASE): #computePercentage(size(X),size(Y)) }}, #coveragePercentages(KEYS,COVERAGE,PGMS) + + syntax Int ::= #computePercentage ( Int, Int ) [function] + // --------------------------------------------------------- + rule #computePercentage (EXECUTED, TOTAL) => (100 *Int EXECUTED) /Int TOTAL endmodule -``` \ No newline at end of file +```