aboutsummaryrefslogtreecommitdiffstats
path: root/accounts/abi/bind/base.go
diff options
context:
space:
mode:
authorFelix Lange <fjl@twurst.com>2016-08-22 20:01:28 +0800
committerFelix Lange <fjl@twurst.com>2016-08-22 20:01:28 +0800
commitd62d5fe59aedf9635a175d663632512b1cbbf2c3 (patch)
treea1c4844c3a7e339308cfe14cd42a33dd094d7152 /accounts/abi/bind/base.go
parent056f15aa53470e6cc7b1054f72165f5646ce7e73 (diff)
downloaddexon-d62d5fe59aedf9635a175d663632512b1cbbf2c3.tar
dexon-d62d5fe59aedf9635a175d663632512b1cbbf2c3.tar.gz
dexon-d62d5fe59aedf9635a175d663632512b1cbbf2c3.tar.bz2
dexon-d62d5fe59aedf9635a175d663632512b1cbbf2c3.tar.lz
dexon-d62d5fe59aedf9635a175d663632512b1cbbf2c3.tar.xz
dexon-d62d5fe59aedf9635a175d663632512b1cbbf2c3.tar.zst
dexon-d62d5fe59aedf9635a175d663632512b1cbbf2c3.zip
accounts/abi/bind: use ethereum interfaces
In this commit, contract bindings and their backend start using the Ethereum Go API interfaces offered by ethclient. This makes ethclient a suitable replacement for the old remote backend and gets us one step closer to the final stable Go API that is planned for go-ethereum 1.5. The changes in detail: * Pending state is optional for read only contract bindings. BoundContract attempts to discover the Pending* methods via an interface assertion. There are a couple of advantages to this: ContractCaller is just two methods and can be implemented on top of pretty much anything that provides Ethereum data. Since the backend interfaces are now disjoint, ContractBackend can simply be declared as a union of the reader and writer side. * Caching of HasCode is removed. The caching could go wrong in case of chain reorganisations and removing it simplifies the code a lot. We'll figure out a performant way of providing ErrNoCode before the 1.5 release. * BoundContract now ensures that the backend receives a non-nil context with every call.
Diffstat (limited to 'accounts/abi/bind/base.go')
-rw-r--r--accounts/abi/bind/base.go75
1 files changed, 48 insertions, 27 deletions
diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go
index 80948d3f1..965f51e85 100644
--- a/accounts/abi/bind/base.go
+++ b/accounts/abi/bind/base.go
@@ -20,8 +20,8 @@ import (
"errors"
"fmt"
"math/big"
- "sync/atomic"
+ "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
@@ -62,9 +62,6 @@ type BoundContract struct {
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
-
- latestHasCode uint32 // Cached verification that the latest state contains code for this contract
- pendingHasCode uint32 // Cached verification that the pending state contains code for this contract
}
// NewBoundContract creates a low level contract interface through which calls
@@ -105,25 +102,42 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string,
if opts == nil {
opts = new(CallOpts)
}
- // Make sure we have a contract to operate on, and bail out otherwise
- if (opts.Pending && atomic.LoadUint32(&c.pendingHasCode) == 0) || (!opts.Pending && atomic.LoadUint32(&c.latestHasCode) == 0) {
- if code, err := c.caller.HasCode(opts.Context, c.address, opts.Pending); err != nil {
- return err
- } else if !code {
- return ErrNoCode
- }
- if opts.Pending {
- atomic.StoreUint32(&c.pendingHasCode, 1)
- } else {
- atomic.StoreUint32(&c.latestHasCode, 1)
- }
- }
// Pack the input, call and unpack the results
input, err := c.abi.Pack(method, params...)
if err != nil {
return err
}
- output, err := c.caller.ContractCall(opts.Context, c.address, input, opts.Pending)
+ var (
+ msg = ethereum.CallMsg{To: &c.address, Data: input}
+ ctx = ensureContext(opts.Context)
+ code []byte
+ output []byte
+ )
+ if opts.Pending {
+ pb, ok := c.caller.(PendingContractCaller)
+ if !ok {
+ return ErrNoPendingState
+ }
+ output, err = pb.PendingCallContract(ctx, msg)
+ if err == nil && len(output) == 0 {
+ // Make sure we have a contract to operate on, and bail out otherwise.
+ if code, err = pb.PendingCodeAt(ctx, c.address); err != nil {
+ return err
+ } else if len(code) == 0 {
+ return ErrNoCode
+ }
+ }
+ } else {
+ output, err = c.caller.CallContract(ctx, msg, nil)
+ if err == nil && len(output) == 0 {
+ // Make sure we have a contract to operate on, and bail out otherwise.
+ if code, err = c.caller.CodeAt(ctx, c.address, nil); err != nil {
+ return err
+ } else if len(code) == 0 {
+ return ErrNoCode
+ }
+ }
+ }
if err != nil {
return err
}
@@ -158,7 +172,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
}
nonce := uint64(0)
if opts.Nonce == nil {
- nonce, err = c.transactor.PendingAccountNonce(opts.Context, opts.From)
+ nonce, err = c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
if err != nil {
return nil, fmt.Errorf("failed to retrieve account nonce: %v", err)
}
@@ -168,7 +182,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
// Figure out the gas allowance and gas price values
gasPrice := opts.GasPrice
if gasPrice == nil {
- gasPrice, err = c.transactor.SuggestGasPrice(opts.Context)
+ gasPrice, err = c.transactor.SuggestGasPrice(ensureContext(opts.Context))
if err != nil {
return nil, fmt.Errorf("failed to suggest gas price: %v", err)
}
@@ -176,18 +190,18 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
gasLimit := opts.GasLimit
if gasLimit == nil {
// Gas estimation cannot succeed without code for method invocations
- if contract != nil && atomic.LoadUint32(&c.pendingHasCode) == 0 {
- if code, err := c.transactor.HasCode(opts.Context, c.address, true); err != nil {
+ if contract != nil {
+ if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
return nil, err
- } else if !code {
+ } else if len(code) == 0 {
return nil, ErrNoCode
}
- atomic.StoreUint32(&c.pendingHasCode, 1)
}
// If the contract surely has code (or code is not needed), estimate the transaction
- gasLimit, err = c.transactor.EstimateGasLimit(opts.Context, opts.From, contract, value, input)
+ msg := ethereum.CallMsg{From: opts.From, To: contract, Value: value, Data: input}
+ gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg)
if err != nil {
- return nil, fmt.Errorf("failed to exstimate gas needed: %v", err)
+ return nil, fmt.Errorf("failed to estimate gas needed: %v", err)
}
}
// Create the transaction, sign it and schedule it for execution
@@ -204,8 +218,15 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
if err != nil {
return nil, err
}
- if err := c.transactor.SendTransaction(opts.Context, signedTx); err != nil {
+ if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
return nil, err
}
return signedTx, nil
}
+
+func ensureContext(ctx context.Context) context.Context {
+ if ctx == nil {
+ return context.TODO()
+ }
+ return ctx
+}