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