diff options
-rw-r--r-- | accounts/abi/bind/backend.go | 10 | ||||
-rw-r--r-- | accounts/abi/bind/backends/remote.go | 15 | ||||
-rw-r--r-- | accounts/abi/bind/backends/simulated.go | 11 | ||||
-rw-r--r-- | accounts/abi/bind/bind_test.go | 28 | ||||
-rw-r--r-- | accounts/watch.go | 2 | ||||
-rw-r--r-- | accounts/watch_fallback.go | 2 | ||||
-rw-r--r-- | cmd/bootnode/main.go | 46 | ||||
-rw-r--r-- | cmd/geth/js.go | 16 | ||||
-rw-r--r-- | cmd/geth/main.go | 22 | ||||
-rw-r--r-- | common/types.go | 2 | ||||
-rw-r--r-- | common/types_test.go | 38 | ||||
-rw-r--r-- | eth/api.go | 15 | ||||
-rw-r--r-- | jsre/jsre.go | 9 |
13 files changed, 166 insertions, 50 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/accounts/watch.go b/accounts/watch.go index 19d304fcc..309e4d458 100644 --- a/accounts/watch.go +++ b/accounts/watch.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. -// +build darwin,!ios freebsd linux netbsd solaris windows +// +build darwin,!ios freebsd linux,!arm64 netbsd solaris windows package accounts diff --git a/accounts/watch_fallback.go b/accounts/watch_fallback.go index 0b7016167..7b5e221df 100644 --- a/accounts/watch_fallback.go +++ b/accounts/watch_fallback.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. -// +build ios !darwin,!freebsd,!linux,!netbsd,!solaris,!windows +// +build ios linux,arm64 !darwin,!freebsd,!linux,!netbsd,!solaris,!windows // This is the fallback implementation of directory watching. // It is used on unsupported platforms. diff --git a/cmd/bootnode/main.go b/cmd/bootnode/main.go index 7f74e9c70..7d3f9fdb3 100644 --- a/cmd/bootnode/main.go +++ b/cmd/bootnode/main.go @@ -19,15 +19,12 @@ package main import ( "crypto/ecdsa" - "encoding/hex" "flag" - "fmt" - "io/ioutil" - "log" "os" + "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/nat" ) @@ -43,50 +40,43 @@ func main() { nodeKey *ecdsa.PrivateKey err error ) + flag.Var(glog.GetVerbosity(), "verbosity", "log verbosity (0-9)") + flag.Var(glog.GetVModule(), "vmodule", "log verbosity pattern") + glog.SetToStderr(true) flag.Parse() - logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.DebugLevel)) if *genKey != "" { - writeKey(*genKey) + key, err := crypto.GenerateKey() + if err != nil { + utils.Fatalf("could not generate key: %v", err) + } + if err := crypto.SaveECDSA(*genKey, key); err != nil { + utils.Fatalf("%v", err) + } os.Exit(0) } natm, err := nat.Parse(*natdesc) if err != nil { - log.Fatalf("-nat: %v", err) + utils.Fatalf("-nat: %v", err) } switch { case *nodeKeyFile == "" && *nodeKeyHex == "": - log.Fatal("Use -nodekey or -nodekeyhex to specify a private key") + utils.Fatalf("Use -nodekey or -nodekeyhex to specify a private key") case *nodeKeyFile != "" && *nodeKeyHex != "": - log.Fatal("Options -nodekey and -nodekeyhex are mutually exclusive") + utils.Fatalf("Options -nodekey and -nodekeyhex are mutually exclusive") case *nodeKeyFile != "": if nodeKey, err = crypto.LoadECDSA(*nodeKeyFile); err != nil { - log.Fatalf("-nodekey: %v", err) + utils.Fatalf("-nodekey: %v", err) } case *nodeKeyHex != "": if nodeKey, err = crypto.HexToECDSA(*nodeKeyHex); err != nil { - log.Fatalf("-nodekeyhex: %v", err) + utils.Fatalf("-nodekeyhex: %v", err) } } if _, err := discover.ListenUDP(nodeKey, *listenAddr, natm, ""); err != nil { - log.Fatal(err) + utils.Fatalf("%v", err) } select {} } - -func writeKey(target string) { - key, err := crypto.GenerateKey() - if err != nil { - log.Fatalf("could not generate key: %v", err) - } - b := crypto.FromECDSA(key) - if target == "-" { - fmt.Println(hex.EncodeToString(b)) - } else { - if err := ioutil.WriteFile(target, b, 0600); err != nil { - log.Fatal("write error: ", err) - } - } -} diff --git a/cmd/geth/js.go b/cmd/geth/js.go index 767b513c1..2b64303b2 100644 --- a/cmd/geth/js.go +++ b/cmd/geth/js.go @@ -123,7 +123,7 @@ func (self *jsre) batch(statement string) { err := self.re.EvalAndPrettyPrint(statement) if err != nil { - fmt.Printf("error: %v", err) + fmt.Printf("%v", jsErrorString(err)) } if self.atexit != nil { @@ -301,21 +301,19 @@ func (self *jsre) preloadJSFiles(ctx *cli.Context) error { for _, file := range jsFiles { filename := common.AbsolutePath(assetPath, strings.TrimSpace(file)) if err := self.re.Exec(filename); err != nil { - return fmt.Errorf("%s: %v", file, err) + return fmt.Errorf("%s: %v", file, jsErrorString(err)) } } } return nil } -// exec executes the JS file with the given filename and stops the JSRE -func (self *jsre) exec(filename string) error { - if err := self.re.Exec(filename); err != nil { - self.re.Stop(false) - return fmt.Errorf("Javascript Error: %v", err) +// jsErrorString adds a backtrace to errors generated by otto. +func jsErrorString(err error) string { + if ottoErr, ok := err.(*otto.Error); ok { + return ottoErr.String() } - self.re.Stop(true) - return nil + return err.Error() } func (self *jsre) interactive() { diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 6ab4ed45b..a43daba2f 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -21,6 +21,7 @@ import ( "fmt" "io/ioutil" "os" + "os/signal" "path/filepath" "runtime" "strconv" @@ -353,7 +354,7 @@ func console(ctx *cli.Context) { // preload user defined JS files into the console err = repl.preloadJSFiles(ctx) if err != nil { - utils.Fatalf("unable to preload JS file %v", err) + utils.Fatalf("%v", err) } // in case the exec flag holds a JS statement execute it and return @@ -372,6 +373,7 @@ func execScripts(ctx *cli.Context) { // Create and start the node based on the CLI flags node := utils.MakeSystemNode(ClientIdentifier, nodeNameVersion, makeDefaultExtra(), ctx) startNode(ctx, node) + defer node.Stop() // Attach to the newly started node and execute the given scripts client, err := node.Attach() @@ -383,10 +385,24 @@ func execScripts(ctx *cli.Context) { ctx.GlobalString(utils.RPCCORSDomainFlag.Name), client, false) + // Run all given files. for _, file := range ctx.Args() { - repl.exec(file) + if err = repl.re.Exec(file); err != nil { + break + } } - node.Stop() + if err != nil { + utils.Fatalf("JavaScript Error: %v", jsErrorString(err)) + } + // JS files loaded successfully. + // Wait for pending callbacks, but stop for Ctrl-C. + abort := make(chan os.Signal, 1) + signal.Notify(abort, os.Interrupt) + go func() { + <-abort + repl.re.Stop(false) + }() + repl.re.Stop(true) } // startNode boots up the system node and all registered protocols, after which diff --git a/common/types.go b/common/types.go index fec986164..d00884484 100644 --- a/common/types.go +++ b/common/types.go @@ -167,7 +167,7 @@ func (a Address) MarshalJSON() ([]byte, error) { // Parse address from raw json data func (a *Address) UnmarshalJSON(data []byte) error { if len(data) > 2 && data[0] == '"' && data[len(data)-1] == '"' { - data = data[:len(data)-1][1:] + data = data[1 : len(data)-1] } if len(data) > 2 && data[0] == '0' && data[1] == 'x' { diff --git a/common/types_test.go b/common/types_test.go index f2dfbf0c9..de67cfcb5 100644 --- a/common/types_test.go +++ b/common/types_test.go @@ -16,7 +16,10 @@ package common -import "testing" +import ( + "math/big" + "testing" +) func TestBytesConversion(t *testing.T) { bytes := []byte{5} @@ -47,7 +50,38 @@ func TestHashJsonValidation(t *testing.T) { } for i, test := range tests { if err := h.UnmarshalJSON(append([]byte(test.Prefix), make([]byte, test.Size)...)); err != test.Error { - t.Error(i, "expected", test.Error, "got", err) + t.Errorf("test #%d: error mismatch: have %v, want %v", i, err, test.Error) + } + } +} + +func TestAddressUnmarshalJSON(t *testing.T) { + var a Address + var tests = []struct { + Input string + ShouldErr bool + Output *big.Int + }{ + {"", true, nil}, + {`""`, true, nil}, + {`"0x"`, true, nil}, + {`"0x00"`, true, nil}, + {`"0xG000000000000000000000000000000000000000"`, true, nil}, + {`"0x0000000000000000000000000000000000000000"`, false, big.NewInt(0)}, + {`"0x0000000000000000000000000000000000000010"`, false, big.NewInt(16)}, + } + for i, test := range tests { + err := a.UnmarshalJSON([]byte(test.Input)) + if err != nil && !test.ShouldErr { + t.Errorf("test #%d: unexpected error: %v", i, err) + } + if err == nil { + if test.ShouldErr { + t.Errorf("test #%d: expected error, got none", i) + } + if a.Big().Cmp(test.Output) != 0 { + t.Errorf("test #%d: address mismatch: have %v, want %v", i, a.Big(), test.Output) + } } } } 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{}) { diff --git a/jsre/jsre.go b/jsre/jsre.go index 7df022cb1..59730bc0d 100644 --- a/jsre/jsre.go +++ b/jsre/jsre.go @@ -235,7 +235,14 @@ func (self *JSRE) Exec(file string) error { if err != nil { return err } - self.Do(func(vm *otto.Otto) { _, err = vm.Run(code) }) + var script *otto.Script + self.Do(func(vm *otto.Otto) { + script, err = vm.Compile(file, code) + if err != nil { + return + } + _, err = vm.Run(script) + }) return err } |