Merge pull request #2496 from karalabe/abibind-missing-contract-error
accounts/abi/bind, eth: add contract non-existent error
This commit is contained in:
commit
123aa659e4
@ -17,12 +17,22 @@
|
|||||||
package bind
|
package bind
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"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
|
// ContractCaller defines the methods needed to allow operating with contract on a read
|
||||||
// only basis.
|
// only basis.
|
||||||
type ContractCaller interface {
|
type ContractCaller interface {
|
||||||
|
@ -66,10 +66,16 @@ type request struct {
|
|||||||
type response struct {
|
type response struct {
|
||||||
JSONRPC string `json:"jsonrpc"` // Version of the JSON RPC protocol, always set to 2.0
|
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
|
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
|
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.
|
// 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
|
// 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 {
|
if err := b.client.Recv(res); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(res.Error) > 0 {
|
if res.Error != nil {
|
||||||
return nil, fmt.Errorf("remote error: %s", string(res.Error))
|
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
|
return res.Result, nil
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,10 @@ func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pe
|
|||||||
block = b.blockchain.CurrentBlock()
|
block = b.blockchain.CurrentBlock()
|
||||||
statedb, _ = b.blockchain.State()
|
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
|
// Set infinite balance to the a fake caller account
|
||||||
from := statedb.GetOrNewStateObject(common.Address{})
|
from := statedb.GetOrNewStateObject(common.Address{})
|
||||||
from.SetBalance(common.MaxBig)
|
from.SetBalance(common.MaxBig)
|
||||||
@ -134,7 +138,12 @@ func (b *SimulatedBackend) EstimateGasLimit(sender common.Address, contract *com
|
|||||||
block = b.pendingBlock
|
block = b.pendingBlock
|
||||||
statedb = b.pendingState.Copy()
|
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
|
// Set infinite balance to the a fake caller account
|
||||||
from := statedb.GetOrNewStateObject(sender)
|
from := statedb.GetOrNewStateObject(sender)
|
||||||
from.SetBalance(common.MaxBig)
|
from.SetBalance(common.MaxBig)
|
||||||
|
@ -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
|
// Tests that packages generated by the binder can be successfully compiled and
|
||||||
|
15
eth/api.go
15
eth/api.go
@ -51,6 +51,15 @@ import (
|
|||||||
"golang.org/x/net/context"
|
"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)
|
const defaultGas = uint64(90000)
|
||||||
|
|
||||||
// blockByNumber is a commonly used helper function which retrieves and returns
|
// 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()
|
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
|
// Retrieve the account state object to interact with
|
||||||
var from *state.StateObject
|
var from *state.StateObject
|
||||||
if args.From == (common.Address{}) {
|
if args.From == (common.Address{}) {
|
||||||
|
Loading…
Reference in New Issue
Block a user