diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index 3704e6262e..1ff838960e 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -26,11 +26,6 @@ import ( "github.com/ethereum/go-ethereum/common" ) -// Executer is an executer method for performing state executions. It takes one -// argument which is the input data and expects output data to be returned as -// multiple 32 byte word length concatenated slice -type Executer func(datain []byte) []byte - // The ABI holds information about a contract's context and available // invokable methods. It will allow you to type check function calls and // packs data accordingly. @@ -150,21 +145,6 @@ func toGoType(i int, t Argument, output []byte) (interface{}, error) { return nil, fmt.Errorf("abi: unknown type %v", t.Type.T) } -// Call will unmarshal the output of the call in v. It will return an error if -// invalid type is given or if the output is too short to conform to the ABI -// spec. -// -// Call supports all of the available types and accepts a struct or an interface -// slice if the return is a tuple. -func (abi ABI) Call(executer Executer, v interface{}, name string, args ...interface{}) error { - callData, err := abi.Pack(name, args...) - if err != nil { - return err - } - - return abi.unmarshal(v, name, executer(callData)) -} - // these variable are used to determine certain types during type assertion for // assignment. var ( @@ -174,8 +154,8 @@ var ( r_byte = reflect.TypeOf(byte(0)) ) -// unmarshal output in v according to the abi specification -func (abi ABI) unmarshal(v interface{}, name string, output []byte) error { +// Unpack output in v according to the abi specification +func (abi ABI) Unpack(v interface{}, name string, output []byte) error { var method = abi.Methods[name] if len(output) == 0 { diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index d1b8330e3d..28556821e9 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -451,7 +451,7 @@ func TestMultiReturnWithStruct(t *testing.T) { Int *big.Int String string } - err = abi.unmarshal(&inter, "multi", buff.Bytes()) + err = abi.Unpack(&inter, "multi", buff.Bytes()) if err != nil { t.Error(err) } @@ -469,7 +469,7 @@ func TestMultiReturnWithStruct(t *testing.T) { Int *big.Int } - err = abi.unmarshal(&reversed, "multi", buff.Bytes()) + err = abi.Unpack(&reversed, "multi", buff.Bytes()) if err != nil { t.Error(err) } @@ -501,7 +501,7 @@ func TestMultiReturnWithSlice(t *testing.T) { buff.Write(common.RightPadBytes([]byte(stringOut), 32)) var inter []interface{} - err = abi.unmarshal(&inter, "multi", buff.Bytes()) + err = abi.Unpack(&inter, "multi", buff.Bytes()) if err != nil { t.Error(err) } @@ -533,13 +533,13 @@ func TestMarshalArrays(t *testing.T) { output := common.LeftPadBytes([]byte{1}, 32) var bytes10 [10]byte - err = abi.unmarshal(&bytes10, "bytes32", output) + err = abi.Unpack(&bytes10, "bytes32", output) if err == nil || err.Error() != "abi: cannot unmarshal src (len=32) in to dst (len=10)" { t.Error("expected error or bytes32 not be assignable to bytes10:", err) } var bytes32 [32]byte - err = abi.unmarshal(&bytes32, "bytes32", output) + err = abi.Unpack(&bytes32, "bytes32", output) if err != nil { t.Error("didn't expect error:", err) } @@ -553,13 +553,13 @@ func TestMarshalArrays(t *testing.T) { ) var b10 B10 - err = abi.unmarshal(&b10, "bytes32", output) + err = abi.Unpack(&b10, "bytes32", output) if err == nil || err.Error() != "abi: cannot unmarshal src (len=32) in to dst (len=10)" { t.Error("expected error or bytes32 not be assignable to bytes10:", err) } var b32 B32 - err = abi.unmarshal(&b32, "bytes32", output) + err = abi.Unpack(&b32, "bytes32", output) if err != nil { t.Error("didn't expect error:", err) } @@ -569,7 +569,7 @@ func TestMarshalArrays(t *testing.T) { output[10] = 1 var shortAssignLong [32]byte - err = abi.unmarshal(&shortAssignLong, "bytes10", output) + err = abi.Unpack(&shortAssignLong, "bytes10", output) if err != nil { t.Error("didn't expect error:", err) } @@ -594,7 +594,7 @@ func TestUnmarshal(t *testing.T) { // marshal int var Int *big.Int - err = abi.unmarshal(&Int, "int", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) + err = abi.Unpack(&Int, "int", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) if err != nil { t.Error(err) } @@ -605,7 +605,7 @@ func TestUnmarshal(t *testing.T) { // marshal bool var Bool bool - err = abi.unmarshal(&Bool, "bool", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) + err = abi.Unpack(&Bool, "bool", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) if err != nil { t.Error(err) } @@ -622,7 +622,7 @@ func TestUnmarshal(t *testing.T) { buff.Write(bytesOut) var Bytes []byte - err = abi.unmarshal(&Bytes, "bytes", buff.Bytes()) + err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) if err != nil { t.Error(err) } @@ -638,7 +638,7 @@ func TestUnmarshal(t *testing.T) { bytesOut = common.RightPadBytes([]byte("hello"), 64) buff.Write(bytesOut) - err = abi.unmarshal(&Bytes, "bytes", buff.Bytes()) + err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) if err != nil { t.Error(err) } @@ -654,7 +654,7 @@ func TestUnmarshal(t *testing.T) { bytesOut = common.RightPadBytes([]byte("hello"), 63) buff.Write(bytesOut) - err = abi.unmarshal(&Bytes, "bytes", buff.Bytes()) + err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) if err != nil { t.Error(err) } @@ -664,7 +664,7 @@ func TestUnmarshal(t *testing.T) { } // marshal dynamic bytes output empty - err = abi.unmarshal(&Bytes, "bytes", nil) + err = abi.Unpack(&Bytes, "bytes", nil) if err == nil { t.Error("expected error") } @@ -675,7 +675,7 @@ func TestUnmarshal(t *testing.T) { buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005")) buff.Write(common.RightPadBytes([]byte("hello"), 32)) - err = abi.unmarshal(&Bytes, "bytes", buff.Bytes()) + err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) if err != nil { t.Error(err) } @@ -689,7 +689,7 @@ func TestUnmarshal(t *testing.T) { buff.Write(common.RightPadBytes([]byte("hello"), 32)) var hash common.Hash - err = abi.unmarshal(&hash, "fixed", buff.Bytes()) + err = abi.Unpack(&hash, "fixed", buff.Bytes()) if err != nil { t.Error(err) } @@ -702,12 +702,12 @@ func TestUnmarshal(t *testing.T) { // marshal error buff.Reset() buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) - err = abi.unmarshal(&Bytes, "bytes", buff.Bytes()) + err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) if err == nil { t.Error("expected error") } - err = abi.unmarshal(&Bytes, "multi", make([]byte, 64)) + err = abi.Unpack(&Bytes, "multi", make([]byte, 64)) if err == nil { t.Error("expected error") } @@ -722,7 +722,7 @@ func TestUnmarshal(t *testing.T) { buff.Write(bytesOut) var out []interface{} - err = abi.unmarshal(&out, "mixedBytes", buff.Bytes()) + err = abi.Unpack(&out, "mixedBytes", buff.Bytes()) if err != nil { t.Fatal("didn't expect error:", err) } diff --git a/accounts/abi/bind/backend.go b/accounts/abi/bind/backend.go new file mode 100644 index 0000000000..7fe70cd351 --- /dev/null +++ b/accounts/abi/bind/backend.go @@ -0,0 +1,231 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bind + +import ( + "encoding/json" + "fmt" + "math/big" + "sync" + "sync/atomic" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" +) + +// ContractCaller defines the methods needed to allow operating with contract on a read +// only basis. +type ContractCaller interface { + // ContractCall executes an Ethereum contract call with the specified data as + // the input. + ContractCall(contract common.Address, data []byte) ([]byte, error) +} + +// ContractTransactor defines the methods needed to allow operating with contract +// on a write only basis. Beside the transacting method, the remainder are helpers +// used when the user does not provide some needed values, but rather leaves it up +// to the transactor to decide. +type ContractTransactor interface { + // Nonce retrieves the current pending nonce associated with an account. + AccountNonce(account common.Address) (uint64, error) + + // GasPrice retrieves the currently suggested gas price to allow a timely execution + // of a transaction. + GasPrice() (*big.Int, error) + + // GasLimit tries to estimate the gas needed to execute a specific transaction. + GasLimit(sender, contract common.Address, value *big.Int, data []byte) (*big.Int, error) + + // SendTransaction injects the transaction into the pending pool for execution. + SendTransaction(*types.Transaction) error +} + +// ContractBackend defines the methods needed to allow operating with contract +// on a read-write basis. +type ContractBackend interface { + ContractCaller + ContractTransactor +} + +// rpcBackend implements bind.ContractBackend, and acts as the data provider to +// Ethereum contracts bound to Go structs. It uses an RPC connection to delegate +// all its functionality. +// +// Note: The current implementation is a blocking one. This should be replaced +// by a proper async version when a real RPC client is created. +type rpcBackend struct { + client rpc.Client // RPC client connection to interact with an API server + autoid uint32 // ID number to use for the next API request + lock sync.Mutex // Singleton access until we get to request multiplexing +} + +// NewRPCBackend creates a new binding backend to an RPC provider that can be +// used to interact with remote contracts. +func NewRPCBackend(client rpc.Client) ContractBackend { + return &rpcBackend{ + client: client, + } +} + +// request is a JSON RPC request package assembled internally from the client +// method calls. +type request struct { + JsonRpc string `json:"jsonrpc"` // Version of the JSON RPC protocol, always set to 2.0 + Id int `json:"id"` // Auto incrementing ID number for this request + Method string `json:"method"` // Remote procedure name to invoke on the server + Params []interface{} `json:"params"` // List of parameters to pass through (keep types simple) +} + +// response is a JSON RPC response package sent back from the API server. +type response struct { + JsonRpc string `json:"jsonrpc"` // Version of the JSON RPC protocol, always set to 2.0 + Id int `json:"id"` // Auto incrementing ID number for this request + Error json.RawMessage `json:"error"` // Any error returned by the remote side + Result json.RawMessage `json:"result"` // Whatever the remote side sends us in reply +} + +// request forwards an API request to the RPC server, and parses the response. +// +// This is currently painfully non-concurrent, but it will have to do until we +// find the time for niceties like this :P +func (backend *rpcBackend) request(method string, params []interface{}) (json.RawMessage, error) { + backend.lock.Lock() + defer backend.lock.Unlock() + + // Ugly hack to serialize an empty list properly + if params == nil { + params = []interface{}{} + } + // Assemble the request object + req := &request{ + JsonRpc: "2.0", + Id: int(atomic.AddUint32(&backend.autoid, 1)), + Method: method, + Params: params, + } + if err := backend.client.Send(req); err != nil { + return nil, err + } + res := new(response) + if err := backend.client.Recv(res); err != nil { + return nil, err + } + if len(res.Error) > 0 { + return nil, fmt.Errorf("remote error: %s", string(res.Error)) + } + return res.Result, nil +} + +// ContractCall implements ContractCaller.ContractCall, delegating the execution of +// a contract call to the remote node, returning the reply to for local processing. +func (b *rpcBackend) ContractCall(contract common.Address, data []byte) ([]byte, error) { + // Pack up the request into an RPC argument + args := struct { + To common.Address `json:"to"` + Data string `json:"data"` + }{ + To: contract, + Data: common.ToHex(data), + } + // Execute the RPC call and retrieve the response + res, err := b.request("eth_call", []interface{}{args, "pending"}) + if err != nil { + return nil, err + } + var hex string + if err := json.Unmarshal(res, &hex); err != nil { + return nil, err + } + // Convert the response back to a Go byte slice and return + return common.FromHex(hex), nil +} + +// AccountNonce implements ContractTransactor.AccountNonce, delegating the +// current account nonce retrieval to the remote node. +func (b *rpcBackend) AccountNonce(account common.Address) (uint64, error) { + res, err := b.request("eth_getTransactionCount", []interface{}{account.Hex(), "pending"}) + if err != nil { + return 0, err + } + var hex string + if err := json.Unmarshal(res, &hex); err != nil { + return 0, err + } + return new(big.Int).SetBytes(common.FromHex(hex)).Uint64(), nil +} + +// GasPrice implements ContractTransactor.GasPrice, delegating the gas price +// oracle request to the remote node. +func (b *rpcBackend) GasPrice() (*big.Int, error) { + res, err := b.request("eth_gasPrice", nil) + if err != nil { + return nil, err + } + var hex string + if err := json.Unmarshal(res, &hex); err != nil { + return nil, err + } + return new(big.Int).SetBytes(common.FromHex(hex)), nil +} + +// GasLimit implements ContractTransactor.GasLimit, delegating the gas estimation +// to the remote node. +func (b *rpcBackend) GasLimit(sender, contract common.Address, value *big.Int, data []byte) (*big.Int, error) { + // Pack up the request into an RPC argument + args := struct { + From common.Address `json:"from"` + To common.Address `json:"to"` + Value *rpc.HexNumber `json:"value"` + Data string `json:"data"` + }{ + From: sender, + To: contract, + Data: common.ToHex(data), + Value: rpc.NewHexNumber(value), + } + // Execute the RPC call and retrieve the response + res, err := b.request("eth_estimateGas", []interface{}{args, "pending"}) + if err != nil { + return nil, err + } + var hex string + if err := json.Unmarshal(res, &hex); err != nil { + return nil, err + } + // Convert the response back to a Go byte slice and return + return new(big.Int).SetBytes(common.FromHex(hex)), nil +} + +// Transact implements ContractTransactor.SendTransaction, delegating the raw +// transaction injection to the remote node. +func (b *rpcBackend) SendTransaction(tx *types.Transaction) error { + data, err := rlp.EncodeToBytes(tx) + if err != nil { + return err + } + res, err := b.request("eth_sendRawTransaction", []interface{}{data}) + if err != nil { + return err + } + var hex string + if err := json.Unmarshal(res, &hex); err != nil { + return err + } + return nil +} diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index 16d529f89c..75e3c1d2d0 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -21,40 +21,11 @@ import ( "fmt" "math/big" - "github.com/barakmich/glog" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm/runtime" - "github.com/ethereum/go-ethereum/eth/filters" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/logger" ) -// GasOracleFn is a gas price oracle function callback to request the suggestion -// of an estimated gas price that should be used to execute a paid transaction. -type GasOracleFn func() *big.Int - -// MinerStateFn is a callback method to retrieve the currently pending state -// according ti our local mining instance. -type MinerStateFn func() (*types.Block, *state.StateDB) - -// TxScheduleFn is a callback method to schedule a transaction for execution. -type TxScheduleFn func(*types.Transaction) error - -// ContractOpts is the set of contract parameters that can be used to fine tune -// behavior tailoring to a specific use case. -type ContractOpts struct { - Database ethdb.Database // Chain and state database needed to access past logs - EventMux *event.TypeMux // Event multiplexer to publish log events merged with others - GasOracle GasOracleFn // Gas price oracle to allow not specifying transaction prices - MinerState MinerStateFn // Pending state retriever to allow nonce and gas limit estimation - TxSchedule TxScheduleFn // Transaction scheduler to inject a transaction into the pool -} - // SignerFn is a signer function callback when a contract requires a method to // sign the transaction before submission. type SignerFn func(common.Address, *types.Transaction) (*types.Transaction, error) @@ -74,28 +45,20 @@ type AuthOpts struct { // Ethereum network. It contains a collection of methods that are used by the // higher level contract bindings to operate. type BoundContract struct { - address common.Address // Deployment address of the contract on the Ethereum blockchain - abi abi.ABI // Reflect based ABI to access the correct Ethereum methods - - blockchain *core.BlockChain // Ethereum blockchain to use for state retrieval - options *ContractOpts // Options fine tuning contract behaviour - filters *filters.FilterSystem // Filter system to handle the contract events + address common.Address // Deployment address of the contract on the Ethereum blockchain + abi abi.ABI // Reflect based ABI to access the correct Ethereum methods + caller ContractCaller // Read interface to interact with the blockchain + transactor ContractTransactor // Write interface to interact with the blockchain } -// NewBoundContract initialises a new ABI and returns the contract. It does not -// deploy the contract, hence the name. -func NewBoundContract(address common.Address, abi abi.ABI, blockchain *core.BlockChain, opts ContractOpts) *BoundContract { - // Initialize any needed values for the contract options - if opts.EventMux == nil { - opts.EventMux = new(event.TypeMux) - } - // Create and return the contract base +// NewBoundContract creates a low level contract interface through which calls +// and transactions may be made through. +func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller, transactor ContractTransactor) *BoundContract { return &BoundContract{ address: address, abi: abi, - blockchain: blockchain, - options: &opts, - filters: filters.NewFilterSystem(opts.EventMux), + caller: caller, + transactor: transactor, } } @@ -104,28 +67,20 @@ func NewBoundContract(address common.Address, abi abi.ABI, blockchain *core.Bloc // returns, a slice of interfaces for anonymous returns and a struct for named // returns. func (c *BoundContract) Call(result interface{}, method string, params ...interface{}) error { - return c.abi.Call(c.execute, result, method, params...) -} - -// execute runs the contract code for the given input value and returns the output. -func (c *BoundContract) execute(input []byte) []byte { - state, _ := c.blockchain.State() - - output, err := runtime.Call(c.address, input, &runtime.Config{ - GetHashFn: core.GetHashFn(c.blockchain.CurrentBlock().ParentHash(), c.blockchain), - State: state, - }) + input, err := c.abi.Pack(method, params...) if err != nil { - glog.V(logger.Warn).Infof("contract call failed: %v", err) - return nil + return err } - return output + output, err := c.caller.ContractCall(c.address, input) + if err != nil { + return err + } + return c.abi.Unpack(result, method, output) } // Transact invokes the (paid) contract method with params as input values and // value as the fund transfer to the contract. func (c *BoundContract) Transact(opts *AuthOpts, method string, params ...interface{}) (*types.Transaction, error) { - // Pack up the method and arguments into an input data blob input, err := c.abi.Pack(method, params...) if err != nil { return nil, err @@ -135,39 +90,32 @@ func (c *BoundContract) Transact(opts *AuthOpts, method string, params ...interf if value == nil { value = new(big.Int) } - nonce := opts.Nonce - if nonce == nil { - if c.options.MinerState == nil { - return nil, errors.New("account nonce nil and no miner state retriever specified to estimate") + nonce := uint64(0) + if opts.Nonce == nil { + nonce, err = c.transactor.AccountNonce(opts.Account) + if err != nil { + return nil, fmt.Errorf("failed to retrieve account nonce: %v", err) } - _, statedb := c.options.MinerState() - if statedb == nil { - return nil, errors.New("miner state retriever returned nil") - } - nonce = new(big.Int).SetUint64(statedb.GetNonce(opts.Account)) + } else { + nonce = opts.Nonce.Uint64() } // Figure out the gas allowance and gas price values gasPrice := opts.GasPrice if gasPrice == nil { - if c.options.GasOracle == nil { - return nil, errors.New("gas price nil and no price oracle set") - } - if gasPrice = c.options.GasOracle(); gasPrice == nil { - return nil, errors.New("gas oracle suggested nil price") + gasPrice, err = c.transactor.GasPrice() + if err != nil { + return nil, fmt.Errorf("failed to suggest gas price: %v", err) } } gasLimit := opts.GasLimit if gasLimit == nil { - limit, err := c.estimate(opts.Account, value, gasPrice, input) + gasLimit, err = c.transactor.GasLimit(opts.Account, c.address, value, input) if err != nil { - return nil, fmt.Errorf("gas estimation failed: %v", err) - } - if gasLimit = limit; gasLimit == nil { - return nil, errors.New("gas estimator suggested nil limit") + return nil, fmt.Errorf("failed to exstimate gas needed: %v", err) } } // Create the transaction, sign it and schedule it for execution - rawTx := types.NewTransaction(nonce.Uint64(), c.address, value, gasLimit, gasPrice, input) + rawTx := types.NewTransaction(nonce, c.address, value, gasLimit, gasPrice, input) if opts.Signer == nil { return nil, errors.New("no signer to authorize the transaction with") } @@ -175,63 +123,8 @@ func (c *BoundContract) Transact(opts *AuthOpts, method string, params ...interf if err != nil { return nil, err } - if c.options.TxSchedule == nil { - return nil, errors.New("no transaction scheduler configured") - } - if err := c.options.TxSchedule(signedTx); err != nil { + if err := c.transactor.SendTransaction(signedTx); err != nil { return nil, err } return signedTx, nil } - -// estimate tries to calculate the approximate gas required by a transaction. -func (c *BoundContract) estimate(sender common.Address, value, price *big.Int, input []byte) (*big.Int, error) { - // Create a copy of the current state db to screw around with - if c.options.MinerState == nil { - return nil, errors.New("no miner state retriever configured") - } - block, statedb := c.options.MinerState() - if block == nil || statedb == nil { - return nil, errors.New("pending miner state nil") - } - statedb = statedb.Copy() - - // Set infinite balance to the sender account - from := statedb.GetOrNewStateObject(sender) - from.SetBalance(common.MaxBig) - - // Assemble the call invocation to measure the gas usage - msg := callmsg{ - from: from, - to: &c.address, - gasLimit: block.GasLimit(), - gasPrice: price, - value: value, - data: input, - } - // Execute the call and return - vmenv := core.NewEnv(statedb, c.blockchain, msg, block.Header()) - gaspool := new(core.GasPool).AddGas(common.MaxBig) - - _, gas, err := core.ApplyMessage(vmenv, msg, gaspool) - return gas, err -} - -// callmsg implements core.Message to allow passing it as a transaction simulator. -type callmsg struct { - from *state.StateObject - to *common.Address - gasLimit *big.Int - gasPrice *big.Int - value *big.Int - data []byte -} - -func (m callmsg) From() (common.Address, error) { return m.from.Address(), nil } -func (m callmsg) FromFrontier() (common.Address, error) { return m.from.Address(), nil } -func (m callmsg) Nonce() uint64 { return m.from.Nonce() } -func (m callmsg) To() *common.Address { return m.to } -func (m callmsg) GasPrice() *big.Int { return m.gasPrice } -func (m callmsg) Gas() *big.Int { return m.gasLimit } -func (m callmsg) Value() *big.Int { return m.value } -func (m callmsg) Data() []byte { return m.data } diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go index f951decbe2..24cb8dd8cd 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/bind/bind.go @@ -77,18 +77,61 @@ func bindContract(kind string, abi string) string { // Generate the Go struct with all the maintenance fields code += fmt.Sprintf("// %s is an auto generated Go binding around an Ethereum contract.\n", kind) code += fmt.Sprintf("type %s struct {\n", kind) - code += fmt.Sprintf("contract *bind.BoundContract // Generic contract wrapper for the low level calls\n") + code += fmt.Sprintf(" %sCaller // Read-only binding to the contract\n", kind) + code += fmt.Sprintf(" %sTransactor // Write-only binding to the contract\n", kind) + code += fmt.Sprintf("}\n\n") + + code += fmt.Sprintf("// %sCaller is an auto generated read-only Go binding around an Ethereum contract.\n", kind) + code += fmt.Sprintf("type %sCaller struct {\n", kind) + code += fmt.Sprintf(" common *common%s // Contract binding common to callers and transactors\n", kind) + code += fmt.Sprintf("}\n\n") + + code += fmt.Sprintf("// %sTransactor is an auto generated write-only Go binding around an Ethereum contract.\n", kind) + code += fmt.Sprintf("type %sTransactor struct {\n", kind) + code += fmt.Sprintf(" common *common%s // Contract binding common to callers and transactors\n", kind) + code += fmt.Sprintf("}\n\n") + + code += fmt.Sprintf("// common%s is an auto generated Go binding around an Ethereum contract.\n", kind) + code += fmt.Sprintf("type common%s struct {\n", kind) + code += fmt.Sprintf(" contract *bind.BoundContract // Generic contract wrapper for the low level calls\n") code += fmt.Sprintf("}\n\n") // Generate the constructor to create a bound contract code += fmt.Sprintf("// New%s creates a new instance of %s, bound to a specific deployed contract.\n", kind, kind) - code += fmt.Sprintf("func New%s(address common.Address, blockchain *core.BlockChain, opts bind.ContractOpts) (*%s, error) {\n", kind, kind) + code += fmt.Sprintf("func New%s(address common.Address, backend bind.ContractBackend) (*%s, error) {\n", kind, kind) + code += fmt.Sprintf(" common, err := newCommon%s(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor))\n", kind) + code += fmt.Sprintf(" if err != nil {\n") + code += fmt.Sprintf(" return nil, err\n") + code += fmt.Sprintf(" }\n") + code += fmt.Sprintf(" return &%s{%sCaller: %sCaller{common: common}, %sTransactor: %sTransactor{common: common}}, nil\n", kind, kind, kind, kind, kind) + code += fmt.Sprintf("}\n\n") + + code += fmt.Sprintf("// New%sCaller creates a new read-only instance of %s, bound to a specific deployed contract.\n", kind, kind) + code += fmt.Sprintf("func New%sCaller(address common.Address, caller bind.ContractCaller) (*%sCaller, error) {\n", kind, kind) + code += fmt.Sprintf(" common, err := newCommon%s(address, caller, nil)\n", kind) + code += fmt.Sprintf(" if err != nil {\n") + code += fmt.Sprintf(" return nil, err\n") + code += fmt.Sprintf(" }\n") + code += fmt.Sprintf(" return &%sCaller{common: common}, nil\n", kind) + code += fmt.Sprintf("}\n\n") + + code += fmt.Sprintf("// New%sTransactor creates a new write-only instance of %s, bound to a specific deployed contract.\n", kind, kind) + code += fmt.Sprintf("func New%sTransactor(address common.Address, transactor bind.ContractTransactor) (*%sTransactor, error) {\n", kind, kind) + code += fmt.Sprintf(" common, err := newCommon%s(address, nil, transactor)\n", kind) + code += fmt.Sprintf(" if err != nil {\n") + code += fmt.Sprintf(" return nil, err\n") + code += fmt.Sprintf(" }\n") + code += fmt.Sprintf(" return &%sTransactor{common: common}, nil\n", kind) + code += fmt.Sprintf("}\n\n") + + code += fmt.Sprintf("// newCommon%s creates an internal instance of %s, bound to a specific deployed contract.\n", kind, kind) + code += fmt.Sprintf("func newCommon%s(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*common%s, error) {\n", kind, kind) code += fmt.Sprintf(" parsed, err := abi.JSON(strings.NewReader(%sABI))\n", kind) code += fmt.Sprintf(" if err != nil {\n") code += fmt.Sprintf(" return nil, err\n") code += fmt.Sprintf(" }\n") - code += fmt.Sprintf(" return &%s{\n", kind) - code += fmt.Sprintf(" contract: bind.NewBoundContract(address, parsed, blockchain, opts),\n") + code += fmt.Sprintf(" return &common%s{\n", kind) + code += fmt.Sprintf(" contract: bind.NewBoundContract(address, parsed, caller, transactor),\n") code += fmt.Sprintf(" }, nil\n") code += fmt.Sprintf("}") @@ -121,13 +164,15 @@ func bindMethod(kind string, method abi.Method) string { docs += fmt.Sprintf("// \n") docs += fmt.Sprintf("// Solidity: %s", strings.TrimPrefix(method.String(), "function ")) - // Generate the method itself and return + // Generate the method itself for both the read/write version and the combo too + code := fmt.Sprintf("%s\n", prologue) if method.Const { - return fmt.Sprintf("%s\n%s\nfunc (_%s *%s) %s(%s) (%s) {\n%s\n}\n", prologue, docs, kind, kind, name, strings.Join(args, ","), strings.Join(returns, ","), bindCallBody(kind, method.Name, args, returns)) + code += fmt.Sprintf("%s\nfunc (_%s *%sCaller) %s(%s) (%s) {\n%s\n}\n", docs, kind, kind, name, strings.Join(args, ","), strings.Join(returns, ","), bindCallBody(kind, method.Name, args, returns)) } else { args = append([]string{"auth *bind.AuthOpts"}, args...) - return fmt.Sprintf("%s\n%s\nfunc (_%s *%s) %s(%s) (*types.Transaction, error) {\n%s\n}\n", prologue, docs, kind, kind, name, strings.Join(args, ","), bindTransactionBody(kind, method.Name, args)) + code += fmt.Sprintf("%s\nfunc (_%s *%sTransactor) %s(%s) (*types.Transaction, error) {\n%s\n}\n", docs, kind, kind, name, strings.Join(args, ","), bindTransactionBody(kind, method.Name, args)) } + return code } // bindType converts a Solidity type to a Go one. Since there is no clear mapping @@ -239,7 +284,7 @@ func bindCallBody(kind string, method string, params []string, returns []string) input = "," + strings.Join(inputs, ",") } // Request executing the contract call and return the results with the errors - body += fmt.Sprintf("err := _%s.contract.Call(%s, \"%s\" %s)\n", kind, result, method, input) + body += fmt.Sprintf("err := _%s.common.contract.Call(%s, \"%s\" %s)\n", kind, result, method, input) outs := make([]string, 0, len(returns)) for i, ret := range returns[:len(returns)-1] { // Handle th final error separately @@ -269,5 +314,5 @@ func bindTransactionBody(kind string, method string, params []string) string { input = "," + strings.Join(inputs, ",") } // Request executing the contract call and return the results with the errors - return fmt.Sprintf("return _%s.contract.Transact(auth, \"%s\" %s)", kind, method, input) + return fmt.Sprintf("return _%s.common.contract.Transact(auth, \"%s\" %s)", kind, method, input) } diff --git a/eth/api.go b/eth/api.go index 71de40e509..3dee926a8f 100644 --- a/eth/api.go +++ b/eth/api.go @@ -688,7 +688,7 @@ func (s *PublicBlockChainAPI) Call(args CallArgs, blockNr rpc.BlockNumber) (stri // EstimateGas returns an estimate of the amount of gas needed to execute the given transaction. func (s *PublicBlockChainAPI) EstimateGas(args CallArgs) (*rpc.HexNumber, error) { - _, gas, err := s.doCall(args, rpc.LatestBlockNumber) + _, gas, err := s.doCall(args, rpc.PendingBlockNumber) return rpc.NewHexNumber(gas), err }