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 &ethereum.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