Skip to content

Commit 615db59

Browse files
gtreptaehildenb
authored andcommitted
Start implementing semantics for a Web3 RPC server (#427)
* Skeleton code for Web3 RPC implementation * Implement net_version in web3 * Remove example file * Fix command for kevm interpret * Add top cell and fix formatting * rename web3-json-rpc.md to web3.md * rename WEB3-JSON-RPC to WEB3 * Change control flow to handle multiple calls + optimize * web3.md: Add eth_gasPrice * web3.md: Add eth_blockNumber * web3.md: Add eth_accounts * web3.md: Add eth_getBalance * data.md, web3.md: Add and use handlers for RPC value encodings * data: separate names for different #unparseData * evm, web3: move chainID cell to web3 configuration * web3.md: Fix net_version result format * web3.md: added eth_getStorageAt and eth_getCode * kevm: add run_web3 command * Makefile: add build-web3 target * kevm: no passing CHAINID to kore-json.py, interpreter * web3: formatting * web3: no need to require data * web3.md: Implement web3_clientVersion * web3.md: Implement some getters for JSON sorts
1 parent a2d0dd0 commit 615db59

File tree

4 files changed

+195
-2
lines changed

4 files changed

+195
-2
lines changed

Makefile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export LUA_PATH
3636

3737
.PHONY: all clean clean-submodules distclean install uninstall \
3838
deps all-deps llvm-deps haskell-deps repo-deps system-deps k-deps ocaml-deps plugin-deps libsecp256k1 libff \
39-
build build-ocaml build-java build-node build-kore split-tests \
39+
build build-ocaml build-java build-node build-llvm build-web3 split-tests \
4040
defn java-defn ocaml-defn node-defn haskell-defn llvm-defn \
4141
test test-all test-conformance test-slow-conformance test-all-conformance \
4242
test-vm test-slow-vm test-all-vm test-bchain test-slow-bchain test-all-bchain \
@@ -166,13 +166,17 @@ build-node: $(node_kompiled)
166166
build-haskell: $(haskell_kompiled)
167167
build-llvm: $(llvm_kompiled)
168168

169+
build-web3: MAIN_MODULE=WEB3
170+
build-web3: MAIN_DEFN_FILE=web3
171+
build-web3: $(llvm_kompiled)
172+
169173
# Tangle definition from *.md files
170174

171175
concrete_tangle:=.k:not(.node):not(.symbolic),.standalone,.concrete
172176
symbolic_tangle:=.k:not(.node):not(.concrete),.standalone,.symbolic
173177
node_tangle:=.k:not(.standalone):not(.symbolic),.node,.concrete
174178

175-
k_files=driver.k data.k network.k evm.k krypto.k edsl.k evm-node.k
179+
k_files=driver.k data.k network.k evm.k krypto.k edsl.k evm-node.k web3.k
176180
EXTRA_K_FILES+=$(MAIN_DEFN_FILE).k
177181
ALL_K_FILES:=$(k_files) $(EXTRA_K_FILES)
178182

data.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,18 @@ We need to interperet a `ByteArray` as a `String` again so that we can call `Kec
816816
// -----------------------------------------------
817817
rule #padByte( S ) => S requires lengthString(S) ==K 2
818818
rule #padByte( S ) => "0" +String S requires lengthString(S) ==K 1
819+
820+
syntax String ::= #unparseQuantity( Int ) [function]
821+
// ----------------------------------------------------
822+
rule #unparseQuantity( I ) => "0x" +String Base2String(I, 16)
823+
824+
syntax String ::= #unparseData ( Int, Int ) [function]
825+
| #unparseDataByteArray ( ByteArray ) [function]
826+
// ----------------------------------------------------------------
827+
rule #unparseData( _, 0 ) => "0x"
828+
rule #unparseData( DATA, LENGTH ) => #unparseDataByteArray(#padToWidth(LENGTH,#asByteStack(DATA)))
829+
830+
rule #unparseDataByteArray( DATA ) => replaceFirst(Base2String(#asInteger(#asByteStack(1) ++ DATA), 16), "1", "0x")
819831
```
820832

821833
Recursive Length Prefix (RLP)

kevm

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ run_search() {
6767
run_krun --search --pattern "$search_pattern" "$@"
6868
}
6969

70+
run_web3() {
71+
run_krun -cCHAINID="$cCHAINID" -pCHAINID='printf %s' "$@"
72+
}
73+
7074
run_klab() {
7175
local run_mode klab_log
7276

@@ -140,6 +144,7 @@ if [[ "$run_command" == 'help' ]]; then
140144
$0 kast [--backend (ocaml|java)] <pgm> <output format> <K arg>*
141145
$0 prove [--backend (java|haskell)] <spec> <K arg>* -m <def_module>
142146
$0 search [--backend (java|haskell)] <pgm> <pattern> <K arg>*
147+
$0 web3 [--backend (llvm)] <pgm> <K arg>*
143148
$0 klab-run <pgm> <K arg>*
144149
$0 klab-prove <spec> <K arg>* -m <def_module>
145150
$0 klab-view <spec>
@@ -149,6 +154,7 @@ if [[ "$run_command" == 'help' ]]; then
149154
$0 kast : Parse an EVM program and output it in a supported format
150155
$0 prove : Run an EVM K proof
151156
$0 search : Search for a K pattern in an EVM program execution
157+
$0 web3 : Run version of semantics which understand Web3 RPC (**UNDER CONSTRUCTION**)
152158
$0 klab-(run|prove) : Run program or prove spec and dump StateLogs which KLab can read
153159
$0 klab-view : View the statelog associated with a given program or spec
154160
@@ -181,6 +187,7 @@ run_file="$1" ; shift
181187

182188
cMODE="\`${MODE:-NORMAL}\`(.KList)"
183189
cSCHEDULE="\`${SCHEDULE:-BYZANTIUM}_EVM\`(.KList)"
190+
cCHAINID="#token(\"${CHAINID:-28346}\",\"Int\")"
184191

185192
case "$run_command-$backend" in
186193

@@ -190,6 +197,7 @@ case "$run_command-$backend" in
190197
interpret-@(ocaml|llvm) ) run_interpret "$@" ;;
191198
prove-@(java|haskell) ) run_prove "$@" ;;
192199
search-@(java|haskell) ) run_search "$@" ;;
200+
web3-@(llvm) ) run_web3 "$@" ;;
193201
klab-@(run|prove)-java ) run_klab "${run_command#klab-}" "$@" ;;
194202
klab-view-java ) view_klab "$@" ;;
195203
*) $0 help ; fatal "Unknown command on backend: $run_command $backend" ;;

