以太坊RPC

以太坊提供了RPC服务,可以在geth启动时通过参数设置

geth启动选项参数

--rpc  启动HTTP-RPC服务(基于HTTP的)
--ws  启动WS-RPC服务(基于WebService的)
--rpcapi  value 指定需要调用的HTTP-RPC API接口,默认只有eth,net,web3
--rpcport value  HTTP-RPC服务器监听端口(default: 8545)
--rpcport value  HTTP-RPC服务器监听端口(default: 8545)
例子:geth --rpc --rpcapi "db,eth,net,web3,personal"

执行RPC调用的方式有很多,可以使用web3提供的接口、直接发送Json请求(缺点是拼json会很麻烦)、使用go-ethereum/ethclient包提供的函数(缺点是只有eth接口)、也可以自己定义接口来调用。下面代码是使用go-ethereum/ethclient包中的函数的例子。

package main

import (
    "fmt"
    "github.com/ethereum/go-ethereum/mobile"
)

func main() {
    // NewEthereumClient函数只是创建一个EthereumClient结构,并设置了HTTP连接的一些参数如的head的一些属性,并没有节点建立连接
    cli, err := geth.NewEthereumClient("http://127.0.0.1:8545")
    if err != nil {
        fmt.Printf("create new ethereum rpc client err:%s\n", err.Error())
    } else {
        fmt.Println("create new ethereum rpc client success")
    }
    eth_ctx := geth.NewContext()
    block, err2 := cli.GetBlockByNumber(eth_ctx, 18)
    fmt.Printf("ethereum mobile Context:%+v\n", eth_ctx)
    if err2 != nil {
        fmt.Printf("get block err:%s\n", err2.Error())
    } else {
        fmt.Printf("block:%+v\n", block)
    }
}

连的节点是本地运行的私有链,并且在go-ethereum源码中加了一些日志,执行结果:

mylog:DialContext:u:{Scheme:http Opaque: User: Host:127.0.0.1:8545 Path: RawPath: ForceQuery:false RawQuery: Fragment:};
mylog:u.Scheme:http
create new ethereum rpc client success
mylog:JSON-RPC: Client CallContext
mylog:Client.isHTTP:true
ethereum mobile Context:&{context:0xc4200ac008 cancel:<nil>}
block:Block(#18): Size: 650.00 B {
MinerHash: fd55c05ae10a5b0159b3c2d5803c6aa9469c95f5f063b9c400a2c36b49616ab3
Header(84b2cfd65e3197bdfe3f748ecebb040953af5eb73a05d8595757cf42cb40a492):
[
    ParentHash:     7892a0b31d50d67ae20d4a7ec5c24a6fe85f2f264e9f1639aa2388081305a0bd
    UncleHash:      1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347
    Coinbase:       bdc61c81f67983288a6c375a884661edc77286d0
    Root:           0f30637bfc5bd6e123c6a0c38bdc743c94050626a984f9943eaf38367100b3e3
    TxSha           354d185cfa88e50f1a425e5b89500122e4445e9ec737e7a18cdd61b9350ab72b
    ReceiptSha:     a769d28981014fb6095462148a6300cd0b43fa050d75eb6f5b7595cfd13136bb
    Bloom:          00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    Difficulty:     131072
    Number:         18
    GasLimit:       131877941
    GasUsed:        21000
    Time:           1527044372
    Extra:          ׃��geth�go1.10�darwin
    MixDigest:      70c2bb422b1b834d5173d279e508ffee9dada454650fc3cf63e95deb3073cf32
    Nonce:          58b7495f112ccac2
]
Transactions:
[
    TX(57a3b17f84358098b728fc0f70f0697f175f8ba00d386c88eac0815b3afd6aad)
    Contract: false
    From:     2154bdd7070c99d1a25ff589a08b01dfd6eb65de
    To:       bdc61c81f67983288a6c375a884661edc77286d0
    Nonce:    0
    GasPrice: 0x430e23400
    GasLimit  0x15f90
    Value:    0xde0b6b3a7640000
    Data:     0x
    V:        0x41
    R:        0x45d4952c0190373c56e62ad15e54db54c0246385371b23c70bab4126b51927f8
    S:        0x618e4bb76a36482254352d7e5096c0dff4c1f495218d57c874fc3d8153915ea4
    Hex:      f86d80850430e2340083015f9094bdc61c81f67983288a6c375a884661edc77286d0880de0b6b3a76400008041a045d4952c0190373c56e62ad15e54db54c0246385371b23c70bab4126b51927f8a0618e4bb76a36482254352d7e5096c0dff4c1f495218d57c874fc3d8153915ea4
]
Uncles:
[]
}

