以太坊以JSON RPC的方式提供API service弊仪。本文將從go-ethereum源碼中挖掘服務(wù)端如何提供JSON RPC 服務(wù)亭枷。
服務(wù)端啟動(dòng)rpc server
? go-ethereum git:(master) ? tree -d -L 1
├── cmd
├── geth
├── main.go
├── internal
├── ethapi
├── api.go
├── node
├── rpc
...
go-ethereum的代碼很多劣挫,單從發(fā)起一筆轉(zhuǎn)賬這樣一個(gè)api而言,geth節(jié)點(diǎn)涉及的代碼相對(duì)簡(jiǎn)單。
首先,cmd/geth/main.go
是整個(gè)geth節(jié)點(diǎn)的entrypoint蹦渣,main函數(shù)會(huì)實(shí)例化一個(gè)全功能的節(jié)點(diǎn):
func geth(ctx *cli.Context) error {
node := makeFullNode(ctx)
startNode(ctx, node)
node.Wait()
return nil
}
實(shí)例化之后,將調(diào)用node/node.go
中的Start方法貌亭,來(lái)配置node相應(yīng)的服務(wù), 然后啟動(dòng)柬唯,等到所有的服務(wù)啟動(dòng)完成之后,節(jié)點(diǎn)開啟RPC服務(wù)圃庭,根據(jù)config將相應(yīng)的服務(wù)注冊(cè)到RPC服務(wù)的白名單中:
func (s *Server) RegisterName(name string, rcvr interface{}) error {
...
methods, subscriptions :=
suitableCallbacks(rcvrVal, svc.typ)
...
svc.name = name
svc.callbacks, svc.subscriptions = methods, subscriptions
上述方法將一個(gè)service中的可以rpc調(diào)用的method存儲(chǔ)到server的map中锄奢。
go-ethereum節(jié)點(diǎn)的rpc提供了四種能力的rpc失晴,以HTTP為例:
func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors []string) error {
// Register all the APIs exposed by the services
...
// All APIs registered, start the HTTP listener
var (
listener net.Listener
err error
)
if listener, err = net.Listen("tcp", endpoint); err != nil {
return err
}
go rpc.NewHTTPServer(cors, handler).Serve(listener)
...
}
geth節(jié)點(diǎn)將監(jiān)聽端口,默認(rèn)是8545拘央,然后開啟HTTPServer涂屁,等待http rpc請(qǐng)求。
HTTP RPC 請(qǐng)求響應(yīng)流程
一個(gè)標(biāo)準(zhǔn)的HTTP RPC請(qǐng)求如下:
curl -H "Content-Type: application/json" -X POST --data \
'{"jsonrpc":"2.0","method":"eth_sendRawTransaction","params":["0xd46e8dd67c5d32be8d"],"id":1}' http://localhost:8545
需要jsonrpc
, method
, params
和id
構(gòu)成request body堪滨。當(dāng)我們的geth節(jié)點(diǎn)的rpc server監(jiān)聽到新的request到來(lái)時(shí)胯陋,將會(huì):
- 實(shí)例化一個(gè)NewJSONCodec編碼器蕊温。
- 通過(guò)編碼器來(lái)將request轉(zhuǎn)換成jsonRequest袱箱,然后獲取service_name和service_method以及params。
- 通過(guò)service_name 和service_method义矛,可以找到當(dāng)時(shí)注冊(cè)的rpc服務(wù)发笔。
- 通過(guò)反射方式運(yùn)行rpc服務(wù)
reply := req.callb.method.Func.Call(arguments)
,得到method的返回值 - 利用編碼器將返回值json序列化凉翻,然后返回
codec.Write(response)
針對(duì)一個(gè)轉(zhuǎn)賬交易的話了讨,我們得知service_name 是eth,service_method是sendRawTransaction制轰,其方法在internal/api.go
中前计。運(yùn)行reply := req.callb.method.Func.Call(arguments)
之后我們得到的reply是一個(gè)common.Hash對(duì)象,然后通過(guò)json序列化我們得到的結(jié)果是TxnHash的字符串垃杖。
{
"id":1,
"jsonrpc": "2.0",
"result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
}