web3.md

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
Web3 RPC JSON Handler
2+
====================
3+
4+
```k
5+
requires "evm.k"
6+
7+
module WEB3
8+
imports EVM
9+
imports EVM-DATA
10+
11+
configuration
12+
<kevm-client>
13+
<kevm/>
14+
<blockchain>
15+
<chainID> $CHAINID:Int </chainID>
16+
</blockchain>
17+
<web3request>
18+
<jsonrpc> "" </jsonrpc>
19+
<callid> 0 </callid>
20+
<method> "" </method>
21+
<params> [ .JSONList ] </params>
22+
</web3request>
23+
<web3result> .List </web3result>
24+
</kevm-client>
25+
26+
syntax JSON ::= Int | Bool
27+
| #getJSON ( JSONKey, JSON ) [function]
28+
// -------------------------------------------------------
29+
rule #getJSON( KEY, { KEY : J, _ } ) => J
30+
rule #getJSON( _, { .JSONList } ) => { .JSONList }
31+
rule #getJSON( KEY, { KEY2 : _, REST } ) => #getJSON( KEY, { REST } )
32+
requires KEY =/=K KEY2
33+
34+
syntax Int ::= #getInt ( JSONKey, JSON ) [function]
35+
// ---------------------------------------------------
36+
rule #getInt( KEY, { KEY : VALUE:Int, _ } ) => VALUE
37+
rule #getInt( _ , { .JSONList } ) => 0 // TODO: Need something better for nonexistent key/value
38+
rule #getInt( KEY, { KEY2 : _, REST } ) => #getInt( KEY, { REST } )
39+
requires KEY =/=K KEY2
40+
41+
syntax String ::= #getString ( JSONKey, JSON ) [function]
42+
// ---------------------------------------------------------
43+
rule #getString( KEY, { KEY : VALUE:String, _ } ) => VALUE
44+
rule #getString( _ , { .JSONList } ) => "" // TODO: Need something better for nonexistent key/value
45+
rule #getString( KEY, { KEY2 : _, REST } ) => #getString( KEY, { REST } )
46+
requires KEY =/=K KEY2
47+
48+
syntax EthereumSimulation ::= List{JSON, " "}
49+
// ---------------------------------------------
50+
rule <k> J:JSON REST:EthereumSimulation => #loadRPCCall J ~> REST ... </k>
51+
rule <k> J:JSON => #loadRPCCall J ... </k>
52+
53+
syntax KItem ::= "#loadRPCCall" JSON
54+
// ------------------------------------
55+
rule <k> #loadRPCCall J:JSON => #runRPCCall ... </k>
56+
<jsonrpc> _ => #getString("jsonrpc", J) </jsonrpc>
57+
<callid> _ => #getInt ("id" , J) </callid>
58+
<method> _ => #getString("method" , J) </method>
59+
<params> _ => #getJSON ("params" , J) </params>
60+
61+
syntax KItem ::= #sendResponse ( JSON )
62+
// ---------------------------------------
63+
rule <k> #sendResponse( J:JSON ) => . ... </k>
64+
<web3result> ... ( .List => ListItem( J ) ) </web3result>
65+
66+
syntax KItem ::= "#runRPCCall"
67+
// ------------------------------
68+
rule <k> #runRPCCall => #net_version ... </k>
69+
<method> "net_version" </method>
70+
rule <k> #runRPCCall => #web3_clientVersion ... </k>
71+
<method> "web3_clientVersion" </method>
72+
rule <k> #runRPCCall => #eth_gasPrice ... </k>
73+
<method> "eth_gasPrice" </method>
74+
rule <k> #runRPCCall => #eth_blockNumber ... </k>
75+
<method> "eth_blockNumber" </method>
76+
rule <k> #runRPCCall => #eth_accounts ... </k>
77+
<method> "eth_accounts" </method>
78+
rule <k> #runRPCCall => #eth_getBalance ... </k>
79+
<method> "eth_getBalance" </method>
80+
rule <k> #runRPCCall => #eth_getStorageAt ... </k>
81+
<method> "eth_getStorageAt" </method>
82+
rule <k> #runRPCCall => #eth_getCode ... </k>
83+
<method> "eth_getCode" </method>
84+
85+
syntax KItem ::= "#net_version"
86+
// -------------------------------
87+
rule <k> #net_version => #sendResponse( { "id" : CALLID, "jsonrpc" : JSONRPC, "result" : Int2String( CHAINID ) } ) ... </k>
88+
<jsonrpc> JSONRPC </jsonrpc>
89+
<callid> CALLID </callid>
90+
<chainID> CHAINID </chainID>
91+
92+
syntax KItem ::= "#web3_clientVersion"
93+
// -------------------------------
94+
rule <k> #web3_clientVersion => #sendResponse( { "id" : CALLID, "jsonrpc" : JSONRPC, "result" : "Firefly RPC/v0.0.1/kevm" } ) ... </k>
95+
<jsonrpc> JSONRPC </jsonrpc>
96+
<callid> CALLID </callid>
97+
98+
syntax KItem ::= "#eth_gasPrice"
99+
// --------------------------------
100+
rule <k> #eth_gasPrice => #sendResponse( { "id" : CALLID, "jsonrpc" : JSONRPC, "result" : #unparseQuantity( PRICE ) } ) ... </k>
101+
<jsonrpc> JSONRPC </jsonrpc>
102+
<callid> CALLID </callid>
103+
<gasPrice> PRICE </gasPrice>
104+
105+
syntax KItem ::= "#eth_blockNumber"
106+
// -----------------------------------
107+
rule <k> #eth_blockNumber => #sendResponse( { "id" : CALLID, "jsonrpc" : JSONRPC, "result" : #unparseQuantity( BLOCKNUM ) } ) ... </k>
108+
<jsonrpc> JSONRPC </jsonrpc>
109+
<callid> CALLID </callid>
110+
<number> BLOCKNUM </number>
111+
112+
syntax KItem ::= "#eth_accounts"
113+
// --------------------------------
114+
rule <k> #eth_accounts => #sendResponse( { "id" : CALLID, "jsonrpc" : JSONRPC, "result" : [ #acctsToJArray( ACCTS ) ] } ) ... </k>
115+
<jsonrpc> JSONRPC </jsonrpc>
116+
<callid> CALLID </callid>
117+
<activeAccounts> ACCTS </activeAccounts>
118+
119+
syntax JSONList ::= #acctsToJArray ( Set ) [function]
120+
// -----------------------------------------------------
121+
rule #acctsToJArray( .Set ) => .JSONList
122+
rule #acctsToJArray( SetItem( ACCT ) ACCTS:Set ) => #unparseData( ACCT, 20 ), #acctsToJArray( ACCTS )
123+
124+
syntax KItem ::= "#eth_getBalance"
125+
// ----------------------------------
126+
rule <k> #eth_getBalance ... </k>
127+
<params> [ (DATA => #parseHexWord(DATA)), _ ] </params>
128+
129+
rule <k> #eth_getBalance => #sendResponse( { "id" : CALLID, "jsonrpc" : JSONRPC, "result" : #unparseQuantity( ACCTBALANCE ) } ) ... </k>
130+
<jsonrpc> JSONRPC </jsonrpc>
131+
<callid> CALLID </callid>
132+
<params> [ DATA, TAG, .JSONList ] </params>
133+
<account>
134+
<acctID> DATA </acctID>
135+
<balance> ACCTBALANCE </balance>
136+
...
137+
</account>
138+
139+
syntax KItem ::= "#eth_getStorageAt"
140+
// ------------------------------------
141+
rule <k> #eth_getStorageAt ... </k>
142+
<params> [ (DATA => #parseHexWord(DATA)), QUANTITY:Int, _ ] </params>
143+
144+
rule <k> #eth_getStorageAt => #sendResponse( { "id" : CALLID, "jsonrpc" : JSONRPC, "result" : #unparseQuantity( #lookup (STORAGE, QUANTITY) ) } ) ... </k>
145+
<jsonrpc> JSONRPC </jsonrpc>
146+
<callid> CALLID </callid>
147+
<params> [ DATA, QUANTITY, TAG, .JSONList ] </params>
148+
<account>
149+
<acctID> DATA </acctID>
150+
<storage> STORAGE </storage>
151+
...
152+
</account>
153+
154+
syntax KItem ::= "#eth_getCode"
155+
// -------------------------------
156+
rule <k> #eth_getCode ... </k>
157+
<params> [ (DATA => #parseHexWord(DATA)), _ ] </params>
158+
159+
rule <k> #eth_getCode => #sendResponse( { "id" : CALLID, "jsonrpc" : JSONRPC, "result" : #unparseDataByteArray( CODE ) } ) ... </k>
160+
<jsonrpc> JSONRPC </jsonrpc>
161+
<callid> CALLID </callid>
162+
<params> [ DATA, TAG, .JSONList ] </params>
163+
<account>
164+
<acctID> DATA </acctID>
165+
<code> CODE </code>
166+
...
167+
</account>
168+
endmodule
169+
```

0 commit comments

Comments
 (0)