分析:

go-ethereum/mobile包是发起RPC请求的客户端直接使用的包。
该包中有EthereumClient结构提供了Ethereum API的接入。

// EthereumClient provides access to the Ethereum APIs.
type EthereumClient struct {
    client *ethclient.Client
}

ethclient.Client在ethclient包中,包装了rpc.Client,rpc.Client代表与RPC服务的一个连接。

// Client defines typed wrappers for the Ethereum RPC API.
type Client struct {
    c *rpc.Client
}

RPC请求客户端在使用时,首先传入想要接入的节点的url作为参数,调用mobile包中的NewEthereumClient函数。创建了EthereumClient实例,并与节点建立连接。建立的RPC连接有三种形式:HTTP、WebSocket、IPC,当传入http://127.0.0.1:8545时,建立的是HTTP连接。

// NewEthereumClient connects a client to the given URL.
func NewEthereumClient(rawurl string) (client *EthereumClient, _ error) {
    rawClient, err := ethclient.Dial(rawurl)
    return &EthereumClient{rawClient}, err
}

设置HTTP连接的参数会调用rpc包http.go文件中的DialHTTPWithClient函数。

// DialHTTPWithClient creates a new RPC client that connects to an RPC server over HTTP
// using the provided HTTP Client.
func DialHTTPWithClient(endpoint string, client *http.Client) (*Client, error) {
    req, err := http.NewRequest(http.MethodPost, endpoint, nil)
    if err != nil {
        return nil, err
    }
    // Content-Type和Accept是application/json,即发送的数据类型和接收的数据类型都是json
    req.Header.Set("Content-Type", contentType)
    req.Header.Set("Accept", contentType)
    
    initctx := context.Background()
    return newClient(initctx, func(context.Context) (net.Conn, error) {
        return &httpConn{client: client, req: req, closed: make(chan struct{})}, nil
    })
}

通过HTTP来做JSON-RPC调用时,需要一个geth.Context实例,通过调用mobile包中的NewContext函数,创建一个空的geth.Context实例。

// NewContext returns a non-nil, empty Context. It is never canceled, has no
// values, and has no deadline. It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming requests.
func NewContext() *Context {
    return &Context{
        context: context.Background(),
    }
}

mobile包中封装了请求区块、区块头、交易等函数,这些函数调用ethclient包中的相关函数,再调用更底层rpc包中封装的函数。
mobile包-->ethclient包-->rpc包。如mobile包中根据区块号查找区块的函数最后会调用rpc包中的CallContext函数。

// CallContext扮演JSON-RPC调用角色
// CallContext performs a JSON-RPC call with the given arguments. If the context is
// canceled before the call has successfully returned, CallContext returns immediately.
//
// The result must be a pointer so that package json can unmarshal into it. You
// can also pass nil, in which case the result is ignored.
func (c *Client) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error {
    fmt.Printf("mylog:JSON-RPC: Client CallContext\n")
    msg, err := c.newMessage(method, args...)
    if err != nil {
        return err
    }
    op := &requestOp{ids: []json.RawMessage{msg.ID}, resp: make(chan *jsonrpcMessage, 1)}

    fmt.Printf("mylog:Client.isHTTP:%+v\n",c.isHTTP)
    if c.isHTTP {
        err = c.sendHTTP(ctx, op, msg)
    } else {
        err = c.send(ctx, op, msg)
    }
    if err != nil {
        return err
    }

    // dispatch has accepted the request and will close the channel it when it quits.
    switch resp, err := op.wait(ctx); {
    case err != nil:
        return err
    case resp.Error != nil:
        return resp.Error
    case len(resp.Result) == 0:
        return ErrNoResult
    default:
        return json.Unmarshal(resp.Result, &result)
    }
}

使用POSTMAN

使用POSTMAN发送请求时,注意设置下Content-type和Accept。
body是{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":67}
这种方式虽然直接,但是自己拼json会很麻烦,所以最方便的还是调用已有的接口。

使用POSTMAN

如果是做查询区块号为18的区块,则body是
{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x12",true],"id":1}

参考文献:
ethereum/wiki/JSON RPC
以太坊RPC机制与API实例

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容