aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--accounts/abi/bind/backend.go10
-rw-r--r--accounts/abi/bind/backends/remote.go15
-rw-r--r--accounts/abi/bind/backends/simulated.go11
-rw-r--r--accounts/abi/bind/bind_test.go28
-rw-r--r--eth/api.go15
5 files changed, 75 insertions, 4 deletions
diff --git a/accounts/abi/bind/backend.go b/accounts/abi/bind/backend.go
index 328f9f3b7..7442557cc 100644
--- a/accounts/abi/bind/backend.go
+++ b/accounts/abi/bind/backend.go
@@ -17,12 +17,22 @@
package bind
import (
+ "errors"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
+// ErrNoCode is returned by call and transact operations for which the requested
+// recipient contract to operate on does not exist in the state db or does not
+// have any code associated with it (i.e. suicided).
+//
+// Please note, this error string is part of the RPC API and is expected by the
+// native contract bindings to signal this particular error. Do not change this
+// as it will break all dependent code!
+var ErrNoCode = errors.New("no contract code at given address")
+
// ContractCaller defines the methods needed to allow operating with contract on a read
// only basis.
type ContractCaller interface {
diff --git a/accounts/abi/bind/backends/remote.go b/accounts/abi/bind/backends/remote.go
index 8e990f076..9b3647192 100644
--- a/accounts/abi/bind/backends/remote.go
+++ b/accounts/abi/bind/backends/remote.go
@@ -66,10 +66,16 @@ type request struct {
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
+ Error *failure `json:"error"` // Any error returned by the remote side
Result json.RawMessage `json:"result"` // Whatever the remote side sends us in reply
}
+// failure is a JSON RPC response error field sent back from the API server.
+type failure struct {
+ Code int `json:"code"` // JSON RPC error code associated with the failure
+ Message string `json:"message"` // Specific error message of the failure
+}
+
// 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
@@ -96,8 +102,11 @@ func (b *rpcBackend) request(method string, params []interface{}) (json.RawMessa
if err := b.client.Recv(res); err != nil {
return nil, err
}
- if len(res.Error) > 0 {
- return nil, fmt.Errorf("remote error: %s", string(res.Error))
+ if res.Error != nil {
+ if res.Error.Message == bind.ErrNoCode.Error() {
+ return nil, bind.ErrNoCode
+ }
+ return nil, fmt.Errorf("remote error: %s", res.Error.Message)
}
return res.Result, nil
}
diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index 6cdb9a0cc..4866c4f58 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -92,6 +92,10 @@ func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pe
block = b.blockchain.CurrentBlock()
statedb, _ = b.blockchain.State()
}
+ // If there's no code to interact with, respond with an appropriate error
+ if code := statedb.GetCode(contract); len(code) == 0 {
+ return nil, bind.ErrNoCode
+ }
// Set infinite balance to the a fake caller account
from := statedb.GetOrNewStateObject(common.Address{})
from.SetBalance(common.MaxBig)
@@ -134,7 +138,12 @@ func (b *SimulatedBackend) EstimateGasLimit(sender common.Address, contract *com
block = b.pendingBlock
statedb = b.pendingState.Copy()
)
-
+ // If there's no code to interact with, respond with an appropriate error
+ if contract != nil {
+ if code := statedb.GetCode(*contract); len(code) == 0 {
+ return nil, bind.ErrNoCode
+ }
+ }
// Set infinite balance to the a fake caller account
from := statedb.GetOrNewStateObject(sender)
from.SetBalance(common.MaxBig)
diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go
index 5c36bc48f..f9cc8aba4 100644
--- a/accounts/abi/bind/bind_test.go
+++ b/accounts/abi/bind/bind_test.go
@@ -303,6 +303,34 @@ var bindTests = []struct {
}
`,
},
+ // Tests that non-existent contracts are reported as such (though only simulator test)
+ {
+ `NonExistent`,
+ `
+ contract NonExistent {
+ function String() constant returns(string) {
+ return "I don't exist";
+ }
+ }
+ `,
+ `6060604052609f8060106000396000f3606060405260e060020a6000350463f97a60058114601a575b005b600060605260c0604052600d60809081527f4920646f6e27742065786973740000000000000000000000000000000000000060a052602060c0908152600d60e081905281906101009060a09080838184600060046012f15050815172ffffffffffffffffffffffffffffffffffffff1916909152505060405161012081900392509050f3`,
+ `[{"constant":true,"inputs":[],"name":"String","outputs":[{"name":"","type":"string"}],"type":"function"}]`,
+ `
+ // Create a simulator and wrap a non-deployed contract
+ sim := backends.NewSimulatedBackend()
+
+ nonexistent, err := NewNonExistent(common.Address{}, sim)
+ if err != nil {
+ t.Fatalf("Failed to access non-existent contract: %v", err)
+ }
+ // Ensure that contract calls fail with the appropriate error
+ if res, err := nonexistent.String(nil); err == nil {
+ t.Fatalf("Call succeeded on non-existent contract: %v", res)
+ } else if (err != bind.ErrNoCode) {
+ t.Fatalf("Error mismatch: have %v, want %v", err, bind.ErrNoCode)
+ }
+ `,
+ },
}
// Tests that packages generated by the binder can be successfully compiled and
diff --git a/eth/api.go b/eth/api.go
index a0b1f8ac2..02b34541f 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -51,6 +51,15 @@ import (
"golang.org/x/net/context"
)
+// ErrNoCode is returned by call and transact operations for which the requested
+// recipient contract to operate on does not exist in the state db or does not
+// have any code associated with it (i.e. suicided).
+//
+// Please note, this error string is part of the RPC API and is expected by the
+// native contract bindings to signal this particular error. Do not change this
+// as it will break all dependent code!
+var ErrNoCode = errors.New("no contract code at given address")
+
const defaultGas = uint64(90000)
// blockByNumber is a commonly used helper function which retrieves and returns
@@ -694,6 +703,12 @@ func (s *PublicBlockChainAPI) doCall(args CallArgs, blockNr rpc.BlockNumber) (st
}
stateDb = stateDb.Copy()
+ // If there's no code to interact with, respond with an appropriate error
+ if args.To != nil {
+ if code := stateDb.GetCode(*args.To); len(code) == 0 {
+ return "0x", nil, ErrNoCode
+ }
+ }
// Retrieve the account state object to interact with
var from *state.StateObject
if args.From == (common.Address{}) {