ethereum rpc 调用分析

以太坊以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, paramsid构成request body。当我们的geth节点的rpc server监听到新的request到来时,将会:

  1. 实例化一个NewJSONCodec编码器。
  2. 通过编码器来将request转换成jsonRequest,然后获取service_name和service_method以及params。
  3. 通过service_name 和service_method,可以找到当时注册的rpc服务。
  4. 通过反射方式运行rpc服务reply := req.callb.method.Func.Call(arguments),得到method的返回值
  5. 利用编码器将返回值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"
}

你可能感兴趣的:(ethereum rpc 调用分析)