以太坊以JSON RPC的方式提供API service。本文将从go-ethereum源码中挖掘服务端如何提供JSON RPC 服务。
服务端启动rpc server
➜ go-ethereum git:(master) ✗ tree -d -L 1
├── cmd
├── geth
├── main.go
├── internal
├── ethapi
├── api.go
├── node
├── rpc
...
go-ethereum的代码很多,单从发起一笔转账这样一个api而言,geth节点涉及的代码相对简单。
首先,cmd/geth/main.go
是整个geth节点的entrypoint,main函数会实例化一个全功能的节点:
func geth(ctx *cli.Context) error {
node := makeFullNode(ctx)
startNode(ctx, node)
node.Wait()
return nil
}
实例化之后,将调用node/node.go
中的Start方法,来配置node相应的服务, 然后启动,等到所有的服务启动完成之后,节点开启RPC服务,根据config将相应的服务注册到RPC服务的白名单中:
func (s *Server) RegisterName(name string, rcvr interface{}) error {
...
methods, subscriptions :=
suitableCallbacks(rcvrVal, svc.typ)
...
svc.name = name
svc.callbacks, svc.subscriptions = methods, subscriptions
上述方法将一个service中的可以rpc调用的method存储到server的map中。
go-ethereum节点的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节点将监听端口,默认是8545,然后开启HTTPServer,等待http rpc请求。
HTTP RPC 请求响应流程
一个标准的HTTP RPC请求如下:
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
构成request body。当我们的geth节点的rpc server监听到新的request到来时,将会:
- 实例化一个NewJSONCodec编码器。
- 通过编码器来将request转换成jsonRequest,然后获取service_name和service_method以及params。
- 通过service_name 和service_method,可以找到当时注册的rpc服务。
- 通过反射方式运行rpc服务
reply := req.callb.method.Func.Call(arguments)
,得到method的返回值 - 利用编码器将返回值json序列化,然后返回
codec.Write(response)
针对一个转账交易的话,我们得知service_name 是eth,service_method是sendRawTransaction,其方法在internal/api.go
中。运行reply := req.callb.method.Func.Call(arguments)
之后我们得到的reply是一个common.Hash对象,然后通过json序列化我们得到的结果是TxnHash的字符串。
{
"id":1,
"jsonrpc": "2.0",
"result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
}