remote.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. // Copyright 2016 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. package backends
  17. import (
  18. "encoding/json"
  19. "fmt"
  20. "math/big"
  21. "sync"
  22. "sync/atomic"
  23. "github.com/ethereum/go-ethereum/accounts/abi/bind"
  24. "github.com/ethereum/go-ethereum/common"
  25. "github.com/ethereum/go-ethereum/core/types"
  26. "github.com/ethereum/go-ethereum/rlp"
  27. "github.com/ethereum/go-ethereum/rpc"
  28. "golang.org/x/net/context"
  29. )
  30. // This nil assignment ensures compile time that rpcBackend implements bind.ContractBackend.
  31. var _ bind.ContractBackend = (*rpcBackend)(nil)
  32. // rpcBackend implements bind.ContractBackend, and acts as the data provider to
  33. // Ethereum contracts bound to Go structs. It uses an RPC connection to delegate
  34. // all its functionality.
  35. //
  36. // Note: The current implementation is a blocking one. This should be replaced
  37. // by a proper async version when a real RPC client is created.
  38. type rpcBackend struct {
  39. client rpc.Client // RPC client connection to interact with an API server
  40. autoid uint32 // ID number to use for the next API request
  41. lock sync.Mutex // Singleton access until we get to request multiplexing
  42. }
  43. // NewRPCBackend creates a new binding backend to an RPC provider that can be
  44. // used to interact with remote contracts.
  45. func NewRPCBackend(client rpc.Client) bind.ContractBackend {
  46. return &rpcBackend{
  47. client: client,
  48. }
  49. }
  50. // request is a JSON RPC request package assembled internally from the client
  51. // method calls.
  52. type request struct {
  53. JSONRPC string `json:"jsonrpc"` // Version of the JSON RPC protocol, always set to 2.0
  54. ID int `json:"id"` // Auto incrementing ID number for this request
  55. Method string `json:"method"` // Remote procedure name to invoke on the server
  56. Params []interface{} `json:"params"` // List of parameters to pass through (keep types simple)
  57. }
  58. // response is a JSON RPC response package sent back from the API server.
  59. type response struct {
  60. JSONRPC string `json:"jsonrpc"` // Version of the JSON RPC protocol, always set to 2.0
  61. ID int `json:"id"` // Auto incrementing ID number for this request
  62. Error *failure `json:"error"` // Any error returned by the remote side
  63. Result json.RawMessage `json:"result"` // Whatever the remote side sends us in reply
  64. }
  65. // failure is a JSON RPC response error field sent back from the API server.
  66. type failure struct {
  67. Code int `json:"code"` // JSON RPC error code associated with the failure
  68. Message string `json:"message"` // Specific error message of the failure
  69. }
  70. // request forwards an API request to the RPC server, and parses the response.
  71. //
  72. // This is currently painfully non-concurrent, but it will have to do until we
  73. // find the time for niceties like this :P
  74. func (b *rpcBackend) request(ctx context.Context, method string, params []interface{}) (json.RawMessage, error) {
  75. b.lock.Lock()
  76. defer b.lock.Unlock()
  77. if ctx == nil {
  78. ctx = context.Background()
  79. }
  80. // Ugly hack to serialize an empty list properly
  81. if params == nil {
  82. params = []interface{}{}
  83. }
  84. // Assemble the request object
  85. reqID := int(atomic.AddUint32(&b.autoid, 1))
  86. req := &request{
  87. JSONRPC: "2.0",
  88. ID: reqID,
  89. Method: method,
  90. Params: params,
  91. }
  92. if err := b.client.Send(req); err != nil {
  93. return nil, err
  94. }
  95. res := new(response)
  96. errc := make(chan error, 1)
  97. go func() {
  98. errc <- b.client.Recv(res)
  99. }()
  100. select {
  101. case err := <-errc:
  102. if err != nil {
  103. return nil, err
  104. }
  105. case <-ctx.Done():
  106. return nil, ctx.Err()
  107. }
  108. if res.Error != nil {
  109. if res.Error.Message == bind.ErrNoCode.Error() {
  110. return nil, bind.ErrNoCode
  111. }
  112. return nil, fmt.Errorf("remote error: %s", res.Error.Message)
  113. }
  114. return res.Result, nil
  115. }
  116. // HasCode implements ContractVerifier.HasCode by retrieving any code associated
  117. // with the contract from the remote node, and checking its size.
  118. func (b *rpcBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) {
  119. // Execute the RPC code retrieval
  120. block := "latest"
  121. if pending {
  122. block = "pending"
  123. }
  124. res, err := b.request(ctx, "eth_getCode", []interface{}{contract.Hex(), block})
  125. if err != nil {
  126. return false, err
  127. }
  128. var hex string
  129. if err := json.Unmarshal(res, &hex); err != nil {
  130. return false, err
  131. }
  132. // Convert the response back to a Go byte slice and return
  133. return len(common.FromHex(hex)) > 0, nil
  134. }
  135. // ContractCall implements ContractCaller.ContractCall, delegating the execution of
  136. // a contract call to the remote node, returning the reply to for local processing.
  137. func (b *rpcBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) {
  138. // Pack up the request into an RPC argument
  139. args := struct {
  140. To common.Address `json:"to"`
  141. Data string `json:"data"`
  142. }{
  143. To: contract,
  144. Data: common.ToHex(data),
  145. }
  146. // Execute the RPC call and retrieve the response
  147. block := "latest"
  148. if pending {
  149. block = "pending"
  150. }
  151. res, err := b.request(ctx, "eth_call", []interface{}{args, block})
  152. if err != nil {
  153. return nil, err
  154. }
  155. var hex string
  156. if err := json.Unmarshal(res, &hex); err != nil {
  157. return nil, err
  158. }
  159. // Convert the response back to a Go byte slice and return
  160. return common.FromHex(hex), nil
  161. }
  162. // PendingAccountNonce implements ContractTransactor.PendingAccountNonce, delegating
  163. // the current account nonce retrieval to the remote node.
  164. func (b *rpcBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) {
  165. res, err := b.request(ctx, "eth_getTransactionCount", []interface{}{account.Hex(), "pending"})
  166. if err != nil {
  167. return 0, err
  168. }
  169. var hex string
  170. if err := json.Unmarshal(res, &hex); err != nil {
  171. return 0, err
  172. }
  173. nonce, ok := new(big.Int).SetString(hex, 0)
  174. if !ok {
  175. return 0, fmt.Errorf("invalid nonce hex: %s", hex)
  176. }
  177. return nonce.Uint64(), nil
  178. }
  179. // SuggestGasPrice implements ContractTransactor.SuggestGasPrice, delegating the
  180. // gas price oracle request to the remote node.
  181. func (b *rpcBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
  182. res, err := b.request(ctx, "eth_gasPrice", nil)
  183. if err != nil {
  184. return nil, err
  185. }
  186. var hex string
  187. if err := json.Unmarshal(res, &hex); err != nil {
  188. return nil, err
  189. }
  190. price, ok := new(big.Int).SetString(hex, 0)
  191. if !ok {
  192. return nil, fmt.Errorf("invalid price hex: %s", hex)
  193. }
  194. return price, nil
  195. }
  196. // EstimateGasLimit implements ContractTransactor.EstimateGasLimit, delegating
  197. // the gas estimation to the remote node.
  198. func (b *rpcBackend) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) {
  199. // Pack up the request into an RPC argument
  200. args := struct {
  201. From common.Address `json:"from"`
  202. To *common.Address `json:"to"`
  203. Value *rpc.HexNumber `json:"value"`
  204. Data string `json:"data"`
  205. }{
  206. From: sender,
  207. To: contract,
  208. Data: common.ToHex(data),
  209. Value: rpc.NewHexNumber(value),
  210. }
  211. // Execute the RPC call and retrieve the response
  212. res, err := b.request(ctx, "eth_estimateGas", []interface{}{args})
  213. if err != nil {
  214. return nil, err
  215. }
  216. var hex string
  217. if err := json.Unmarshal(res, &hex); err != nil {
  218. return nil, err
  219. }
  220. estimate, ok := new(big.Int).SetString(hex, 0)
  221. if !ok {
  222. return nil, fmt.Errorf("invalid estimate hex: %s", hex)
  223. }
  224. return estimate, nil
  225. }
  226. // SendTransaction implements ContractTransactor.SendTransaction, delegating the
  227. // raw transaction injection to the remote node.
  228. func (b *rpcBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
  229. data, err := rlp.EncodeToBytes(tx)
  230. if err != nil {
  231. return err
  232. }
  233. res, err := b.request(ctx, "eth_sendRawTransaction", []interface{}{common.ToHex(data)})
  234. if err != nil {
  235. return err
  236. }
  237. var hex string
  238. if err := json.Unmarshal(res, &hex); err != nil {
  239. return err
  240. }
  241. return nil
  242. }