github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/ethclient/ethclient.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 12:09:39</date> 10 //</624342638685851648> 11 12 13 //包ethclient为以太坊RPC API提供客户端。 14 package ethclient 15 16 import ( 17 "context" 18 "encoding/json" 19 "errors" 20 "fmt" 21 "math/big" 22 23 "github.com/ethereum/go-ethereum" 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/common/hexutil" 26 "github.com/ethereum/go-ethereum/core/types" 27 "github.com/ethereum/go-ethereum/rlp" 28 "github.com/ethereum/go-ethereum/rpc" 29 ) 30 31 //客户端为以太坊RPC API定义类型化包装器。 32 type Client struct { 33 c *rpc.Client 34 } 35 36 //拨号将客户机连接到给定的URL。 37 func Dial(rawurl string) (*Client, error) { 38 return DialContext(context.Background(), rawurl) 39 } 40 41 func DialContext(ctx context.Context, rawurl string) (*Client, error) { 42 c, err := rpc.DialContext(ctx, rawurl) 43 if err != nil { 44 return nil, err 45 } 46 return NewClient(c), nil 47 } 48 49 //newclient创建使用给定RPC客户端的客户端。 50 func NewClient(c *rpc.Client) *Client { 51 return &Client{c} 52 } 53 54 func (ec *Client) Close() { 55 ec.c.Close() 56 } 57 58 //区块链访问 59 60 //blockbyhash返回给定的完整块。 61 // 62 //请注意,加载完整块需要两个请求。使用headerbyhash 63 //如果不需要所有事务或叔叔头。 64 func (ec *Client) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { 65 return ec.getBlock(ctx, "eth_getBlockByHash", hash, true) 66 } 67 68 //BlockByNumber返回当前规范链中的块。如果数字为零,则 69 //返回最新的已知块。 70 // 71 //请注意,加载完整块需要两个请求。使用HeaderByNumber 72 //如果不需要所有事务或叔叔头。 73 func (ec *Client) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) { 74 return ec.getBlock(ctx, "eth_getBlockByNumber", toBlockNumArg(number), true) 75 } 76 77 type rpcBlock struct { 78 Hash common.Hash `json:"hash"` 79 Transactions []rpcTransaction `json:"transactions"` 80 UncleHashes []common.Hash `json:"uncles"` 81 } 82 83 func (ec *Client) getBlock(ctx context.Context, method string, args ...interface{}) (*types.Block, error) { 84 var raw json.RawMessage 85 err := ec.c.CallContext(ctx, &raw, method, args...) 86 if err != nil { 87 return nil, err 88 } else if len(raw) == 0 { 89 return nil, ethereum.NotFound 90 } 91 //解码头和事务。 92 var head *types.Header 93 var body rpcBlock 94 if err := json.Unmarshal(raw, &head); err != nil { 95 return nil, err 96 } 97 if err := json.Unmarshal(raw, &body); err != nil { 98 return nil, err 99 } 100 //快速验证事务和叔叔列表。这主要有助于调试服务器。 101 if head.UncleHash == types.EmptyUncleHash && len(body.UncleHashes) > 0 { 102 return nil, fmt.Errorf("server returned non-empty uncle list but block header indicates no uncles") 103 } 104 if head.UncleHash != types.EmptyUncleHash && len(body.UncleHashes) == 0 { 105 return nil, fmt.Errorf("server returned empty uncle list but block header indicates uncles") 106 } 107 if head.TxHash == types.EmptyRootHash && len(body.Transactions) > 0 { 108 return nil, fmt.Errorf("server returned non-empty transaction list but block header indicates no transactions") 109 } 110 if head.TxHash != types.EmptyRootHash && len(body.Transactions) == 0 { 111 return nil, fmt.Errorf("server returned empty transaction list but block header indicates transactions") 112 } 113 //加载uncles,因为它们不包含在块响应中。 114 var uncles []*types.Header 115 if len(body.UncleHashes) > 0 { 116 uncles = make([]*types.Header, len(body.UncleHashes)) 117 reqs := make([]rpc.BatchElem, len(body.UncleHashes)) 118 for i := range reqs { 119 reqs[i] = rpc.BatchElem{ 120 Method: "eth_getUncleByBlockHashAndIndex", 121 Args: []interface{}{body.Hash, hexutil.EncodeUint64(uint64(i))}, 122 Result: &uncles[i], 123 } 124 } 125 if err := ec.c.BatchCallContext(ctx, reqs); err != nil { 126 return nil, err 127 } 128 for i := range reqs { 129 if reqs[i].Error != nil { 130 return nil, reqs[i].Error 131 } 132 if uncles[i] == nil { 133 return nil, fmt.Errorf("got null header for uncle %d of block %x", i, body.Hash[:]) 134 } 135 } 136 } 137 //填充块中事务的发送方缓存。 138 txs := make([]*types.Transaction, len(body.Transactions)) 139 for i, tx := range body.Transactions { 140 if tx.From != nil { 141 setSenderFromServer(tx.tx, *tx.From, body.Hash) 142 } 143 txs[i] = tx.tx 144 } 145 return types.NewBlockWithHeader(head).WithBody(txs, uncles), nil 146 } 147 148 //HeaderByHash返回具有给定哈希的块头。 149 func (ec *Client) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { 150 var head *types.Header 151 err := ec.c.CallContext(ctx, &head, "eth_getBlockByHash", hash, false) 152 if err == nil && head == nil { 153 err = ethereum.NotFound 154 } 155 return head, err 156 } 157 158 //HeaderByNumber返回当前规范链的块头。如果数字是 159 //nil,返回最新的已知头。 160 func (ec *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { 161 var head *types.Header 162 err := ec.c.CallContext(ctx, &head, "eth_getBlockByNumber", toBlockNumArg(number), false) 163 if err == nil && head == nil { 164 err = ethereum.NotFound 165 } 166 return head, err 167 } 168 169 type rpcTransaction struct { 170 tx *types.Transaction 171 txExtraInfo 172 } 173 174 type txExtraInfo struct { 175 BlockNumber *string `json:"blockNumber,omitempty"` 176 BlockHash *common.Hash `json:"blockHash,omitempty"` 177 From *common.Address `json:"from,omitempty"` 178 } 179 180 func (tx *rpcTransaction) UnmarshalJSON(msg []byte) error { 181 if err := json.Unmarshal(msg, &tx.tx); err != nil { 182 return err 183 } 184 return json.Unmarshal(msg, &tx.txExtraInfo) 185 } 186 187 //TransactionByHash返回具有给定哈希的事务。 188 func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) { 189 var json *rpcTransaction 190 err = ec.c.CallContext(ctx, &json, "eth_getTransactionByHash", hash) 191 if err != nil { 192 return nil, false, err 193 } else if json == nil { 194 return nil, false, ethereum.NotFound 195 } else if _, r, _ := json.tx.RawSignatureValues(); r == nil { 196 return nil, false, fmt.Errorf("server returned transaction without signature") 197 } 198 if json.From != nil && json.BlockHash != nil { 199 setSenderFromServer(json.tx, *json.From, *json.BlockHash) 200 } 201 return json.tx, json.BlockNumber == nil, nil 202 } 203 204 //TransactionSender返回给定事务的发件人地址。交易 205 //必须为远程节点所知,并包含在给定块的区块链中,并且 206 //索引。发送方是包含时协议派生的发送方。 207 // 208 //TransactionByHash和 209 //事务处理块。获取他们的发送者地址可以在没有RPC交互的情况下完成。 210 func (ec *Client) TransactionSender(ctx context.Context, tx *types.Transaction, block common.Hash, index uint) (common.Address, error) { 211 //尝试从缓存中加载地址。 212 sender, err := types.Sender(&senderFromServer{blockhash: block}, tx) 213 if err == nil { 214 return sender, nil 215 } 216 var meta struct { 217 Hash common.Hash 218 From common.Address 219 } 220 if err = ec.c.CallContext(ctx, &meta, "eth_getTransactionByBlockHashAndIndex", block, hexutil.Uint64(index)); err != nil { 221 return common.Address{}, err 222 } 223 if meta.Hash == (common.Hash{}) || meta.Hash != tx.Hash() { 224 return common.Address{}, errors.New("wrong inclusion block/index") 225 } 226 return meta.From, nil 227 } 228 229 //TransactionCount返回给定块中的事务总数。 230 func (ec *Client) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) { 231 var num hexutil.Uint 232 err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByHash", blockHash) 233 return uint(num), err 234 } 235 236 //TransactionBlock返回给定块中索引处的单个事务。 237 func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) { 238 var json *rpcTransaction 239 err := ec.c.CallContext(ctx, &json, "eth_getTransactionByBlockHashAndIndex", blockHash, hexutil.Uint64(index)) 240 if err == nil { 241 if json == nil { 242 return nil, ethereum.NotFound 243 } else if _, r, _ := json.tx.RawSignatureValues(); r == nil { 244 return nil, fmt.Errorf("server returned transaction without signature") 245 } 246 } 247 if json.From != nil && json.BlockHash != nil { 248 setSenderFromServer(json.tx, *json.From, *json.BlockHash) 249 } 250 return json.tx, err 251 } 252 253 //TransactionReceipt按事务哈希返回事务的接收。 254 //请注意,收据不可用于待处理的交易。 255 func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { 256 var r *types.Receipt 257 err := ec.c.CallContext(ctx, &r, "eth_getTransactionReceipt", txHash) 258 if err == nil { 259 if r == nil { 260 return nil, ethereum.NotFound 261 } 262 } 263 return r, err 264 } 265 266 func toBlockNumArg(number *big.Int) string { 267 if number == nil { 268 return "latest" 269 } 270 return hexutil.EncodeBig(number) 271 } 272 273 type rpcProgress struct { 274 StartingBlock hexutil.Uint64 275 CurrentBlock hexutil.Uint64 276 HighestBlock hexutil.Uint64 277 PulledStates hexutil.Uint64 278 KnownStates hexutil.Uint64 279 } 280 281 //SyncProgress检索同步算法的当前进度。如果有 282 //当前没有运行同步,它返回零。 283 func (ec *Client) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) { 284 var raw json.RawMessage 285 if err := ec.c.CallContext(ctx, &raw, "eth_syncing"); err != nil { 286 return nil, err 287 } 288 //处理可能的响应类型 289 var syncing bool 290 if err := json.Unmarshal(raw, &syncing); err == nil { 291 return nil, nil //不同步(始终为假) 292 } 293 var progress *rpcProgress 294 if err := json.Unmarshal(raw, &progress); err != nil { 295 return nil, err 296 } 297 return ðereum.SyncProgress{ 298 StartingBlock: uint64(progress.StartingBlock), 299 CurrentBlock: uint64(progress.CurrentBlock), 300 HighestBlock: uint64(progress.HighestBlock), 301 PulledStates: uint64(progress.PulledStates), 302 KnownStates: uint64(progress.KnownStates), 303 }, nil 304 } 305 306 //订阅订阅当前区块链头的通知 307 //在给定的频道上。 308 func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) { 309 return ec.c.EthSubscribe(ctx, ch, "newHeads") 310 } 311 312 //状态访问 313 314 //network id返回此链的网络ID(也称为链ID)。 315 func (ec *Client) NetworkID(ctx context.Context) (*big.Int, error) { 316 version := new(big.Int) 317 var ver string 318 if err := ec.c.CallContext(ctx, &ver, "net_version"); err != nil { 319 return nil, err 320 } 321 if _, ok := version.SetString(ver, 10); !ok { 322 return nil, fmt.Errorf("invalid net_version result %q", ver) 323 } 324 return version, nil 325 } 326 327 //balanceat返回给定帐户的wei余额。 328 //块编号可以为零,在这种情况下,余额取自最新的已知块。 329 func (ec *Client) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { 330 var result hexutil.Big 331 err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, toBlockNumArg(blockNumber)) 332 return (*big.Int)(&result), err 333 } 334 335 //storageat返回给定帐户的合同存储中密钥的值。 336 //块编号可以为零,在这种情况下,值取自最新的已知块。 337 func (ec *Client) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) { 338 var result hexutil.Bytes 339 err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, toBlockNumArg(blockNumber)) 340 return result, err 341 } 342 343 //codeat返回给定帐户的合同代码。 344 //块编号可以为零,在这种情况下,代码取自最新的已知块。 345 func (ec *Client) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { 346 var result hexutil.Bytes 347 err := ec.c.CallContext(ctx, &result, "eth_getCode", account, toBlockNumArg(blockNumber)) 348 return result, err 349 } 350 351 //nonceat返回给定帐户的nonce帐户。 352 //块编号可以为零,在这种情况下,nonce是从最新的已知块中获取的。 353 func (ec *Client) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { 354 var result hexutil.Uint64 355 err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, toBlockNumArg(blockNumber)) 356 return uint64(result), err 357 } 358 359 //过滤器 360 361 //filterlogs执行筛选器查询。 362 func (ec *Client) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { 363 var result []types.Log 364 err := ec.c.CallContext(ctx, &result, "eth_getLogs", toFilterArg(q)) 365 return result, err 366 } 367 368 //subscribeFilterLogs订阅流式筛选查询的结果。 369 func (ec *Client) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) { 370 return ec.c.EthSubscribe(ctx, ch, "logs", toFilterArg(q)) 371 } 372 373 func toFilterArg(q ethereum.FilterQuery) interface{} { 374 arg := map[string]interface{}{ 375 "fromBlock": toBlockNumArg(q.FromBlock), 376 "toBlock": toBlockNumArg(q.ToBlock), 377 "address": q.Addresses, 378 "topics": q.Topics, 379 } 380 if q.FromBlock == nil { 381 arg["fromBlock"] = "0x0" 382 } 383 return arg 384 } 385 386 //预备状态 387 388 //PendingBalanceAt返回处于挂起状态的给定帐户的wei余额。 389 func (ec *Client) PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) { 390 var result hexutil.Big 391 err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, "pending") 392 return (*big.Int)(&result), err 393 } 394 395 //PendingStorageAt返回处于挂起状态的给定帐户的合同存储中密钥的值。 396 func (ec *Client) PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) { 397 var result hexutil.Bytes 398 err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, "pending") 399 return result, err 400 } 401 402 //PendingCodeAt返回处于挂起状态的给定帐户的合同代码。 403 func (ec *Client) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { 404 var result hexutil.Bytes 405 err := ec.c.CallContext(ctx, &result, "eth_getCode", account, "pending") 406 return result, err 407 } 408 409 //pendingnonceat返回处于挂起状态的给定帐户的帐户nonce。 410 //这是应该用于下一个事务的nonce。 411 func (ec *Client) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { 412 var result hexutil.Uint64 413 err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, "pending") 414 return uint64(result), err 415 } 416 417 //PendingtransactionCount返回处于挂起状态的事务总数。 418 func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) { 419 var num hexutil.Uint 420 err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByNumber", "pending") 421 return uint(num), err 422 } 423 424 //TODO:订阅挂起的事务(需要服务器端) 425 426 //合同呼叫 427 428 //CallContract执行消息调用事务,该事务直接在VM中执行 429 //但从未开发到区块链中。 430 // 431 //BlockNumber选择运行调用的块高度。可以是零,其中 432 //如果代码取自最新的已知块。注意这个状态从很老 433 //块可能不可用。 434 func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { 435 var hex hexutil.Bytes 436 err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), toBlockNumArg(blockNumber)) 437 if err != nil { 438 return nil, err 439 } 440 return hex, nil 441 } 442 443 //PendingCallContract使用EVM执行消息调用事务。 444 //合同调用所看到的状态是挂起状态。 445 func (ec *Client) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) { 446 var hex hexutil.Bytes 447 err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), "pending") 448 if err != nil { 449 return nil, err 450 } 451 return hex, nil 452 } 453 454 //SuggestGasprice检索当前建议的天然气价格,以便及时 455 //交易的执行。 456 func (ec *Client) SuggestGasPrice(ctx context.Context) (*big.Int, error) { 457 var hex hexutil.Big 458 if err := ec.c.CallContext(ctx, &hex, "eth_gasPrice"); err != nil { 459 return nil, err 460 } 461 return (*big.Int)(&hex), nil 462 } 463 464 //EstimateGas试图根据 465 //后端区块链的当前挂起状态。不能保证这是 466 //矿工可能增加或移除的其他交易的实际气体限制要求, 467 //但它应为设定合理违约提供依据。 468 func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) { 469 var hex hexutil.Uint64 470 err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg)) 471 if err != nil { 472 return 0, err 473 } 474 return uint64(hex), nil 475 } 476 477 //sendTransaction将签名的事务注入挂起池以执行。 478 // 479 //如果事务是合同创建,请使用TransactionReceipt方法获取 480 //挖掘交易记录后的合同地址。 481 func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) error { 482 data, err := rlp.EncodeToBytes(tx) 483 if err != nil { 484 return err 485 } 486 return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", common.ToHex(data)) 487 } 488 489 func toCallArg(msg ethereum.CallMsg) interface{} { 490 arg := map[string]interface{}{ 491 "from": msg.From, 492 "to": msg.To, 493 } 494 if len(msg.Data) > 0 { 495 arg["data"] = hexutil.Bytes(msg.Data) 496 } 497 if msg.Value != nil { 498 arg["value"] = (*hexutil.Big)(msg.Value) 499 } 500 if msg.Gas != 0 { 501 arg["gas"] = hexutil.Uint64(msg.Gas) 502 } 503 if msg.GasPrice != nil { 504 arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) 505 } 506 return arg 507 } 508