ethereum rpc 调用分析

作者: wade_van | 来源:发表于2017-09-26 22:41 被阅读5037次

    以太坊以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 调用分析

        本文链接:https://www.haomeiwen.com/subject/rspeextx.html