diff options
137 files changed, 4842 insertions, 14418 deletions
diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 06f4b3b2d..b5703f7f2 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,6 +1,6 @@ { "ImportPath": "github.com/ethereum/go-ethereum", - "GoVersion": "go1.4", + "GoVersion": "go1.5.2", "Packages": [ "./..." ], @@ -41,10 +41,6 @@ "Rev": "a45aa3d54aef73b504e15eb71bea0e5565b5e6e1" }, { - "ImportPath": "github.com/kardianos/osext", - "Rev": "ccfcd0245381f0c94c68f50626665eed3c6b726a" - }, - { "ImportPath": "github.com/mattn/go-isatty", "Rev": "7fcbc72f853b92b5720db4a6b8482be612daef24" }, @@ -74,10 +70,6 @@ "Rev": "dea31a3d392779af358ec41f77a07fcc7e9d04ba" }, { - "ImportPath": "github.com/rs/cors", - "Rev": "6e0c3cb65fc0fdb064c743d176a620e3ca446dfb" - }, - { "ImportPath": "github.com/shiena/ansicolor", "Rev": "a5e2b567a4dd6cc74545b8a4f27c9d63b9e7735b" }, @@ -110,6 +102,10 @@ "Rev": "e0403b4e005737430c05a57aac078479844f919c" }, { + "ImportPath": "golang.org/x/net/websocket", + "Rev": "e0403b4e005737430c05a57aac078479844f919c" + }, + { "ImportPath": "golang.org/x/text/encoding", "Rev": "c93e7c9fff19fb9139b5ab04ce041833add0134e" }, diff --git a/Godeps/_workspace/src/github.com/kardianos/osext/LICENSE b/Godeps/_workspace/src/github.com/kardianos/osext/LICENSE deleted file mode 100644 index 744875676..000000000 --- a/Godeps/_workspace/src/github.com/kardianos/osext/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/kardianos/osext/README.md b/Godeps/_workspace/src/github.com/kardianos/osext/README.md deleted file mode 100644 index 820e1ecb5..000000000 --- a/Godeps/_workspace/src/github.com/kardianos/osext/README.md +++ /dev/null @@ -1,14 +0,0 @@ -### Extensions to the "os" package. - -## Find the current Executable and ExecutableFolder. - -There is sometimes utility in finding the current executable file -that is running. This can be used for upgrading the current executable -or finding resources located relative to the executable file. - -Multi-platform and supports: - * Linux - * OS X - * Windows - * Plan 9 - * BSDs. diff --git a/Godeps/_workspace/src/github.com/kardianos/osext/osext.go b/Godeps/_workspace/src/github.com/kardianos/osext/osext.go deleted file mode 100644 index 4ed4b9aa3..000000000 --- a/Godeps/_workspace/src/github.com/kardianos/osext/osext.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Extensions to the standard "os" package. -package osext - -import "path/filepath" - -// Executable returns an absolute path that can be used to -// re-invoke the current program. -// It may not be valid after the current program exits. -func Executable() (string, error) { - p, err := executable() - return filepath.Clean(p), err -} - -// Returns same path as Executable, returns just the folder -// path. Excludes the executable name. -func ExecutableFolder() (string, error) { - p, err := Executable() - if err != nil { - return "", err - } - folder, _ := filepath.Split(p) - return folder, nil -} diff --git a/Godeps/_workspace/src/github.com/kardianos/osext/osext_plan9.go b/Godeps/_workspace/src/github.com/kardianos/osext/osext_plan9.go deleted file mode 100644 index 655750c54..000000000 --- a/Godeps/_workspace/src/github.com/kardianos/osext/osext_plan9.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package osext - -import ( - "os" - "strconv" - "syscall" -) - -func executable() (string, error) { - f, err := os.Open("/proc/" + strconv.Itoa(os.Getpid()) + "/text") - if err != nil { - return "", err - } - defer f.Close() - return syscall.Fd2path(int(f.Fd())) -} diff --git a/Godeps/_workspace/src/github.com/kardianos/osext/osext_procfs.go b/Godeps/_workspace/src/github.com/kardianos/osext/osext_procfs.go deleted file mode 100644 index a50021ad5..000000000 --- a/Godeps/_workspace/src/github.com/kardianos/osext/osext_procfs.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build linux netbsd openbsd solaris dragonfly - -package osext - -import ( - "errors" - "fmt" - "os" - "runtime" -) - -func executable() (string, error) { - switch runtime.GOOS { - case "linux": - return os.Readlink("/proc/self/exe") - case "netbsd": - return os.Readlink("/proc/curproc/exe") - case "openbsd", "dragonfly": - return os.Readlink("/proc/curproc/file") - case "solaris": - return os.Readlink(fmt.Sprintf("/proc/%d/path/a.out", os.Getpid())) - } - return "", errors.New("ExecPath not implemented for " + runtime.GOOS) -} diff --git a/Godeps/_workspace/src/github.com/kardianos/osext/osext_sysctl.go b/Godeps/_workspace/src/github.com/kardianos/osext/osext_sysctl.go deleted file mode 100644 index b66cac878..000000000 --- a/Godeps/_workspace/src/github.com/kardianos/osext/osext_sysctl.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin freebsd - -package osext - -import ( - "os" - "path/filepath" - "runtime" - "syscall" - "unsafe" -) - -var initCwd, initCwdErr = os.Getwd() - -func executable() (string, error) { - var mib [4]int32 - switch runtime.GOOS { - case "freebsd": - mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1} - case "darwin": - mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1} - } - - n := uintptr(0) - // Get length. - _, _, errNum := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0) - if errNum != 0 { - return "", errNum - } - if n == 0 { // This shouldn't happen. - return "", nil - } - buf := make([]byte, n) - _, _, errNum = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0) - if errNum != 0 { - return "", errNum - } - if n == 0 { // This shouldn't happen. - return "", nil - } - for i, v := range buf { - if v == 0 { - buf = buf[:i] - break - } - } - var err error - execPath := string(buf) - // execPath will not be empty due to above checks. - // Try to get the absolute path if the execPath is not rooted. - if execPath[0] != '/' { - execPath, err = getAbs(execPath) - if err != nil { - return execPath, err - } - } - // For darwin KERN_PROCARGS may return the path to a symlink rather than the - // actual executable. - if runtime.GOOS == "darwin" { - if execPath, err = filepath.EvalSymlinks(execPath); err != nil { - return execPath, err - } - } - return execPath, nil -} - -func getAbs(execPath string) (string, error) { - if initCwdErr != nil { - return execPath, initCwdErr - } - // The execPath may begin with a "../" or a "./" so clean it first. - // Join the two paths, trailing and starting slashes undetermined, so use - // the generic Join function. - return filepath.Join(initCwd, filepath.Clean(execPath)), nil -} diff --git a/Godeps/_workspace/src/github.com/kardianos/osext/osext_test.go b/Godeps/_workspace/src/github.com/kardianos/osext/osext_test.go deleted file mode 100644 index dc661dbc2..000000000 --- a/Godeps/_workspace/src/github.com/kardianos/osext/osext_test.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin linux freebsd netbsd windows - -package osext - -import ( - "fmt" - "os" - oexec "os/exec" - "path/filepath" - "runtime" - "testing" -) - -const execPath_EnvVar = "OSTEST_OUTPUT_EXECPATH" - -func TestExecPath(t *testing.T) { - ep, err := Executable() - if err != nil { - t.Fatalf("ExecPath failed: %v", err) - } - // we want fn to be of the form "dir/prog" - dir := filepath.Dir(filepath.Dir(ep)) - fn, err := filepath.Rel(dir, ep) - if err != nil { - t.Fatalf("filepath.Rel: %v", err) - } - cmd := &oexec.Cmd{} - // make child start with a relative program path - cmd.Dir = dir - cmd.Path = fn - // forge argv[0] for child, so that we can verify we could correctly - // get real path of the executable without influenced by argv[0]. - cmd.Args = []string{"-", "-test.run=XXXX"} - cmd.Env = []string{fmt.Sprintf("%s=1", execPath_EnvVar)} - out, err := cmd.CombinedOutput() - if err != nil { - t.Fatalf("exec(self) failed: %v", err) - } - outs := string(out) - if !filepath.IsAbs(outs) { - t.Fatalf("Child returned %q, want an absolute path", out) - } - if !sameFile(outs, ep) { - t.Fatalf("Child returned %q, not the same file as %q", out, ep) - } -} - -func sameFile(fn1, fn2 string) bool { - fi1, err := os.Stat(fn1) - if err != nil { - return false - } - fi2, err := os.Stat(fn2) - if err != nil { - return false - } - return os.SameFile(fi1, fi2) -} - -func init() { - if e := os.Getenv(execPath_EnvVar); e != "" { - // first chdir to another path - dir := "/" - if runtime.GOOS == "windows" { - dir = filepath.VolumeName(".") - } - os.Chdir(dir) - if ep, err := Executable(); err != nil { - fmt.Fprint(os.Stderr, "ERROR: ", err) - } else { - fmt.Fprint(os.Stderr, ep) - } - os.Exit(0) - } -} diff --git a/Godeps/_workspace/src/github.com/kardianos/osext/osext_windows.go b/Godeps/_workspace/src/github.com/kardianos/osext/osext_windows.go deleted file mode 100644 index 72d282cf8..000000000 --- a/Godeps/_workspace/src/github.com/kardianos/osext/osext_windows.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package osext - -import ( - "syscall" - "unicode/utf16" - "unsafe" -) - -var ( - kernel = syscall.MustLoadDLL("kernel32.dll") - getModuleFileNameProc = kernel.MustFindProc("GetModuleFileNameW") -) - -// GetModuleFileName() with hModule = NULL -func executable() (exePath string, err error) { - return getModuleFileName() -} - -func getModuleFileName() (string, error) { - var n uint32 - b := make([]uint16, syscall.MAX_PATH) - size := uint32(len(b)) - - r0, _, e1 := getModuleFileNameProc.Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size)) - n = uint32(r0) - if n == 0 { - return "", e1 - } - return string(utf16.Decode(b[0:n])), nil -} diff --git a/Godeps/_workspace/src/github.com/rs/cors/.travis.yml b/Godeps/_workspace/src/github.com/rs/cors/.travis.yml deleted file mode 100644 index bbb5185a2..000000000 --- a/Godeps/_workspace/src/github.com/rs/cors/.travis.yml +++ /dev/null @@ -1,4 +0,0 @@ -language: go -go: -- 1.3 -- 1.4 diff --git a/Godeps/_workspace/src/github.com/rs/cors/LICENSE b/Godeps/_workspace/src/github.com/rs/cors/LICENSE deleted file mode 100644 index d8e2df5a4..000000000 --- a/Godeps/_workspace/src/github.com/rs/cors/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2014 Olivier Poitrey <rs@dailymotion.com> - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/rs/cors/README.md b/Godeps/_workspace/src/github.com/rs/cors/README.md deleted file mode 100644 index 6f70c30ac..000000000 --- a/Godeps/_workspace/src/github.com/rs/cors/README.md +++ /dev/null @@ -1,84 +0,0 @@ -# Go CORS handler [![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/rs/cors) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/rs/cors/master/LICENSE) [![build](https://img.shields.io/travis/rs/cors.svg?style=flat)](https://travis-ci.org/rs/cors) - -CORS is a `net/http` handler implementing [Cross Origin Resource Sharing W3 specification](http://www.w3.org/TR/cors/) in Golang. - -## Getting Started - -After installing Go and setting up your [GOPATH](http://golang.org/doc/code.html#GOPATH), create your first `.go` file. We'll call it `server.go`. - -```go -package main - -import ( - "net/http" - - "github.com/rs/cors" -) - -func main() { - h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Write([]byte("{\"hello\": \"world\"}")) - }) - - // cors.Default() setup the middleware with default options being - // all origins accepted with simple methods (GET, POST). See - // documentation below for more options. - handler = cors.Default().Handler(h) - http.ListenAndServe(":8080", handler) -} -``` - -Install `cors`: - - go get github.com/rs/cors - -Then run your server: - - go run server.go - -The server now runs on `localhost:8080`: - - $ curl -D - -H 'Origin: http://foo.com' http://localhost:8080/ - HTTP/1.1 200 OK - Access-Control-Allow-Origin: foo.com - Content-Type: application/json - Date: Sat, 25 Oct 2014 03:43:57 GMT - Content-Length: 18 - - {"hello": "world"} - -### More Examples - -* `net/http`: [examples/nethttp/server.go](https://github.com/rs/cors/blob/master/examples/nethttp/server.go) -* [Goji](https://goji.io): [examples/goji/server.go](https://github.com/rs/cors/blob/master/examples/goji/server.go) -* [Martini](http://martini.codegangsta.io): [examples/martini/server.go](https://github.com/rs/cors/blob/master/examples/martini/server.go) -* [Negroni](https://github.com/codegangsta/negroni): [examples/negroni/server.go](https://github.com/rs/cors/blob/master/examples/negroni/server.go) -* [Alice](https://github.com/justinas/alice): [examples/alice/server.go](https://github.com/rs/cors/blob/master/examples/alice/server.go) - -## Parameters - -Parameters are passed to the middleware thru the `cors.New` method as follow: - -```go -c := cors.New(cors.Options{ - AllowedOrigins: []string{"http://foo.com"}, - AllowCredentials: true, -}) - -// Insert the middleware -handler = c.Handler(handler) -``` - -* **AllowedOrigins** `[]string`: A list of origins a cross-domain request can be executed from. If the special `*` value is present in the list, all origins will be allowed. The default value is `*`. -* **AllowedMethods** `[]string`: A list of methods the client is allowed to use with cross-domain requests. -* **AllowedHeaders** `[]string`: A list of non simple headers the client is allowed to use with cross-domain requests. Default value is simple methods (`GET` and `POST`) -* **ExposedHeaders** `[]string`: Indicates which headers are safe to expose to the API of a CORS API specification -* **AllowCredentials** `bool`: Indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates. The default is `false`. -* **MaxAge** `int`: Indicates how long (in seconds) the results of a preflight request can be cached. The default is `0` which stands for no max age. - -See [API documentation](http://godoc.org/github.com/rs/cors) for more info. - -## Licenses - -All source code is licensed under the [MIT License](https://raw.github.com/rs/cors/master/LICENSE). diff --git a/Godeps/_workspace/src/github.com/rs/cors/bench_test.go b/Godeps/_workspace/src/github.com/rs/cors/bench_test.go deleted file mode 100644 index 454375d2c..000000000 --- a/Godeps/_workspace/src/github.com/rs/cors/bench_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package cors - -import ( - "net/http" - "net/http/httptest" - "testing" -) - -func BenchmarkWithout(b *testing.B) { - res := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "http://example.com/foo", nil) - - for i := 0; i < b.N; i++ { - testHandler.ServeHTTP(res, req) - } -} - -func BenchmarkDefault(b *testing.B) { - res := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "http://example.com/foo", nil) - handler := Default() - - for i := 0; i < b.N; i++ { - handler.Handler(testHandler).ServeHTTP(res, req) - } -} - -func BenchmarkPreflight(b *testing.B) { - res := httptest.NewRecorder() - req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) - req.Header.Add("Access-Control-Request-Method", "GET") - handler := Default() - - for i := 0; i < b.N; i++ { - handler.Handler(testHandler).ServeHTTP(res, req) - } -} diff --git a/Godeps/_workspace/src/github.com/rs/cors/cors.go b/Godeps/_workspace/src/github.com/rs/cors/cors.go deleted file mode 100644 index 276bc40bb..000000000 --- a/Godeps/_workspace/src/github.com/rs/cors/cors.go +++ /dev/null @@ -1,308 +0,0 @@ -/* -Package cors is net/http handler to handle CORS related requests -as defined by http://www.w3.org/TR/cors/ - -You can configure it by passing an option struct to cors.New: - - c := cors.New(cors.Options{ - AllowedOrigins: []string{"foo.com"}, - AllowedMethods: []string{"GET", "POST", "DELETE"}, - AllowCredentials: true, - }) - -Then insert the handler in the chain: - - handler = c.Handler(handler) - -See Options documentation for more options. - -The resulting handler is a standard net/http handler. -*/ -package cors - -import ( - "log" - "net/http" - "os" - "strconv" - "strings" -) - -// Options is a configuration container to setup the CORS middleware. -type Options struct { - // AllowedOrigins is a list of origins a cross-domain request can be executed from. - // If the special "*" value is present in the list, all origins will be allowed. - // Default value is ["*"] - AllowedOrigins []string - // AllowedMethods is a list of methods the client is allowed to use with - // cross-domain requests. Default value is simple methods (GET and POST) - AllowedMethods []string - // AllowedHeaders is list of non simple headers the client is allowed to use with - // cross-domain requests. - // If the special "*" value is present in the list, all headers will be allowed. - // Default value is [] but "Origin" is always appended to the list. - AllowedHeaders []string - // ExposedHeaders indicates which headers are safe to expose to the API of a CORS - // API specification - ExposedHeaders []string - // AllowCredentials indicates whether the request can include user credentials like - // cookies, HTTP authentication or client side SSL certificates. - AllowCredentials bool - // MaxAge indicates how long (in seconds) the results of a preflight request - // can be cached - MaxAge int - // Debugging flag adds additional output to debug server side CORS issues - Debug bool - // log object to use when debugging - log *log.Logger -} - -type Cors struct { - // The CORS Options - options Options -} - -// New creates a new Cors handler with the provided options. -func New(options Options) *Cors { - // Normalize options - // Note: for origins and methods matching, the spec requires a case-sensitive matching. - // As it may error prone, we chose to ignore the spec here. - normOptions := Options{ - AllowedOrigins: convert(options.AllowedOrigins, strings.ToLower), - AllowedMethods: convert(options.AllowedMethods, strings.ToUpper), - // Origin is always appended as some browsers will always request - // for this header at preflight - AllowedHeaders: convert(append(options.AllowedHeaders, "Origin"), http.CanonicalHeaderKey), - ExposedHeaders: convert(options.ExposedHeaders, http.CanonicalHeaderKey), - AllowCredentials: options.AllowCredentials, - MaxAge: options.MaxAge, - Debug: options.Debug, - log: log.New(os.Stdout, "[cors] ", log.LstdFlags), - } - if len(normOptions.AllowedOrigins) == 0 { - // Default is all origins - normOptions.AllowedOrigins = []string{"*"} - } - if len(normOptions.AllowedHeaders) == 1 { - // Add some sensible defaults - normOptions.AllowedHeaders = []string{"Origin", "Accept", "Content-Type"} - } - if len(normOptions.AllowedMethods) == 0 { - // Default is simple methods - normOptions.AllowedMethods = []string{"GET", "POST"} - } - - if normOptions.Debug { - normOptions.log.Printf("Options: %v", normOptions) - } - return &Cors{ - options: normOptions, - } -} - -// Default creates a new Cors handler with default options -func Default() *Cors { - return New(Options{}) -} - -// Handler apply the CORS specification on the request, and add relevant CORS headers -// as necessary. -func (cors *Cors) Handler(h http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method == "OPTIONS" { - cors.logf("Handler: Preflight request") - cors.handlePreflight(w, r) - // Preflight requests are standalone and should stop the chain as some other - // middleware may not handle OPTIONS requests correctly. One typical example - // is authentication middleware ; OPTIONS requests won't carry authentication - // headers (see #1) - } else { - cors.logf("Handler: Actual request") - cors.handleActualRequest(w, r) - h.ServeHTTP(w, r) - } - }) -} - -// Martini compatible handler -func (cors *Cors) HandlerFunc(w http.ResponseWriter, r *http.Request) { - if r.Method == "OPTIONS" { - cors.logf("HandlerFunc: Preflight request") - cors.handlePreflight(w, r) - } else { - cors.logf("HandlerFunc: Actual request") - cors.handleActualRequest(w, r) - } -} - -// Negroni compatible interface -func (cors *Cors) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { - if r.Method == "OPTIONS" { - cors.logf("ServeHTTP: Preflight request") - cors.handlePreflight(w, r) - // Preflight requests are standalone and should stop the chain as some other - // middleware may not handle OPTIONS requests correctly. One typical example - // is authentication middleware ; OPTIONS requests won't carry authentication - // headers (see #1) - } else { - cors.logf("ServeHTTP: Actual request") - cors.handleActualRequest(w, r) - next(w, r) - } -} - -// handlePreflight handles pre-flight CORS requests -func (cors *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) { - options := cors.options - headers := w.Header() - origin := r.Header.Get("Origin") - - if r.Method != "OPTIONS" { - cors.logf(" Preflight aborted: %s!=OPTIONS", r.Method) - return - } - if origin == "" { - cors.logf(" Preflight aborted: empty origin") - return - } - if !cors.isOriginAllowed(origin) { - cors.logf(" Preflight aborted: origin '%s' not allowed", origin) - return - } - - reqMethod := r.Header.Get("Access-Control-Request-Method") - if !cors.isMethodAllowed(reqMethod) { - cors.logf(" Preflight aborted: method '%s' not allowed", reqMethod) - return - } - reqHeaders := parseHeaderList(r.Header.Get("Access-Control-Request-Headers")) - if !cors.areHeadersAllowed(reqHeaders) { - cors.logf(" Preflight aborted: headers '%v' not allowed", reqHeaders) - return - } - headers.Set("Access-Control-Allow-Origin", origin) - headers.Add("Vary", "Origin") - // Spec says: Since the list of methods can be unbounded, simply returning the method indicated - // by Access-Control-Request-Method (if supported) can be enough - headers.Set("Access-Control-Allow-Methods", strings.ToUpper(reqMethod)) - if len(reqHeaders) > 0 { - - // Spec says: Since the list of headers can be unbounded, simply returning supported headers - // from Access-Control-Request-Headers can be enough - headers.Set("Access-Control-Allow-Headers", strings.Join(reqHeaders, ", ")) - } - if options.AllowCredentials { - headers.Set("Access-Control-Allow-Credentials", "true") - } - if options.MaxAge > 0 { - headers.Set("Access-Control-Max-Age", strconv.Itoa(options.MaxAge)) - } - cors.logf(" Preflight response headers: %v", headers) -} - -// handleActualRequest handles simple cross-origin requests, actual request or redirects -func (cors *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) { - options := cors.options - headers := w.Header() - origin := r.Header.Get("Origin") - - if r.Method == "OPTIONS" { - cors.logf(" Actual request no headers added: method == %s", r.Method) - return - } - if origin == "" { - cors.logf(" Actual request no headers added: missing origin") - return - } - if !cors.isOriginAllowed(origin) { - cors.logf(" Actual request no headers added: origin '%s' not allowed", origin) - return - } - - // Note that spec does define a way to specifically disallow a simple method like GET or - // POST. Access-Control-Allow-Methods is only used for pre-flight requests and the - // spec doesn't instruct to check the allowed methods for simple cross-origin requests. - // We think it's a nice feature to be able to have control on those methods though. - if !cors.isMethodAllowed(r.Method) { - if cors.options.Debug { - cors.logf(" Actual request no headers added: method '%s' not allowed", - r.Method) - } - - return - } - headers.Set("Access-Control-Allow-Origin", origin) - headers.Add("Vary", "Origin") - if len(options.ExposedHeaders) > 0 { - headers.Set("Access-Control-Expose-Headers", strings.Join(options.ExposedHeaders, ", ")) - } - if options.AllowCredentials { - headers.Set("Access-Control-Allow-Credentials", "true") - } - cors.logf(" Actual response added headers: %v", headers) -} - -// convenience method. checks if debugging is turned on before printing -func (cors *Cors) logf(format string, a ...interface{}) { - if cors.options.Debug { - cors.options.log.Printf(format, a...) - } -} - -// isOriginAllowed checks if a given origin is allowed to perform cross-domain requests -// on the endpoint -func (cors *Cors) isOriginAllowed(origin string) bool { - allowedOrigins := cors.options.AllowedOrigins - origin = strings.ToLower(origin) - for _, allowedOrigin := range allowedOrigins { - switch allowedOrigin { - case "*": - return true - case origin: - return true - } - } - return false -} - -// isMethodAllowed checks if a given method can be used as part of a cross-domain request -// on the endpoing -func (cors *Cors) isMethodAllowed(method string) bool { - allowedMethods := cors.options.AllowedMethods - if len(allowedMethods) == 0 { - // If no method allowed, always return false, even for preflight request - return false - } - method = strings.ToUpper(method) - if method == "OPTIONS" { - // Always allow preflight requests - return true - } - for _, allowedMethod := range allowedMethods { - if allowedMethod == method { - return true - } - } - return false -} - -// areHeadersAllowed checks if a given list of headers are allowed to used within -// a cross-domain request. -func (cors *Cors) areHeadersAllowed(requestedHeaders []string) bool { - if len(requestedHeaders) == 0 { - return true - } - for _, header := range requestedHeaders { - found := false - for _, allowedHeader := range cors.options.AllowedHeaders { - if allowedHeader == "*" || allowedHeader == header { - found = true - break - } - } - if !found { - return false - } - } - return true -} diff --git a/Godeps/_workspace/src/github.com/rs/cors/cors_test.go b/Godeps/_workspace/src/github.com/rs/cors/cors_test.go deleted file mode 100644 index f215018c9..000000000 --- a/Godeps/_workspace/src/github.com/rs/cors/cors_test.go +++ /dev/null @@ -1,288 +0,0 @@ -package cors - -import ( - "net/http" - "net/http/httptest" - "testing" -) - -var testHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("bar")) -}) - -func assertHeaders(t *testing.T, resHeaders http.Header, reqHeaders map[string]string) { - for name, value := range reqHeaders { - if resHeaders.Get(name) != value { - t.Errorf("Invalid header `%s', wanted `%s', got `%s'", name, value, resHeaders.Get(name)) - } - } -} - -func TestNoConfig(t *testing.T) { - s := New(Options{ - // Intentionally left blank. - }) - - res := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "http://example.com/foo", nil) - - s.Handler(testHandler).ServeHTTP(res, req) - - assertHeaders(t, res.Header(), map[string]string{ - "Access-Control-Allow-Origin": "", - "Access-Control-Allow-Methods": "", - "Access-Control-Allow-Headers": "", - "Access-Control-Allow-Credentials": "", - "Access-Control-Max-Age": "", - "Access-Control-Expose-Headers": "", - }) -} - -func TestWildcardOrigin(t *testing.T) { - s := New(Options{ - AllowedOrigins: []string{"*"}, - }) - - res := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "http://example.com/foo", nil) - req.Header.Add("Origin", "http://foobar.com") - - s.Handler(testHandler).ServeHTTP(res, req) - - assertHeaders(t, res.Header(), map[string]string{ - "Access-Control-Allow-Origin": "http://foobar.com", - "Access-Control-Allow-Methods": "", - "Access-Control-Allow-Headers": "", - "Access-Control-Allow-Credentials": "", - "Access-Control-Max-Age": "", - "Access-Control-Expose-Headers": "", - }) -} - -func TestAllowedOrigin(t *testing.T) { - s := New(Options{ - AllowedOrigins: []string{"http://foobar.com"}, - }) - - res := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "http://example.com/foo", nil) - req.Header.Add("Origin", "http://foobar.com") - - s.Handler(testHandler).ServeHTTP(res, req) - - assertHeaders(t, res.Header(), map[string]string{ - "Access-Control-Allow-Origin": "http://foobar.com", - "Access-Control-Allow-Methods": "", - "Access-Control-Allow-Headers": "", - "Access-Control-Allow-Credentials": "", - "Access-Control-Max-Age": "", - "Access-Control-Expose-Headers": "", - }) -} - -func TestDisallowedOrigin(t *testing.T) { - s := New(Options{ - AllowedOrigins: []string{"http://foobar.com"}, - }) - - res := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "http://example.com/foo", nil) - req.Header.Add("Origin", "http://barbaz.com") - - s.Handler(testHandler).ServeHTTP(res, req) - - assertHeaders(t, res.Header(), map[string]string{ - "Access-Control-Allow-Origin": "", - "Access-Control-Allow-Methods": "", - "Access-Control-Allow-Headers": "", - "Access-Control-Allow-Credentials": "", - "Access-Control-Max-Age": "", - "Access-Control-Expose-Headers": "", - }) -} - -func TestAllowedMethod(t *testing.T) { - s := New(Options{ - AllowedOrigins: []string{"http://foobar.com"}, - AllowedMethods: []string{"PUT", "DELETE"}, - }) - - res := httptest.NewRecorder() - req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) - req.Header.Add("Origin", "http://foobar.com") - req.Header.Add("Access-Control-Request-Method", "PUT") - - s.Handler(testHandler).ServeHTTP(res, req) - - assertHeaders(t, res.Header(), map[string]string{ - "Access-Control-Allow-Origin": "http://foobar.com", - "Access-Control-Allow-Methods": "PUT", - "Access-Control-Allow-Headers": "", - "Access-Control-Allow-Credentials": "", - "Access-Control-Max-Age": "", - "Access-Control-Expose-Headers": "", - }) -} - -func TestDisallowedMethod(t *testing.T) { - s := New(Options{ - AllowedOrigins: []string{"http://foobar.com"}, - AllowedMethods: []string{"PUT", "DELETE"}, - }) - - res := httptest.NewRecorder() - req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) - req.Header.Add("Origin", "http://foobar.com") - req.Header.Add("Access-Control-Request-Method", "PATCH") - - s.Handler(testHandler).ServeHTTP(res, req) - - assertHeaders(t, res.Header(), map[string]string{ - "Access-Control-Allow-Origin": "", - "Access-Control-Allow-Methods": "", - "Access-Control-Allow-Headers": "", - "Access-Control-Allow-Credentials": "", - "Access-Control-Max-Age": "", - "Access-Control-Expose-Headers": "", - }) -} - -func TestAllowedHeader(t *testing.T) { - s := New(Options{ - AllowedOrigins: []string{"http://foobar.com"}, - AllowedHeaders: []string{"X-Header-1", "x-header-2"}, - }) - - res := httptest.NewRecorder() - req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) - req.Header.Add("Origin", "http://foobar.com") - req.Header.Add("Access-Control-Request-Method", "GET") - req.Header.Add("Access-Control-Request-Headers", "X-Header-2, X-HEADER-1") - - s.Handler(testHandler).ServeHTTP(res, req) - - assertHeaders(t, res.Header(), map[string]string{ - "Access-Control-Allow-Origin": "http://foobar.com", - "Access-Control-Allow-Methods": "GET", - "Access-Control-Allow-Headers": "X-Header-2, X-Header-1", - "Access-Control-Allow-Credentials": "", - "Access-Control-Max-Age": "", - "Access-Control-Expose-Headers": "", - }) -} - -func TestAllowedWildcardHeader(t *testing.T) { - s := New(Options{ - AllowedOrigins: []string{"http://foobar.com"}, - AllowedHeaders: []string{"*"}, - }) - - res := httptest.NewRecorder() - req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) - req.Header.Add("Origin", "http://foobar.com") - req.Header.Add("Access-Control-Request-Method", "GET") - req.Header.Add("Access-Control-Request-Headers", "X-Header-2, X-HEADER-1") - - s.Handler(testHandler).ServeHTTP(res, req) - - assertHeaders(t, res.Header(), map[string]string{ - "Access-Control-Allow-Origin": "http://foobar.com", - "Access-Control-Allow-Methods": "GET", - "Access-Control-Allow-Headers": "X-Header-2, X-Header-1", - "Access-Control-Allow-Credentials": "", - "Access-Control-Max-Age": "", - "Access-Control-Expose-Headers": "", - }) -} - -func TestDisallowedHeader(t *testing.T) { - s := New(Options{ - AllowedOrigins: []string{"http://foobar.com"}, - AllowedHeaders: []string{"X-Header-1", "x-header-2"}, - }) - - res := httptest.NewRecorder() - req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) - req.Header.Add("Origin", "http://foobar.com") - req.Header.Add("Access-Control-Request-Method", "GET") - req.Header.Add("Access-Control-Request-Headers", "X-Header-3, X-Header-1") - - s.Handler(testHandler).ServeHTTP(res, req) - - assertHeaders(t, res.Header(), map[string]string{ - "Access-Control-Allow-Origin": "", - "Access-Control-Allow-Methods": "", - "Access-Control-Allow-Headers": "", - "Access-Control-Allow-Credentials": "", - "Access-Control-Max-Age": "", - "Access-Control-Expose-Headers": "", - }) -} - -func TestOriginHeader(t *testing.T) { - s := New(Options{ - AllowedOrigins: []string{"http://foobar.com"}, - }) - - res := httptest.NewRecorder() - req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) - req.Header.Add("Origin", "http://foobar.com") - req.Header.Add("Access-Control-Request-Method", "GET") - req.Header.Add("Access-Control-Request-Headers", "origin") - - s.Handler(testHandler).ServeHTTP(res, req) - - assertHeaders(t, res.Header(), map[string]string{ - "Access-Control-Allow-Origin": "http://foobar.com", - "Access-Control-Allow-Methods": "GET", - "Access-Control-Allow-Headers": "Origin", - "Access-Control-Allow-Credentials": "", - "Access-Control-Max-Age": "", - "Access-Control-Expose-Headers": "", - }) -} - -func TestExposedHeader(t *testing.T) { - s := New(Options{ - AllowedOrigins: []string{"http://foobar.com"}, - ExposedHeaders: []string{"X-Header-1", "x-header-2"}, - }) - - res := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "http://example.com/foo", nil) - req.Header.Add("Origin", "http://foobar.com") - - s.Handler(testHandler).ServeHTTP(res, req) - - assertHeaders(t, res.Header(), map[string]string{ - "Access-Control-Allow-Origin": "http://foobar.com", - "Access-Control-Allow-Methods": "", - "Access-Control-Allow-Headers": "", - "Access-Control-Allow-Credentials": "", - "Access-Control-Max-Age": "", - "Access-Control-Expose-Headers": "X-Header-1, X-Header-2", - }) -} - -func TestAllowedCredentials(t *testing.T) { - s := New(Options{ - AllowedOrigins: []string{"http://foobar.com"}, - AllowCredentials: true, - }) - - res := httptest.NewRecorder() - req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) - req.Header.Add("Origin", "http://foobar.com") - req.Header.Add("Access-Control-Request-Method", "GET") - - s.Handler(testHandler).ServeHTTP(res, req) - - assertHeaders(t, res.Header(), map[string]string{ - "Access-Control-Allow-Origin": "http://foobar.com", - "Access-Control-Allow-Methods": "GET", - "Access-Control-Allow-Headers": "", - "Access-Control-Allow-Credentials": "true", - "Access-Control-Max-Age": "", - "Access-Control-Expose-Headers": "", - }) -} diff --git a/Godeps/_workspace/src/github.com/rs/cors/examples/alice/server.go b/Godeps/_workspace/src/github.com/rs/cors/examples/alice/server.go deleted file mode 100644 index 0a3e15cb8..000000000 --- a/Godeps/_workspace/src/github.com/rs/cors/examples/alice/server.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "net/http" - - "github.com/justinas/alice" - "github.com/rs/cors" -) - -func main() { - c := cors.New(cors.Options{ - AllowedOrigins: []string{"http://foo.com"}, - }) - - mux := http.NewServeMux() - - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Write([]byte("{\"hello\": \"world\"}")) - }) - - chain := alice.New(c.Handler).Then(mux) - http.ListenAndServe(":8080", chain) -} diff --git a/Godeps/_workspace/src/github.com/rs/cors/examples/default/server.go b/Godeps/_workspace/src/github.com/rs/cors/examples/default/server.go deleted file mode 100644 index 851ac41d0..000000000 --- a/Godeps/_workspace/src/github.com/rs/cors/examples/default/server.go +++ /dev/null @@ -1,18 +0,0 @@ -package main - -import ( - "net/http" - - "github.com/rs/cors" -) - -func main() { - h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Write([]byte("{\"hello\": \"world\"}")) - }) - - // Use default options - handler := cors.Default().Handler(h) - http.ListenAndServe(":8080", handler) -} diff --git a/Godeps/_workspace/src/github.com/rs/cors/examples/goji/server.go b/Godeps/_workspace/src/github.com/rs/cors/examples/goji/server.go deleted file mode 100644 index 1fb4073aa..000000000 --- a/Godeps/_workspace/src/github.com/rs/cors/examples/goji/server.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import ( - "net/http" - - "github.com/rs/cors" - "github.com/zenazn/goji" -) - -func main() { - c := cors.New(cors.Options{ - AllowedOrigins: []string{"http://foo.com"}, - }) - goji.Use(c.Handler) - - goji.Get("/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Write([]byte("{\"hello\": \"world\"}")) - }) - - goji.Serve() -} diff --git a/Godeps/_workspace/src/github.com/rs/cors/examples/martini/server.go b/Godeps/_workspace/src/github.com/rs/cors/examples/martini/server.go deleted file mode 100644 index 081af32f9..000000000 --- a/Godeps/_workspace/src/github.com/rs/cors/examples/martini/server.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import ( - "github.com/go-martini/martini" - "github.com/martini-contrib/render" - "github.com/rs/cors" -) - -func main() { - c := cors.New(cors.Options{ - AllowedOrigins: []string{"http://foo.com"}, - }) - - m := martini.Classic() - m.Use(render.Renderer()) - m.Use(c.HandlerFunc) - - m.Get("/", func(r render.Render) { - r.JSON(200, map[string]interface{}{"hello": "world"}) - }) - - m.Run() -} diff --git a/Godeps/_workspace/src/github.com/rs/cors/examples/negroni/server.go b/Godeps/_workspace/src/github.com/rs/cors/examples/negroni/server.go deleted file mode 100644 index 3cb33bff6..000000000 --- a/Godeps/_workspace/src/github.com/rs/cors/examples/negroni/server.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import ( - "net/http" - - "github.com/codegangsta/negroni" - "github.com/rs/cors" -) - -func main() { - c := cors.New(cors.Options{ - AllowedOrigins: []string{"http://foo.com"}, - }) - - mux := http.NewServeMux() - - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Write([]byte("{\"hello\": \"world\"}")) - }) - - n := negroni.Classic() - n.Use(c) - n.UseHandler(mux) - n.Run(":3000") -} diff --git a/Godeps/_workspace/src/github.com/rs/cors/examples/nethttp/server.go b/Godeps/_workspace/src/github.com/rs/cors/examples/nethttp/server.go deleted file mode 100644 index eaa775e44..000000000 --- a/Godeps/_workspace/src/github.com/rs/cors/examples/nethttp/server.go +++ /dev/null @@ -1,20 +0,0 @@ -package main - -import ( - "net/http" - - "github.com/rs/cors" -) - -func main() { - c := cors.New(cors.Options{ - AllowedOrigins: []string{"http://foo.com"}, - }) - - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Write([]byte("{\"hello\": \"world\"}")) - }) - - http.ListenAndServe(":8080", c.Handler(handler)) -} diff --git a/Godeps/_workspace/src/github.com/rs/cors/examples/openbar/server.go b/Godeps/_workspace/src/github.com/rs/cors/examples/openbar/server.go deleted file mode 100644 index 094042300..000000000 --- a/Godeps/_workspace/src/github.com/rs/cors/examples/openbar/server.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import ( - "net/http" - - "github.com/rs/cors" -) - -func main() { - c := cors.New(cors.Options{ - AllowedOrigins: []string{"*"}, - AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"}, - AllowCredentials: true, - }) - - h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Write([]byte("{\"hello\": \"world\"}")) - }) - - http.ListenAndServe(":8080", c.Handler(h)) -} diff --git a/Godeps/_workspace/src/github.com/rs/cors/utils.go b/Godeps/_workspace/src/github.com/rs/cors/utils.go deleted file mode 100644 index 429ab1114..000000000 --- a/Godeps/_workspace/src/github.com/rs/cors/utils.go +++ /dev/null @@ -1,27 +0,0 @@ -package cors - -import ( - "net/http" - "strings" -) - -type converter func(string) string - -// convert converts a list of string using the passed converter function -func convert(s []string, c converter) []string { - out := []string{} - for _, i := range s { - out = append(out, c(i)) - } - return out -} - -func parseHeaderList(headerList string) (headers []string) { - for _, header := range strings.Split(headerList, ",") { - header = http.CanonicalHeaderKey(strings.TrimSpace(header)) - if header != "" { - headers = append(headers, header) - } - } - return headers -} diff --git a/Godeps/_workspace/src/github.com/rs/cors/utils_test.go b/Godeps/_workspace/src/github.com/rs/cors/utils_test.go deleted file mode 100644 index 3fc77fc1e..000000000 --- a/Godeps/_workspace/src/github.com/rs/cors/utils_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package cors - -import ( - "strings" - "testing" -) - -func TestConvert(t *testing.T) { - s := convert([]string{"A", "b", "C"}, strings.ToLower) - e := []string{"a", "b", "c"} - if s[0] != e[0] || s[1] != e[1] || s[2] != e[2] { - t.Errorf("%v != %v", s, e) - } -} - -func TestParseHeaderList(t *testing.T) { - h := parseHeaderList("header, second-header, THIRD-HEADER") - e := []string{"Header", "Second-Header", "Third-Header"} - if h[0] != e[0] || h[1] != e[1] || h[2] != e[2] { - t.Errorf("%v != %v", h, e) - } -} - -func TestParseHeaderListEmpty(t *testing.T) { - if len(parseHeaderList("")) != 0 { - t.Error("should be empty sclice") - } -} diff --git a/Godeps/_workspace/src/golang.org/x/net/websocket/client.go b/Godeps/_workspace/src/golang.org/x/net/websocket/client.go new file mode 100644 index 000000000..20d1e1e38 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/net/websocket/client.go @@ -0,0 +1,113 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bufio" + "crypto/tls" + "io" + "net" + "net/http" + "net/url" +) + +// DialError is an error that occurs while dialling a websocket server. +type DialError struct { + *Config + Err error +} + +func (e *DialError) Error() string { + return "websocket.Dial " + e.Config.Location.String() + ": " + e.Err.Error() +} + +// NewConfig creates a new WebSocket config for client connection. +func NewConfig(server, origin string) (config *Config, err error) { + config = new(Config) + config.Version = ProtocolVersionHybi13 + config.Location, err = url.ParseRequestURI(server) + if err != nil { + return + } + config.Origin, err = url.ParseRequestURI(origin) + if err != nil { + return + } + config.Header = http.Header(make(map[string][]string)) + return +} + +// NewClient creates a new WebSocket client connection over rwc. +func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err error) { + br := bufio.NewReader(rwc) + bw := bufio.NewWriter(rwc) + err = hybiClientHandshake(config, br, bw) + if err != nil { + return + } + buf := bufio.NewReadWriter(br, bw) + ws = newHybiClientConn(config, buf, rwc) + return +} + +// Dial opens a new client connection to a WebSocket. +func Dial(url_, protocol, origin string) (ws *Conn, err error) { + config, err := NewConfig(url_, origin) + if err != nil { + return nil, err + } + if protocol != "" { + config.Protocol = []string{protocol} + } + return DialConfig(config) +} + +var portMap = map[string]string{ + "ws": "80", + "wss": "443", +} + +func parseAuthority(location *url.URL) string { + if _, ok := portMap[location.Scheme]; ok { + if _, _, err := net.SplitHostPort(location.Host); err != nil { + return net.JoinHostPort(location.Host, portMap[location.Scheme]) + } + } + return location.Host +} + +// DialConfig opens a new client connection to a WebSocket with a config. +func DialConfig(config *Config) (ws *Conn, err error) { + var client net.Conn + if config.Location == nil { + return nil, &DialError{config, ErrBadWebSocketLocation} + } + if config.Origin == nil { + return nil, &DialError{config, ErrBadWebSocketOrigin} + } + switch config.Location.Scheme { + case "ws": + client, err = net.Dial("tcp", parseAuthority(config.Location)) + + case "wss": + client, err = tls.Dial("tcp", parseAuthority(config.Location), config.TlsConfig) + + default: + err = ErrBadScheme + } + if err != nil { + goto Error + } + + ws, err = NewClient(config, client) + if err != nil { + client.Close() + goto Error + } + return + +Error: + return nil, &DialError{config, err} +} diff --git a/Godeps/_workspace/src/golang.org/x/net/websocket/exampledial_test.go b/Godeps/_workspace/src/golang.org/x/net/websocket/exampledial_test.go new file mode 100644 index 000000000..72bb9d48e --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/net/websocket/exampledial_test.go @@ -0,0 +1,31 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket_test + +import ( + "fmt" + "log" + + "golang.org/x/net/websocket" +) + +// This example demonstrates a trivial client. +func ExampleDial() { + origin := "http://localhost/" + url := "ws://localhost:12345/ws" + ws, err := websocket.Dial(url, "", origin) + if err != nil { + log.Fatal(err) + } + if _, err := ws.Write([]byte("hello, world!\n")); err != nil { + log.Fatal(err) + } + var msg = make([]byte, 512) + var n int + if n, err = ws.Read(msg); err != nil { + log.Fatal(err) + } + fmt.Printf("Received: %s.\n", msg[:n]) +} diff --git a/Godeps/_workspace/src/golang.org/x/net/websocket/examplehandler_test.go b/Godeps/_workspace/src/golang.org/x/net/websocket/examplehandler_test.go new file mode 100644 index 000000000..f22a98fcd --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/net/websocket/examplehandler_test.go @@ -0,0 +1,26 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket_test + +import ( + "io" + "net/http" + + "golang.org/x/net/websocket" +) + +// Echo the data received on the WebSocket. +func EchoServer(ws *websocket.Conn) { + io.Copy(ws, ws) +} + +// This example demonstrates a trivial echo server. +func ExampleHandler() { + http.Handle("/echo", websocket.Handler(EchoServer)) + err := http.ListenAndServe(":12345", nil) + if err != nil { + panic("ListenAndServe: " + err.Error()) + } +} diff --git a/Godeps/_workspace/src/golang.org/x/net/websocket/hybi.go b/Godeps/_workspace/src/golang.org/x/net/websocket/hybi.go new file mode 100644 index 000000000..f8c0b2e29 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/net/websocket/hybi.go @@ -0,0 +1,564 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +// This file implements a protocol of hybi draft. +// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 + +import ( + "bufio" + "bytes" + "crypto/rand" + "crypto/sha1" + "encoding/base64" + "encoding/binary" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +const ( + websocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + + closeStatusNormal = 1000 + closeStatusGoingAway = 1001 + closeStatusProtocolError = 1002 + closeStatusUnsupportedData = 1003 + closeStatusFrameTooLarge = 1004 + closeStatusNoStatusRcvd = 1005 + closeStatusAbnormalClosure = 1006 + closeStatusBadMessageData = 1007 + closeStatusPolicyViolation = 1008 + closeStatusTooBigData = 1009 + closeStatusExtensionMismatch = 1010 + + maxControlFramePayloadLength = 125 +) + +var ( + ErrBadMaskingKey = &ProtocolError{"bad masking key"} + ErrBadPongMessage = &ProtocolError{"bad pong message"} + ErrBadClosingStatus = &ProtocolError{"bad closing status"} + ErrUnsupportedExtensions = &ProtocolError{"unsupported extensions"} + ErrNotImplemented = &ProtocolError{"not implemented"} + + handshakeHeader = map[string]bool{ + "Host": true, + "Upgrade": true, + "Connection": true, + "Sec-Websocket-Key": true, + "Sec-Websocket-Origin": true, + "Sec-Websocket-Version": true, + "Sec-Websocket-Protocol": true, + "Sec-Websocket-Accept": true, + } +) + +// A hybiFrameHeader is a frame header as defined in hybi draft. +type hybiFrameHeader struct { + Fin bool + Rsv [3]bool + OpCode byte + Length int64 + MaskingKey []byte + + data *bytes.Buffer +} + +// A hybiFrameReader is a reader for hybi frame. +type hybiFrameReader struct { + reader io.Reader + + header hybiFrameHeader + pos int64 + length int +} + +func (frame *hybiFrameReader) Read(msg []byte) (n int, err error) { + n, err = frame.reader.Read(msg) + if err != nil { + return 0, err + } + if frame.header.MaskingKey != nil { + for i := 0; i < n; i++ { + msg[i] = msg[i] ^ frame.header.MaskingKey[frame.pos%4] + frame.pos++ + } + } + return n, err +} + +func (frame *hybiFrameReader) PayloadType() byte { return frame.header.OpCode } + +func (frame *hybiFrameReader) HeaderReader() io.Reader { + if frame.header.data == nil { + return nil + } + if frame.header.data.Len() == 0 { + return nil + } + return frame.header.data +} + +func (frame *hybiFrameReader) TrailerReader() io.Reader { return nil } + +func (frame *hybiFrameReader) Len() (n int) { return frame.length } + +// A hybiFrameReaderFactory creates new frame reader based on its frame type. +type hybiFrameReaderFactory struct { + *bufio.Reader +} + +// NewFrameReader reads a frame header from the connection, and creates new reader for the frame. +// See Section 5.2 Base Framing protocol for detail. +// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5.2 +func (buf hybiFrameReaderFactory) NewFrameReader() (frame frameReader, err error) { + hybiFrame := new(hybiFrameReader) + frame = hybiFrame + var header []byte + var b byte + // First byte. FIN/RSV1/RSV2/RSV3/OpCode(4bits) + b, err = buf.ReadByte() + if err != nil { + return + } + header = append(header, b) + hybiFrame.header.Fin = ((header[0] >> 7) & 1) != 0 + for i := 0; i < 3; i++ { + j := uint(6 - i) + hybiFrame.header.Rsv[i] = ((header[0] >> j) & 1) != 0 + } + hybiFrame.header.OpCode = header[0] & 0x0f + + // Second byte. Mask/Payload len(7bits) + b, err = buf.ReadByte() + if err != nil { + return + } + header = append(header, b) + mask := (b & 0x80) != 0 + b &= 0x7f + lengthFields := 0 + switch { + case b <= 125: // Payload length 7bits. + hybiFrame.header.Length = int64(b) + case b == 126: // Payload length 7+16bits + lengthFields = 2 + case b == 127: // Payload length 7+64bits + lengthFields = 8 + } + for i := 0; i < lengthFields; i++ { + b, err = buf.ReadByte() + if err != nil { + return + } + header = append(header, b) + hybiFrame.header.Length = hybiFrame.header.Length*256 + int64(b) + } + if mask { + // Masking key. 4 bytes. + for i := 0; i < 4; i++ { + b, err = buf.ReadByte() + if err != nil { + return + } + header = append(header, b) + hybiFrame.header.MaskingKey = append(hybiFrame.header.MaskingKey, b) + } + } + hybiFrame.reader = io.LimitReader(buf.Reader, hybiFrame.header.Length) + hybiFrame.header.data = bytes.NewBuffer(header) + hybiFrame.length = len(header) + int(hybiFrame.header.Length) + return +} + +// A HybiFrameWriter is a writer for hybi frame. +type hybiFrameWriter struct { + writer *bufio.Writer + + header *hybiFrameHeader +} + +func (frame *hybiFrameWriter) Write(msg []byte) (n int, err error) { + var header []byte + var b byte + if frame.header.Fin { + b |= 0x80 + } + for i := 0; i < 3; i++ { + if frame.header.Rsv[i] { + j := uint(6 - i) + b |= 1 << j + } + } + b |= frame.header.OpCode + header = append(header, b) + if frame.header.MaskingKey != nil { + b = 0x80 + } else { + b = 0 + } + lengthFields := 0 + length := len(msg) + switch { + case length <= 125: + b |= byte(length) + case length < 65536: + b |= 126 + lengthFields = 2 + default: + b |= 127 + lengthFields = 8 + } + header = append(header, b) + for i := 0; i < lengthFields; i++ { + j := uint((lengthFields - i - 1) * 8) + b = byte((length >> j) & 0xff) + header = append(header, b) + } + if frame.header.MaskingKey != nil { + if len(frame.header.MaskingKey) != 4 { + return 0, ErrBadMaskingKey + } + header = append(header, frame.header.MaskingKey...) + frame.writer.Write(header) + data := make([]byte, length) + for i := range data { + data[i] = msg[i] ^ frame.header.MaskingKey[i%4] + } + frame.writer.Write(data) + err = frame.writer.Flush() + return length, err + } + frame.writer.Write(header) + frame.writer.Write(msg) + err = frame.writer.Flush() + return length, err +} + +func (frame *hybiFrameWriter) Close() error { return nil } + +type hybiFrameWriterFactory struct { + *bufio.Writer + needMaskingKey bool +} + +func (buf hybiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) { + frameHeader := &hybiFrameHeader{Fin: true, OpCode: payloadType} + if buf.needMaskingKey { + frameHeader.MaskingKey, err = generateMaskingKey() + if err != nil { + return nil, err + } + } + return &hybiFrameWriter{writer: buf.Writer, header: frameHeader}, nil +} + +type hybiFrameHandler struct { + conn *Conn + payloadType byte +} + +func (handler *hybiFrameHandler) HandleFrame(frame frameReader) (r frameReader, err error) { + if handler.conn.IsServerConn() { + // The client MUST mask all frames sent to the server. + if frame.(*hybiFrameReader).header.MaskingKey == nil { + handler.WriteClose(closeStatusProtocolError) + return nil, io.EOF + } + } else { + // The server MUST NOT mask all frames. + if frame.(*hybiFrameReader).header.MaskingKey != nil { + handler.WriteClose(closeStatusProtocolError) + return nil, io.EOF + } + } + if header := frame.HeaderReader(); header != nil { + io.Copy(ioutil.Discard, header) + } + switch frame.PayloadType() { + case ContinuationFrame: + frame.(*hybiFrameReader).header.OpCode = handler.payloadType + case TextFrame, BinaryFrame: + handler.payloadType = frame.PayloadType() + case CloseFrame: + return nil, io.EOF + case PingFrame: + pingMsg := make([]byte, maxControlFramePayloadLength) + n, err := io.ReadFull(frame, pingMsg) + if err != nil && err != io.ErrUnexpectedEOF { + return nil, err + } + io.Copy(ioutil.Discard, frame) + n, err = handler.WritePong(pingMsg[:n]) + if err != nil { + return nil, err + } + return nil, nil + case PongFrame: + return nil, ErrNotImplemented + } + return frame, nil +} + +func (handler *hybiFrameHandler) WriteClose(status int) (err error) { + handler.conn.wio.Lock() + defer handler.conn.wio.Unlock() + w, err := handler.conn.frameWriterFactory.NewFrameWriter(CloseFrame) + if err != nil { + return err + } + msg := make([]byte, 2) + binary.BigEndian.PutUint16(msg, uint16(status)) + _, err = w.Write(msg) + w.Close() + return err +} + +func (handler *hybiFrameHandler) WritePong(msg []byte) (n int, err error) { + handler.conn.wio.Lock() + defer handler.conn.wio.Unlock() + w, err := handler.conn.frameWriterFactory.NewFrameWriter(PongFrame) + if err != nil { + return 0, err + } + n, err = w.Write(msg) + w.Close() + return n, err +} + +// newHybiConn creates a new WebSocket connection speaking hybi draft protocol. +func newHybiConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { + if buf == nil { + br := bufio.NewReader(rwc) + bw := bufio.NewWriter(rwc) + buf = bufio.NewReadWriter(br, bw) + } + ws := &Conn{config: config, request: request, buf: buf, rwc: rwc, + frameReaderFactory: hybiFrameReaderFactory{buf.Reader}, + frameWriterFactory: hybiFrameWriterFactory{ + buf.Writer, request == nil}, + PayloadType: TextFrame, + defaultCloseStatus: closeStatusNormal} + ws.frameHandler = &hybiFrameHandler{conn: ws} + return ws +} + +// generateMaskingKey generates a masking key for a frame. +func generateMaskingKey() (maskingKey []byte, err error) { + maskingKey = make([]byte, 4) + if _, err = io.ReadFull(rand.Reader, maskingKey); err != nil { + return + } + return +} + +// generateNonce generates a nonce consisting of a randomly selected 16-byte +// value that has been base64-encoded. +func generateNonce() (nonce []byte) { + key := make([]byte, 16) + if _, err := io.ReadFull(rand.Reader, key); err != nil { + panic(err) + } + nonce = make([]byte, 24) + base64.StdEncoding.Encode(nonce, key) + return +} + +// getNonceAccept computes the base64-encoded SHA-1 of the concatenation of +// the nonce ("Sec-WebSocket-Key" value) with the websocket GUID string. +func getNonceAccept(nonce []byte) (expected []byte, err error) { + h := sha1.New() + if _, err = h.Write(nonce); err != nil { + return + } + if _, err = h.Write([]byte(websocketGUID)); err != nil { + return + } + expected = make([]byte, 28) + base64.StdEncoding.Encode(expected, h.Sum(nil)) + return +} + +// Client handshake described in draft-ietf-hybi-thewebsocket-protocol-17 +func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) { + bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n") + + bw.WriteString("Host: " + config.Location.Host + "\r\n") + bw.WriteString("Upgrade: websocket\r\n") + bw.WriteString("Connection: Upgrade\r\n") + nonce := generateNonce() + if config.handshakeData != nil { + nonce = []byte(config.handshakeData["key"]) + } + bw.WriteString("Sec-WebSocket-Key: " + string(nonce) + "\r\n") + bw.WriteString("Origin: " + strings.ToLower(config.Origin.String()) + "\r\n") + + if config.Version != ProtocolVersionHybi13 { + return ErrBadProtocolVersion + } + + bw.WriteString("Sec-WebSocket-Version: " + fmt.Sprintf("%d", config.Version) + "\r\n") + if len(config.Protocol) > 0 { + bw.WriteString("Sec-WebSocket-Protocol: " + strings.Join(config.Protocol, ", ") + "\r\n") + } + // TODO(ukai): send Sec-WebSocket-Extensions. + err = config.Header.WriteSubset(bw, handshakeHeader) + if err != nil { + return err + } + + bw.WriteString("\r\n") + if err = bw.Flush(); err != nil { + return err + } + + resp, err := http.ReadResponse(br, &http.Request{Method: "GET"}) + if err != nil { + return err + } + if resp.StatusCode != 101 { + return ErrBadStatus + } + if strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" || + strings.ToLower(resp.Header.Get("Connection")) != "upgrade" { + return ErrBadUpgrade + } + expectedAccept, err := getNonceAccept(nonce) + if err != nil { + return err + } + if resp.Header.Get("Sec-WebSocket-Accept") != string(expectedAccept) { + return ErrChallengeResponse + } + if resp.Header.Get("Sec-WebSocket-Extensions") != "" { + return ErrUnsupportedExtensions + } + offeredProtocol := resp.Header.Get("Sec-WebSocket-Protocol") + if offeredProtocol != "" { + protocolMatched := false + for i := 0; i < len(config.Protocol); i++ { + if config.Protocol[i] == offeredProtocol { + protocolMatched = true + break + } + } + if !protocolMatched { + return ErrBadWebSocketProtocol + } + config.Protocol = []string{offeredProtocol} + } + + return nil +} + +// newHybiClientConn creates a client WebSocket connection after handshake. +func newHybiClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn { + return newHybiConn(config, buf, rwc, nil) +} + +// A HybiServerHandshaker performs a server handshake using hybi draft protocol. +type hybiServerHandshaker struct { + *Config + accept []byte +} + +func (c *hybiServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) { + c.Version = ProtocolVersionHybi13 + if req.Method != "GET" { + return http.StatusMethodNotAllowed, ErrBadRequestMethod + } + // HTTP version can be safely ignored. + + if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" || + !strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") { + return http.StatusBadRequest, ErrNotWebSocket + } + + key := req.Header.Get("Sec-Websocket-Key") + if key == "" { + return http.StatusBadRequest, ErrChallengeResponse + } + version := req.Header.Get("Sec-Websocket-Version") + switch version { + case "13": + c.Version = ProtocolVersionHybi13 + default: + return http.StatusBadRequest, ErrBadWebSocketVersion + } + var scheme string + if req.TLS != nil { + scheme = "wss" + } else { + scheme = "ws" + } + c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI()) + if err != nil { + return http.StatusBadRequest, err + } + protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol")) + if protocol != "" { + protocols := strings.Split(protocol, ",") + for i := 0; i < len(protocols); i++ { + c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) + } + } + c.accept, err = getNonceAccept([]byte(key)) + if err != nil { + return http.StatusInternalServerError, err + } + return http.StatusSwitchingProtocols, nil +} + +// Origin parses Origin header in "req". +// If origin is "null", returns (nil, nil). +func Origin(config *Config, req *http.Request) (*url.URL, error) { + var origin string + switch config.Version { + case ProtocolVersionHybi13: + origin = req.Header.Get("Origin") + } + if origin == "null" { + return nil, nil + } + return url.ParseRequestURI(origin) +} + +func (c *hybiServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) { + if len(c.Protocol) > 0 { + if len(c.Protocol) != 1 { + // You need choose a Protocol in Handshake func in Server. + return ErrBadWebSocketProtocol + } + } + buf.WriteString("HTTP/1.1 101 Switching Protocols\r\n") + buf.WriteString("Upgrade: websocket\r\n") + buf.WriteString("Connection: Upgrade\r\n") + buf.WriteString("Sec-WebSocket-Accept: " + string(c.accept) + "\r\n") + if len(c.Protocol) > 0 { + buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n") + } + // TODO(ukai): send Sec-WebSocket-Extensions. + if c.Header != nil { + err := c.Header.WriteSubset(buf, handshakeHeader) + if err != nil { + return err + } + } + buf.WriteString("\r\n") + return buf.Flush() +} + +func (c *hybiServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { + return newHybiServerConn(c.Config, buf, rwc, request) +} + +// newHybiServerConn returns a new WebSocket connection speaking hybi draft protocol. +func newHybiServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { + return newHybiConn(config, buf, rwc, request) +} diff --git a/Godeps/_workspace/src/golang.org/x/net/websocket/hybi_test.go b/Godeps/_workspace/src/golang.org/x/net/websocket/hybi_test.go new file mode 100644 index 000000000..d6a19108a --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/net/websocket/hybi_test.go @@ -0,0 +1,590 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bufio" + "bytes" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "testing" +) + +// Test the getNonceAccept function with values in +// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 +func TestSecWebSocketAccept(t *testing.T) { + nonce := []byte("dGhlIHNhbXBsZSBub25jZQ==") + expected := []byte("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=") + accept, err := getNonceAccept(nonce) + if err != nil { + t.Errorf("getNonceAccept: returned error %v", err) + return + } + if !bytes.Equal(expected, accept) { + t.Errorf("getNonceAccept: expected %q got %q", expected, accept) + } +} + +func TestHybiClientHandshake(t *testing.T) { + b := bytes.NewBuffer([]byte{}) + bw := bufio.NewWriter(b) + br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 Switching Protocols +Upgrade: websocket +Connection: Upgrade +Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= +Sec-WebSocket-Protocol: chat + +`)) + var err error + config := new(Config) + config.Location, err = url.ParseRequestURI("ws://server.example.com/chat") + if err != nil { + t.Fatal("location url", err) + } + config.Origin, err = url.ParseRequestURI("http://example.com") + if err != nil { + t.Fatal("origin url", err) + } + config.Protocol = append(config.Protocol, "chat") + config.Protocol = append(config.Protocol, "superchat") + config.Version = ProtocolVersionHybi13 + + config.handshakeData = map[string]string{ + "key": "dGhlIHNhbXBsZSBub25jZQ==", + } + err = hybiClientHandshake(config, br, bw) + if err != nil { + t.Errorf("handshake failed: %v", err) + } + req, err := http.ReadRequest(bufio.NewReader(b)) + if err != nil { + t.Fatalf("read request: %v", err) + } + if req.Method != "GET" { + t.Errorf("request method expected GET, but got %q", req.Method) + } + if req.URL.Path != "/chat" { + t.Errorf("request path expected /chat, but got %q", req.URL.Path) + } + if req.Proto != "HTTP/1.1" { + t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto) + } + if req.Host != "server.example.com" { + t.Errorf("request Host expected server.example.com, but got %v", req.Host) + } + var expectedHeader = map[string]string{ + "Connection": "Upgrade", + "Upgrade": "websocket", + "Sec-Websocket-Key": config.handshakeData["key"], + "Origin": config.Origin.String(), + "Sec-Websocket-Protocol": "chat, superchat", + "Sec-Websocket-Version": fmt.Sprintf("%d", ProtocolVersionHybi13), + } + for k, v := range expectedHeader { + if req.Header.Get(k) != v { + t.Errorf(fmt.Sprintf("%s expected %q but got %q", k, v, req.Header.Get(k))) + } + } +} + +func TestHybiClientHandshakeWithHeader(t *testing.T) { + b := bytes.NewBuffer([]byte{}) + bw := bufio.NewWriter(b) + br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 Switching Protocols +Upgrade: websocket +Connection: Upgrade +Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= +Sec-WebSocket-Protocol: chat + +`)) + var err error + config := new(Config) + config.Location, err = url.ParseRequestURI("ws://server.example.com/chat") + if err != nil { + t.Fatal("location url", err) + } + config.Origin, err = url.ParseRequestURI("http://example.com") + if err != nil { + t.Fatal("origin url", err) + } + config.Protocol = append(config.Protocol, "chat") + config.Protocol = append(config.Protocol, "superchat") + config.Version = ProtocolVersionHybi13 + config.Header = http.Header(make(map[string][]string)) + config.Header.Add("User-Agent", "test") + + config.handshakeData = map[string]string{ + "key": "dGhlIHNhbXBsZSBub25jZQ==", + } + err = hybiClientHandshake(config, br, bw) + if err != nil { + t.Errorf("handshake failed: %v", err) + } + req, err := http.ReadRequest(bufio.NewReader(b)) + if err != nil { + t.Fatalf("read request: %v", err) + } + if req.Method != "GET" { + t.Errorf("request method expected GET, but got %q", req.Method) + } + if req.URL.Path != "/chat" { + t.Errorf("request path expected /chat, but got %q", req.URL.Path) + } + if req.Proto != "HTTP/1.1" { + t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto) + } + if req.Host != "server.example.com" { + t.Errorf("request Host expected server.example.com, but got %v", req.Host) + } + var expectedHeader = map[string]string{ + "Connection": "Upgrade", + "Upgrade": "websocket", + "Sec-Websocket-Key": config.handshakeData["key"], + "Origin": config.Origin.String(), + "Sec-Websocket-Protocol": "chat, superchat", + "Sec-Websocket-Version": fmt.Sprintf("%d", ProtocolVersionHybi13), + "User-Agent": "test", + } + for k, v := range expectedHeader { + if req.Header.Get(k) != v { + t.Errorf(fmt.Sprintf("%s expected %q but got %q", k, v, req.Header.Get(k))) + } + } +} + +func TestHybiServerHandshake(t *testing.T) { + config := new(Config) + handshaker := &hybiServerHandshaker{Config: config} + br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 +Host: server.example.com +Upgrade: websocket +Connection: Upgrade +Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== +Origin: http://example.com +Sec-WebSocket-Protocol: chat, superchat +Sec-WebSocket-Version: 13 + +`)) + req, err := http.ReadRequest(br) + if err != nil { + t.Fatal("request", err) + } + code, err := handshaker.ReadHandshake(br, req) + if err != nil { + t.Errorf("handshake failed: %v", err) + } + if code != http.StatusSwitchingProtocols { + t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code) + } + expectedProtocols := []string{"chat", "superchat"} + if fmt.Sprintf("%v", config.Protocol) != fmt.Sprintf("%v", expectedProtocols) { + t.Errorf("protocol expected %q but got %q", expectedProtocols, config.Protocol) + } + b := bytes.NewBuffer([]byte{}) + bw := bufio.NewWriter(b) + + config.Protocol = config.Protocol[:1] + + err = handshaker.AcceptHandshake(bw) + if err != nil { + t.Errorf("handshake response failed: %v", err) + } + expectedResponse := strings.Join([]string{ + "HTTP/1.1 101 Switching Protocols", + "Upgrade: websocket", + "Connection: Upgrade", + "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", + "Sec-WebSocket-Protocol: chat", + "", ""}, "\r\n") + + if b.String() != expectedResponse { + t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) + } +} + +func TestHybiServerHandshakeNoSubProtocol(t *testing.T) { + config := new(Config) + handshaker := &hybiServerHandshaker{Config: config} + br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 +Host: server.example.com +Upgrade: websocket +Connection: Upgrade +Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== +Origin: http://example.com +Sec-WebSocket-Version: 13 + +`)) + req, err := http.ReadRequest(br) + if err != nil { + t.Fatal("request", err) + } + code, err := handshaker.ReadHandshake(br, req) + if err != nil { + t.Errorf("handshake failed: %v", err) + } + if code != http.StatusSwitchingProtocols { + t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code) + } + if len(config.Protocol) != 0 { + t.Errorf("len(config.Protocol) expected 0, but got %q", len(config.Protocol)) + } + b := bytes.NewBuffer([]byte{}) + bw := bufio.NewWriter(b) + + err = handshaker.AcceptHandshake(bw) + if err != nil { + t.Errorf("handshake response failed: %v", err) + } + expectedResponse := strings.Join([]string{ + "HTTP/1.1 101 Switching Protocols", + "Upgrade: websocket", + "Connection: Upgrade", + "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", + "", ""}, "\r\n") + + if b.String() != expectedResponse { + t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) + } +} + +func TestHybiServerHandshakeHybiBadVersion(t *testing.T) { + config := new(Config) + handshaker := &hybiServerHandshaker{Config: config} + br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 +Host: server.example.com +Upgrade: websocket +Connection: Upgrade +Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== +Sec-WebSocket-Origin: http://example.com +Sec-WebSocket-Protocol: chat, superchat +Sec-WebSocket-Version: 9 + +`)) + req, err := http.ReadRequest(br) + if err != nil { + t.Fatal("request", err) + } + code, err := handshaker.ReadHandshake(br, req) + if err != ErrBadWebSocketVersion { + t.Errorf("handshake expected err %q but got %q", ErrBadWebSocketVersion, err) + } + if code != http.StatusBadRequest { + t.Errorf("status expected %q but got %q", http.StatusBadRequest, code) + } +} + +func testHybiFrame(t *testing.T, testHeader, testPayload, testMaskedPayload []byte, frameHeader *hybiFrameHeader) { + b := bytes.NewBuffer([]byte{}) + frameWriterFactory := &hybiFrameWriterFactory{bufio.NewWriter(b), false} + w, _ := frameWriterFactory.NewFrameWriter(TextFrame) + w.(*hybiFrameWriter).header = frameHeader + _, err := w.Write(testPayload) + w.Close() + if err != nil { + t.Errorf("Write error %q", err) + } + var expectedFrame []byte + expectedFrame = append(expectedFrame, testHeader...) + expectedFrame = append(expectedFrame, testMaskedPayload...) + if !bytes.Equal(expectedFrame, b.Bytes()) { + t.Errorf("frame expected %q got %q", expectedFrame, b.Bytes()) + } + frameReaderFactory := &hybiFrameReaderFactory{bufio.NewReader(b)} + r, err := frameReaderFactory.NewFrameReader() + if err != nil { + t.Errorf("Read error %q", err) + } + if header := r.HeaderReader(); header == nil { + t.Errorf("no header") + } else { + actualHeader := make([]byte, r.Len()) + n, err := header.Read(actualHeader) + if err != nil { + t.Errorf("Read header error %q", err) + } else { + if n < len(testHeader) { + t.Errorf("header too short %q got %q", testHeader, actualHeader[:n]) + } + if !bytes.Equal(testHeader, actualHeader[:n]) { + t.Errorf("header expected %q got %q", testHeader, actualHeader[:n]) + } + } + } + if trailer := r.TrailerReader(); trailer != nil { + t.Errorf("unexpected trailer %q", trailer) + } + frame := r.(*hybiFrameReader) + if frameHeader.Fin != frame.header.Fin || + frameHeader.OpCode != frame.header.OpCode || + len(testPayload) != int(frame.header.Length) { + t.Errorf("mismatch %v (%d) vs %v", frameHeader, len(testPayload), frame) + } + payload := make([]byte, len(testPayload)) + _, err = r.Read(payload) + if err != nil { + t.Errorf("read %v", err) + } + if !bytes.Equal(testPayload, payload) { + t.Errorf("payload %q vs %q", testPayload, payload) + } +} + +func TestHybiShortTextFrame(t *testing.T) { + frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame} + payload := []byte("hello") + testHybiFrame(t, []byte{0x81, 0x05}, payload, payload, frameHeader) + + payload = make([]byte, 125) + testHybiFrame(t, []byte{0x81, 125}, payload, payload, frameHeader) +} + +func TestHybiShortMaskedTextFrame(t *testing.T) { + frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame, + MaskingKey: []byte{0xcc, 0x55, 0x80, 0x20}} + payload := []byte("hello") + maskedPayload := []byte{0xa4, 0x30, 0xec, 0x4c, 0xa3} + header := []byte{0x81, 0x85} + header = append(header, frameHeader.MaskingKey...) + testHybiFrame(t, header, payload, maskedPayload, frameHeader) +} + +func TestHybiShortBinaryFrame(t *testing.T) { + frameHeader := &hybiFrameHeader{Fin: true, OpCode: BinaryFrame} + payload := []byte("hello") + testHybiFrame(t, []byte{0x82, 0x05}, payload, payload, frameHeader) + + payload = make([]byte, 125) + testHybiFrame(t, []byte{0x82, 125}, payload, payload, frameHeader) +} + +func TestHybiControlFrame(t *testing.T) { + frameHeader := &hybiFrameHeader{Fin: true, OpCode: PingFrame} + payload := []byte("hello") + testHybiFrame(t, []byte{0x89, 0x05}, payload, payload, frameHeader) + + frameHeader = &hybiFrameHeader{Fin: true, OpCode: PongFrame} + testHybiFrame(t, []byte{0x8A, 0x05}, payload, payload, frameHeader) + + frameHeader = &hybiFrameHeader{Fin: true, OpCode: CloseFrame} + payload = []byte{0x03, 0xe8} // 1000 + testHybiFrame(t, []byte{0x88, 0x02}, payload, payload, frameHeader) +} + +func TestHybiLongFrame(t *testing.T) { + frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame} + payload := make([]byte, 126) + testHybiFrame(t, []byte{0x81, 126, 0x00, 126}, payload, payload, frameHeader) + + payload = make([]byte, 65535) + testHybiFrame(t, []byte{0x81, 126, 0xff, 0xff}, payload, payload, frameHeader) + + payload = make([]byte, 65536) + testHybiFrame(t, []byte{0x81, 127, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00}, payload, payload, frameHeader) +} + +func TestHybiClientRead(t *testing.T) { + wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o', + 0x89, 0x05, 'h', 'e', 'l', 'l', 'o', // ping + 0x81, 0x05, 'w', 'o', 'r', 'l', 'd'} + br := bufio.NewReader(bytes.NewBuffer(wireData)) + bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) + conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil) + + msg := make([]byte, 512) + n, err := conn.Read(msg) + if err != nil { + t.Errorf("read 1st frame, error %q", err) + } + if n != 5 { + t.Errorf("read 1st frame, expect 5, got %d", n) + } + if !bytes.Equal(wireData[2:7], msg[:n]) { + t.Errorf("read 1st frame %v, got %v", wireData[2:7], msg[:n]) + } + n, err = conn.Read(msg) + if err != nil { + t.Errorf("read 2nd frame, error %q", err) + } + if n != 5 { + t.Errorf("read 2nd frame, expect 5, got %d", n) + } + if !bytes.Equal(wireData[16:21], msg[:n]) { + t.Errorf("read 2nd frame %v, got %v", wireData[16:21], msg[:n]) + } + n, err = conn.Read(msg) + if err == nil { + t.Errorf("read not EOF") + } + if n != 0 { + t.Errorf("expect read 0, got %d", n) + } +} + +func TestHybiShortRead(t *testing.T) { + wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o', + 0x89, 0x05, 'h', 'e', 'l', 'l', 'o', // ping + 0x81, 0x05, 'w', 'o', 'r', 'l', 'd'} + br := bufio.NewReader(bytes.NewBuffer(wireData)) + bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) + conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil) + + step := 0 + pos := 0 + expectedPos := []int{2, 5, 16, 19} + expectedLen := []int{3, 2, 3, 2} + for { + msg := make([]byte, 3) + n, err := conn.Read(msg) + if step >= len(expectedPos) { + if err == nil { + t.Errorf("read not EOF") + } + if n != 0 { + t.Errorf("expect read 0, got %d", n) + } + return + } + pos = expectedPos[step] + endPos := pos + expectedLen[step] + if err != nil { + t.Errorf("read from %d, got error %q", pos, err) + return + } + if n != endPos-pos { + t.Errorf("read from %d, expect %d, got %d", pos, endPos-pos, n) + } + if !bytes.Equal(wireData[pos:endPos], msg[:n]) { + t.Errorf("read from %d, frame %v, got %v", pos, wireData[pos:endPos], msg[:n]) + } + step++ + } +} + +func TestHybiServerRead(t *testing.T) { + wireData := []byte{0x81, 0x85, 0xcc, 0x55, 0x80, 0x20, + 0xa4, 0x30, 0xec, 0x4c, 0xa3, // hello + 0x89, 0x85, 0xcc, 0x55, 0x80, 0x20, + 0xa4, 0x30, 0xec, 0x4c, 0xa3, // ping: hello + 0x81, 0x85, 0xed, 0x83, 0xb4, 0x24, + 0x9a, 0xec, 0xc6, 0x48, 0x89, // world + } + br := bufio.NewReader(bytes.NewBuffer(wireData)) + bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) + conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, new(http.Request)) + + expected := [][]byte{[]byte("hello"), []byte("world")} + + msg := make([]byte, 512) + n, err := conn.Read(msg) + if err != nil { + t.Errorf("read 1st frame, error %q", err) + } + if n != 5 { + t.Errorf("read 1st frame, expect 5, got %d", n) + } + if !bytes.Equal(expected[0], msg[:n]) { + t.Errorf("read 1st frame %q, got %q", expected[0], msg[:n]) + } + + n, err = conn.Read(msg) + if err != nil { + t.Errorf("read 2nd frame, error %q", err) + } + if n != 5 { + t.Errorf("read 2nd frame, expect 5, got %d", n) + } + if !bytes.Equal(expected[1], msg[:n]) { + t.Errorf("read 2nd frame %q, got %q", expected[1], msg[:n]) + } + + n, err = conn.Read(msg) + if err == nil { + t.Errorf("read not EOF") + } + if n != 0 { + t.Errorf("expect read 0, got %d", n) + } +} + +func TestHybiServerReadWithoutMasking(t *testing.T) { + wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o'} + br := bufio.NewReader(bytes.NewBuffer(wireData)) + bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) + conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, new(http.Request)) + // server MUST close the connection upon receiving a non-masked frame. + msg := make([]byte, 512) + _, err := conn.Read(msg) + if err != io.EOF { + t.Errorf("read 1st frame, expect %q, but got %q", io.EOF, err) + } +} + +func TestHybiClientReadWithMasking(t *testing.T) { + wireData := []byte{0x81, 0x85, 0xcc, 0x55, 0x80, 0x20, + 0xa4, 0x30, 0xec, 0x4c, 0xa3, // hello + } + br := bufio.NewReader(bytes.NewBuffer(wireData)) + bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) + conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil) + + // client MUST close the connection upon receiving a masked frame. + msg := make([]byte, 512) + _, err := conn.Read(msg) + if err != io.EOF { + t.Errorf("read 1st frame, expect %q, but got %q", io.EOF, err) + } +} + +// Test the hybiServerHandshaker supports firefox implementation and +// checks Connection request header include (but it's not necessary +// equal to) "upgrade" +func TestHybiServerFirefoxHandshake(t *testing.T) { + config := new(Config) + handshaker := &hybiServerHandshaker{Config: config} + br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 +Host: server.example.com +Upgrade: websocket +Connection: keep-alive, upgrade +Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== +Origin: http://example.com +Sec-WebSocket-Protocol: chat, superchat +Sec-WebSocket-Version: 13 + +`)) + req, err := http.ReadRequest(br) + if err != nil { + t.Fatal("request", err) + } + code, err := handshaker.ReadHandshake(br, req) + if err != nil { + t.Errorf("handshake failed: %v", err) + } + if code != http.StatusSwitchingProtocols { + t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code) + } + b := bytes.NewBuffer([]byte{}) + bw := bufio.NewWriter(b) + + config.Protocol = []string{"chat"} + + err = handshaker.AcceptHandshake(bw) + if err != nil { + t.Errorf("handshake response failed: %v", err) + } + expectedResponse := strings.Join([]string{ + "HTTP/1.1 101 Switching Protocols", + "Upgrade: websocket", + "Connection: Upgrade", + "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", + "Sec-WebSocket-Protocol: chat", + "", ""}, "\r\n") + + if b.String() != expectedResponse { + t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) + } +} diff --git a/Godeps/_workspace/src/golang.org/x/net/websocket/server.go b/Godeps/_workspace/src/golang.org/x/net/websocket/server.go new file mode 100644 index 000000000..70322133c --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/net/websocket/server.go @@ -0,0 +1,114 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bufio" + "fmt" + "io" + "net/http" +) + +func newServerConn(rwc io.ReadWriteCloser, buf *bufio.ReadWriter, req *http.Request, config *Config, handshake func(*Config, *http.Request) error) (conn *Conn, err error) { + var hs serverHandshaker = &hybiServerHandshaker{Config: config} + code, err := hs.ReadHandshake(buf.Reader, req) + if err == ErrBadWebSocketVersion { + fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) + fmt.Fprintf(buf, "Sec-WebSocket-Version: %s\r\n", SupportedProtocolVersion) + buf.WriteString("\r\n") + buf.WriteString(err.Error()) + buf.Flush() + return + } + if err != nil { + fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) + buf.WriteString("\r\n") + buf.WriteString(err.Error()) + buf.Flush() + return + } + if handshake != nil { + err = handshake(config, req) + if err != nil { + code = http.StatusForbidden + fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) + buf.WriteString("\r\n") + buf.Flush() + return + } + } + err = hs.AcceptHandshake(buf.Writer) + if err != nil { + code = http.StatusBadRequest + fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) + buf.WriteString("\r\n") + buf.Flush() + return + } + conn = hs.NewServerConn(buf, rwc, req) + return +} + +// Server represents a server of a WebSocket. +type Server struct { + // Config is a WebSocket configuration for new WebSocket connection. + Config + + // Handshake is an optional function in WebSocket handshake. + // For example, you can check, or don't check Origin header. + // Another example, you can select config.Protocol. + Handshake func(*Config, *http.Request) error + + // Handler handles a WebSocket connection. + Handler +} + +// ServeHTTP implements the http.Handler interface for a WebSocket +func (s Server) ServeHTTP(w http.ResponseWriter, req *http.Request) { + s.serveWebSocket(w, req) +} + +func (s Server) serveWebSocket(w http.ResponseWriter, req *http.Request) { + rwc, buf, err := w.(http.Hijacker).Hijack() + if err != nil { + panic("Hijack failed: " + err.Error()) + return + } + // The server should abort the WebSocket connection if it finds + // the client did not send a handshake that matches with protocol + // specification. + defer rwc.Close() + conn, err := newServerConn(rwc, buf, req, &s.Config, s.Handshake) + if err != nil { + return + } + if conn == nil { + panic("unexpected nil conn") + } + s.Handler(conn) +} + +// Handler is a simple interface to a WebSocket browser client. +// It checks if Origin header is valid URL by default. +// You might want to verify websocket.Conn.Config().Origin in the func. +// If you use Server instead of Handler, you could call websocket.Origin and +// check the origin in your Handshake func. So, if you want to accept +// non-browser client, which doesn't send Origin header, you could use Server +//. that doesn't check origin in its Handshake. +type Handler func(*Conn) + +func checkOrigin(config *Config, req *http.Request) (err error) { + config.Origin, err = Origin(config, req) + if err == nil && config.Origin == nil { + return fmt.Errorf("null origin") + } + return err +} + +// ServeHTTP implements the http.Handler interface for a WebSocket +func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + s := Server{Handler: h, Handshake: checkOrigin} + s.serveWebSocket(w, req) +} diff --git a/Godeps/_workspace/src/golang.org/x/net/websocket/websocket.go b/Godeps/_workspace/src/golang.org/x/net/websocket/websocket.go new file mode 100644 index 000000000..0f4917bf7 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/net/websocket/websocket.go @@ -0,0 +1,411 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package websocket implements a client and server for the WebSocket protocol +// as specified in RFC 6455. +package websocket + +import ( + "bufio" + "crypto/tls" + "encoding/json" + "errors" + "io" + "io/ioutil" + "net" + "net/http" + "net/url" + "sync" + "time" +) + +const ( + ProtocolVersionHybi13 = 13 + ProtocolVersionHybi = ProtocolVersionHybi13 + SupportedProtocolVersion = "13" + + ContinuationFrame = 0 + TextFrame = 1 + BinaryFrame = 2 + CloseFrame = 8 + PingFrame = 9 + PongFrame = 10 + UnknownFrame = 255 +) + +// ProtocolError represents WebSocket protocol errors. +type ProtocolError struct { + ErrorString string +} + +func (err *ProtocolError) Error() string { return err.ErrorString } + +var ( + ErrBadProtocolVersion = &ProtocolError{"bad protocol version"} + ErrBadScheme = &ProtocolError{"bad scheme"} + ErrBadStatus = &ProtocolError{"bad status"} + ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"} + ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"} + ErrBadWebSocketLocation = &ProtocolError{"missing or bad WebSocket-Location"} + ErrBadWebSocketProtocol = &ProtocolError{"missing or bad WebSocket-Protocol"} + ErrBadWebSocketVersion = &ProtocolError{"missing or bad WebSocket Version"} + ErrChallengeResponse = &ProtocolError{"mismatch challenge/response"} + ErrBadFrame = &ProtocolError{"bad frame"} + ErrBadFrameBoundary = &ProtocolError{"not on frame boundary"} + ErrNotWebSocket = &ProtocolError{"not websocket protocol"} + ErrBadRequestMethod = &ProtocolError{"bad method"} + ErrNotSupported = &ProtocolError{"not supported"} +) + +// Addr is an implementation of net.Addr for WebSocket. +type Addr struct { + *url.URL +} + +// Network returns the network type for a WebSocket, "websocket". +func (addr *Addr) Network() string { return "websocket" } + +// Config is a WebSocket configuration +type Config struct { + // A WebSocket server address. + Location *url.URL + + // A Websocket client origin. + Origin *url.URL + + // WebSocket subprotocols. + Protocol []string + + // WebSocket protocol version. + Version int + + // TLS config for secure WebSocket (wss). + TlsConfig *tls.Config + + // Additional header fields to be sent in WebSocket opening handshake. + Header http.Header + + handshakeData map[string]string +} + +// serverHandshaker is an interface to handle WebSocket server side handshake. +type serverHandshaker interface { + // ReadHandshake reads handshake request message from client. + // Returns http response code and error if any. + ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) + + // AcceptHandshake accepts the client handshake request and sends + // handshake response back to client. + AcceptHandshake(buf *bufio.Writer) (err error) + + // NewServerConn creates a new WebSocket connection. + NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) +} + +// frameReader is an interface to read a WebSocket frame. +type frameReader interface { + // Reader is to read payload of the frame. + io.Reader + + // PayloadType returns payload type. + PayloadType() byte + + // HeaderReader returns a reader to read header of the frame. + HeaderReader() io.Reader + + // TrailerReader returns a reader to read trailer of the frame. + // If it returns nil, there is no trailer in the frame. + TrailerReader() io.Reader + + // Len returns total length of the frame, including header and trailer. + Len() int +} + +// frameReaderFactory is an interface to creates new frame reader. +type frameReaderFactory interface { + NewFrameReader() (r frameReader, err error) +} + +// frameWriter is an interface to write a WebSocket frame. +type frameWriter interface { + // Writer is to write payload of the frame. + io.WriteCloser +} + +// frameWriterFactory is an interface to create new frame writer. +type frameWriterFactory interface { + NewFrameWriter(payloadType byte) (w frameWriter, err error) +} + +type frameHandler interface { + HandleFrame(frame frameReader) (r frameReader, err error) + WriteClose(status int) (err error) +} + +// Conn represents a WebSocket connection. +type Conn struct { + config *Config + request *http.Request + + buf *bufio.ReadWriter + rwc io.ReadWriteCloser + + rio sync.Mutex + frameReaderFactory + frameReader + + wio sync.Mutex + frameWriterFactory + + frameHandler + PayloadType byte + defaultCloseStatus int +} + +// Read implements the io.Reader interface: +// it reads data of a frame from the WebSocket connection. +// if msg is not large enough for the frame data, it fills the msg and next Read +// will read the rest of the frame data. +// it reads Text frame or Binary frame. +func (ws *Conn) Read(msg []byte) (n int, err error) { + ws.rio.Lock() + defer ws.rio.Unlock() +again: + if ws.frameReader == nil { + frame, err := ws.frameReaderFactory.NewFrameReader() + if err != nil { + return 0, err + } + ws.frameReader, err = ws.frameHandler.HandleFrame(frame) + if err != nil { + return 0, err + } + if ws.frameReader == nil { + goto again + } + } + n, err = ws.frameReader.Read(msg) + if err == io.EOF { + if trailer := ws.frameReader.TrailerReader(); trailer != nil { + io.Copy(ioutil.Discard, trailer) + } + ws.frameReader = nil + goto again + } + return n, err +} + +// Write implements the io.Writer interface: +// it writes data as a frame to the WebSocket connection. +func (ws *Conn) Write(msg []byte) (n int, err error) { + ws.wio.Lock() + defer ws.wio.Unlock() + w, err := ws.frameWriterFactory.NewFrameWriter(ws.PayloadType) + if err != nil { + return 0, err + } + n, err = w.Write(msg) + w.Close() + if err != nil { + return n, err + } + return n, err +} + +// Close implements the io.Closer interface. +func (ws *Conn) Close() error { + err := ws.frameHandler.WriteClose(ws.defaultCloseStatus) + if err != nil { + return err + } + return ws.rwc.Close() +} + +func (ws *Conn) IsClientConn() bool { return ws.request == nil } +func (ws *Conn) IsServerConn() bool { return ws.request != nil } + +// LocalAddr returns the WebSocket Origin for the connection for client, or +// the WebSocket location for server. +func (ws *Conn) LocalAddr() net.Addr { + if ws.IsClientConn() { + return &Addr{ws.config.Origin} + } + return &Addr{ws.config.Location} +} + +// RemoteAddr returns the WebSocket location for the connection for client, or +// the Websocket Origin for server. +func (ws *Conn) RemoteAddr() net.Addr { + if ws.IsClientConn() { + return &Addr{ws.config.Location} + } + return &Addr{ws.config.Origin} +} + +var errSetDeadline = errors.New("websocket: cannot set deadline: not using a net.Conn") + +// SetDeadline sets the connection's network read & write deadlines. +func (ws *Conn) SetDeadline(t time.Time) error { + if conn, ok := ws.rwc.(net.Conn); ok { + return conn.SetDeadline(t) + } + return errSetDeadline +} + +// SetReadDeadline sets the connection's network read deadline. +func (ws *Conn) SetReadDeadline(t time.Time) error { + if conn, ok := ws.rwc.(net.Conn); ok { + return conn.SetReadDeadline(t) + } + return errSetDeadline +} + +// SetWriteDeadline sets the connection's network write deadline. +func (ws *Conn) SetWriteDeadline(t time.Time) error { + if conn, ok := ws.rwc.(net.Conn); ok { + return conn.SetWriteDeadline(t) + } + return errSetDeadline +} + +// Config returns the WebSocket config. +func (ws *Conn) Config() *Config { return ws.config } + +// Request returns the http request upgraded to the WebSocket. +// It is nil for client side. +func (ws *Conn) Request() *http.Request { return ws.request } + +// Codec represents a symmetric pair of functions that implement a codec. +type Codec struct { + Marshal func(v interface{}) (data []byte, payloadType byte, err error) + Unmarshal func(data []byte, payloadType byte, v interface{}) (err error) +} + +// Send sends v marshaled by cd.Marshal as single frame to ws. +func (cd Codec) Send(ws *Conn, v interface{}) (err error) { + data, payloadType, err := cd.Marshal(v) + if err != nil { + return err + } + ws.wio.Lock() + defer ws.wio.Unlock() + w, err := ws.frameWriterFactory.NewFrameWriter(payloadType) + if err != nil { + return err + } + _, err = w.Write(data) + w.Close() + return err +} + +// Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores in v. +func (cd Codec) Receive(ws *Conn, v interface{}) (err error) { + ws.rio.Lock() + defer ws.rio.Unlock() + if ws.frameReader != nil { + _, err = io.Copy(ioutil.Discard, ws.frameReader) + if err != nil { + return err + } + ws.frameReader = nil + } +again: + frame, err := ws.frameReaderFactory.NewFrameReader() + if err != nil { + return err + } + frame, err = ws.frameHandler.HandleFrame(frame) + if err != nil { + return err + } + if frame == nil { + goto again + } + payloadType := frame.PayloadType() + data, err := ioutil.ReadAll(frame) + if err != nil { + return err + } + return cd.Unmarshal(data, payloadType, v) +} + +func marshal(v interface{}) (msg []byte, payloadType byte, err error) { + switch data := v.(type) { + case string: + return []byte(data), TextFrame, nil + case []byte: + return data, BinaryFrame, nil + } + return nil, UnknownFrame, ErrNotSupported +} + +func unmarshal(msg []byte, payloadType byte, v interface{}) (err error) { + switch data := v.(type) { + case *string: + *data = string(msg) + return nil + case *[]byte: + *data = msg + return nil + } + return ErrNotSupported +} + +/* +Message is a codec to send/receive text/binary data in a frame on WebSocket connection. +To send/receive text frame, use string type. +To send/receive binary frame, use []byte type. + +Trivial usage: + + import "websocket" + + // receive text frame + var message string + websocket.Message.Receive(ws, &message) + + // send text frame + message = "hello" + websocket.Message.Send(ws, message) + + // receive binary frame + var data []byte + websocket.Message.Receive(ws, &data) + + // send binary frame + data = []byte{0, 1, 2} + websocket.Message.Send(ws, data) + +*/ +var Message = Codec{marshal, unmarshal} + +func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err error) { + msg, err = json.Marshal(v) + return msg, TextFrame, err +} + +func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err error) { + return json.Unmarshal(msg, v) +} + +/* +JSON is a codec to send/receive JSON data in a frame from a WebSocket connection. + +Trivial usage: + + import "websocket" + + type T struct { + Msg string + Count int + } + + // receive JSON type T + var data T + websocket.JSON.Receive(ws, &data) + + // send JSON type T + websocket.JSON.Send(ws, data) +*/ +var JSON = Codec{jsonMarshal, jsonUnmarshal} diff --git a/Godeps/_workspace/src/golang.org/x/net/websocket/websocket_test.go b/Godeps/_workspace/src/golang.org/x/net/websocket/websocket_test.go new file mode 100644 index 000000000..a376abacf --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/net/websocket/websocket_test.go @@ -0,0 +1,414 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bytes" + "fmt" + "io" + "log" + "net" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "sync" + "testing" +) + +var serverAddr string +var once sync.Once + +func echoServer(ws *Conn) { io.Copy(ws, ws) } + +type Count struct { + S string + N int +} + +func countServer(ws *Conn) { + for { + var count Count + err := JSON.Receive(ws, &count) + if err != nil { + return + } + count.N++ + count.S = strings.Repeat(count.S, count.N) + err = JSON.Send(ws, count) + if err != nil { + return + } + } +} + +func subProtocolHandshake(config *Config, req *http.Request) error { + for _, proto := range config.Protocol { + if proto == "chat" { + config.Protocol = []string{proto} + return nil + } + } + return ErrBadWebSocketProtocol +} + +func subProtoServer(ws *Conn) { + for _, proto := range ws.Config().Protocol { + io.WriteString(ws, proto) + } +} + +func startServer() { + http.Handle("/echo", Handler(echoServer)) + http.Handle("/count", Handler(countServer)) + subproto := Server{ + Handshake: subProtocolHandshake, + Handler: Handler(subProtoServer), + } + http.Handle("/subproto", subproto) + server := httptest.NewServer(nil) + serverAddr = server.Listener.Addr().String() + log.Print("Test WebSocket server listening on ", serverAddr) +} + +func newConfig(t *testing.T, path string) *Config { + config, _ := NewConfig(fmt.Sprintf("ws://%s%s", serverAddr, path), "http://localhost") + return config +} + +func TestEcho(t *testing.T) { + once.Do(startServer) + + // websocket.Dial() + client, err := net.Dial("tcp", serverAddr) + if err != nil { + t.Fatal("dialing", err) + } + conn, err := NewClient(newConfig(t, "/echo"), client) + if err != nil { + t.Errorf("WebSocket handshake error: %v", err) + return + } + + msg := []byte("hello, world\n") + if _, err := conn.Write(msg); err != nil { + t.Errorf("Write: %v", err) + } + var actual_msg = make([]byte, 512) + n, err := conn.Read(actual_msg) + if err != nil { + t.Errorf("Read: %v", err) + } + actual_msg = actual_msg[0:n] + if !bytes.Equal(msg, actual_msg) { + t.Errorf("Echo: expected %q got %q", msg, actual_msg) + } + conn.Close() +} + +func TestAddr(t *testing.T) { + once.Do(startServer) + + // websocket.Dial() + client, err := net.Dial("tcp", serverAddr) + if err != nil { + t.Fatal("dialing", err) + } + conn, err := NewClient(newConfig(t, "/echo"), client) + if err != nil { + t.Errorf("WebSocket handshake error: %v", err) + return + } + + ra := conn.RemoteAddr().String() + if !strings.HasPrefix(ra, "ws://") || !strings.HasSuffix(ra, "/echo") { + t.Errorf("Bad remote addr: %v", ra) + } + la := conn.LocalAddr().String() + if !strings.HasPrefix(la, "http://") { + t.Errorf("Bad local addr: %v", la) + } + conn.Close() +} + +func TestCount(t *testing.T) { + once.Do(startServer) + + // websocket.Dial() + client, err := net.Dial("tcp", serverAddr) + if err != nil { + t.Fatal("dialing", err) + } + conn, err := NewClient(newConfig(t, "/count"), client) + if err != nil { + t.Errorf("WebSocket handshake error: %v", err) + return + } + + var count Count + count.S = "hello" + if err := JSON.Send(conn, count); err != nil { + t.Errorf("Write: %v", err) + } + if err := JSON.Receive(conn, &count); err != nil { + t.Errorf("Read: %v", err) + } + if count.N != 1 { + t.Errorf("count: expected %d got %d", 1, count.N) + } + if count.S != "hello" { + t.Errorf("count: expected %q got %q", "hello", count.S) + } + if err := JSON.Send(conn, count); err != nil { + t.Errorf("Write: %v", err) + } + if err := JSON.Receive(conn, &count); err != nil { + t.Errorf("Read: %v", err) + } + if count.N != 2 { + t.Errorf("count: expected %d got %d", 2, count.N) + } + if count.S != "hellohello" { + t.Errorf("count: expected %q got %q", "hellohello", count.S) + } + conn.Close() +} + +func TestWithQuery(t *testing.T) { + once.Do(startServer) + + client, err := net.Dial("tcp", serverAddr) + if err != nil { + t.Fatal("dialing", err) + } + + config := newConfig(t, "/echo") + config.Location, err = url.ParseRequestURI(fmt.Sprintf("ws://%s/echo?q=v", serverAddr)) + if err != nil { + t.Fatal("location url", err) + } + + ws, err := NewClient(config, client) + if err != nil { + t.Errorf("WebSocket handshake: %v", err) + return + } + ws.Close() +} + +func testWithProtocol(t *testing.T, subproto []string) (string, error) { + once.Do(startServer) + + client, err := net.Dial("tcp", serverAddr) + if err != nil { + t.Fatal("dialing", err) + } + + config := newConfig(t, "/subproto") + config.Protocol = subproto + + ws, err := NewClient(config, client) + if err != nil { + return "", err + } + msg := make([]byte, 16) + n, err := ws.Read(msg) + if err != nil { + return "", err + } + ws.Close() + return string(msg[:n]), nil +} + +func TestWithProtocol(t *testing.T) { + proto, err := testWithProtocol(t, []string{"chat"}) + if err != nil { + t.Errorf("SubProto: unexpected error: %v", err) + } + if proto != "chat" { + t.Errorf("SubProto: expected %q, got %q", "chat", proto) + } +} + +func TestWithTwoProtocol(t *testing.T) { + proto, err := testWithProtocol(t, []string{"test", "chat"}) + if err != nil { + t.Errorf("SubProto: unexpected error: %v", err) + } + if proto != "chat" { + t.Errorf("SubProto: expected %q, got %q", "chat", proto) + } +} + +func TestWithBadProtocol(t *testing.T) { + _, err := testWithProtocol(t, []string{"test"}) + if err != ErrBadStatus { + t.Errorf("SubProto: expected %v, got %v", ErrBadStatus, err) + } +} + +func TestHTTP(t *testing.T) { + once.Do(startServer) + + // If the client did not send a handshake that matches the protocol + // specification, the server MUST return an HTTP response with an + // appropriate error code (such as 400 Bad Request) + resp, err := http.Get(fmt.Sprintf("http://%s/echo", serverAddr)) + if err != nil { + t.Errorf("Get: error %#v", err) + return + } + if resp == nil { + t.Error("Get: resp is null") + return + } + if resp.StatusCode != http.StatusBadRequest { + t.Errorf("Get: expected %q got %q", http.StatusBadRequest, resp.StatusCode) + } +} + +func TestTrailingSpaces(t *testing.T) { + // http://code.google.com/p/go/issues/detail?id=955 + // The last runs of this create keys with trailing spaces that should not be + // generated by the client. + once.Do(startServer) + config := newConfig(t, "/echo") + for i := 0; i < 30; i++ { + // body + ws, err := DialConfig(config) + if err != nil { + t.Errorf("Dial #%d failed: %v", i, err) + break + } + ws.Close() + } +} + +func TestDialConfigBadVersion(t *testing.T) { + once.Do(startServer) + config := newConfig(t, "/echo") + config.Version = 1234 + + _, err := DialConfig(config) + + if dialerr, ok := err.(*DialError); ok { + if dialerr.Err != ErrBadProtocolVersion { + t.Errorf("dial expected err %q but got %q", ErrBadProtocolVersion, dialerr.Err) + } + } +} + +func TestSmallBuffer(t *testing.T) { + // http://code.google.com/p/go/issues/detail?id=1145 + // Read should be able to handle reading a fragment of a frame. + once.Do(startServer) + + // websocket.Dial() + client, err := net.Dial("tcp", serverAddr) + if err != nil { + t.Fatal("dialing", err) + } + conn, err := NewClient(newConfig(t, "/echo"), client) + if err != nil { + t.Errorf("WebSocket handshake error: %v", err) + return + } + + msg := []byte("hello, world\n") + if _, err := conn.Write(msg); err != nil { + t.Errorf("Write: %v", err) + } + var small_msg = make([]byte, 8) + n, err := conn.Read(small_msg) + if err != nil { + t.Errorf("Read: %v", err) + } + if !bytes.Equal(msg[:len(small_msg)], small_msg) { + t.Errorf("Echo: expected %q got %q", msg[:len(small_msg)], small_msg) + } + var second_msg = make([]byte, len(msg)) + n, err = conn.Read(second_msg) + if err != nil { + t.Errorf("Read: %v", err) + } + second_msg = second_msg[0:n] + if !bytes.Equal(msg[len(small_msg):], second_msg) { + t.Errorf("Echo: expected %q got %q", msg[len(small_msg):], second_msg) + } + conn.Close() +} + +var parseAuthorityTests = []struct { + in *url.URL + out string +}{ + { + &url.URL{ + Scheme: "ws", + Host: "www.google.com", + }, + "www.google.com:80", + }, + { + &url.URL{ + Scheme: "wss", + Host: "www.google.com", + }, + "www.google.com:443", + }, + { + &url.URL{ + Scheme: "ws", + Host: "www.google.com:80", + }, + "www.google.com:80", + }, + { + &url.URL{ + Scheme: "wss", + Host: "www.google.com:443", + }, + "www.google.com:443", + }, + // some invalid ones for parseAuthority. parseAuthority doesn't + // concern itself with the scheme unless it actually knows about it + { + &url.URL{ + Scheme: "http", + Host: "www.google.com", + }, + "www.google.com", + }, + { + &url.URL{ + Scheme: "http", + Host: "www.google.com:80", + }, + "www.google.com:80", + }, + { + &url.URL{ + Scheme: "asdf", + Host: "127.0.0.1", + }, + "127.0.0.1", + }, + { + &url.URL{ + Scheme: "asdf", + Host: "www.google.com", + }, + "www.google.com", + }, +} + +func TestParseAuthority(t *testing.T) { + for _, tt := range parseAuthorityTests { + out := parseAuthority(tt.in) + if out != tt.out { + t.Errorf("got %v; want %v", out, tt.out) + } + } +} diff --git a/accounts/account_manager.go b/accounts/account_manager.go index 74006395c..34cf0fa53 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -100,7 +100,7 @@ func (am *Manager) Lock(addr common.Address) error { am.mutex.Lock() if unl, found := am.unlocked[addr]; found { am.mutex.Unlock() - am.expire(addr, unl, time.Duration(0) * time.Nanosecond) + am.expire(addr, unl, time.Duration(0)*time.Nanosecond) } else { am.mutex.Unlock() } diff --git a/cmd/geth/js.go b/cmd/geth/js.go index cdafab7fa..3d0251f08 100644 --- a/cmd/geth/js.go +++ b/cmd/geth/js.go @@ -24,23 +24,16 @@ import ( "os/signal" "path/filepath" "regexp" - "strings" - "sort" + "strings" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/natspec" "github.com/ethereum/go-ethereum/common/registrar" "github.com/ethereum/go-ethereum/eth" re "github.com/ethereum/go-ethereum/jsre" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/rpc/api" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/comms" - "github.com/ethereum/go-ethereum/rpc/shared" - "github.com/ethereum/go-ethereum/xeth" "github.com/peterh/liner" "github.com/robertkrimen/otto" ) @@ -79,82 +72,90 @@ func (r dumbterm) AppendHistory(string) {} type jsre struct { re *re.JSRE stack *node.Node - xeth *xeth.XEth wait chan *big.Int ps1 string atexit func() corsDomain string - client comms.EthereumClient + client rpc.Client prompter } var ( - loadedModulesMethods map[string][]string + loadedModulesMethods map[string][]string + autoCompleteStatement = "function _autocomplete(obj) {var results = []; for (var e in obj) { results.push(e); }; return results; }; _autocomplete(%s)" ) -func keywordCompleter(line string) []string { - results := make([]string, 0) - - if strings.Contains(line, ".") { - elements := strings.Split(line, ".") - if len(elements) == 2 { - module := elements[0] - partialMethod := elements[1] - if methods, found := loadedModulesMethods[module]; found { - for _, method := range methods { - if strings.HasPrefix(method, partialMethod) { // e.g. debug.se - results = append(results, module+"."+method) - } - } - } - } - } else { - for module, methods := range loadedModulesMethods { - if line == module { // user typed in full module name, show all methods - for _, method := range methods { - results = append(results, module+"."+method) +func keywordCompleter(jsre *jsre, line string) []string { + var results []string + parts := strings.Split(line, ".") + objRef := "this" + prefix := line + if len(parts) > 1 { + objRef = strings.Join(parts[0:len(parts) - 1], ".") + prefix = parts[len(parts) - 1] + } + + result, _ := jsre.re.Run(fmt.Sprintf(autoCompleteStatement, objRef)) + raw, _ := result.Export() + if keys, ok := raw.([]interface{}); ok { + for _, k := range keys { + if strings.HasPrefix(fmt.Sprintf("%s", k), prefix) { + if objRef == "this" { + results = append(results, fmt.Sprintf("%s", k)) + } else { + results = append(results, fmt.Sprintf("%s.%s", strings.Join(parts[:len(parts) - 1], "."), k)) } - } else if strings.HasPrefix(module, line) { // partial method name, e.g. admi - results = append(results, module) } } } - return results -} -func apiWordCompleter(line string, pos int) (head string, completions []string, tail string) { - if len(line) == 0 || pos == 0 { - return "", nil, "" + // e.g. web3<tab><tab> append dot since its an object + isObj, _ := jsre.re.Run(fmt.Sprintf("typeof(%s) === 'object'", line)) + if isObject, _ := isObj.ToBoolean(); isObject { + results = append(results, line + ".") } - i := 0 - for i = pos - 1; i > 0; i-- { - if line[i] == '.' || (line[i] >= 'a' && line[i] <= 'z') || (line[i] >= 'A' && line[i] <= 'Z') { - continue + sort.Strings(results) + return results +} + +func apiWordCompleterWithContext(jsre *jsre) liner.WordCompleter { + completer := func(line string, pos int) (head string, completions []string, tail string) { + if len(line) == 0 || pos == 0 { + return "", nil, "" } - if i >= 3 && line[i] == '3' && line[i-3] == 'w' && line[i-2] == 'e' && line[i-1] == 'b' { - continue + + // chuck data to relevant part for autocompletion, e.g. in case of nested lines eth.getBalance(eth.coinb<tab><tab> + i := 0 + for i = pos - 1; i > 0; i-- { + if line[i] == '.' || (line[i] >= 'a' && line[i] <= 'z') || (line[i] >= 'A' && line[i] <= 'Z') { + continue + } + if i >= 3 && line[i] == '3' && line[i - 3] == 'w' && line[i - 2] == 'e' && line[i - 1] == 'b' { + continue + } + i += 1 + break } - i += 1 - break - } - begin := line[:i] - keyword := line[i:pos] - end := line[pos:] + begin := line[:i] + keyword := line[i:pos] + end := line[pos:] + + completionWords := keywordCompleter(jsre, keyword) + return begin, completionWords, end + } - completionWords := keywordCompleter(keyword) - return begin, completionWords, end + return completer } -func newLightweightJSRE(docRoot string, client comms.EthereumClient, datadir string, interactive bool) *jsre { +func newLightweightJSRE(docRoot string, client rpc.Client, datadir string, interactive bool) *jsre { js := &jsre{ps1: "> "} js.wait = make(chan *big.Int) js.client = client - // update state in separare forever blocks js.re = re.New(docRoot) - if err := js.apiBindings(js); err != nil { + if err := js.apiBindings(); err != nil { utils.Fatalf("Unable to initialize console - %v", err) } @@ -165,7 +166,7 @@ func newLightweightJSRE(docRoot string, client comms.EthereumClient, datadir str js.withHistory(datadir, func(hist *os.File) { lr.ReadHistory(hist) }) lr.SetCtrlCAborts(true) js.loadAutoCompletion() - lr.SetWordCompleter(apiWordCompleter) + lr.SetWordCompleter(apiWordCompleterWithContext(js)) lr.SetTabCompletionStyle(liner.TabPrints) js.prompter = lr js.atexit = func() { @@ -177,25 +178,15 @@ func newLightweightJSRE(docRoot string, client comms.EthereumClient, datadir str return js } -func newJSRE(stack *node.Node, docRoot, corsDomain string, client comms.EthereumClient, interactive bool, f xeth.Frontend) *jsre { +func newJSRE(stack *node.Node, docRoot, corsDomain string, client rpc.Client, interactive bool) *jsre { js := &jsre{stack: stack, ps1: "> "} // set default cors domain used by startRpc from CLI flag js.corsDomain = corsDomain - if f == nil { - f = js - } - js.xeth = xeth.New(stack, f) - js.wait = js.xeth.UpdateState() + js.wait = make(chan *big.Int) js.client = client - if clt, ok := js.client.(*comms.InProcClient); ok { - if offeredApis, err := api.ParseApiString(shared.AllApis, codec.JSON, js.xeth, stack); err == nil { - clt.Initialize(api.Merge(offeredApis...)) - } - } - // update state in separare forever blocks js.re = re.New(docRoot) - if err := js.apiBindings(f); err != nil { + if err := js.apiBindings(); err != nil { utils.Fatalf("Unable to connect - %v", err) } @@ -206,7 +197,7 @@ func newJSRE(stack *node.Node, docRoot, corsDomain string, client comms.Ethereum js.withHistory(stack.DataDir(), func(hist *os.File) { lr.ReadHistory(hist) }) lr.SetCtrlCAborts(true) js.loadAutoCompletion() - lr.SetWordCompleter(apiWordCompleter) + lr.SetWordCompleter(apiWordCompleterWithContext(js)) lr.SetTabCompletionStyle(liner.TabPrints) js.prompter = lr js.atexit = func() { @@ -222,7 +213,7 @@ func (self *jsre) loadAutoCompletion() { if modules, err := self.supportedApis(); err == nil { loadedModulesMethods = make(map[string][]string) for module, _ := range modules { - loadedModulesMethods[module] = api.AutoCompletion[module] + loadedModulesMethods[module] = rpc.AutoCompletion[module] } } } @@ -258,7 +249,6 @@ func (self *jsre) welcome() { loadedModules = append(loadedModules, fmt.Sprintf("%s:%s", api, version)) } sort.Strings(loadedModules) - } } @@ -266,7 +256,7 @@ func (self *jsre) supportedApis() (map[string]string, error) { return self.client.SupportedModules() } -func (js *jsre) apiBindings(f xeth.Frontend) error { +func (js *jsre) apiBindings() error { apis, err := js.supportedApis() if err != nil { return err @@ -277,12 +267,7 @@ func (js *jsre) apiBindings(f xeth.Frontend) error { apiNames = append(apiNames, a) } - apiImpl, err := api.ParseApiString(strings.Join(apiNames, ","), codec.JSON, js.xeth, js.stack) - if err != nil { - utils.Fatalf("Unable to determine supported api's: %v", err) - } - - jeth := rpc.NewJeth(api.Merge(apiImpl...), js.re, js.client, f) + jeth := utils.NewJeth(js.re, js.client) js.re.Set("jeth", struct{}{}) t, _ := js.re.Get("jeth") jethObj := t.Object() @@ -313,14 +298,16 @@ func (js *jsre) apiBindings(f xeth.Frontend) error { // load only supported API's in javascript runtime shortcuts := "var eth = web3.eth; " for _, apiName := range apiNames { - if apiName == shared.Web3ApiName { - continue // manually mapped + if apiName == "web3" || apiName == "rpc" { + continue // manually mapped or ignore } - if err = js.re.Compile(fmt.Sprintf("%s.js", apiName), api.Javascript(apiName)); err == nil { - shortcuts += fmt.Sprintf("var %s = web3.%s; ", apiName, apiName) - } else { - utils.Fatalf("Error loading %s.js: %v", apiName, err) + if jsFile, ok := rpc.WEB3Extensions[apiName]; ok { + if err = js.re.Compile(fmt.Sprintf("%s.js", apiName), jsFile); err == nil { + shortcuts += fmt.Sprintf("var %s = web3.%s; ", apiName, apiName) + } else { + utils.Fatalf("Error loading %s.js: %v", apiName, err) + } } } @@ -375,14 +362,13 @@ func (self *jsre) ConfirmTransaction(tx string) bool { return false } // If natspec is enabled, ask for permission - if ethereum.NatSpec { - notice := natspec.GetNotice(self.xeth, tx, ethereum.HTTPClient()) - fmt.Println(notice) - answer, _ := self.Prompt("Confirm Transaction [y/n]") - return strings.HasPrefix(strings.Trim(answer, " "), "y") - } else { - return true + if ethereum.NatSpec && false /* disabled for now */ { + // notice := natspec.GetNotice(self.xeth, tx, ethereum.HTTPClient()) + // fmt.Println(notice) + // answer, _ := self.Prompt("Confirm Transaction [y/n]") + // return strings.HasPrefix(strings.Trim(answer, " "), "y") } + return true } func (self *jsre) UnlockAccount(addr []byte) bool { diff --git a/cmd/geth/js_test.go b/cmd/geth/js_test.go index ca636188f..19583c5ef 100644 --- a/cmd/geth/js_test.go +++ b/cmd/geth/js_test.go @@ -32,30 +32,27 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/compiler" "github.com/ethereum/go-ethereum/common/httpclient" - "github.com/ethereum/go-ethereum/common/natspec" - "github.com/ethereum/go-ethereum/common/registrar" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/comms" + "github.com/ethereum/go-ethereum/cmd/utils" ) const ( testSolcPath = "" - solcVersion = "0.9.23" + solcVersion = "0.9.23" - testKey = "e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674" + testKey = "e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674" testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" testBalance = "10000000000000000000" - // of empty string +// of empty string testHash = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" ) var ( - versionRE = regexp.MustCompile(strconv.Quote(`"compilerVersion":"` + solcVersion + `"`)) + versionRE = regexp.MustCompile(strconv.Quote(`"compilerVersion":"` + solcVersion + `"`)) testNodeKey = crypto.ToECDSA(common.Hex2Bytes("4b50fa71f5c3eeb8fdc452224b2395af2fcc3d125e06c32c82e048c0559db03f")) testGenesis = `{"` + testAddress[2:] + `": {"balance": "` + testBalance + `"}}` ) @@ -77,15 +74,16 @@ func (self *testjethre) UnlockAccount(acc []byte) bool { return true } -func (self *testjethre) ConfirmTransaction(tx string) bool { - var ethereum *eth.Ethereum - self.stack.Service(ðereum) - - if ethereum.NatSpec { - self.lastConfirm = natspec.GetNotice(self.xeth, tx, self.client) - } - return true -} +// Temporary disabled while natspec hasn't been migrated +//func (self *testjethre) ConfirmTransaction(tx string) bool { +// var ethereum *eth.Ethereum +// self.stack.Service(ðereum) +// +// if ethereum.NatSpec { +// self.lastConfirm = natspec.GetNotice(self.xeth, tx, self.client) +// } +// return true +//} func testJEthRE(t *testing.T) (string, *testjethre, *node.Node) { return testREPL(t, nil) @@ -118,7 +116,9 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod if config != nil { config(ethConf) } - if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { return eth.New(ctx, ethConf) }); err != nil { + if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + return eth.New(ctx, ethConf) + }); err != nil { t.Fatalf("failed to register ethereum protocol: %v", err) } // Initialize all the keys for testing @@ -141,9 +141,10 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod stack.Service(ðereum) assetPath := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext") - client := comms.NewInProcClient(codec.JSON) + //client := comms.NewInProcClient(codec.JSON) + client := utils.NewInProcRPCClient(stack) tf := &testjethre{client: ethereum.HTTPClient()} - repl := newJSRE(stack, assetPath, "", client, false, tf) + repl := newJSRE(stack, assetPath, "", client, false) tf.jsre = repl return tmp, tf, stack } @@ -166,8 +167,8 @@ func TestAccounts(t *testing.T) { defer node.Stop() defer os.RemoveAll(tmp) - checkEvalJSON(t, repl, `eth.accounts`, `["`+testAddress+`"]`) - checkEvalJSON(t, repl, `eth.coinbase`, `"`+testAddress+`"`) + checkEvalJSON(t, repl, `eth.accounts`, `["` + testAddress + `"]`) + checkEvalJSON(t, repl, `eth.coinbase`, `"` + testAddress + `"`) val, err := repl.re.Run(`jeth.newAccount("password")`) if err != nil { t.Errorf("expected no error, got %v", err) @@ -177,7 +178,7 @@ func TestAccounts(t *testing.T) { t.Errorf("address not hex: %q", addr) } - checkEvalJSON(t, repl, `eth.accounts`, `["`+testAddress+`","`+addr+`"]`) + checkEvalJSON(t, repl, `eth.accounts`, `["` + testAddress + `","` + addr + `"]`) } @@ -205,13 +206,13 @@ func TestBlockChain(t *testing.T) { node.Service(ðereum) ethereum.BlockChain().Reset() - checkEvalJSON(t, repl, `admin.exportChain(`+tmpfileq+`)`, `true`) + checkEvalJSON(t, repl, `admin.exportChain(` + tmpfileq + `)`, `true`) if _, err := os.Stat(tmpfile); err != nil { t.Fatal(err) } // check import, verify that dumpBlock gives the same result. - checkEvalJSON(t, repl, `admin.importChain(`+tmpfileq+`)`, `true`) + checkEvalJSON(t, repl, `admin.importChain(` + tmpfileq + `)`, `true`) checkEvalJSON(t, repl, `debug.dumpBlock(eth.blockNumber)`, beforeExport) } @@ -239,7 +240,7 @@ func TestCheckTestAccountBalance(t *testing.T) { defer os.RemoveAll(tmp) repl.re.Run(`primary = "` + testAddress + `"`) - checkEvalJSON(t, repl, `eth.getBalance(primary)`, `"`+testBalance+`"`) + checkEvalJSON(t, repl, `eth.getBalance(primary)`, `"` + testBalance + `"`) } func TestSignature(t *testing.T) { @@ -278,19 +279,20 @@ func TestContract(t *testing.T) { defer ethereum.Stop() defer os.RemoveAll(tmp) - reg := registrar.New(repl.xeth) - _, err := reg.SetGlobalRegistrar("", coinbase) - if err != nil { - t.Errorf("error setting HashReg: %v", err) - } - _, err = reg.SetHashReg("", coinbase) - if err != nil { - t.Errorf("error setting HashReg: %v", err) - } - _, err = reg.SetUrlHint("", coinbase) - if err != nil { - t.Errorf("error setting HashReg: %v", err) - } + // Temporary disabled while registrar isn't migrated + //reg := registrar.New(repl.xeth) + //_, err := reg.SetGlobalRegistrar("", coinbase) + //if err != nil { + // t.Errorf("error setting HashReg: %v", err) + //} + //_, err = reg.SetHashReg("", coinbase) + //if err != nil { + // t.Errorf("error setting HashReg: %v", err) + //} + //_, err = reg.SetUrlHint("", coinbase) + //if err != nil { + // t.Errorf("error setting HashReg: %v", err) + //} /* TODO: * lookup receipt and contract addresses by tx hash * name registration for HashReg and UrlHint addresses @@ -299,11 +301,11 @@ func TestContract(t *testing.T) { */ source := `contract test {\n` + - " /// @notice Will multiply `a` by 7." + `\n` + - ` function multiply(uint a) returns(uint d) {\n` + - ` return a * 7;\n` + - ` }\n` + - `}\n` + " /// @notice Will multiply `a` by 7." + `\n` + + ` function multiply(uint a) returns(uint d) {\n` + + ` return a * 7;\n` + + ` }\n` + + `}\n` if checkEvalJSON(t, repl, `admin.stopNatSpec()`, `true`) != nil { return @@ -313,10 +315,10 @@ func TestContract(t *testing.T) { if err != nil { t.Fatalf("%v", err) } - if checkEvalJSON(t, repl, `primary = eth.accounts[0]`, `"`+testAddress+`"`) != nil { + if checkEvalJSON(t, repl, `primary = eth.accounts[0]`, `"` + testAddress + `"`) != nil { return } - if checkEvalJSON(t, repl, `source = "`+source+`"`, `"`+source+`"`) != nil { + if checkEvalJSON(t, repl, `source = "` + source + `"`, `"` + source + `"`) != nil { return } @@ -394,7 +396,7 @@ multiply7 = Multiply7.at(contractaddress); var contentHash = `"0x86d2b7cf1e72e9a7a3f8d96601f0151742a2f780f1526414304fbe413dc7f9bd"` if sol != nil && solcVersion != sol.Version() { - modContractInfo := versionRE.ReplaceAll(contractInfo, []byte(`"compilerVersion":"`+sol.Version()+`"`)) + modContractInfo := versionRE.ReplaceAll(contractInfo, []byte(`"compilerVersion":"` + sol.Version() + `"`)) fmt.Printf("modified contractinfo:\n%s\n", modContractInfo) contentHash = `"` + common.ToHex(crypto.Sha3([]byte(modContractInfo))) + `"` } @@ -474,11 +476,12 @@ func processTxs(repl *testjethre, t *testing.T, expTxc int) bool { defer ethereum.StopMining() timer := time.NewTimer(100 * time.Second) - height := new(big.Int).Add(repl.xeth.CurrentBlock().Number(), big.NewInt(1)) + blockNr := ethereum.BlockChain().CurrentBlock().Number() + height := new(big.Int).Add(blockNr, big.NewInt(1)) repl.wait <- height select { case <-timer.C: - // if times out make sure the xeth loop does not block + // if times out make sure the xeth loop does not block go func() { select { case repl.wait <- nil: diff --git a/cmd/geth/main.go b/cmd/geth/main.go index f2bb27552..e6d190914 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -40,8 +40,6 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/comms" ) const ( @@ -263,11 +261,11 @@ See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console Name: "attach", Usage: `Geth Console: interactive JavaScript environment (connect to node)`, Description: ` -The Geth console is an interactive shell for the JavaScript runtime environment -which exposes a node admin interface as well as the Ðapp JavaScript API. -See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console. -This command allows to open a console on a running geth node. -`, + The Geth console is an interactive shell for the JavaScript runtime environment + which exposes a node admin interface as well as the Ðapp JavaScript API. + See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console. + This command allows to open a console on a running geth node. + `, }, { Action: execScripts, @@ -309,11 +307,15 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso utils.RPCEnabledFlag, utils.RPCListenAddrFlag, utils.RPCPortFlag, - utils.RpcApiFlag, + utils.RPCApiFlag, + utils.WSEnabledFlag, + utils.WSListenAddrFlag, + utils.WSPortFlag, + utils.WSApiFlag, + utils.WSAllowedDomainsFlag, utils.IPCDisabledFlag, utils.IPCApiFlag, utils.IPCPathFlag, - utils.IPCExperimental, utils.ExecFlag, utils.WhisperEnabledFlag, utils.DevModeFlag, @@ -392,20 +394,12 @@ func geth(ctx *cli.Context) { node.Wait() } +// attach will connect to a running geth instance attaching a JavaScript console and to it. func attach(ctx *cli.Context) { - var client comms.EthereumClient - var err error - if ctx.Args().Present() { - client, err = comms.ClientFromEndpoint(ctx.Args().First(), codec.JSON) - } else { - cfg := comms.IpcConfig{ - Endpoint: utils.IpcSocketPath(ctx), - } - client, err = comms.NewIpcClient(cfg, codec.JSON) - } - + // attach to a running geth instance + client, err := utils.NewRemoteRPCClient(ctx) if err != nil { - utils.Fatalf("Unable to attach to geth node - %v", err) + utils.Fatalf("Unable to attach to geth - %v", err) } repl := newLightweightJSRE( @@ -431,11 +425,12 @@ func console(ctx *cli.Context) { startNode(ctx, node) // Attach to the newly started node, and either execute script or become interactive - client := comms.NewInProcClient(codec.JSON) + client := utils.NewInProcRPCClient(node) + repl := newJSRE(node, ctx.GlobalString(utils.JSpathFlag.Name), ctx.GlobalString(utils.RPCCORSDomainFlag.Name), - client, true, nil) + client, true) if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" { repl.batch(script) @@ -454,11 +449,12 @@ func execScripts(ctx *cli.Context) { startNode(ctx, node) // Attach to the newly started node and execute the given scripts - client := comms.NewInProcClient(codec.JSON) + client := utils.NewInProcRPCClient(node) + repl := newJSRE(node, ctx.GlobalString(utils.JSpathFlag.Name), ctx.GlobalString(utils.RPCCORSDomainFlag.Name), - client, false, nil) + client, false) for _, file := range ctx.Args() { repl.exec(file) @@ -517,6 +513,11 @@ func startNode(ctx *cli.Context, stack *node.Node) { utils.Fatalf("Failed to start RPC: %v", err) } } + if ctx.GlobalBool(utils.WSEnabledFlag.Name) { + if err := utils.StartWS(stack, ctx); err != nil { + utils.Fatalf("Failed to start WS: %v", err) + } + } if ctx.GlobalBool(utils.MiningEnabledFlag.Name) { if err := ethereum.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name), ctx.GlobalString(utils.MiningGPUFlag.Name)); err != nil { utils.Fatalf("Failed to start mining: %v", err) diff --git a/cmd/geth/monitorcmd.go b/cmd/geth/monitorcmd.go index a45d29b8f..1d7bf3f6a 100644 --- a/cmd/geth/monitorcmd.go +++ b/cmd/geth/monitorcmd.go @@ -21,16 +21,15 @@ import ( "math" "reflect" "runtime" - "sort" "strings" "time" + "sort" + "github.com/codegangsta/cli" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/comms" "github.com/gizak/termui" ) @@ -70,20 +69,18 @@ to display multiple metrics simultaneously. // monitor starts a terminal UI based monitoring tool for the requested metrics. func monitor(ctx *cli.Context) { var ( - client comms.EthereumClient + client rpc.Client err error ) // Attach to an Ethereum node over IPC or RPC endpoint := ctx.String(monitorCommandAttachFlag.Name) - if client, err = comms.ClientFromEndpoint(endpoint, codec.JSON); err != nil { + if client, err = utils.NewRemoteRPCClientFromString(endpoint); err != nil { utils.Fatalf("Unable to attach to geth node: %v", err) } defer client.Close() - xeth := rpc.NewXeth(client) - // Retrieve all the available metrics and resolve the user pattens - metrics, err := retrieveMetrics(xeth) + metrics, err := retrieveMetrics(client) if err != nil { utils.Fatalf("Failed to retrieve system metrics: %v", err) } @@ -133,7 +130,7 @@ func monitor(ctx *cli.Context) { } termui.Body.AddRows(termui.NewRow(termui.NewCol(12, 0, footer))) - refreshCharts(xeth, monitored, data, units, charts, ctx, footer) + refreshCharts(client, monitored, data, units, charts, ctx, footer) termui.Body.Align() termui.Render(termui.Body) @@ -154,7 +151,7 @@ func monitor(ctx *cli.Context) { termui.Render(termui.Body) } case <-refresh: - if refreshCharts(xeth, monitored, data, units, charts, ctx, footer) { + if refreshCharts(client, monitored, data, units, charts, ctx, footer) { termui.Body.Align() } termui.Render(termui.Body) @@ -164,8 +161,30 @@ func monitor(ctx *cli.Context) { // retrieveMetrics contacts the attached geth node and retrieves the entire set // of collected system metrics. -func retrieveMetrics(xeth *rpc.Xeth) (map[string]interface{}, error) { - return xeth.Call("debug_metrics", []interface{}{true}) +func retrieveMetrics(client rpc.Client) (map[string]interface{}, error) { + req := map[string]interface{}{ + "id": new(int64), + "method": "debug_metrics", + "jsonrpc": "2.0", + "params": []interface{}{true}, + } + + if err := client.Send(req); err != nil { + return nil, err + } + + var res rpc.JSONSuccessResponse + if err := client.Recv(&res); err != nil { + return nil, err + } + + if res.Result != nil { + if mets, ok := res.Result.(map[string]interface{}); ok { + return mets, nil + } + } + + return nil, fmt.Errorf("unable to retrieve metrics") } // resolveMetrics takes a list of input metric patterns, and resolves each to one @@ -253,8 +272,8 @@ func fetchMetric(metrics map[string]interface{}, metric string) float64 { // refreshCharts retrieves a next batch of metrics, and inserts all the new // values into the active datasets and charts -func refreshCharts(xeth *rpc.Xeth, metrics []string, data [][]float64, units []int, charts []*termui.LineChart, ctx *cli.Context, footer *termui.Par) (realign bool) { - values, err := retrieveMetrics(xeth) +func refreshCharts(client rpc.Client, metrics []string, data [][]float64, units []int, charts []*termui.LineChart, ctx *cli.Context, footer *termui.Par) (realign bool) { + values, err := retrieveMetrics(client) for i, metric := range metrics { if len(data) < 512 { data[i] = append([]float64{fetchMetric(values, metric)}, data[i]...) diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index 7a6ff704c..a9fce6418 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -87,7 +87,12 @@ var AppHelpFlagGroups = []flagGroup{ utils.RPCEnabledFlag, utils.RPCListenAddrFlag, utils.RPCPortFlag, - utils.RpcApiFlag, + utils.RPCApiFlag, + utils.WSEnabledFlag, + utils.WSListenAddrFlag, + utils.WSPortFlag, + utils.WSApiFlag, + utils.WSAllowedDomainsFlag, utils.IPCDisabledFlag, utils.IPCApiFlag, utils.IPCPathFlag, @@ -158,7 +163,6 @@ var AppHelpFlagGroups = []flagGroup{ Flags: []cli.Flag{ utils.WhisperEnabledFlag, utils.NatspecEnabledFlag, - utils.IPCExperimental, }, }, { diff --git a/cmd/gethrpctest/main.go b/cmd/gethrpctest/main.go index ae815c4a6..b4530ca51 100644 --- a/cmd/gethrpctest/main.go +++ b/cmd/gethrpctest/main.go @@ -26,8 +26,9 @@ import ( "path/filepath" "runtime" + "errors" + "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" @@ -35,13 +36,9 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/rpc/api" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/comms" - rpc "github.com/ethereum/go-ethereum/rpc/v2" + "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/tests" "github.com/ethereum/go-ethereum/whisper" - "github.com/ethereum/go-ethereum/xeth" ) const defaultTestKey = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291" @@ -176,21 +173,25 @@ func RunTest(stack *node.Node, test *tests.BlockTest) error { // StartRPC initializes an RPC interface to the given protocol stack. func StartRPC(stack *node.Node) error { - config := comms.HttpConfig{ - ListenAddress: "127.0.0.1", - ListenPort: 8545, + /* + web3 := NewPublicWeb3API(stack) + server.RegisterName("web3", web3) + net := NewPublicNetAPI(stack.Server(), ethereum.NetVersion()) + server.RegisterName("net", net) + */ + + for _, api := range stack.APIs() { + if adminApi, ok := api.Service.(*node.PrivateAdminAPI); ok { + _, err := adminApi.StartRPC("127.0.0.1", 8545, "", "admin,db,eth,debug,miner,net,shh,txpool,personal,web3") + return err + } } - xeth := xeth.New(stack, nil) - codec := codec.JSON - apis, err := api.ParseApiString(comms.DefaultHttpRpcApis, codec, xeth, stack) - if err != nil { - return err - } - return comms.StartHttp(config, codec, api.Merge(apis...)) + glog.V(logger.Error).Infof("Unable to start RPC-HTTP interface, could not find admin API") + return errors.New("Unable to start RPC-HTTP interface") } -// StartRPC initializes an IPC interface to the given protocol stack. +// StartIPC initializes an IPC interface to the given protocol stack. func StartIPC(stack *node.Node) error { var ethereum *eth.Ethereum if err := stack.Service(ðereum); err != nil { @@ -202,11 +203,7 @@ func StartIPC(stack *node.Node) error { endpoint = filepath.Join(common.DefaultDataDir(), "geth.ipc") } - config := comms.IpcConfig{ - Endpoint: endpoint, - } - - listener, err := comms.CreateListener(config) + listener, err := rpc.CreateIPCListener(endpoint) if err != nil { return err } @@ -217,16 +214,16 @@ func StartIPC(stack *node.Node) error { offered := stack.APIs() for _, api := range offered { server.RegisterName(api.Namespace, api.Service) - glog.V(logger.Debug).Infof("Register %T@%s for IPC service\n", api.Service, api.Namespace) + glog.V(logger.Debug).Infof("Register %T under namespace '%s' for IPC service\n", api.Service, api.Namespace) } - web3 := utils.NewPublicWeb3API(stack) - server.RegisterName("web3", web3) - net := utils.NewPublicNetAPI(stack.Server(), ethereum.NetVersion()) - server.RegisterName("net", net) + //var ethereum *eth.Ethereum + //if err := stack.Service(ðereum); err != nil { + // return err + //} go func() { - glog.V(logger.Info).Infof("Start IPC server on %s\n", config.Endpoint) + glog.V(logger.Info).Infof("Start IPC server on %s\n", endpoint) for { conn, err := listener.Accept() if err != nil { diff --git a/cmd/utils/api.go b/cmd/utils/api.go deleted file mode 100644 index 59f0dab74..000000000 --- a/cmd/utils/api.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package utils - -import ( - "fmt" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/p2p" - rpc "github.com/ethereum/go-ethereum/rpc/v2" -) - -// PublicWeb3API offers helper utils -type PublicWeb3API struct { - stack *node.Node -} - -// NewPublicWeb3API creates a new Web3Service instance -func NewPublicWeb3API(stack *node.Node) *PublicWeb3API { - return &PublicWeb3API{stack} -} - -// ClientVersion returns the node name -func (s *PublicWeb3API) ClientVersion() string { - return s.stack.Server().Name -} - -// Sha3 applies the ethereum sha3 implementation on the input. -// It assumes the input is hex encoded. -func (s *PublicWeb3API) Sha3(input string) string { - return common.ToHex(crypto.Sha3(common.FromHex(input))) -} - -// PublicNetAPI offers network related RPC methods -type PublicNetAPI struct { - net *p2p.Server - networkVersion int -} - -// NewPublicNetAPI creates a new net api instance. -func NewPublicNetAPI(net *p2p.Server, networkVersion int) *PublicNetAPI { - return &PublicNetAPI{net, networkVersion} -} - -// Listening returns an indication if the node is listening for network connections. -func (s *PublicNetAPI) Listening() bool { - return true // always listening -} - -// Peercount returns the number of connected peers -func (s *PublicNetAPI) PeerCount() *rpc.HexNumber { - return rpc.NewHexNumber(s.net.PeerCount()) -} - -// ProtocolVersion returns the current ethereum protocol version. -func (s *PublicNetAPI) Version() string { - return fmt.Sprintf("%d", s.networkVersion) -} diff --git a/cmd/utils/client.go b/cmd/utils/client.go new file mode 100644 index 000000000..bac456491 --- /dev/null +++ b/cmd/utils/client.go @@ -0,0 +1,176 @@ +// Copyright 2015 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 <http://www.gnu.org/licenses/>. + +package utils + +import ( + "encoding/json" + "fmt" + + "strings" + + "github.com/codegangsta/cli" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/rpc" +) + +// NewInProcRPCClient will start a new RPC server for the given node and returns a client to interact with it. +func NewInProcRPCClient(stack *node.Node) *inProcClient { + server := rpc.NewServer() + + offered := stack.APIs() + for _, api := range offered { + server.RegisterName(api.Namespace, api.Service) + } + + web3 := node.NewPublicWeb3API(stack) + server.RegisterName("web3", web3) + + var ethereum *eth.Ethereum + if err := stack.Service(ðereum); err == nil { + net := eth.NewPublicNetAPI(stack.Server(), ethereum.NetVersion()) + server.RegisterName("net", net) + } else { + glog.V(logger.Warn).Infof("%v\n", err) + } + + buf := &buf{ + requests: make(chan []byte), + responses: make(chan []byte), + } + client := &inProcClient{ + server: server, + buf: buf, + } + + go func() { + server.ServeCodec(rpc.NewJSONCodec(client.buf)) + }() + + return client +} + +// buf represents the connection between the RPC server and console +type buf struct { + readBuf []byte // store remaining request bytes after a partial read + requests chan []byte // list with raw serialized requests + responses chan []byte // list with raw serialized responses +} + +// will read the next request in json format +func (b *buf) Read(p []byte) (int, error) { + // last read didn't read entire request, return remaining bytes + if len(b.readBuf) > 0 { + n := copy(p, b.readBuf) + if n < len(b.readBuf) { + b.readBuf = b.readBuf[:n] + } else { + b.readBuf = b.readBuf[:0] + } + return n, nil + } + + // read next request + req := <-b.requests + n := copy(p, req) + if n < len(req) { + // buf too small, store remaining chunk for next read + b.readBuf = req[n:] + } + + return n, nil +} + +// Write send the given buffer to the backend +func (b *buf) Write(p []byte) (n int, err error) { + b.responses <- p + return len(p), nil +} + +// Close cleans up obtained resources. +func (b *buf) Close() error { + close(b.requests) + close(b.responses) + + return nil +} + +// inProcClient starts a RPC server and uses buf to communicate with it. +type inProcClient struct { + server *rpc.Server + buf *buf +} + +// Close will stop the RPC server +func (c *inProcClient) Close() { + c.server.Stop() +} + +// Send a msg to the endpoint +func (c *inProcClient) Send(msg interface{}) error { + d, err := json.Marshal(msg) + if err != nil { + return err + } + c.buf.requests <- d + return nil +} + +// Recv reads a message and tries to parse it into the given msg +func (c *inProcClient) Recv(msg interface{}) error { + data := <-c.buf.responses + return json.Unmarshal(data, &msg) +} + +// Returns the collection of modules the RPC server offers. +func (c *inProcClient) SupportedModules() (map[string]string, error) { + return rpc.SupportedModules(c) +} + +// NewRemoteRPCClient returns a RPC client which connects to a running geth instance. +// Depending on the given context this can either be a IPC or a HTTP client. +func NewRemoteRPCClient(ctx *cli.Context) (rpc.Client, error) { + if ctx.Args().Present() { + endpoint := ctx.Args().First() + return NewRemoteRPCClientFromString(endpoint) + } + + // use IPC by default + endpoint := IPCSocketPath(ctx) + return rpc.NewIPCClient(endpoint) +} + +// NewRemoteRPCClientFromString returns a RPC client which connects to the given +// endpoint. It must start with either `ipc:` or `rpc:` (HTTP). +func NewRemoteRPCClientFromString(endpoint string) (rpc.Client, error) { + if strings.HasPrefix(endpoint, "ipc:") { + return rpc.NewIPCClient(endpoint[4:]) + } + if strings.HasPrefix(endpoint, "rpc:") { + return rpc.NewHTTPClient(endpoint[4:]) + } + if strings.HasPrefix(endpoint, "http://") { + return rpc.NewHTTPClient(endpoint) + } + if strings.HasPrefix(endpoint, "ws:") { + return rpc.NewWSClient(endpoint) + } + + return nil, fmt.Errorf("invalid endpoint") +} diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 63efa08ee..9199432d8 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -23,7 +23,6 @@ import ( "log" "math" "math/big" - "net" "net/http" "os" "path/filepath" @@ -31,6 +30,8 @@ import ( "strconv" "strings" + "errors" + "github.com/codegangsta/cli" "github.com/ethereum/ethash" "github.com/ethereum/go-ethereum/accounts" @@ -49,14 +50,8 @@ import ( "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rpc/api" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/comms" - "github.com/ethereum/go-ethereum/rpc/shared" - "github.com/ethereum/go-ethereum/rpc/useragent" - rpc "github.com/ethereum/go-ethereum/rpc/v2" + "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/whisper" - "github.com/ethereum/go-ethereum/xeth" ) func init() { @@ -282,10 +277,10 @@ var ( Usage: "Domains from which to accept cross origin requests (browser enforced)", Value: "", } - RpcApiFlag = cli.StringFlag{ + RPCApiFlag = cli.StringFlag{ Name: "rpcapi", Usage: "API's offered over the HTTP-RPC interface", - Value: comms.DefaultHttpRpcApis, + Value: rpc.DefaultHttpRpcApis, } IPCDisabledFlag = cli.BoolFlag{ Name: "ipcdisable", @@ -294,16 +289,36 @@ var ( IPCApiFlag = cli.StringFlag{ Name: "ipcapi", Usage: "API's offered over the IPC-RPC interface", - Value: comms.DefaultIpcApis, + Value: rpc.DefaultIpcApis, } IPCPathFlag = DirectoryFlag{ Name: "ipcpath", Usage: "Filename for IPC socket/pipe", Value: DirectoryString{common.DefaultIpcPath()}, } - IPCExperimental = cli.BoolFlag{ - Name: "ipcexp", - Usage: "Enable the new RPC implementation", + WSEnabledFlag = cli.BoolFlag{ + Name: "ws", + Usage: "Enable the WS-RPC server", + } + WSListenAddrFlag = cli.StringFlag{ + Name: "wsaddr", + Usage: "WS-RPC server listening interface", + Value: "127.0.0.1", + } + WSPortFlag = cli.IntFlag{ + Name: "wsport", + Usage: "WS-RPC server listening port", + Value: 8546, + } + WSApiFlag = cli.StringFlag{ + Name: "wsapi", + Usage: "API's offered over the WS-RPC interface", + Value: rpc.DefaultHttpRpcApis, + } + WSAllowedDomainsFlag = cli.StringFlag{ + Name: "wsdomains", + Usage: "Domains from which to accept websockets requests", + Value: "", } ExecFlag = cli.StringFlag{ Name: "exec", @@ -760,7 +775,7 @@ func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database return chain, chainDb } -func IpcSocketPath(ctx *cli.Context) (ipcpath string) { +func IPCSocketPath(ctx *cli.Context) (ipcpath string) { if runtime.GOOS == "windows" { ipcpath = common.DefaultIpcPath() if ctx.GlobalIsSet(IPCPathFlag.Name) { @@ -780,79 +795,83 @@ func IpcSocketPath(ctx *cli.Context) (ipcpath string) { } func StartIPC(stack *node.Node, ctx *cli.Context) error { - config := comms.IpcConfig{ - Endpoint: IpcSocketPath(ctx), - } - var ethereum *eth.Ethereum if err := stack.Service(ðereum); err != nil { return err } - if ctx.GlobalIsSet(IPCExperimental.Name) { - listener, err := comms.CreateListener(config) - if err != nil { - return err - } + endpoint := IPCSocketPath(ctx) + listener, err := rpc.CreateIPCListener(endpoint) + if err != nil { + return err + } - server := rpc.NewServer() + server := rpc.NewServer() - // register package API's this node provides - offered := stack.APIs() - for _, api := range offered { - server.RegisterName(api.Namespace, api.Service) - glog.V(logger.Debug).Infof("Register %T under namespace '%s' for IPC service\n", api.Service, api.Namespace) - } + // register package API's this node provides + offered := stack.APIs() + for _, api := range offered { + server.RegisterName(api.Namespace, api.Service) + glog.V(logger.Debug).Infof("Register %T under namespace '%s' for IPC service\n", api.Service, api.Namespace) + } - web3 := NewPublicWeb3API(stack) - server.RegisterName("web3", web3) - net := NewPublicNetAPI(stack.Server(), ethereum.NetVersion()) - server.RegisterName("net", net) - - go func() { - glog.V(logger.Info).Infof("Start IPC server on %s\n", config.Endpoint) - for { - conn, err := listener.Accept() - if err != nil { - glog.V(logger.Error).Infof("Unable to accept connection - %v\n", err) - } - - codec := rpc.NewJSONCodec(conn) - go server.ServeCodec(codec) + go func() { + glog.V(logger.Info).Infof("Start IPC server on %s\n", endpoint) + for { + conn, err := listener.Accept() + if err != nil { + glog.V(logger.Error).Infof("Unable to accept connection - %v\n", err) } - }() - - return nil - } - initializer := func(conn net.Conn) (comms.Stopper, shared.EthereumApi, error) { - fe := useragent.NewRemoteFrontend(conn, ethereum.AccountManager()) - xeth := xeth.New(stack, fe) - apis, err := api.ParseApiString(ctx.GlobalString(IPCApiFlag.Name), codec.JSON, xeth, stack) - if err != nil { - return nil, nil, err + codec := rpc.NewJSONCodec(conn) + go server.ServeCodec(codec) } - return xeth, api.Merge(apis...), nil - } - return comms.StartIpc(config, codec.JSON, initializer) + }() + + return nil + } // StartRPC starts a HTTP JSON-RPC API server. func StartRPC(stack *node.Node, ctx *cli.Context) error { - config := comms.HttpConfig{ - ListenAddress: ctx.GlobalString(RPCListenAddrFlag.Name), - ListenPort: uint(ctx.GlobalInt(RPCPortFlag.Name)), - CorsDomain: ctx.GlobalString(RPCCORSDomainFlag.Name), + for _, api := range stack.APIs() { + if adminApi, ok := api.Service.(*node.PrivateAdminAPI); ok { + address := ctx.GlobalString(RPCListenAddrFlag.Name) + port := ctx.GlobalInt(RPCPortFlag.Name) + cors := ctx.GlobalString(RPCCORSDomainFlag.Name) + apiStr := "" + if ctx.GlobalIsSet(RPCApiFlag.Name) { + apiStr = ctx.GlobalString(RPCApiFlag.Name) + } + + _, err := adminApi.StartRPC(address, port, cors, apiStr) + return err + } } - xeth := xeth.New(stack, nil) - codec := codec.JSON + glog.V(logger.Error).Infof("Unable to start RPC-HTTP interface, could not find admin API") + return errors.New("Unable to start RPC-HTTP interface") +} - apis, err := api.ParseApiString(ctx.GlobalString(RpcApiFlag.Name), codec, xeth, stack) - if err != nil { - return err +// StartWS starts a websocket JSON-RPC API server. +func StartWS(stack *node.Node, ctx *cli.Context) error { + for _, api := range stack.APIs() { + if adminApi, ok := api.Service.(*node.PrivateAdminAPI); ok { + address := ctx.GlobalString(WSListenAddrFlag.Name) + port := ctx.GlobalInt(WSAllowedDomainsFlag.Name) + allowedDomains := ctx.GlobalString(WSAllowedDomainsFlag.Name) + apiStr := "" + if ctx.GlobalIsSet(WSApiFlag.Name) { + apiStr = ctx.GlobalString(WSApiFlag.Name) + } + + _, err := adminApi.StartWS(address, port, allowedDomains, apiStr) + return err + } } - return comms.StartHttp(config, codec, api.Merge(apis...)) + + glog.V(logger.Error).Infof("Unable to start RPC-WS interface, could not find admin API") + return errors.New("Unable to start RPC-WS interface") } func StartPProf(ctx *cli.Context) { diff --git a/rpc/jeth.go b/cmd/utils/jeth.go index b195a4965..b460597c1 100644 --- a/rpc/jeth.go +++ b/cmd/utils/jeth.go @@ -14,38 +14,39 @@ // 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/>. -package rpc +package utils import ( "encoding/json" "fmt" "time" - "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/jsre" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/rpc/comms" - "github.com/ethereum/go-ethereum/rpc/shared" - "github.com/ethereum/go-ethereum/rpc/useragent" - "github.com/ethereum/go-ethereum/xeth" + "github.com/ethereum/go-ethereum/rpc" "github.com/robertkrimen/otto" ) type Jeth struct { - ethApi shared.EthereumApi re *jsre.JSRE - client comms.EthereumClient - fe xeth.Frontend + client rpc.Client } -func NewJeth(ethApi shared.EthereumApi, re *jsre.JSRE, client comms.EthereumClient, fe xeth.Frontend) *Jeth { - return &Jeth{ethApi, re, client, fe} +// NewJeth create a new backend for the JSRE console +func NewJeth(re *jsre.JSRE, client rpc.Client) *Jeth { + return &Jeth{re, client} } -func (self *Jeth) err(call otto.FunctionCall, code int, msg string, id interface{}) (response otto.Value) { - m := shared.NewRpcErrorResponse(id, shared.JsonRpcVersion, code, fmt.Errorf(msg)) +func (self *Jeth) err(call otto.FunctionCall, code int, msg string, id *int64) (response otto.Value) { + m := rpc.JSONErrResponse{ + Version: "2.0", + Id: id, + Error: rpc.JSONError{ + Code: code, + Message: msg, + }, + } + errObj, _ := json.Marshal(m.Error) errRes, _ := json.Marshal(m) @@ -71,7 +72,7 @@ func (self *Jeth) UnlockAccount(call otto.FunctionCall) (response otto.Value) { if account, ok = accountExport.(string); ok { if len(call.ArgumentList) == 1 { fmt.Printf("Unlock account %s\n", account) - passwd, err = utils.PromptPassword("Passphrase: ", true) + passwd, err = PromptPassword("Passphrase: ", true) if err != nil { return otto.FalseValue() } @@ -102,11 +103,11 @@ func (self *Jeth) UnlockAccount(call otto.FunctionCall) (response otto.Value) { // NewAccount asks the user for the password and than executes the jeth.newAccount callback in the jsre func (self *Jeth) NewAccount(call otto.FunctionCall) (response otto.Value) { if len(call.ArgumentList) == 0 { - passwd, err := utils.PromptPassword("Passphrase: ", true) + passwd, err := PromptPassword("Passphrase: ", true) if err != nil { return otto.FalseValue() } - passwd2, err := utils.PromptPassword("Repeat passphrase: ", true) + passwd2, err := PromptPassword("Repeat passphrase: ", true) if err != nil { return otto.FalseValue() } @@ -134,11 +135,11 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { } jsonreq, err := json.Marshal(reqif) - var reqs []shared.Request + var reqs []rpc.JSONRequest batch := true err = json.Unmarshal(jsonreq, &reqs) if err != nil { - reqs = make([]shared.Request, 1) + reqs = make([]rpc.JSONRequest, 1) err = json.Unmarshal(jsonreq, &reqs[0]) batch = false } @@ -147,42 +148,38 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { call.Otto.Run("var ret_response = new Array(response_len);") for i, req := range reqs { - var respif interface{} err := self.client.Send(&req) if err != nil { return self.err(call, -32603, err.Error(), req.Id) } - recv: - respif, err = self.client.Recv() + result := make(map[string]interface{}) + err = self.client.Recv(&result) if err != nil { return self.err(call, -32603, err.Error(), req.Id) } - agentreq, isRequest := respif.(*shared.Request) - if isRequest { - self.handleRequest(agentreq) - goto recv // receive response after agent interaction - } - - sucres, isSuccessResponse := respif.(*shared.SuccessResponse) - errres, isErrorResponse := respif.(*shared.ErrorResponse) + _, isSuccessResponse := result["result"] + _, isErrorResponse := result["error"] if !isSuccessResponse && !isErrorResponse { - return self.err(call, -32603, fmt.Sprintf("Invalid response type (%T)", respif), req.Id) + return self.err(call, -32603, fmt.Sprintf("Invalid response"), new(int64)) } - call.Otto.Set("ret_jsonrpc", shared.JsonRpcVersion) - call.Otto.Set("ret_id", req.Id) + id, _ := result["id"] + call.Otto.Set("ret_id", id) - var res []byte + jsonver, _ := result["jsonrpc"] + call.Otto.Set("ret_jsonrpc", jsonver) + + var payload []byte if isSuccessResponse { - res, err = json.Marshal(sucres.Result) + payload, _ = json.Marshal(result["result"]) } else if isErrorResponse { - res, err = json.Marshal(errres.Error) + payload, _ = json.Marshal(result["error"]) } - - call.Otto.Set("ret_result", string(res)) + call.Otto.Set("ret_result", string(payload)) call.Otto.Set("response_idx", i) + response, err = call.Otto.Run(` ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, result: JSON.parse(ret_result) }; `) @@ -195,7 +192,7 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { if call.Argument(1).IsObject() { call.Otto.Set("callback", call.Argument(1)) call.Otto.Run(` - if (Object.prototype.toString.call(callback) == '[object Function]') { + if (Object.prototype.toString.call(callback) == '[object Function]') { callback(null, ret_response); } `) @@ -204,6 +201,7 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { return } +/* // handleRequest will handle user agent requests by interacting with the user and sending // the user response back to the geth service func (self *Jeth) handleRequest(req *shared.Request) bool { @@ -235,7 +233,7 @@ func (self *Jeth) askPassword(id interface{}, jsonrpc string, args []interface{} return false } } - passwd, err = utils.PromptPassword("Passphrase: ", true) + passwd, err = PromptPassword("Passphrase: ", true) if err = self.client.Send(shared.NewRpcResponse(id, jsonrpc, passwd, err)); err != nil { glog.V(logger.Info).Infof("Unable to send user agent ask password response - %v\n", err) @@ -248,6 +246,7 @@ func (self *Jeth) confirmTransaction(id interface{}, jsonrpc string, args []inte // Accept all tx which are send from this console return self.client.Send(shared.NewRpcResponse(id, jsonrpc, true, nil)) == nil } +*/ // throwJSExeception panics on an otto value, the Otto VM will then throw msg as a javascript error. func throwJSExeception(msg interface{}) otto.Value { diff --git a/common/natspec/natspec.go b/common/natspec/natspec.go index d9627b4e1..2e4d8d7a4 100644 --- a/common/natspec/natspec.go +++ b/common/natspec/natspec.go @@ -13,6 +13,8 @@ // // 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 ignore package natspec diff --git a/common/natspec/natspec_e2e_test.go b/common/natspec/natspec_e2e_test.go index 95109ec07..4a9b92eb6 100644 --- a/common/natspec/natspec_e2e_test.go +++ b/common/natspec/natspec_e2e_test.go @@ -13,6 +13,8 @@ // // 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 ignore package natspec diff --git a/common/natspec/natspec_test.go b/common/natspec/natspec_test.go index 9ec14829a..53e6a3a90 100644 --- a/common/natspec/natspec_test.go +++ b/common/natspec/natspec_test.go @@ -13,6 +13,8 @@ // // 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 ignore package natspec diff --git a/common/registrar/ethreg/api.go b/common/registrar/ethreg/api.go new file mode 100644 index 000000000..92e885b10 --- /dev/null +++ b/common/registrar/ethreg/api.go @@ -0,0 +1,265 @@ +// Copyright 2015 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 <http://www.gnu.org/licenses/>. + +package ethreg + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/compiler" + "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/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/common/registrar" +) + +// registryAPIBackend is a backend for an Ethereum Registry. +type registryAPIBackend struct { + bc *core.BlockChain + chainDb ethdb.Database + txPool *core.TxPool + am *accounts.Manager +} + +// PrivateRegistarAPI offers various functions to access the Ethereum registry. +type PrivateRegistarAPI struct { + be *registryAPIBackend +} + +// NewPrivateRegistarAPI creates a new PrivateRegistarAPI instance. +func NewPrivateRegistarAPI(bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI { + return &PrivateRegistarAPI{®istryAPIBackend{bc, chainDb, txPool, am}} +} + +// SetGlobalRegistrar allows clients to set the global registry for the node. +// This method can be used to deploy a new registry. First zero out the current +// address by calling the method with namereg = '0x0' and then call this method +// again with '' as namereg. This will submit a transaction to the network which +// will deploy a new registry on execution. The TX hash is returned. When called +// with namereg '' and the current address is not zero the current global is +// address is returned.. +func (api *PrivateRegistarAPI) SetGlobalRegistrar(namereg string, from common.Address) (string, error) { + return registrar.New(api.be).SetGlobalRegistrar(namereg, from) +} + +// SetHashReg queries the registry for a hash. +func (api *PrivateRegistarAPI) SetHashReg(hashreg string, from common.Address) (string, error) { + return registrar.New(api.be).SetHashReg(hashreg, from) +} + +// SetUrlHint queries the registry for an url. +func (api *PrivateRegistarAPI) SetUrlHint(hashreg string, from common.Address) (string, error) { + return registrar.New(api.be).SetUrlHint(hashreg, from) +} + +// SaveInfo stores contract information on the local file system. +func (api *PrivateRegistarAPI) SaveInfo(info *compiler.ContractInfo, filename string) (contenthash common.Hash, err error) { + return compiler.SaveInfo(info, filename) +} + +// Register registers a new content hash in the registry. +func (api *PrivateRegistarAPI) Register(sender common.Address, addr common.Address, contentHashHex string) (bool, error) { + block := api.be.bc.CurrentBlock() + state, err := state.New(block.Root(), api.be.chainDb) + if err != nil { + return false, err + } + + codeb := state.GetCode(addr) + codeHash := common.BytesToHash(crypto.Sha3(codeb)) + contentHash := common.HexToHash(contentHashHex) + + _, err = registrar.New(api.be).SetHashToHash(sender, codeHash, contentHash) + return err == nil, err +} + +// RegisterUrl registers a new url in the registry. +func (api *PrivateRegistarAPI) RegisterUrl(sender common.Address, contentHashHex string, url string) (bool, error) { + _, err := registrar.New(api.be).SetUrlToHash(sender, common.HexToHash(contentHashHex), url) + return err == nil, err +} + +// callmsg is the message type used for call transations. +type callmsg struct { + from *state.StateObject + to *common.Address + gas, gasPrice *big.Int + value *big.Int + data []byte +} + +// accessor boilerplate to implement core.Message +func (m callmsg) From() (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.gas +} +func (m callmsg) Value() *big.Int { + return m.value +} +func (m callmsg) Data() []byte { + return m.data +} + +// Call forms a transaction from the given arguments and tries to execute it on +// a private VM with a copy of the state. Any changes are therefore only temporary +// and not part of the actual state. This allows for local execution/queries. +func (be *registryAPIBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, string, error) { + block := be.bc.CurrentBlock() + statedb, err := state.New(block.Root(), be.chainDb) + if err != nil { + return "", "", err + } + + var from *state.StateObject + if len(fromStr) == 0 { + accounts, err := be.am.Accounts() + if err != nil || len(accounts) == 0 { + from = statedb.GetOrNewStateObject(common.Address{}) + } else { + from = statedb.GetOrNewStateObject(accounts[0].Address) + } + } else { + from = statedb.GetOrNewStateObject(common.HexToAddress(fromStr)) + } + + from.SetBalance(common.MaxBig) + + msg := callmsg{ + from: from, + gas: common.Big(gasStr), + gasPrice: common.Big(gasPriceStr), + value: common.Big(valueStr), + data: common.FromHex(dataStr), + } + if len(toStr) > 0 { + addr := common.HexToAddress(toStr) + msg.to = &addr + } + + if msg.gas.Cmp(big.NewInt(0)) == 0 { + msg.gas = big.NewInt(50000000) + } + + if msg.gasPrice.Cmp(big.NewInt(0)) == 0 { + msg.gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) + } + + header := be.bc.CurrentBlock().Header() + vmenv := core.NewEnv(statedb, be.bc, msg, header) + gp := new(core.GasPool).AddGas(common.MaxBig) + res, gas, err := core.ApplyMessage(vmenv, msg, gp) + + return common.ToHex(res), gas.String(), err +} + +// StorageAt returns the data stores in the state for the given address and location. +func (be *registryAPIBackend) StorageAt(addr string, storageAddr string) string { + block := be.bc.CurrentBlock() + state, err := state.New(block.Root(), be.chainDb) + if err != nil { + return "" + } + return state.GetState(common.HexToAddress(addr), common.HexToHash(storageAddr)).Hex() +} + +// Transact forms a transaction from the given arguments and submits it to the +// transactio pool for execution. +func (be *registryAPIBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { + if len(toStr) > 0 && toStr != "0x" && !common.IsHexAddress(toStr) { + return "", errors.New("invalid address") + } + + var ( + from = common.HexToAddress(fromStr) + to = common.HexToAddress(toStr) + value = common.Big(valueStr) + gas *big.Int + price *big.Int + data []byte + contractCreation bool + ) + + if len(gasStr) == 0 { + gas = big.NewInt(90000) + } else { + gas = common.Big(gasStr) + } + + if len(gasPriceStr) == 0 { + price = big.NewInt(10000000000000) + } else { + price = common.Big(gasPriceStr) + } + + data = common.FromHex(codeStr) + if len(toStr) == 0 { + contractCreation = true + } + + nonce := be.txPool.State().GetNonce(from) + if len(nonceStr) != 0 { + nonce = common.Big(nonceStr).Uint64() + } + + var tx *types.Transaction + if contractCreation { + tx = types.NewContractCreation(nonce, value, gas, price, data) + } else { + tx = types.NewTransaction(nonce, to, value, gas, price, data) + } + + acc := accounts.Account{from} + signature, err := be.am.Sign(acc, tx.SigHash().Bytes()) + if err != nil { + return "", err + } + signedTx, err := tx.WithSignature(signature) + if err != nil { + return "", err + } + + be.txPool.SetLocal(signedTx) + if err := be.txPool.Add(signedTx); err != nil { + return "", nil + } + + if contractCreation { + addr := crypto.CreateAddress(from, nonce) + glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signedTx.Hash().Hex(), addr.Hex()) + } else { + glog.V(logger.Info).Infof("Tx(%s) to: %s\n", signedTx.Hash().Hex(), tx.To().Hex()) + } + + return signedTx.Hash().Hex(), nil +} diff --git a/common/registrar/ethreg/ethreg.go b/common/registrar/ethreg/ethreg.go deleted file mode 100644 index 626ebe1d7..000000000 --- a/common/registrar/ethreg/ethreg.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package ethreg - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common/registrar" - "github.com/ethereum/go-ethereum/xeth" -) - -// implements a versioned Registrar on an archiving full node -type EthReg struct { - backend *xeth.XEth - registry *registrar.Registrar -} - -func New(xe *xeth.XEth) (self *EthReg) { - self = &EthReg{backend: xe} - self.registry = registrar.New(xe) - return -} - -func (self *EthReg) Registry() *registrar.Registrar { - return self.registry -} - -func (self *EthReg) Resolver(n *big.Int) *registrar.Registrar { - xe := self.backend - if n != nil { - xe = self.backend.AtStateNum(n.Int64()) - } - return registrar.New(xe) -} diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index ba5b05d14..000000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -FROM ubuntu:wily -MAINTAINER caktux - -ENV DEBIAN_FRONTEND noninteractive - -# Usual update / upgrade -RUN apt-get update -RUN apt-get upgrade -q -y -RUN apt-get dist-upgrade -q -y - -# Install Ethereum -RUN apt-get install -q -y software-properties-common -RUN add-apt-repository ppa:ethereum/ethereum -RUN add-apt-repository ppa:ethereum/ethereum-dev -RUN apt-get update -RUN apt-get install -q -y geth - -EXPOSE 8545 -EXPOSE 30303 - -ENTRYPOINT ["/usr/bin/geth"] diff --git a/docker/develop/Dockerfile b/docker/develop/Dockerfile new file mode 100644 index 000000000..98b4aadf8 --- /dev/null +++ b/docker/develop/Dockerfile @@ -0,0 +1,17 @@ +FROM ubuntu:wily +MAINTAINER caktux + +ENV DEBIAN_FRONTEND noninteractive + +RUN apt-get update && \ + apt-get upgrade -q -y && \ + apt-get dist-upgrade -q -y && \ + apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 923F6CA9 && \ + echo "deb http://ppa.launchpad.net/ethereum/ethereum-dev/ubuntu wily main" | tee -a /etc/apt/sources.list.d/ethereum.list && \ + apt-get update && \ + apt-get install -q -y geth + +EXPOSE 8545 +EXPOSE 30303 + +ENTRYPOINT ["/usr/bin/geth"] diff --git a/docker/master/Dockerfile b/docker/master/Dockerfile new file mode 100644 index 000000000..2c6de28c9 --- /dev/null +++ b/docker/master/Dockerfile @@ -0,0 +1,17 @@ +FROM ubuntu:wily +MAINTAINER caktux + +ENV DEBIAN_FRONTEND noninteractive + +RUN apt-get update && \ + apt-get upgrade -q -y && \ + apt-get dist-upgrade -q -y && \ + apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 923F6CA9 && \ + echo "deb http://ppa.launchpad.net/ethereum/ethereum/ubuntu wily main" | tee -a /etc/apt/sources.list.d/ethereum.list && \ + apt-get update && \ + apt-get install -q -y geth + +EXPOSE 8545 +EXPOSE 30303 + +ENTRYPOINT ["/usr/bin/geth"] diff --git a/eth/api.go b/eth/api.go index a1630e2d1..08b103a4c 100644 --- a/eth/api.go +++ b/eth/api.go @@ -42,8 +42,10 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/miner" + "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rlp" - rpc "github.com/ethereum/go-ethereum/rpc/v2" + "github.com/ethereum/go-ethereum/rpc" ) const ( @@ -51,6 +53,19 @@ const ( defaultGas = uint64(90000) ) +// blockByNumber is a commonly used helper function which retrieves and returns the block for the given block number. It +// returns nil when no block could be found. +func blockByNumber(m *miner.Miner, bc *core.BlockChain, blockNr rpc.BlockNumber) *types.Block { + if blockNr == rpc.PendingBlockNumber { + return m.PendingBlock() + } + if blockNr == rpc.LatestBlockNumber { + return bc.CurrentBlock() + } + + return bc.GetBlockByNumber(uint64(blockNr)) +} + // PublicEthereumAPI provides an API to access Ethereum related information. // It offers only methods that operate on public data that is freely available to anyone. type PublicEthereumAPI struct { @@ -293,11 +308,12 @@ type PublicBlockChainAPI struct { chainDb ethdb.Database eventMux *event.TypeMux am *accounts.Manager + miner *miner.Miner } // NewPublicBlockChainAPI creates a new Etheruem blockchain API. -func NewPublicBlockChainAPI(bc *core.BlockChain, chainDb ethdb.Database, eventMux *event.TypeMux, am *accounts.Manager) *PublicBlockChainAPI { - return &PublicBlockChainAPI{bc: bc, chainDb: chainDb, eventMux: eventMux, am: am} +func NewPublicBlockChainAPI(bc *core.BlockChain, m *miner.Miner, chainDb ethdb.Database, eventMux *event.TypeMux, am *accounts.Manager) *PublicBlockChainAPI { + return &PublicBlockChainAPI{bc: bc, miner: m, chainDb: chainDb, eventMux: eventMux, am: am} } // BlockNumber returns the block number of the chain head. @@ -308,7 +324,7 @@ func (s *PublicBlockChainAPI) BlockNumber() *big.Int { // GetBalance returns the amount of wei for the given address in the state of the given block number. // When block number equals rpc.LatestBlockNumber the current block is used. func (s *PublicBlockChainAPI) GetBalance(address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) { - block := blockByNumber(s.bc, blockNr) + block := blockByNumber(s.miner, s.bc, blockNr) if block == nil { return nil, nil } @@ -320,20 +336,10 @@ func (s *PublicBlockChainAPI) GetBalance(address common.Address, blockNr rpc.Blo return state.GetBalance(address), nil } -// blockByNumber is a commonly used helper function which retrieves and returns the block for the given block number. It -// returns nil when no block could be found. -func blockByNumber(bc *core.BlockChain, blockNr rpc.BlockNumber) *types.Block { - if blockNr == rpc.LatestBlockNumber { - return bc.CurrentBlock() - } - - return bc.GetBlockByNumber(uint64(blockNr)) -} - // GetBlockByNumber returns the requested block. When blockNr is -1 the chain head is returned. When fullTx is true all // transactions in the block are returned in full detail, otherwise only the transaction hash is returned. func (s *PublicBlockChainAPI) GetBlockByNumber(blockNr rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) { - if block := blockByNumber(s.bc, blockNr); block != nil { + if block := blockByNumber(s.miner, s.bc, blockNr); block != nil { return s.rpcOutputBlock(block, true, fullTx) } return nil, nil @@ -355,7 +361,7 @@ func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(blockNr rpc.BlockNum return nil, nil } - if block := blockByNumber(s.bc, blockNr); block != nil { + if block := blockByNumber(s.miner, s.bc, blockNr); block != nil { uncles := block.Uncles() if index.Int() < 0 || index.Int() >= len(uncles) { glog.V(logger.Debug).Infof("uncle block on index %d not found for block #%d", index.Int(), blockNr) @@ -388,7 +394,7 @@ func (s *PublicBlockChainAPI) GetUncleCountByBlockNumber(blockNr rpc.BlockNumber return rpc.NewHexNumber(0) } - if block := blockByNumber(s.bc, blockNr); block != nil { + if block := blockByNumber(s.miner, s.bc, blockNr); block != nil { return rpc.NewHexNumber(len(block.Uncles())) } return nil @@ -433,7 +439,7 @@ func (s *PublicBlockChainAPI) GetCode(address common.Address, blockNr rpc.BlockN // GetData returns the data stored at the given address in the state for the given block number. func (s *PublicBlockChainAPI) GetData(address common.Address, blockNr rpc.BlockNumber) (string, error) { - if block := blockByNumber(s.bc, blockNr); block != nil { + if block := blockByNumber(s.miner, s.bc, blockNr); block != nil { state, err := state.New(block.Root(), s.chainDb) if err != nil { return "", err @@ -450,7 +456,7 @@ func (s *PublicBlockChainAPI) GetData(address common.Address, blockNr rpc.BlockN // GetStorageAt returns the storage from the state at the given address, key and block number. func (s *PublicBlockChainAPI) GetStorageAt(address common.Address, key string, blockNr rpc.BlockNumber) (string, error) { - if block := blockByNumber(s.bc, blockNr); block != nil { + if block := blockByNumber(s.miner, s.bc, blockNr); block != nil { state, err := state.New(block.Root(), s.chainDb) if err != nil { return "", err @@ -490,7 +496,7 @@ type CallArgs struct { } func (s *PublicBlockChainAPI) doCall(args CallArgs, blockNr rpc.BlockNumber) (string, *big.Int, error) { - if block := blockByNumber(s.bc, blockNr); block != nil { + if block := blockByNumber(s.miner, s.bc, blockNr); block != nil { stateDb, err := state.New(block.Root(), s.chainDb) if err != nil { return "0x", nil, err @@ -684,19 +690,21 @@ type PublicTransactionPoolAPI struct { eventMux *event.TypeMux chainDb ethdb.Database bc *core.BlockChain + miner *miner.Miner am *accounts.Manager txPool *core.TxPool txMu sync.Mutex } // NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool. -func NewPublicTransactionPoolAPI(txPool *core.TxPool, chainDb ethdb.Database, eventMux *event.TypeMux, bc *core.BlockChain, am *accounts.Manager) *PublicTransactionPoolAPI { +func NewPublicTransactionPoolAPI(txPool *core.TxPool, m *miner.Miner, chainDb ethdb.Database, eventMux *event.TypeMux, bc *core.BlockChain, am *accounts.Manager) *PublicTransactionPoolAPI { return &PublicTransactionPoolAPI{ eventMux: eventMux, chainDb: chainDb, bc: bc, am: am, txPool: txPool, + miner: m, } } @@ -724,7 +732,7 @@ func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(blockNr rpc. return rpc.NewHexNumber(0) } - if block := blockByNumber(s.bc, blockNr); block != nil { + if block := blockByNumber(s.miner, s.bc, blockNr); block != nil { return rpc.NewHexNumber(len(block.Transactions())) } @@ -741,7 +749,7 @@ func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByHash(blockHash comm // GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index. func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(blockNr rpc.BlockNumber, index rpc.HexNumber) (*RPCTransaction, error) { - if block := blockByNumber(s.bc, blockNr); block != nil { + if block := blockByNumber(s.miner, s.bc, blockNr); block != nil { return newRPCTransactionFromBlockIndex(block, index.Int()) } return nil, nil @@ -757,7 +765,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(blockHash c // GetTransactionCount returns the number of transactions the given address has sent for the given block number func (s *PublicTransactionPoolAPI) GetTransactionCount(address common.Address, blockNr rpc.BlockNumber) (*rpc.HexNumber, error) { - block := blockByNumber(s.bc, blockNr) + block := blockByNumber(s.miner, s.bc, blockNr) if block == nil { return nil, nil } @@ -1256,6 +1264,16 @@ func (api *PrivateAdminAPI) ExportChain(file string) (bool, error) { return true, nil } +func hasAllBlocks(chain *core.BlockChain, bs []*types.Block) bool { + for _, b := range bs { + if !chain.HasBlock(b.Hash()) { + return false + } + } + + return true +} + // ImportChain imports a blockchain from a local file. func (api *PrivateAdminAPI) ImportChain(file string) (bool, error) { // Make sure the can access the file to import @@ -1284,6 +1302,11 @@ func (api *PrivateAdminAPI) ImportChain(file string) (bool, error) { if len(blocks) == 0 { break } + + if hasAllBlocks(api.eth.BlockChain(), blocks) { + blocks = blocks[:0] + continue + } // Import the batch and reset the buffer if _, err := api.eth.BlockChain().InsertChain(blocks); err != nil { return false, fmt.Errorf("batch %d: failed to insert: %v", batch, err) @@ -1403,3 +1426,29 @@ func (api *PrivateDebugAPI) ProcessBlock(number uint64) (bool, error) { func (api *PrivateDebugAPI) SetHead(number uint64) { api.eth.BlockChain().SetHead(number) } + +// PublicNetAPI offers network related RPC methods +type PublicNetAPI struct { + net *p2p.Server + networkVersion int +} + +// NewPublicNetAPI creates a new net api instance. +func NewPublicNetAPI(net *p2p.Server, networkVersion int) *PublicNetAPI { + return &PublicNetAPI{net, networkVersion} +} + +// Listening returns an indication if the node is listening for network connections. +func (s *PublicNetAPI) Listening() bool { + return true // always listening +} + +// Peercount returns the number of connected peers +func (s *PublicNetAPI) PeerCount() *rpc.HexNumber { + return rpc.NewHexNumber(s.net.PeerCount()) +} + +// ProtocolVersion returns the current ethereum protocol version. +func (s *PublicNetAPI) Version() string { + return fmt.Sprintf("%d", s.networkVersion) +} diff --git a/eth/backend.go b/eth/backend.go index abd1214ca..352522f61 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/compiler" "github.com/ethereum/go-ethereum/common/httpclient" + "github.com/ethereum/go-ethereum/common/registrar/ethreg" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/downloader" @@ -44,7 +45,7 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rlp" - rpc "github.com/ethereum/go-ethereum/rpc/v2" + "github.com/ethereum/go-ethereum/rpc" ) const ( @@ -121,14 +122,15 @@ type Ethereum struct { eventMux *event.TypeMux miner *miner.Miner - Mining bool - MinerThreads int - NatSpec bool - AutoDAG bool - PowTest bool - autodagquit chan bool - etherbase common.Address - netVersionId int + Mining bool + MinerThreads int + NatSpec bool + AutoDAG bool + PowTest bool + autodagquit chan bool + etherbase common.Address + netVersionId int + netRPCService *PublicNetAPI } func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { @@ -262,12 +264,12 @@ func (s *Ethereum) APIs() []rpc.API { }, { Namespace: "eth", Version: "1.0", - Service: NewPublicBlockChainAPI(s.BlockChain(), s.ChainDb(), s.EventMux(), s.AccountManager()), + Service: NewPublicBlockChainAPI(s.BlockChain(), s.Miner(), s.ChainDb(), s.EventMux(), s.AccountManager()), Public: true, }, { Namespace: "eth", Version: "1.0", - Service: NewPublicTransactionPoolAPI(s.TxPool(), s.ChainDb(), s.EventMux(), s.BlockChain(), s.AccountManager()), + Service: NewPublicTransactionPoolAPI(s.TxPool(), s.Miner(), s.ChainDb(), s.EventMux(), s.BlockChain(), s.AccountManager()), Public: true, }, { Namespace: "eth", @@ -307,6 +309,15 @@ func (s *Ethereum) APIs() []rpc.API { Namespace: "debug", Version: "1.0", Service: NewPrivateDebugAPI(s), + }, { + Namespace: "net", + Version: "1.0", + Service: s.netRPCService, + Public: true, + }, { + Namespace: "admin", + Version: "1.0", + Service: ethreg.NewPrivateRegistarAPI(s.BlockChain(), s.ChainDb(), s.TxPool(), s.AccountManager()), }, } } @@ -356,11 +367,12 @@ func (s *Ethereum) Protocols() []p2p.Protocol { // Start implements node.Service, starting all internal goroutines needed by the // Ethereum protocol implementation. -func (s *Ethereum) Start(*p2p.Server) error { +func (s *Ethereum) Start(srvr *p2p.Server) error { if s.AutoDAG { s.StartAutoDAG() } s.protocolManager.Start() + s.netRPCService = NewPublicNetAPI(srvr, s.NetVersion()) return nil } diff --git a/eth/downloader/api.go b/eth/downloader/api.go index 9deff22a1..cc79e669f 100644 --- a/eth/downloader/api.go +++ b/eth/downloader/api.go @@ -17,7 +17,7 @@ package downloader import ( - rpc "github.com/ethereum/go-ethereum/rpc/v2" + "github.com/ethereum/go-ethereum/rpc" ) // PublicDownloaderAPI provides an API which gives informatoin about the current synchronisation status. diff --git a/eth/filters/api.go b/eth/filters/api.go index 411d8e5a3..f2b0ed32f 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -32,7 +32,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" - rpc "github.com/ethereum/go-ethereum/rpc/v2" + "github.com/ethereum/go-ethereum/rpc" ) var ( diff --git a/jsre/ethereum_js.go b/jsre/ethereum_js.go index 7063a90ec..94e4fde82 100644 --- a/jsre/ethereum_js.go +++ b/jsre/ethereum_js.go @@ -5740,7 +5740,9 @@ Property.prototype.extractCallback = function (args) { */ Property.prototype.attachToObject = function (obj) { var proto = { - get: this.buildGet() + //get: this.buildGet() + get: this.buildGet(), + enumerable: true }; var names = this.name.split('.'); diff --git a/light/state_object.go b/light/state_object.go index 7660c3883..d67fd7e40 100644 --- a/light/state_object.go +++ b/light/state_object.go @@ -264,4 +264,4 @@ func DecodeObject(ctx context.Context, address common.Address, odr OdrBackend, d obj.balance = ext.Balance obj.codeHash = ext.CodeHash return obj, nil -}
\ No newline at end of file +} diff --git a/logger/glog/glog.go b/logger/glog/glog.go index 008c0e036..b8eaf9359 100644 --- a/logger/glog/glog.go +++ b/logger/glog/glog.go @@ -63,11 +63,17 @@ // Enable V-leveled logging at the specified level. // -vmodule="" // The syntax of the argument is a comma-separated list of pattern=N, -// where pattern is a literal file name (minus the ".go" suffix) or -// "glob" pattern and N is a V level. For instance, -// -vmodule=gopher*=3 -// sets the V level to 3 in all Go files whose names begin "gopher". +// where pattern is a literal file name or "glob" pattern matching +// and N is a V level. For instance, // +// -vmodule=gopher.go=3 +// sets the V level to 3 in all Go files named "gopher.go". +// +// -vmodule=foo=3 +// sets V to 3 in all files of any packages whose import path ends in "foo". +// +// -vmodule=foo/*=3 +// sets V to 3 in all files of any packages whose import path contains "foo". package glog import ( @@ -78,7 +84,7 @@ import ( "io" stdLog "log" "os" - "path/filepath" + "regexp" "runtime" "strconv" "strings" @@ -113,11 +119,30 @@ var severityName = []string{ fatalLog: "FATAL", } +// these path prefixes are trimmed for display, but not when +// matching vmodule filters. +var trimPrefixes = []string{ + "/github.com/ethereum/go-ethereum", + "/github.com/ethereum/ethash", +} + +func trimToImportPath(file string) string { + if root := strings.LastIndex(file, "src/"); root != 0 { + file = file[root+3:] + } + return file +} + // SetV sets the global verbosity level func SetV(v int) { logging.verbosity.set(Level(v)) } +// SetVmodule sets the global verbosity patterns. +func SetVmodule(pat string) error { + return logging.vmodule.Set(pat) +} + // SetToStderr sets the global output style func SetToStderr(toStderr bool) { logging.toStderr = toStderr @@ -261,21 +286,10 @@ type moduleSpec struct { // modulePat contains a filter for the -vmodule flag. // It holds a verbosity level and a file pattern to match. type modulePat struct { - pattern string - literal bool // The pattern is a literal string + pattern *regexp.Regexp level Level } -// match reports whether the file matches the pattern. It uses a string -// comparison if the pattern contains no metacharacters. -func (m *modulePat) match(file string) bool { - if m.literal { - return file == m.pattern - } - match, _ := filepath.Match(m.pattern, file) - return match -} - func (m *moduleSpec) String() string { // Lock because the type is not atomic. TODO: clean this up. logging.mu.Lock() @@ -322,7 +336,8 @@ func (m *moduleSpec) Set(value string) error { continue // Ignore. It's harmless but no point in paying the overhead. } // TODO: check syntax of filter? - filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)}) + re, _ := compileModulePattern(pattern) + filter = append(filter, modulePat{re, Level(v)}) } logging.mu.Lock() defer logging.mu.Unlock() @@ -330,10 +345,21 @@ func (m *moduleSpec) Set(value string) error { return nil } -// isLiteral reports whether the pattern is a literal string, that is, has no metacharacters -// that require filepath.Match to be called to match the pattern. -func isLiteral(pattern string) bool { - return !strings.ContainsAny(pattern, `\*?[]`) +// compiles a vmodule pattern to a regular expression. +func compileModulePattern(pat string) (*regexp.Regexp, error) { + re := ".*" + for _, comp := range strings.Split(pat, "/") { + if comp == "*" { + re += "(/.*)?" + } else if comp != "" { + // TODO: maybe return error if comp contains * + re += "/" + regexp.QuoteMeta(comp) + } + } + if !strings.HasSuffix(pat, ".go") { + re += "/[^/]+\\.go" + } + return regexp.Compile(re + "$") } // traceLocation represents the setting of the -log_backtrace_at flag. @@ -556,10 +582,14 @@ func (l *loggingT) header(s severity, depth int) (*buffer, string, int) { file = "???" line = 1 } else { - slash := strings.LastIndex(file, "/") - if slash >= 0 { - file = file[slash+1:] + file = trimToImportPath(file) + for _, p := range trimPrefixes { + if strings.HasPrefix(file, p) { + file = file[len(p):] + break + } } + file = file[1:] // drop '/' } return l.formatHeader(s, file, line), file, line } @@ -592,9 +622,7 @@ func (l *loggingT) formatHeader(s severity, file string, line int) *buffer { buf.tmp[14] = '.' buf.nDigits(6, 15, now.Nanosecond()/1000, '0') buf.tmp[21] = ' ' - buf.nDigits(7, 22, pid, ' ') // TODO: should be TID - buf.tmp[29] = ' ' - buf.Write(buf.tmp[:30]) + buf.Write(buf.tmp[:22]) buf.WriteString(file) buf.tmp[0] = ':' n := buf.someDigits(1, line) @@ -976,15 +1004,9 @@ func (lb logBridge) Write(b []byte) (n int, err error) { func (l *loggingT) setV(pc uintptr) Level { fn := runtime.FuncForPC(pc) file, _ := fn.FileLine(pc) - // The file is something like /a/b/c/d.go. We want just the d. - if strings.HasSuffix(file, ".go") { - file = file[:len(file)-3] - } - if slash := strings.LastIndex(file, "/"); slash >= 0 { - file = file[slash+1:] - } + file = trimToImportPath(file) for _, filter := range l.vmodule.filter { - if filter.match(file) { + if filter.pattern.MatchString(file) { l.vmap[pc] = filter.level return filter.level } diff --git a/logger/glog/glog_test.go b/logger/glog/glog_test.go index 0fb376e1f..30861a48d 100644 --- a/logger/glog/glog_test.go +++ b/logger/glog/glog_test.go @@ -180,7 +180,7 @@ func TestHeader(t *testing.T) { pid = 1234 Info("test") var line int - format := "I0102 15:04:05.067890 1234 glog_test.go:%d] test\n" + format := "I0102 15:04:05.067890 logger/glog/glog_test.go:%d] test\n" n, err := fmt.Sscanf(contents(infoLog), format, &line) if n != 1 || err != nil { t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog)) @@ -253,7 +253,7 @@ func TestV(t *testing.T) { func TestVmoduleOn(t *testing.T) { setFlags() defer logging.swap(logging.newBuffers()) - logging.vmodule.Set("glog_test=2") + logging.vmodule.Set("glog_test.go=2") defer logging.vmodule.Set("") if !V(1) { t.Error("V not enabled for 1") @@ -290,22 +290,43 @@ func TestVmoduleOff(t *testing.T) { } } +var patternTests = []struct{ input, want string }{ + {"foo/bar/x.go", ".*/foo/bar/x\\.go$"}, + {"foo/*/x.go", ".*/foo(/.*)?/x\\.go$"}, + {"foo/*", ".*/foo(/.*)?/[^/]+\\.go$"}, +} + +func TestCompileModulePattern(t *testing.T) { + for _, test := range patternTests { + re, err := compileModulePattern(test.input) + if err != nil { + t.Fatalf("%s: %v", err) + } + if re.String() != test.want { + t.Errorf("mismatch for %q: got %q, want %q", test.input, re.String(), test.want) + } + } +} + // vGlobs are patterns that match/don't match this file at V=2. var vGlobs = map[string]bool{ // Easy to test the numeric match here. - "glog_test=1": false, // If -vmodule sets V to 1, V(2) will fail. - "glog_test=2": true, - "glog_test=3": true, // If -vmodule sets V to 1, V(3) will succeed. - // These all use 2 and check the patterns. All are true. - "*=2": true, - "?l*=2": true, - "????_*=2": true, - "??[mno]?_*t=2": true, - // These all use 2 and check the patterns. All are false. - "*x=2": false, - "m*=2": false, - "??_*=2": false, - "?[abc]?_*t=2": false, + "glog_test.go=1": false, // If -vmodule sets V to 1, V(2) will fail. + "glog_test.go=2": true, + "glog_test.go=3": true, // If -vmodule sets V to 1, V(3) will succeed. + + // Import path prefix matching + "logger/glog=1": false, + "logger/glog=2": true, + "logger/glog=3": true, + + // Import path glob matching + "logger/*=1": false, + "logger/*=2": true, + "logger/*=3": true, + + // These all use 2 and check the patterns. + "*=2": true, } // Test that vmodule globbing works as advertised. diff --git a/miner/api.go b/miner/api.go index 65d106afd..42678b5ad 100644 --- a/miner/api.go +++ b/miner/api.go @@ -21,7 +21,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/logger/glog" - rpc "github.com/ethereum/go-ethereum/rpc/v2" + "github.com/ethereum/go-ethereum/rpc" ) // PublicMinerAPI provides an API to control the miner. diff --git a/node/api.go b/node/api.go index a44ee16c0..7b247dc51 100644 --- a/node/api.go +++ b/node/api.go @@ -21,11 +21,15 @@ import ( "strings" "time" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" - "github.com/ethereum/go-ethereum/rpc/comms" + "github.com/ethereum/go-ethereum/rpc" "github.com/rcrowley/go-metrics" + + "gopkg.in/fatih/set.v0" ) // PrivateAdminAPI is the collection of administrative API methods exposed only @@ -59,27 +63,83 @@ func (api *PrivateAdminAPI) AddPeer(url string) (bool, error) { // StartRPC starts the HTTP RPC API server. func (api *PrivateAdminAPI) StartRPC(address string, port int, cors string, apis string) (bool, error) { - /*// Parse the list of API modules to make available - apis, err := api.ParseApiString(apis, codec.JSON, xeth.New(api.node, nil), api.node) - if err != nil { - return false, err + var offeredAPIs []rpc.API + if len(apis) > 0 { + namespaces := set.New() + for _, a := range strings.Split(apis, ",") { + namespaces.Add(strings.TrimSpace(a)) + } + for _, api := range api.node.APIs() { + if namespaces.Has(api.Namespace) { + offeredAPIs = append(offeredAPIs, api) + } + } + } else { // use by default all public API's + for _, api := range api.node.APIs() { + if api.Public { + offeredAPIs = append(offeredAPIs, api) + } + } } - // Configure and start the HTTP RPC server - config := comms.HttpConfig{ - ListenAddress: address, - ListenPort: port, - CorsDomain: cors, + + if address == "" { + address = "127.0.0.1" } - if err := comms.StartHttp(config, self.codec, api.Merge(apis...)); err != nil { - return false, err + if port == 0 { + port = 8545 } - return true, nil*/ - return false, fmt.Errorf("needs new RPC implementation to resolve circular dependency") + + corsDomains := strings.Split(cors, " ") + err := rpc.StartHTTP(address, port, corsDomains, offeredAPIs) + return err == nil, err } // StopRPC terminates an already running HTTP RPC API endpoint. -func (api *PrivateAdminAPI) StopRPC() { - comms.StopHttp() +func (api *PrivateAdminAPI) StopRPC() (bool, error) { + err := rpc.StopHTTP() + return err == nil, err +} + + +// StartWS starts the websocket RPC API server. +func (api *PrivateAdminAPI) StartWS(address string, port int, cors string, apis string) (bool, error) { + var offeredAPIs []rpc.API + if len(apis) > 0 { + namespaces := set.New() + for _, a := range strings.Split(apis, ",") { + namespaces.Add(strings.TrimSpace(a)) + } + for _, api := range api.node.APIs() { + if namespaces.Has(api.Namespace) { + offeredAPIs = append(offeredAPIs, api) + } + } + } else { + // use by default all public API's + for _, api := range api.node.APIs() { + if api.Public { + offeredAPIs = append(offeredAPIs, api) + } + } + } + + if address == "" { + address = "127.0.0.1" + } + if port == 0 { + port = 8546 + } + + corsDomains := strings.Split(cors, " ") + + err := rpc.StartWS(address, port, corsDomains, offeredAPIs) + return err == nil, err +} + +// StopRPC terminates an already running websocket RPC API endpoint. +func (api *PrivateAdminAPI) StopWS() (bool, error) { + err := rpc.StopWS() + return err == nil, err } // PublicAdminAPI is the collection of administrative API methods exposed over @@ -138,6 +198,11 @@ func (api *PrivateDebugAPI) Verbosity(level int) { glog.SetV(level) } +// Vmodule updates the node's logging verbosity pattern. +func (api *PrivateDebugAPI) Vmodule(pattern string) error { + return glog.SetVmodule(pattern) +} + // PublicDebugAPI is the collection of debugging related API methods exposed over // both secure and unsecure RPC channels. type PublicDebugAPI struct { @@ -242,3 +307,24 @@ func (api *PublicDebugAPI) Metrics(raw bool) (map[string]interface{}, error) { }) return counters, nil } + +// PublicWeb3API offers helper utils +type PublicWeb3API struct { + stack *Node +} + +// NewPublicWeb3API creates a new Web3Service instance +func NewPublicWeb3API(stack *Node) *PublicWeb3API { + return &PublicWeb3API{stack} +} + +// ClientVersion returns the node name +func (s *PublicWeb3API) ClientVersion() string { + return s.stack.Server().Name +} + +// Sha3 applies the ethereum sha3 implementation on the input. +// It assumes the input is hex encoded. +func (s *PublicWeb3API) Sha3(input string) string { + return common.ToHex(crypto.Sha3(common.FromHex(input))) +} diff --git a/node/node.go b/node/node.go index 5d7b5869c..3d077b0bd 100644 --- a/node/node.go +++ b/node/node.go @@ -27,7 +27,7 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/p2p" - rpc "github.com/ethereum/go-ethereum/rpc/v2" + "github.com/ethereum/go-ethereum/rpc" ) var ( @@ -290,6 +290,11 @@ func (n *Node) APIs() []rpc.API { Version: "1.0", Service: NewPublicDebugAPI(n), Public: true, + }, { + Namespace: "web3", + Version: "1.0", + Service: NewPublicWeb3API(n), + Public: true, }, } // Inject all the APIs owned by various services diff --git a/node/node_example_test.go b/node/node_example_test.go index ef41dddaf..5ff5d06a6 100644 --- a/node/node_example_test.go +++ b/node/node_example_test.go @@ -23,7 +23,7 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" - rpc "github.com/ethereum/go-ethereum/rpc/v2" + "github.com/ethereum/go-ethereum/rpc" ) // SampleService is a trivial network service that can be attached to a node for diff --git a/node/service.go b/node/service.go index b83d4c80d..26e9f1624 100644 --- a/node/service.go +++ b/node/service.go @@ -23,7 +23,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/p2p" - rpc "github.com/ethereum/go-ethereum/rpc/v2" + "github.com/ethereum/go-ethereum/rpc" ) // ServiceContext is a collection of service independent options inherited from diff --git a/node/utils_test.go b/node/utils_test.go index 2b7bfadbe..7755605ae 100644 --- a/node/utils_test.go +++ b/node/utils_test.go @@ -23,7 +23,7 @@ import ( "reflect" "github.com/ethereum/go-ethereum/p2p" - rpc "github.com/ethereum/go-ethereum/rpc/v2" + "github.com/ethereum/go-ethereum/rpc" ) // NoopService is a trivial implementation of the Service interface. diff --git a/rpc/api/admin.go b/rpc/api/admin.go deleted file mode 100644 index 1133c9bca..000000000 --- a/rpc/api/admin.go +++ /dev/null @@ -1,477 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "fmt" - "io" - "math/big" - "os" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/compiler" - "github.com/ethereum/go-ethereum/common/natspec" - "github.com/ethereum/go-ethereum/common/registrar" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/p2p/discover" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/comms" - "github.com/ethereum/go-ethereum/rpc/shared" - "github.com/ethereum/go-ethereum/rpc/useragent" - "github.com/ethereum/go-ethereum/xeth" -) - -const ( - AdminApiversion = "1.0" - importBatchSize = 2500 -) - -var ( - // mapping between methods and handlers - AdminMapping = map[string]adminhandler{ - "admin_addPeer": (*adminApi).AddPeer, - "admin_peers": (*adminApi).Peers, - "admin_nodeInfo": (*adminApi).NodeInfo, - "admin_exportChain": (*adminApi).ExportChain, - "admin_importChain": (*adminApi).ImportChain, - "admin_verbosity": (*adminApi).Verbosity, - "admin_setSolc": (*adminApi).SetSolc, - "admin_datadir": (*adminApi).DataDir, - "admin_startRPC": (*adminApi).StartRPC, - "admin_stopRPC": (*adminApi).StopRPC, - "admin_setGlobalRegistrar": (*adminApi).SetGlobalRegistrar, - "admin_setHashReg": (*adminApi).SetHashReg, - "admin_setUrlHint": (*adminApi).SetUrlHint, - "admin_saveInfo": (*adminApi).SaveInfo, - "admin_register": (*adminApi).Register, - "admin_registerUrl": (*adminApi).RegisterUrl, - "admin_startNatSpec": (*adminApi).StartNatSpec, - "admin_stopNatSpec": (*adminApi).StopNatSpec, - "admin_getContractInfo": (*adminApi).GetContractInfo, - "admin_httpGet": (*adminApi).HttpGet, - "admin_sleepBlocks": (*adminApi).SleepBlocks, - "admin_sleep": (*adminApi).Sleep, - "admin_enableUserAgent": (*adminApi).EnableUserAgent, - } -) - -// admin callback handler -type adminhandler func(*adminApi, *shared.Request) (interface{}, error) - -// admin api provider -type adminApi struct { - xeth *xeth.XEth - stack *node.Node - ethereum *eth.Ethereum - codec codec.Codec - coder codec.ApiCoder -} - -// create a new admin api instance -func NewAdminApi(xeth *xeth.XEth, stack *node.Node, codec codec.Codec) *adminApi { - api := &adminApi{ - xeth: xeth, - stack: stack, - codec: codec, - coder: codec.New(nil), - } - if stack != nil { - stack.Service(&api.ethereum) - } - return api -} - -// collection with supported methods -func (self *adminApi) Methods() []string { - methods := make([]string, len(AdminMapping)) - i := 0 - for k := range AdminMapping { - methods[i] = k - i++ - } - return methods -} - -// Execute given request -func (self *adminApi) Execute(req *shared.Request) (interface{}, error) { - if callback, ok := AdminMapping[req.Method]; ok { - return callback(self, req) - } - - return nil, &shared.NotImplementedError{req.Method} -} - -func (self *adminApi) Name() string { - return shared.AdminApiName -} - -func (self *adminApi) ApiVersion() string { - return AdminApiversion -} - -func (self *adminApi) AddPeer(req *shared.Request) (interface{}, error) { - args := new(AddPeerArgs) - if err := self.coder.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - node, err := discover.ParseNode(args.Url) - if err != nil { - return nil, fmt.Errorf("invalid node URL: %v", err) - } - self.stack.Server().AddPeer(node) - return true, nil -} - -func (self *adminApi) Peers(req *shared.Request) (interface{}, error) { - return self.stack.Server().PeersInfo(), nil -} - -func (self *adminApi) NodeInfo(req *shared.Request) (interface{}, error) { - return self.stack.Server().NodeInfo(), nil -} - -func (self *adminApi) DataDir(req *shared.Request) (interface{}, error) { - return self.stack.DataDir(), nil -} - -func hasAllBlocks(chain *core.BlockChain, bs []*types.Block) bool { - for _, b := range bs { - if !chain.HasBlock(b.Hash()) { - return false - } - } - return true -} - -func (self *adminApi) ImportChain(req *shared.Request) (interface{}, error) { - args := new(ImportExportChainArgs) - if err := self.coder.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - fh, err := os.Open(args.Filename) - if err != nil { - return false, err - } - defer fh.Close() - stream := rlp.NewStream(fh, 0) - - // Run actual the import. - blocks := make(types.Blocks, importBatchSize) - n := 0 - for batch := 0; ; batch++ { - - i := 0 - for ; i < importBatchSize; i++ { - var b types.Block - if err := stream.Decode(&b); err == io.EOF { - break - } else if err != nil { - return false, fmt.Errorf("at block %d: %v", n, err) - } - blocks[i] = &b - n++ - } - if i == 0 { - break - } - // Import the batch. - if hasAllBlocks(self.ethereum.BlockChain(), blocks[:i]) { - continue - } - if _, err := self.ethereum.BlockChain().InsertChain(blocks[:i]); err != nil { - return false, fmt.Errorf("invalid block %d: %v", n, err) - } - } - return true, nil -} - -func (self *adminApi) ExportChain(req *shared.Request) (interface{}, error) { - args := new(ImportExportChainArgs) - if err := self.coder.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - fh, err := os.OpenFile(args.Filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) - if err != nil { - return false, err - } - defer fh.Close() - if err := self.ethereum.BlockChain().Export(fh); err != nil { - return false, err - } - - return true, nil -} - -func (self *adminApi) Verbosity(req *shared.Request) (interface{}, error) { - args := new(VerbosityArgs) - if err := self.coder.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - glog.SetV(args.Level) - return true, nil -} - -func (self *adminApi) SetSolc(req *shared.Request) (interface{}, error) { - args := new(SetSolcArgs) - if err := self.coder.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - solc, err := self.xeth.SetSolc(args.Path) - if err != nil { - return nil, err - } - return solc.Info(), nil -} - -func (self *adminApi) StartRPC(req *shared.Request) (interface{}, error) { - args := new(StartRPCArgs) - if err := self.coder.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - cfg := comms.HttpConfig{ - ListenAddress: args.ListenAddress, - ListenPort: args.ListenPort, - CorsDomain: args.CorsDomain, - } - - apis, err := ParseApiString(args.Apis, self.codec, self.xeth, self.stack) - if err != nil { - return false, err - } - - err = comms.StartHttp(cfg, self.codec, Merge(apis...)) - if err == nil { - return true, nil - } - return false, err -} - -func (self *adminApi) StopRPC(req *shared.Request) (interface{}, error) { - comms.StopHttp() - return true, nil -} - -func (self *adminApi) SleepBlocks(req *shared.Request) (interface{}, error) { - args := new(SleepBlocksArgs) - if err := self.coder.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - var timer <-chan time.Time - var height *big.Int - var err error - if args.Timeout > 0 { - timer = time.NewTimer(time.Duration(args.Timeout) * time.Second).C - } - - height = new(big.Int).Add(self.xeth.CurrentBlock().Number(), big.NewInt(args.N)) - height, err = sleepBlocks(self.xeth.UpdateState(), height, timer) - if err != nil { - return nil, err - } - return height.Uint64(), nil -} - -func sleepBlocks(wait chan *big.Int, height *big.Int, timer <-chan time.Time) (newHeight *big.Int, err error) { - wait <- height - select { - case <-timer: - // if times out make sure the xeth loop does not block - go func() { - select { - case wait <- nil: - case <-wait: - } - }() - return nil, fmt.Errorf("timeout") - case newHeight = <-wait: - } - return -} - -func (self *adminApi) Sleep(req *shared.Request) (interface{}, error) { - args := new(SleepArgs) - if err := self.coder.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - time.Sleep(time.Duration(args.S) * time.Second) - return nil, nil -} - -func (self *adminApi) SetGlobalRegistrar(req *shared.Request) (interface{}, error) { - args := new(SetGlobalRegistrarArgs) - if err := self.coder.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - sender := common.HexToAddress(args.ContractAddress) - - reg := registrar.New(self.xeth) - txhash, err := reg.SetGlobalRegistrar(args.NameReg, sender) - if err != nil { - return false, err - } - - return txhash, nil -} - -func (self *adminApi) SetHashReg(req *shared.Request) (interface{}, error) { - args := new(SetHashRegArgs) - if err := self.coder.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - reg := registrar.New(self.xeth) - sender := common.HexToAddress(args.Sender) - txhash, err := reg.SetHashReg(args.HashReg, sender) - if err != nil { - return false, err - } - - return txhash, nil -} - -func (self *adminApi) SetUrlHint(req *shared.Request) (interface{}, error) { - args := new(SetUrlHintArgs) - if err := self.coder.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - urlHint := args.UrlHint - sender := common.HexToAddress(args.Sender) - - reg := registrar.New(self.xeth) - txhash, err := reg.SetUrlHint(urlHint, sender) - if err != nil { - return nil, err - } - - return txhash, nil -} - -func (self *adminApi) SaveInfo(req *shared.Request) (interface{}, error) { - args := new(SaveInfoArgs) - if err := self.coder.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - contenthash, err := compiler.SaveInfo(&args.ContractInfo, args.Filename) - if err != nil { - return nil, err - } - - return contenthash.Hex(), nil -} - -func (self *adminApi) Register(req *shared.Request) (interface{}, error) { - args := new(RegisterArgs) - if err := self.coder.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - sender := common.HexToAddress(args.Sender) - // sender and contract address are passed as hex strings - codeb := self.xeth.CodeAtBytes(args.Address) - codeHash := common.BytesToHash(crypto.Sha3(codeb)) - contentHash := common.HexToHash(args.ContentHashHex) - registry := registrar.New(self.xeth) - - _, err := registry.SetHashToHash(sender, codeHash, contentHash) - if err != nil { - return false, err - } - - return true, nil -} - -func (self *adminApi) RegisterUrl(req *shared.Request) (interface{}, error) { - args := new(RegisterUrlArgs) - if err := self.coder.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - sender := common.HexToAddress(args.Sender) - registry := registrar.New(self.xeth) - _, err := registry.SetUrlToHash(sender, common.HexToHash(args.ContentHash), args.Url) - if err != nil { - return false, err - } - - return true, nil -} - -func (self *adminApi) StartNatSpec(req *shared.Request) (interface{}, error) { - self.ethereum.NatSpec = true - return true, nil -} - -func (self *adminApi) StopNatSpec(req *shared.Request) (interface{}, error) { - self.ethereum.NatSpec = false - return true, nil -} - -func (self *adminApi) GetContractInfo(req *shared.Request) (interface{}, error) { - args := new(GetContractInfoArgs) - if err := self.coder.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - infoDoc, err := natspec.FetchDocsForContract(args.Contract, self.xeth, self.ethereum.HTTPClient()) - if err != nil { - return nil, err - } - - var info interface{} - err = self.coder.Decode(infoDoc, &info) - if err != nil { - return nil, err - } - - return info, nil -} - -func (self *adminApi) HttpGet(req *shared.Request) (interface{}, error) { - args := new(HttpGetArgs) - if err := self.coder.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - resp, err := self.ethereum.HTTPClient().Get(args.Uri, args.Path) - if err != nil { - return nil, err - } - - return string(resp), nil -} - -func (self *adminApi) EnableUserAgent(req *shared.Request) (interface{}, error) { - if fe, ok := self.xeth.Frontend().(*useragent.RemoteFrontend); ok { - fe.Enable() - } - return true, nil -} diff --git a/rpc/api/admin_args.go b/rpc/api/admin_args.go deleted file mode 100644 index e3a2f72bf..000000000 --- a/rpc/api/admin_args.go +++ /dev/null @@ -1,490 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "encoding/json" - - "github.com/ethereum/go-ethereum/common/compiler" - "github.com/ethereum/go-ethereum/rpc/shared" -) - -type AddPeerArgs struct { - Url string -} - -func (args *AddPeerArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) != 1 { - return shared.NewDecodeParamError("Expected enode as argument") - } - - urlstr, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("url", "not a string") - } - args.Url = urlstr - - return nil -} - -type ImportExportChainArgs struct { - Filename string -} - -func (args *ImportExportChainArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) != 1 { - return shared.NewDecodeParamError("Expected filename as argument") - } - - filename, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("filename", "not a string") - } - args.Filename = filename - - return nil -} - -type VerbosityArgs struct { - Level int -} - -func (args *VerbosityArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) != 1 { - return shared.NewDecodeParamError("Expected enode as argument") - } - - level, err := numString(obj[0]) - if err == nil { - args.Level = int(level.Int64()) - } - - return nil -} - -type SetSolcArgs struct { - Path string -} - -func (args *SetSolcArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) != 1 { - return shared.NewDecodeParamError("Expected path as argument") - } - - if pathstr, ok := obj[0].(string); ok { - args.Path = pathstr - return nil - } - - return shared.NewInvalidTypeError("path", "not a string") -} - -type StartRPCArgs struct { - ListenAddress string - ListenPort uint - CorsDomain string - Apis string -} - -func (args *StartRPCArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - args.ListenAddress = "127.0.0.1" - args.ListenPort = 8545 - args.Apis = "net,eth,web3" - - if len(obj) >= 1 && obj[0] != nil { - if addr, ok := obj[0].(string); ok { - args.ListenAddress = addr - } else { - return shared.NewInvalidTypeError("listenAddress", "not a string") - } - } - - if len(obj) >= 2 && obj[1] != nil { - if port, ok := obj[1].(float64); ok && port >= 0 && port <= 64*1024 { - args.ListenPort = uint(port) - } else { - return shared.NewInvalidTypeError("listenPort", "not a valid port number") - } - } - - if len(obj) >= 3 && obj[2] != nil { - if corsDomain, ok := obj[2].(string); ok { - args.CorsDomain = corsDomain - } else { - return shared.NewInvalidTypeError("corsDomain", "not a string") - } - } - - if len(obj) >= 4 && obj[3] != nil { - if apis, ok := obj[3].(string); ok { - args.Apis = apis - } else { - return shared.NewInvalidTypeError("apis", "not a string") - } - } - - return nil -} - -type SleepArgs struct { - S int -} - -func (args *SleepArgs) UnmarshalJSON(b []byte) (err error) { - - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - if len(obj) >= 1 { - if obj[0] != nil { - if n, err := numString(obj[0]); err == nil { - args.S = int(n.Int64()) - } else { - return shared.NewInvalidTypeError("N", "not an integer: "+err.Error()) - } - } else { - return shared.NewInsufficientParamsError(0, 1) - } - } - return nil -} - -type SleepBlocksArgs struct { - N int64 - Timeout int64 -} - -func (args *SleepBlocksArgs) UnmarshalJSON(b []byte) (err error) { - - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - args.N = 1 - args.Timeout = 0 - if len(obj) >= 1 && obj[0] != nil { - if n, err := numString(obj[0]); err == nil { - args.N = n.Int64() - } else { - return shared.NewInvalidTypeError("N", "not an integer: "+err.Error()) - } - } - - if len(obj) >= 2 && obj[1] != nil { - if n, err := numString(obj[1]); err == nil { - args.Timeout = n.Int64() - } else { - return shared.NewInvalidTypeError("Timeout", "not an integer: "+err.Error()) - } - } - - return nil -} - -type SetGlobalRegistrarArgs struct { - NameReg string - ContractAddress string -} - -func (args *SetGlobalRegistrarArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) == 0 { - return shared.NewDecodeParamError("Expected namereg address") - } - - if len(obj) >= 1 { - if namereg, ok := obj[0].(string); ok { - args.NameReg = namereg - } else { - return shared.NewInvalidTypeError("NameReg", "not a string") - } - } - - if len(obj) >= 2 && obj[1] != nil { - if addr, ok := obj[1].(string); ok { - args.ContractAddress = addr - } else { - return shared.NewInvalidTypeError("ContractAddress", "not a string") - } - } - - return nil -} - -type SetHashRegArgs struct { - HashReg string - Sender string -} - -func (args *SetHashRegArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) >= 1 && obj[0] != nil { - if hashreg, ok := obj[0].(string); ok { - args.HashReg = hashreg - } else { - return shared.NewInvalidTypeError("HashReg", "not a string") - } - } - - if len(obj) >= 2 && obj[1] != nil { - if sender, ok := obj[1].(string); ok { - args.Sender = sender - } else { - return shared.NewInvalidTypeError("Sender", "not a string") - } - } - - return nil -} - -type SetUrlHintArgs struct { - UrlHint string - Sender string -} - -func (args *SetUrlHintArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) >= 1 && obj[0] != nil { - if urlhint, ok := obj[0].(string); ok { - args.UrlHint = urlhint - } else { - return shared.NewInvalidTypeError("UrlHint", "not a string") - } - } - - if len(obj) >= 2 && obj[1] != nil { - if sender, ok := obj[1].(string); ok { - args.Sender = sender - } else { - return shared.NewInvalidTypeError("Sender", "not a string") - } - } - - return nil -} - -type SaveInfoArgs struct { - ContractInfo compiler.ContractInfo - Filename string -} - -func (args *SaveInfoArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 2 { - return shared.NewInsufficientParamsError(len(obj), 2) - } - - if jsonraw, err := json.Marshal(obj[0]); err == nil { - if err = json.Unmarshal(jsonraw, &args.ContractInfo); err != nil { - return err - } - } else { - return err - } - - if filename, ok := obj[1].(string); ok { - args.Filename = filename - } else { - return shared.NewInvalidTypeError("Filename", "not a string") - } - - return nil -} - -type RegisterArgs struct { - Sender string - Address string - ContentHashHex string -} - -func (args *RegisterArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 3 { - return shared.NewInsufficientParamsError(len(obj), 3) - } - - if len(obj) >= 1 { - if sender, ok := obj[0].(string); ok { - args.Sender = sender - } else { - return shared.NewInvalidTypeError("Sender", "not a string") - } - } - - if len(obj) >= 2 { - if address, ok := obj[1].(string); ok { - args.Address = address - } else { - return shared.NewInvalidTypeError("Address", "not a string") - } - } - - if len(obj) >= 3 { - if hex, ok := obj[2].(string); ok { - args.ContentHashHex = hex - } else { - return shared.NewInvalidTypeError("ContentHashHex", "not a string") - } - } - - return nil -} - -type RegisterUrlArgs struct { - Sender string - ContentHash string - Url string -} - -func (args *RegisterUrlArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) >= 1 { - if sender, ok := obj[0].(string); ok { - args.Sender = sender - } else { - return shared.NewInvalidTypeError("Sender", "not a string") - } - } - - if len(obj) >= 2 { - if sender, ok := obj[1].(string); ok { - args.ContentHash = sender - } else { - return shared.NewInvalidTypeError("ContentHash", "not a string") - } - } - - if len(obj) >= 3 { - if sender, ok := obj[2].(string); ok { - args.Url = sender - } else { - return shared.NewInvalidTypeError("Url", "not a string") - } - } - - return nil -} - -type GetContractInfoArgs struct { - Contract string -} - -func (args *GetContractInfoArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - if len(obj) >= 1 { - if contract, ok := obj[0].(string); ok { - args.Contract = contract - } else { - return shared.NewInvalidTypeError("Contract", "not a string") - } - } - - return nil -} - -type HttpGetArgs struct { - Uri string - Path string -} - -func (args *HttpGetArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - if len(obj) >= 1 { - if uri, ok := obj[0].(string); ok { - args.Uri = uri - } else { - return shared.NewInvalidTypeError("Uri", "not a string") - } - } - - if len(obj) >= 2 && obj[1] != nil { - if path, ok := obj[1].(string); ok { - args.Path = path - } else { - return shared.NewInvalidTypeError("Path", "not a string") - } - } - - return nil -} diff --git a/rpc/api/admin_js.go b/rpc/api/admin_js.go deleted file mode 100644 index 413ea8d47..000000000 --- a/rpc/api/admin_js.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -const Admin_JS = ` -web3._extend({ - property: 'admin', - methods: - [ - new web3._extend.Method({ - name: 'addPeer', - call: 'admin_addPeer', - params: 1, - inputFormatter: [null] - }), - new web3._extend.Method({ - name: 'exportChain', - call: 'admin_exportChain', - params: 1, - inputFormatter: [null] - }), - new web3._extend.Method({ - name: 'importChain', - call: 'admin_importChain', - params: 1, - inputFormatter: [null] - }), - new web3._extend.Method({ - name: 'sleepBlocks', - call: 'admin_sleepBlocks', - params: 2, - inputFormatter: [null, null] - }), - new web3._extend.Method({ - name: 'verbosity', - call: 'admin_verbosity', - params: 1, - inputFormatter: [web3._extend.utils.fromDecimal] - }), - new web3._extend.Method({ - name: 'setSolc', - call: 'admin_setSolc', - params: 1, - inputFormatter: [null] - }), - new web3._extend.Method({ - name: 'startRPC', - call: 'admin_startRPC', - params: 4, - inputFormatter: [null, null, null, null] - }), - new web3._extend.Method({ - name: 'stopRPC', - call: 'admin_stopRPC', - params: 0, - inputFormatter: [] - }), - new web3._extend.Method({ - name: 'setGlobalRegistrar', - call: 'admin_setGlobalRegistrar', - params: 2, - inputFormatter: [null,null] - }), - new web3._extend.Method({ - name: 'setHashReg', - call: 'admin_setHashReg', - params: 2, - inputFormatter: [null,null] - }), - new web3._extend.Method({ - name: 'setUrlHint', - call: 'admin_setUrlHint', - params: 2, - inputFormatter: [null,null] - }), - new web3._extend.Method({ - name: 'saveInfo', - call: 'admin_saveInfo', - params: 2, - inputFormatter: [null,null] - }), - new web3._extend.Method({ - name: 'register', - call: 'admin_register', - params: 3, - inputFormatter: [null,null,null] - }), - new web3._extend.Method({ - name: 'registerUrl', - call: 'admin_registerUrl', - params: 3, - inputFormatter: [null,null,null] - }), - new web3._extend.Method({ - name: 'startNatSpec', - call: 'admin_startNatSpec', - params: 0, - inputFormatter: [] - }), - new web3._extend.Method({ - name: 'stopNatSpec', - call: 'admin_stopNatSpec', - params: 0, - inputFormatter: [] - }), - new web3._extend.Method({ - name: 'getContractInfo', - call: 'admin_getContractInfo', - params: 1, - inputFormatter: [null], - }), - new web3._extend.Method({ - name: 'httpGet', - call: 'admin_httpGet', - params: 2, - inputFormatter: [null, null] - }) - ], - properties: - [ - new web3._extend.Property({ - name: 'nodeInfo', - getter: 'admin_nodeInfo' - }), - new web3._extend.Property({ - name: 'peers', - getter: 'admin_peers' - }), - new web3._extend.Property({ - name: 'datadir', - getter: 'admin_datadir' - }) - ] -}); -` diff --git a/rpc/api/api.go b/rpc/api/api.go deleted file mode 100644 index e03250ec6..000000000 --- a/rpc/api/api.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "github.com/ethereum/go-ethereum/rpc/shared" -) - -// Merge multiple API's to a single API instance -func Merge(apis ...shared.EthereumApi) shared.EthereumApi { - return newMergedApi(apis...) -} diff --git a/rpc/api/api_test.go b/rpc/api/api_test.go deleted file mode 100644 index eb63e8151..000000000 --- a/rpc/api/api_test.go +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "testing" - - "encoding/json" - "strconv" - - "github.com/ethereum/go-ethereum/common/compiler" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/shared" - "github.com/ethereum/go-ethereum/xeth" -) - -func TestParseApiString(t *testing.T) { - apis, err := ParseApiString("", codec.JSON, nil, nil) - if err == nil { - t.Errorf("Expected an err from parsing empty API string but got nil") - } - - if len(apis) != 0 { - t.Errorf("Expected 0 apis from empty API string") - } - - apis, err = ParseApiString("eth", codec.JSON, nil, nil) - if err != nil { - t.Errorf("Expected nil err from parsing empty API string but got %v", err) - } - - if len(apis) != 1 { - t.Errorf("Expected 1 apis but got %d - %v", apis, apis) - } - - apis, err = ParseApiString("eth,eth", codec.JSON, nil, nil) - if err != nil { - t.Errorf("Expected nil err from parsing empty API string but got \"%v\"", err) - } - - if len(apis) != 2 { - t.Errorf("Expected 2 apis but got %d - %v", apis, apis) - } - - apis, err = ParseApiString("eth,invalid", codec.JSON, nil, nil) - if err == nil { - t.Errorf("Expected an err but got no err") - } - -} - -const solcVersion = "0.9.23" - -func TestCompileSolidity(t *testing.T) { - - solc, err := compiler.New("") - if solc == nil { - t.Skip("no solc found: skip") - } else if solc.Version() != solcVersion { - t.Skip("WARNING: skipping test because of solc different version (%v, test written for %v, may need to update)", solc.Version(), solcVersion) - } - source := `contract test {\n` + - " /// @notice Will multiply `a` by 7." + `\n` + - ` function multiply(uint a) returns(uint d) {\n` + - ` return a * 7;\n` + - ` }\n` + - `}\n` - - jsonstr := `{"jsonrpc":"2.0","method":"eth_compileSolidity","params":["` + source + `"],"id":64}` - - expCode := "0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056" - expAbiDefinition := `[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]` - expUserDoc := `{"methods":{"multiply(uint256)":{"notice":"Will multiply ` + "`a`" + ` by 7."}}}` - expDeveloperDoc := `{"methods":{}}` - expCompilerVersion := solc.Version() - expLanguage := "Solidity" - expLanguageVersion := "0" - expSource := source - - eth := ð.Ethereum{} - xeth := xeth.NewTest(nil, nil) - api := NewEthApi(xeth, eth, codec.JSON) - - var rpcRequest shared.Request - json.Unmarshal([]byte(jsonstr), &rpcRequest) - - response, err := api.CompileSolidity(&rpcRequest) - if err != nil { - t.Errorf("Execution failed, %v", err) - } - - respjson, err := json.Marshal(response) - if err != nil { - t.Errorf("expected no error, got %v", err) - } - - var contracts = make(map[string]*compiler.Contract) - err = json.Unmarshal(respjson, &contracts) - if err != nil { - t.Errorf("expected no error, got %v", err) - } - - if len(contracts) != 1 { - t.Errorf("expected one contract, got %v", len(contracts)) - } - - contract := contracts["test"] - - if contract.Code != expCode { - t.Errorf("Expected \n%s got \n%s", expCode, contract.Code) - } - - if strconv.Quote(contract.Info.Source) != `"`+expSource+`"` { - t.Errorf("Expected \n'%s' got \n'%s'", expSource, strconv.Quote(contract.Info.Source)) - } - - if contract.Info.Language != expLanguage { - t.Errorf("Expected %s got %s", expLanguage, contract.Info.Language) - } - - if contract.Info.LanguageVersion != expLanguageVersion { - t.Errorf("Expected %s got %s", expLanguageVersion, contract.Info.LanguageVersion) - } - - if contract.Info.CompilerVersion != expCompilerVersion { - t.Errorf("Expected %s got %s", expCompilerVersion, contract.Info.CompilerVersion) - } - - userdoc, err := json.Marshal(contract.Info.UserDoc) - if err != nil { - t.Errorf("expected no error, got %v", err) - } - - devdoc, err := json.Marshal(contract.Info.DeveloperDoc) - if err != nil { - t.Errorf("expected no error, got %v", err) - } - - abidef, err := json.Marshal(contract.Info.AbiDefinition) - if err != nil { - t.Errorf("expected no error, got %v", err) - } - - if string(abidef) != expAbiDefinition { - t.Errorf("Expected \n'%s' got \n'%s'", expAbiDefinition, string(abidef)) - } - - if string(userdoc) != expUserDoc { - t.Errorf("Expected \n'%s' got \n'%s'", expUserDoc, string(userdoc)) - } - - if string(devdoc) != expDeveloperDoc { - t.Errorf("Expected %s got %s", expDeveloperDoc, string(devdoc)) - } -} diff --git a/rpc/api/args.go b/rpc/api/args.go deleted file mode 100644 index 20f073b67..000000000 --- a/rpc/api/args.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "encoding/json" - - "github.com/ethereum/go-ethereum/rpc/shared" -) - -type CompileArgs struct { - Source string -} - -func (args *CompileArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - argstr, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("arg0", "is not a string") - } - args.Source = argstr - - return nil -} - -type FilterStringArgs struct { - Word string -} - -func (args *FilterStringArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - var argstr string - argstr, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("filter", "not a string") - } - switch argstr { - case "latest", "pending": - break - default: - return shared.NewValidationError("Word", "Must be `latest` or `pending`") - } - args.Word = argstr - return nil -} diff --git a/rpc/api/args_test.go b/rpc/api/args_test.go deleted file mode 100644 index 130315bd9..000000000 --- a/rpc/api/args_test.go +++ /dev/null @@ -1,2649 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "bytes" - "encoding/json" - "fmt" - "math/big" - "testing" - - "github.com/ethereum/go-ethereum/rpc/shared" -) - -func TestBlockheightInvalidString(t *testing.T) { - v := "foo" - var num int64 - - str := ExpectInvalidTypeError(blockHeight(v, &num)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestBlockheightEarliest(t *testing.T) { - v := "earliest" - e := int64(0) - var num int64 - - err := blockHeight(v, &num) - if err != nil { - t.Error(err) - } - - if num != e { - t.Errorf("Expected %s but got %s", e, num) - } -} - -func TestBlockheightLatest(t *testing.T) { - v := "latest" - e := int64(-1) - var num int64 - - err := blockHeight(v, &num) - if err != nil { - t.Error(err) - } - - if num != e { - t.Errorf("Expected %s but got %s", e, num) - } -} - -func TestBlockheightPending(t *testing.T) { - v := "pending" - e := int64(-2) - var num int64 - - err := blockHeight(v, &num) - if err != nil { - t.Error(err) - } - - if num != e { - t.Errorf("Expected %s but got %s", e, num) - } -} - -func ExpectValidationError(err error) string { - var str string - switch err.(type) { - case nil: - str = "Expected error but didn't get one" - case *shared.ValidationError: - break - default: - str = fmt.Sprintf("Expected *rpc.ValidationError but got %T with message `%s`", err, err.Error()) - } - return str -} - -func ExpectInvalidTypeError(err error) string { - var str string - switch err.(type) { - case nil: - str = "Expected error but didn't get one" - case *shared.InvalidTypeError: - break - default: - str = fmt.Sprintf("Expected *rpc.InvalidTypeError but got %T with message `%s`", err, err.Error()) - } - return str -} - -func ExpectInsufficientParamsError(err error) string { - var str string - switch err.(type) { - case nil: - str = "Expected error but didn't get one" - case *shared.InsufficientParamsError: - break - default: - str = fmt.Sprintf("Expected *rpc.InsufficientParamsError but got %T with message %s", err, err.Error()) - } - return str -} - -func ExpectDecodeParamError(err error) string { - var str string - switch err.(type) { - case nil: - str = "Expected error but didn't get one" - case *shared.DecodeParamError: - break - default: - str = fmt.Sprintf("Expected *rpc.DecodeParamError but got %T with message `%s`", err, err.Error()) - } - return str -} - -func TestSha3(t *testing.T) { - input := `["0x68656c6c6f20776f726c64"]` - expected := "0x68656c6c6f20776f726c64" - - args := new(Sha3Args) - json.Unmarshal([]byte(input), &args) - - if args.Data != expected { - t.Error("got %s expected %s", input, expected) - } -} - -func TestSha3ArgsInvalid(t *testing.T) { - input := `{}` - - args := new(Sha3Args) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestSha3ArgsEmpty(t *testing.T) { - input := `[]` - - args := new(Sha3Args) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} -func TestSha3ArgsDataInvalid(t *testing.T) { - input := `[4]` - - args := new(Sha3Args) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetBalanceArgs(t *testing.T) { - input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "0x1f"]` - expected := new(GetBalanceArgs) - expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" - expected.BlockNumber = 31 - - args := new(GetBalanceArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if args.Address != expected.Address { - t.Errorf("Address should be %v but is %v", expected.Address, args.Address) - } - - if args.BlockNumber != expected.BlockNumber { - t.Errorf("BlockNumber should be %v but is %v", expected.BlockNumber, args.BlockNumber) - } -} - -func TestGetBalanceArgsBlocknumMissing(t *testing.T) { - input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1"]` - expected := new(GetBalanceArgs) - expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" - expected.BlockNumber = -1 - - args := new(GetBalanceArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if args.Address != expected.Address { - t.Errorf("Address should be %v but is %v", expected.Address, args.Address) - } - - if args.BlockNumber != expected.BlockNumber { - t.Errorf("BlockNumber should be %v but is %v", expected.BlockNumber, args.BlockNumber) - } -} - -func TestGetBalanceArgsLatest(t *testing.T) { - input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "latest"]` - expected := new(GetBalanceArgs) - expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" - expected.BlockNumber = -1 - - args := new(GetBalanceArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if args.Address != expected.Address { - t.Errorf("Address should be %v but is %v", expected.Address, args.Address) - } - - if args.BlockNumber != expected.BlockNumber { - t.Errorf("BlockNumber should be %v but is %v", expected.BlockNumber, args.BlockNumber) - } -} - -func TestGetBalanceArgsEmpty(t *testing.T) { - input := `[]` - - args := new(GetBalanceArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetBalanceArgsInvalid(t *testing.T) { - input := `6` - - args := new(GetBalanceArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetBalanceArgsBlockInvalid(t *testing.T) { - input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", false]` - - args := new(GetBalanceArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetBalanceArgsAddressInvalid(t *testing.T) { - input := `[-9, "latest"]` - - args := new(GetBalanceArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetBlockByHashArgs(t *testing.T) { - input := `["0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", true]` - expected := new(GetBlockByHashArgs) - expected.BlockHash = "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331" - expected.IncludeTxs = true - - args := new(GetBlockByHashArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if args.BlockHash != expected.BlockHash { - t.Errorf("BlockHash should be %v but is %v", expected.BlockHash, args.BlockHash) - } - - if args.IncludeTxs != expected.IncludeTxs { - t.Errorf("IncludeTxs should be %v but is %v", expected.IncludeTxs, args.IncludeTxs) - } -} - -func TestGetBlockByHashArgsEmpty(t *testing.T) { - input := `[]` - - args := new(GetBlockByHashArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetBlockByHashArgsInvalid(t *testing.T) { - input := `{}` - - args := new(GetBlockByHashArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetBlockByHashArgsHashInt(t *testing.T) { - input := `[8]` - - args := new(GetBlockByHashArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetBlockByHashArgsHashBool(t *testing.T) { - input := `[false, true]` - - args := new(GetBlockByHashArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetBlockByNumberArgsBlockNum(t *testing.T) { - input := `[436, false]` - expected := new(GetBlockByNumberArgs) - expected.BlockNumber = 436 - expected.IncludeTxs = false - - args := new(GetBlockByNumberArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if args.BlockNumber != expected.BlockNumber { - t.Errorf("BlockNumber should be %v but is %v", expected.BlockNumber, args.BlockNumber) - } - - if args.IncludeTxs != expected.IncludeTxs { - t.Errorf("IncludeTxs should be %v but is %v", expected.IncludeTxs, args.IncludeTxs) - } -} - -func TestGetBlockByNumberArgsBlockHex(t *testing.T) { - input := `["0x1b4", false]` - expected := new(GetBlockByNumberArgs) - expected.BlockNumber = 436 - expected.IncludeTxs = false - - args := new(GetBlockByNumberArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if args.BlockNumber != expected.BlockNumber { - t.Errorf("BlockNumber should be %v but is %v", expected.BlockNumber, args.BlockNumber) - } - - if args.IncludeTxs != expected.IncludeTxs { - t.Errorf("IncludeTxs should be %v but is %v", expected.IncludeTxs, args.IncludeTxs) - } -} -func TestGetBlockByNumberArgsWords(t *testing.T) { - input := `["earliest", true]` - expected := new(GetBlockByNumberArgs) - expected.BlockNumber = 0 - expected.IncludeTxs = true - - args := new(GetBlockByNumberArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if args.BlockNumber != expected.BlockNumber { - t.Errorf("BlockNumber should be %v but is %v", expected.BlockNumber, args.BlockNumber) - } - - if args.IncludeTxs != expected.IncludeTxs { - t.Errorf("IncludeTxs should be %v but is %v", expected.IncludeTxs, args.IncludeTxs) - } -} - -func TestGetBlockByNumberEmpty(t *testing.T) { - input := `[]` - - args := new(GetBlockByNumberArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetBlockByNumberShort(t *testing.T) { - input := `["0xbbb"]` - - args := new(GetBlockByNumberArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetBlockByNumberBool(t *testing.T) { - input := `[true, true]` - - args := new(GetBlockByNumberArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} -func TestGetBlockByNumberBlockObject(t *testing.T) { - input := `{}` - - args := new(GetBlockByNumberArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestNewTxArgs(t *testing.T) { - input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", - "gas": "0x76c0", - "gasPrice": "0x9184e72a000", - "value": "0x9184e72a000", - "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}, - "0x10"]` - expected := new(NewTxArgs) - expected.From = "0xb60e8dd61c5d32be8058bb8eb970870f07233155" - expected.To = "0xd46e8dd67c5d32be8058bb8eb970870f072445675" - expected.Gas = big.NewInt(30400) - expected.GasPrice = big.NewInt(10000000000000) - expected.Value = big.NewInt(10000000000000) - expected.Data = "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" - expected.BlockNumber = big.NewInt(16).Int64() - - args := new(NewTxArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.From != args.From { - t.Errorf("From shoud be %#v but is %#v", expected.From, args.From) - } - - if expected.To != args.To { - t.Errorf("To shoud be %#v but is %#v", expected.To, args.To) - } - - if bytes.Compare(expected.Gas.Bytes(), args.Gas.Bytes()) != 0 { - t.Errorf("Gas shoud be %#v but is %#v", expected.Gas.Bytes(), args.Gas.Bytes()) - } - - if bytes.Compare(expected.GasPrice.Bytes(), args.GasPrice.Bytes()) != 0 { - t.Errorf("GasPrice shoud be %#v but is %#v", expected.GasPrice, args.GasPrice) - } - - if bytes.Compare(expected.Value.Bytes(), args.Value.Bytes()) != 0 { - t.Errorf("Value shoud be %#v but is %#v", expected.Value, args.Value) - } - - if expected.Data != args.Data { - t.Errorf("Data shoud be %#v but is %#v", expected.Data, args.Data) - } - - if expected.BlockNumber != args.BlockNumber { - t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) - } -} - -func TestNewTxArgsInt(t *testing.T) { - input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", - "gas": 100, - "gasPrice": 50, - "value": 8765456789, - "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}, - 5]` - expected := new(NewTxArgs) - expected.Gas = big.NewInt(100) - expected.GasPrice = big.NewInt(50) - expected.Value = big.NewInt(8765456789) - expected.BlockNumber = int64(5) - - args := new(NewTxArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if bytes.Compare(expected.Gas.Bytes(), args.Gas.Bytes()) != 0 { - t.Errorf("Gas shoud be %v but is %v", expected.Gas, args.Gas) - } - - if bytes.Compare(expected.GasPrice.Bytes(), args.GasPrice.Bytes()) != 0 { - t.Errorf("GasPrice shoud be %v but is %v", expected.GasPrice, args.GasPrice) - } - - if bytes.Compare(expected.Value.Bytes(), args.Value.Bytes()) != 0 { - t.Errorf("Value shoud be %v but is %v", expected.Value, args.Value) - } - - if expected.BlockNumber != args.BlockNumber { - t.Errorf("BlockNumber shoud be %v but is %v", expected.BlockNumber, args.BlockNumber) - } -} - -func TestNewTxArgsBlockBool(t *testing.T) { - input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", - "gas": "0x76c0", - "gasPrice": "0x9184e72a000", - "value": "0x9184e72a000", - "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}, - false]` - - args := new(NewTxArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestNewTxArgsGasInvalid(t *testing.T) { - input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", - "gas": false, - "gasPrice": "0x9184e72a000", - "value": "0x9184e72a000", - "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" - }]` - - args := new(NewTxArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestNewTxArgsGaspriceInvalid(t *testing.T) { - input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", - "gas": "0x76c0", - "gasPrice": false, - "value": "0x9184e72a000", - "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" - }]` - - args := new(NewTxArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestNewTxArgsValueInvalid(t *testing.T) { - input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", - "gas": "0x76c0", - "gasPrice": "0x9184e72a000", - "value": false, - "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" - }]` - - args := new(NewTxArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestNewTxArgsGasMissing(t *testing.T) { - input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", - "gasPrice": "0x9184e72a000", - "value": "0x9184e72a000", - "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" - }]` - expected := new(NewTxArgs) - expected.Gas = nil - - args := new(NewTxArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if args.Gas != expected.Gas { - // if bytes.Compare(expected.Gas.Bytes(), args.Gas.Bytes()) != 0 { - t.Errorf("Gas shoud be %v but is %v", expected.Gas, args.Gas) - } -} - -func TestNewTxArgsBlockGaspriceMissing(t *testing.T) { - input := `[{ - "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", - "gas": "0x76c0", - "value": "0x9184e72a000", - "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" - }]` - expected := new(NewTxArgs) - expected.GasPrice = nil - - args := new(NewTxArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if args.GasPrice != expected.GasPrice { - // if bytes.Compare(expected.GasPrice.Bytes(), args.GasPrice.Bytes()) != 0 { - t.Errorf("GasPrice shoud be %v but is %v", expected.GasPrice, args.GasPrice) - } - -} - -func TestNewTxArgsValueMissing(t *testing.T) { - input := `[{ - "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", - "gas": "0x76c0", - "gasPrice": "0x9184e72a000", - "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" - }]` - expected := new(NewTxArgs) - expected.Value = big.NewInt(0) - - args := new(NewTxArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if bytes.Compare(expected.Value.Bytes(), args.Value.Bytes()) != 0 { - t.Errorf("Value shoud be %v but is %v", expected.Value, args.Value) - } - -} - -func TestNewTxArgsEmpty(t *testing.T) { - input := `[]` - - args := new(NewTxArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestNewTxArgsInvalid(t *testing.T) { - input := `{}` - - args := new(NewTxArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} -func TestNewTxArgsNotStrings(t *testing.T) { - input := `[{"from":6}]` - - args := new(NewTxArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestNewTxArgsFromEmpty(t *testing.T) { - input := `[{"to": "0xb60e8dd61c5d32be8058bb8eb970870f07233155"}]` - - args := new(NewTxArgs) - str := ExpectValidationError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestCallArgs(t *testing.T) { - input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", - "gas": "0x76c0", - "gasPrice": "0x9184e72a000", - "value": "0x9184e72a000", - "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}, - "0x10"]` - expected := new(CallArgs) - expected.From = "0xb60e8dd61c5d32be8058bb8eb970870f07233155" - expected.To = "0xd46e8dd67c5d32be8058bb8eb970870f072445675" - expected.Gas = big.NewInt(30400) - expected.GasPrice = big.NewInt(10000000000000) - expected.Value = big.NewInt(10000000000000) - expected.Data = "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" - expected.BlockNumber = big.NewInt(16).Int64() - - args := new(CallArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.To != args.To { - t.Errorf("To shoud be %#v but is %#v", expected.To, args.To) - } - - if bytes.Compare(expected.Gas.Bytes(), args.Gas.Bytes()) != 0 { - t.Errorf("Gas shoud be %#v but is %#v", expected.Gas.Bytes(), args.Gas.Bytes()) - } - - if bytes.Compare(expected.GasPrice.Bytes(), args.GasPrice.Bytes()) != 0 { - t.Errorf("GasPrice shoud be %#v but is %#v", expected.GasPrice, args.GasPrice) - } - - if bytes.Compare(expected.Value.Bytes(), args.Value.Bytes()) != 0 { - t.Errorf("Value shoud be %#v but is %#v", expected.Value, args.Value) - } - - if expected.Data != args.Data { - t.Errorf("Data shoud be %#v but is %#v", expected.Data, args.Data) - } - - if expected.BlockNumber != args.BlockNumber { - t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) - } -} - -func TestCallArgsInt(t *testing.T) { - input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", - "gas": 100, - "gasPrice": 50, - "value": 8765456789, - "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}, - 5]` - expected := new(CallArgs) - expected.Gas = big.NewInt(100) - expected.GasPrice = big.NewInt(50) - expected.Value = big.NewInt(8765456789) - expected.BlockNumber = int64(5) - - args := new(CallArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if bytes.Compare(expected.Gas.Bytes(), args.Gas.Bytes()) != 0 { - t.Errorf("Gas shoud be %v but is %v", expected.Gas, args.Gas) - } - - if bytes.Compare(expected.GasPrice.Bytes(), args.GasPrice.Bytes()) != 0 { - t.Errorf("GasPrice shoud be %v but is %v", expected.GasPrice, args.GasPrice) - } - - if bytes.Compare(expected.Value.Bytes(), args.Value.Bytes()) != 0 { - t.Errorf("Value shoud be %v but is %v", expected.Value, args.Value) - } - - if expected.BlockNumber != args.BlockNumber { - t.Errorf("BlockNumber shoud be %v but is %v", expected.BlockNumber, args.BlockNumber) - } -} - -func TestCallArgsBlockBool(t *testing.T) { - input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", - "gas": "0x76c0", - "gasPrice": "0x9184e72a000", - "value": "0x9184e72a000", - "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}, - false]` - - args := new(CallArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestCallArgsGasInvalid(t *testing.T) { - input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", - "gas": false, - "gasPrice": "0x9184e72a000", - "value": "0x9184e72a000", - "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" - }]` - - args := new(CallArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestCallArgsGaspriceInvalid(t *testing.T) { - input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", - "gas": "0x76c0", - "gasPrice": false, - "value": "0x9184e72a000", - "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" - }]` - - args := new(CallArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestCallArgsValueInvalid(t *testing.T) { - input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", - "gas": "0x76c0", - "gasPrice": "0x9184e72a000", - "value": false, - "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" - }]` - - args := new(CallArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestCallArgsGasMissing(t *testing.T) { - input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", - "gasPrice": "0x9184e72a000", - "value": "0x9184e72a000", - "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" - }]` - - args := new(CallArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - expected := new(CallArgs) - expected.Gas = nil - - if args.Gas != expected.Gas { - // if bytes.Compare(expected.Gas.Bytes(), args.Gas.Bytes()) != 0 { - t.Errorf("Gas shoud be %v but is %v", expected.Gas, args.Gas) - } - -} - -func TestCallArgsBlockGaspriceMissing(t *testing.T) { - input := `[{ - "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", - "gas": "0x76c0", - "value": "0x9184e72a000", - "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" - }]` - - args := new(CallArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - expected := new(CallArgs) - expected.GasPrice = nil - - if args.GasPrice != expected.GasPrice { - // if bytes.Compare(expected.GasPrice.Bytes(), args.GasPrice.Bytes()) != 0 { - t.Errorf("GasPrice shoud be %v but is %v", expected.GasPrice, args.GasPrice) - } -} - -func TestCallArgsValueMissing(t *testing.T) { - input := `[{ - "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", - "gas": "0x76c0", - "gasPrice": "0x9184e72a000", - "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" - }]` - - args := new(CallArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - expected := new(CallArgs) - expected.Value = big.NewInt(int64(0)) - - if bytes.Compare(expected.Value.Bytes(), args.Value.Bytes()) != 0 { - t.Errorf("GasPrice shoud be %v but is %v", expected.Value, args.Value) - } -} - -func TestCallArgsEmpty(t *testing.T) { - input := `[]` - - args := new(CallArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestCallArgsInvalid(t *testing.T) { - input := `{}` - - args := new(CallArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} -func TestCallArgsNotStrings(t *testing.T) { - input := `[{"from":6}]` - - args := new(CallArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestCallArgsToEmpty(t *testing.T) { - input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155"}]` - args := new(CallArgs) - err := json.Unmarshal([]byte(input), &args) - if err != nil { - t.Error("Did not expect error. Got", err) - } -} - -func TestGetStorageArgs(t *testing.T) { - input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "latest"]` - expected := new(GetStorageArgs) - expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" - expected.BlockNumber = -1 - - args := new(GetStorageArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.Address != args.Address { - t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) - } - - if expected.BlockNumber != args.BlockNumber { - t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) - } -} - -func TestGetStorageArgsMissingBlocknum(t *testing.T) { - input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1"]` - expected := new(GetStorageArgs) - expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" - expected.BlockNumber = -1 - - args := new(GetStorageArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.Address != args.Address { - t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) - } - - if expected.BlockNumber != args.BlockNumber { - t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) - } -} - -func TestGetStorageInvalidArgs(t *testing.T) { - input := `{}` - - args := new(GetStorageArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetStorageInvalidBlockheight(t *testing.T) { - input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", {}]` - - args := new(GetStorageArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetStorageEmptyArgs(t *testing.T) { - input := `[]` - - args := new(GetStorageArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetStorageAddressInt(t *testing.T) { - input := `[32456785432456, "latest"]` - - args := new(GetStorageArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetStorageAtArgs(t *testing.T) { - input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "0x0", "0x2"]` - expected := new(GetStorageAtArgs) - expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" - expected.Key = "0x0" - expected.BlockNumber = 2 - - args := new(GetStorageAtArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.Address != args.Address { - t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) - } - - if expected.Key != args.Key { - t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) - } - - if expected.BlockNumber != args.BlockNumber { - t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) - } -} - -func TestGetStorageAtArgsMissingBlocknum(t *testing.T) { - input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "0x0"]` - expected := new(GetStorageAtArgs) - expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" - expected.Key = "0x0" - expected.BlockNumber = -1 - - args := new(GetStorageAtArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.Address != args.Address { - t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) - } - - if expected.Key != args.Key { - t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) - } - - if expected.BlockNumber != args.BlockNumber { - t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) - } -} - -func TestGetStorageAtEmptyArgs(t *testing.T) { - input := `[]` - - args := new(GetStorageAtArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetStorageAtArgsInvalid(t *testing.T) { - input := `{}` - - args := new(GetStorageAtArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetStorageAtArgsAddressNotString(t *testing.T) { - input := `[true, "0x0", "0x2"]` - - args := new(GetStorageAtArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetStorageAtArgsKeyNotString(t *testing.T) { - input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", true, "0x2"]` - - args := new(GetStorageAtArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetStorageAtArgsValueNotString(t *testing.T) { - input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "0x1", true]` - - args := new(GetStorageAtArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetTxCountArgs(t *testing.T) { - input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "pending"]` - expected := new(GetTxCountArgs) - expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" - expected.BlockNumber = -2 - - args := new(GetTxCountArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.Address != args.Address { - t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) - } - - if expected.BlockNumber != args.BlockNumber { - t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) - } -} - -func TestGetTxCountEmptyArgs(t *testing.T) { - input := `[]` - - args := new(GetTxCountArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetTxCountEmptyArgsInvalid(t *testing.T) { - input := `false` - - args := new(GetTxCountArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetTxCountAddressNotString(t *testing.T) { - input := `[false, "pending"]` - - args := new(GetTxCountArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetTxCountBlockheightMissing(t *testing.T) { - input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1"]` - expected := new(GetTxCountArgs) - expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" - expected.BlockNumber = -1 - - args := new(GetTxCountArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.Address != args.Address { - t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) - } - - if expected.BlockNumber != args.BlockNumber { - t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) - } -} - -func TestGetTxCountBlockheightInvalid(t *testing.T) { - input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", {}]` - - args := new(GetTxCountArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetDataArgs(t *testing.T) { - input := `["0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", "latest"]` - expected := new(GetDataArgs) - expected.Address = "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8" - expected.BlockNumber = -1 - - args := new(GetDataArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.Address != args.Address { - t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) - } - - if expected.BlockNumber != args.BlockNumber { - t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) - } -} - -func TestGetDataArgsBlocknumMissing(t *testing.T) { - input := `["0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8"]` - expected := new(GetDataArgs) - expected.Address = "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8" - expected.BlockNumber = -1 - - args := new(GetDataArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.Address != args.Address { - t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) - } - - if expected.BlockNumber != args.BlockNumber { - t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) - } -} - -func TestGetDataArgsEmpty(t *testing.T) { - input := `[]` - - args := new(GetDataArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetDataArgsInvalid(t *testing.T) { - input := `{}` - - args := new(GetDataArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetDataArgsAddressNotString(t *testing.T) { - input := `[12, "latest"]` - - args := new(GetDataArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestGetDataArgsBlocknumberNotString(t *testing.T) { - input := `["0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", false]` - - args := new(GetDataArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestBlockFilterArgs(t *testing.T) { - input := `[{ - "fromBlock": "0x1", - "toBlock": "0x2", - "limit": "0x3", - "offset": "0x0", - "address": "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", - "topics": - [ - ["0xAA", "0xBB"], - ["0xCC", "0xDD"] - ] - }]` - - expected := new(BlockFilterArgs) - expected.Earliest = 1 - expected.Latest = 2 - expected.Max = 3 - expected.Skip = 0 - expected.Address = []string{"0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8"} - expected.Topics = [][]string{ - []string{"0xAA", "0xBB"}, - []string{"0xCC", "0xDD"}, - } - - args := new(BlockFilterArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.Earliest != args.Earliest { - t.Errorf("Earliest shoud be %#v but is %#v", expected.Earliest, args.Earliest) - } - - if expected.Latest != args.Latest { - t.Errorf("Latest shoud be %#v but is %#v", expected.Latest, args.Latest) - } - - if expected.Max != args.Max { - t.Errorf("Max shoud be %#v but is %#v", expected.Max, args.Max) - } - - if expected.Skip != args.Skip { - t.Errorf("Skip shoud be %#v but is %#v", expected.Skip, args.Skip) - } - - if expected.Address[0] != args.Address[0] { - t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) - } - - if expected.Topics[0][0] != args.Topics[0][0] { - t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) - } - if expected.Topics[0][1] != args.Topics[0][1] { - t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) - } - if expected.Topics[1][0] != args.Topics[1][0] { - t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) - } - if expected.Topics[1][1] != args.Topics[1][1] { - t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) - } - -} - -func TestBlockFilterArgsDefaults(t *testing.T) { - input := `[{ - "address": ["0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8"], - "topics": ["0xAA","0xBB"] - }]` - expected := new(BlockFilterArgs) - expected.Earliest = -1 - expected.Latest = -1 - expected.Max = 100 - expected.Skip = 0 - expected.Address = []string{"0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8"} - expected.Topics = [][]string{[]string{"0xAA"}, []string{"0xBB"}} - - args := new(BlockFilterArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.Earliest != args.Earliest { - t.Errorf("Earliest shoud be %#v but is %#v", expected.Earliest, args.Earliest) - } - - if expected.Latest != args.Latest { - t.Errorf("Latest shoud be %#v but is %#v", expected.Latest, args.Latest) - } - - if expected.Max != args.Max { - t.Errorf("Max shoud be %#v but is %#v", expected.Max, args.Max) - } - - if expected.Skip != args.Skip { - t.Errorf("Skip shoud be %#v but is %#v", expected.Skip, args.Skip) - } - - if expected.Address[0] != args.Address[0] { - t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) - } - - if expected.Topics[0][0] != args.Topics[0][0] { - t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) - } - - if expected.Topics[1][0] != args.Topics[1][0] { - t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) - } -} - -func TestBlockFilterArgsWords(t *testing.T) { - input := `[{"fromBlock": "latest", "toBlock": "latest"}]` - expected := new(BlockFilterArgs) - expected.Earliest = -1 - expected.Latest = -1 - - args := new(BlockFilterArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.Earliest != args.Earliest { - t.Errorf("Earliest shoud be %#v but is %#v", expected.Earliest, args.Earliest) - } - - input = `[{"toBlock": "pending"}]` - if err := json.Unmarshal([]byte(input), &args); err == nil { - t.Errorf("Pending isn't currently supported and should raise an unsupported error") - } -} - -func TestBlockFilterArgsInvalid(t *testing.T) { - input := `{}` - - args := new(BlockFilterArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestBlockFilterArgsFromBool(t *testing.T) { - input := `[{ - "fromBlock": true, - "toBlock": "pending" - }]` - - args := new(BlockFilterArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestBlockFilterArgsToBool(t *testing.T) { - input := `[{ - "fromBlock": "pending", - "toBlock": true - }]` - - args := new(BlockFilterArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestBlockFilterArgsEmptyArgs(t *testing.T) { - input := `[]` - - args := new(BlockFilterArgs) - err := json.Unmarshal([]byte(input), &args) - if err == nil { - t.Error("Expected error but didn't get one") - } -} - -func TestBlockFilterArgsLimitInvalid(t *testing.T) { - input := `[{"limit": false}]` - - args := new(BlockFilterArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestBlockFilterArgsOffsetInvalid(t *testing.T) { - input := `[{"offset": true}]` - - args := new(BlockFilterArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestBlockFilterArgsAddressInt(t *testing.T) { - input := `[{ - "address": 1, - "topics": "0x12341234"}]` - - args := new(BlockFilterArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestBlockFilterArgsAddressSliceInt(t *testing.T) { - input := `[{ - "address": [1], - "topics": "0x12341234"}]` - - args := new(BlockFilterArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestBlockFilterArgsTopicInt(t *testing.T) { - input := `[{ - "address": ["0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8"], - "topics": 1}]` - - args := new(BlockFilterArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestBlockFilterArgsTopicSliceInt(t *testing.T) { - input := `[{ - "address": "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", - "topics": [1]}]` - - args := new(BlockFilterArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestBlockFilterArgsTopicSliceInt2(t *testing.T) { - input := `[{ - "address": "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", - "topics": ["0xAA", [1]]}]` - - args := new(BlockFilterArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestBlockFilterArgsTopicComplex(t *testing.T) { - input := `[{ - "address": "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", - "topics": ["0xAA", ["0xBB", "0xCC"]] - }]` - - args := new(BlockFilterArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - fmt.Printf("%v\n", args) - return - } - - if args.Topics[0][0] != "0xAA" { - t.Errorf("Topic should be %s but is %s", "0xAA", args.Topics[0][0]) - } - - if args.Topics[1][0] != "0xBB" { - t.Errorf("Topic should be %s but is %s", "0xBB", args.Topics[0][0]) - } - - if args.Topics[1][1] != "0xCC" { - t.Errorf("Topic should be %s but is %s", "0xCC", args.Topics[0][0]) - } -} - -func TestDbArgs(t *testing.T) { - input := `["testDB","myKey","0xbeef"]` - expected := new(DbArgs) - expected.Database = "testDB" - expected.Key = "myKey" - expected.Value = []byte("0xbeef") - - args := new(DbArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if err := args.requirements(); err != nil { - t.Error(err) - } - - if expected.Database != args.Database { - t.Errorf("Database shoud be %#v but is %#v", expected.Database, args.Database) - } - - if expected.Key != args.Key { - t.Errorf("Key shoud be %#v but is %#v", expected.Key, args.Key) - } - - if bytes.Compare(expected.Value, args.Value) != 0 { - t.Errorf("Value shoud be %#v but is %#v", expected.Value, args.Value) - } -} - -func TestDbArgsInvalid(t *testing.T) { - input := `{}` - - args := new(DbArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestDbArgsEmpty(t *testing.T) { - input := `[]` - - args := new(DbArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestDbArgsDatabaseType(t *testing.T) { - input := `[true, "keyval", "valval"]` - - args := new(DbArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestDbArgsKeyType(t *testing.T) { - input := `["dbval", 3, "valval"]` - - args := new(DbArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestDbArgsValType(t *testing.T) { - input := `["dbval", "keyval", {}]` - - args := new(DbArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestDbArgsDatabaseEmpty(t *testing.T) { - input := `["", "keyval", "valval"]` - - args := new(DbArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err.Error()) - } - - str := ExpectValidationError(args.requirements()) - if len(str) > 0 { - t.Error(str) - } -} - -func TestDbArgsKeyEmpty(t *testing.T) { - input := `["dbval", "", "valval"]` - - args := new(DbArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err.Error()) - } - - str := ExpectValidationError(args.requirements()) - if len(str) > 0 { - t.Error(str) - } -} - -func TestDbHexArgs(t *testing.T) { - input := `["testDB","myKey","0xbeef"]` - expected := new(DbHexArgs) - expected.Database = "testDB" - expected.Key = "myKey" - expected.Value = []byte{0xbe, 0xef} - - args := new(DbHexArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if err := args.requirements(); err != nil { - t.Error(err) - } - - if expected.Database != args.Database { - t.Errorf("Database shoud be %#v but is %#v", expected.Database, args.Database) - } - - if expected.Key != args.Key { - t.Errorf("Key shoud be %#v but is %#v", expected.Key, args.Key) - } - - if bytes.Compare(expected.Value, args.Value) != 0 { - t.Errorf("Value shoud be %#v but is %#v", expected.Value, args.Value) - } -} - -func TestDbHexArgsInvalid(t *testing.T) { - input := `{}` - - args := new(DbHexArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestDbHexArgsEmpty(t *testing.T) { - input := `[]` - - args := new(DbHexArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestDbHexArgsDatabaseType(t *testing.T) { - input := `[true, "keyval", "valval"]` - - args := new(DbHexArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestDbHexArgsKeyType(t *testing.T) { - input := `["dbval", 3, "valval"]` - - args := new(DbHexArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestDbHexArgsValType(t *testing.T) { - input := `["dbval", "keyval", {}]` - - args := new(DbHexArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestDbHexArgsDatabaseEmpty(t *testing.T) { - input := `["", "keyval", "valval"]` - - args := new(DbHexArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err.Error()) - } - - str := ExpectValidationError(args.requirements()) - if len(str) > 0 { - t.Error(str) - } -} - -func TestDbHexArgsKeyEmpty(t *testing.T) { - input := `["dbval", "", "valval"]` - - args := new(DbHexArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err.Error()) - } - - str := ExpectValidationError(args.requirements()) - if len(str) > 0 { - t.Error(str) - } -} - -func TestWhisperMessageArgs(t *testing.T) { - input := `[{"from":"0xc931d93e97ab07fe42d923478ba2465f2", - "topics": ["0x68656c6c6f20776f726c64"], - "payload":"0x68656c6c6f20776f726c64", - "ttl": "0x64", - "priority": "0x64"}]` - expected := new(WhisperMessageArgs) - expected.From = "0xc931d93e97ab07fe42d923478ba2465f2" - expected.To = "" - expected.Payload = "0x68656c6c6f20776f726c64" - expected.Priority = 100 - expected.Ttl = 100 - // expected.Topics = []string{"0x68656c6c6f20776f726c64"} - - args := new(WhisperMessageArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.From != args.From { - t.Errorf("From shoud be %#v but is %#v", expected.From, args.From) - } - - if expected.To != args.To { - t.Errorf("To shoud be %#v but is %#v", expected.To, args.To) - } - - if expected.Payload != args.Payload { - t.Errorf("Value shoud be %#v but is %#v", expected.Payload, args.Payload) - } - - if expected.Ttl != args.Ttl { - t.Errorf("Ttl shoud be %#v but is %#v", expected.Ttl, args.Ttl) - } - - if expected.Priority != args.Priority { - t.Errorf("Priority shoud be %#v but is %#v", expected.Priority, args.Priority) - } - - // if expected.Topics != args.Topics { - // t.Errorf("Topic shoud be %#v but is %#v", expected.Topic, args.Topic) - // } -} - -func TestWhisperMessageArgsInt(t *testing.T) { - input := `[{"from":"0xc931d93e97ab07fe42d923478ba2465f2", - "topics": ["0x68656c6c6f20776f726c64"], - "payload":"0x68656c6c6f20776f726c64", - "ttl": 12, - "priority": 16}]` - expected := new(WhisperMessageArgs) - expected.From = "0xc931d93e97ab07fe42d923478ba2465f2" - expected.To = "" - expected.Payload = "0x68656c6c6f20776f726c64" - expected.Priority = 16 - expected.Ttl = 12 - // expected.Topics = []string{"0x68656c6c6f20776f726c64"} - - args := new(WhisperMessageArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.From != args.From { - t.Errorf("From shoud be %#v but is %#v", expected.From, args.From) - } - - if expected.To != args.To { - t.Errorf("To shoud be %#v but is %#v", expected.To, args.To) - } - - if expected.Payload != args.Payload { - t.Errorf("Value shoud be %#v but is %#v", expected.Payload, args.Payload) - } - - if expected.Ttl != args.Ttl { - t.Errorf("Ttl shoud be %v but is %v", expected.Ttl, args.Ttl) - } - - if expected.Priority != args.Priority { - t.Errorf("Priority shoud be %v but is %v", expected.Priority, args.Priority) - } - - // if expected.Topics != args.Topics { - // t.Errorf("Topic shoud be %#v but is %#v", expected.Topic, args.Topic) - // } -} - -func TestWhisperMessageArgsInvalid(t *testing.T) { - input := `{}` - - args := new(WhisperMessageArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestWhisperMessageArgsEmpty(t *testing.T) { - input := `[]` - - args := new(WhisperMessageArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestWhisperMessageArgsTtlBool(t *testing.T) { - input := `[{"from":"0xc931d93e97ab07fe42d923478ba2465f2", - "topics": ["0x68656c6c6f20776f726c64"], - "payload":"0x68656c6c6f20776f726c64", - "ttl": true, - "priority": "0x64"}]` - args := new(WhisperMessageArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestWhisperMessageArgsPriorityBool(t *testing.T) { - input := `[{"from":"0xc931d93e97ab07fe42d923478ba2465f2", - "topics": ["0x68656c6c6f20776f726c64"], - "payload":"0x68656c6c6f20776f726c64", - "ttl": "0x12", - "priority": true}]` - args := new(WhisperMessageArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestFilterIdArgs(t *testing.T) { - input := `["0x7"]` - expected := new(FilterIdArgs) - expected.Id = 7 - - args := new(FilterIdArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.Id != args.Id { - t.Errorf("Id shoud be %#v but is %#v", expected.Id, args.Id) - } -} - -func TestFilterIdArgsInvalid(t *testing.T) { - input := `{}` - - args := new(FilterIdArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Errorf(str) - } -} - -func TestFilterIdArgsEmpty(t *testing.T) { - input := `[]` - - args := new(FilterIdArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Errorf(str) - } -} - -func TestFilterIdArgsBool(t *testing.T) { - input := `[true]` - - args := new(FilterIdArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Errorf(str) - } -} - -func TestWhisperFilterArgs(t *testing.T) { - input := `[{"topics": ["0x68656c6c6f20776f726c64"], "to": "0x34ag445g3455b34"}]` - expected := new(WhisperFilterArgs) - expected.To = "0x34ag445g3455b34" - expected.Topics = [][]string{[]string{"0x68656c6c6f20776f726c64"}} - - args := new(WhisperFilterArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.To != args.To { - t.Errorf("To shoud be %#v but is %#v", expected.To, args.To) - } - - // if expected.Topics != args.Topics { - // t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) - // } -} - -func TestWhisperFilterArgsInvalid(t *testing.T) { - input := `{}` - - args := new(WhisperFilterArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestWhisperFilterArgsEmpty(t *testing.T) { - input := `[]` - - args := new(WhisperFilterArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestWhisperFilterArgsToInt(t *testing.T) { - input := `[{"to": 2}]` - - args := new(WhisperFilterArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestWhisperFilterArgsToBool(t *testing.T) { - input := `[{"topics": ["0x68656c6c6f20776f726c64"], "to": false}]` - - args := new(WhisperFilterArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestWhisperFilterArgsToMissing(t *testing.T) { - input := `[{"topics": ["0x68656c6c6f20776f726c64"]}]` - expected := new(WhisperFilterArgs) - expected.To = "" - - args := new(WhisperFilterArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if args.To != expected.To { - t.Errorf("To shoud be %v but is %v", expected.To, args.To) - } -} - -func TestWhisperFilterArgsTopicInt(t *testing.T) { - input := `[{"topics": [6], "to": "0x34ag445g3455b34"}]` - - args := new(WhisperFilterArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestCompileArgs(t *testing.T) { - input := `["contract test { function multiply(uint a) returns(uint d) { return a * 7; } }"]` - expected := new(CompileArgs) - expected.Source = `contract test { function multiply(uint a) returns(uint d) { return a * 7; } }` - - args := new(CompileArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.Source != args.Source { - t.Errorf("Source shoud be %#v but is %#v", expected.Source, args.Source) - } -} - -func TestCompileArgsInvalid(t *testing.T) { - input := `{}` - - args := new(CompileArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestCompileArgsEmpty(t *testing.T) { - input := `[]` - - args := new(CompileArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestCompileArgsBool(t *testing.T) { - input := `[false]` - - args := new(CompileArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestFilterStringArgs(t *testing.T) { - input := `["pending"]` - expected := new(FilterStringArgs) - expected.Word = "pending" - - args := new(FilterStringArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.Word != args.Word { - t.Errorf("Word shoud be %#v but is %#v", expected.Word, args.Word) - } -} - -func TestFilterStringEmptyArgs(t *testing.T) { - input := `[]` - - args := new(FilterStringArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Errorf(str) - } -} - -func TestFilterStringInvalidArgs(t *testing.T) { - input := `{}` - - args := new(FilterStringArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Errorf(str) - } -} - -func TestFilterStringWordInt(t *testing.T) { - input := `[7]` - - args := new(FilterStringArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Errorf(str) - } -} - -func TestFilterStringWordWrong(t *testing.T) { - input := `["foo"]` - - args := new(FilterStringArgs) - str := ExpectValidationError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Errorf(str) - } -} - -func TestWhisperIdentityArgs(t *testing.T) { - input := `["0xc931d93e97ab07fe42d923478ba2465f283"]` - expected := new(WhisperIdentityArgs) - expected.Identity = "0xc931d93e97ab07fe42d923478ba2465f283" - - args := new(WhisperIdentityArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.Identity != args.Identity { - t.Errorf("Identity shoud be %#v but is %#v", expected.Identity, args.Identity) - } -} - -func TestWhisperIdentityArgsInvalid(t *testing.T) { - input := `{}` - - args := new(WhisperIdentityArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Errorf(str) - } -} - -func TestWhisperIdentityArgsEmpty(t *testing.T) { - input := `[]` - - args := new(WhisperIdentityArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Errorf(str) - } -} - -func TestWhisperIdentityArgsInt(t *testing.T) { - input := `[4]` - - args := new(WhisperIdentityArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Errorf(str) - } -} - -func TestBlockNumArgs(t *testing.T) { - input := `["0x29a"]` - expected := new(BlockNumIndexArgs) - expected.BlockNumber = 666 - - args := new(BlockNumArg) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.BlockNumber != args.BlockNumber { - t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) - } -} - -func TestBlockNumArgsWord(t *testing.T) { - input := `["pending"]` - expected := new(BlockNumIndexArgs) - expected.BlockNumber = -2 - - args := new(BlockNumArg) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.BlockNumber != args.BlockNumber { - t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) - } -} - -func TestBlockNumArgsInvalid(t *testing.T) { - input := `{}` - - args := new(BlockNumArg) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestBlockNumArgsEmpty(t *testing.T) { - input := `[]` - - args := new(BlockNumArg) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} -func TestBlockNumArgsBool(t *testing.T) { - input := `[true]` - - args := new(BlockNumArg) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestBlockNumIndexArgs(t *testing.T) { - input := `["0x29a", "0x0"]` - expected := new(BlockNumIndexArgs) - expected.BlockNumber = 666 - expected.Index = 0 - - args := new(BlockNumIndexArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.BlockNumber != args.BlockNumber { - t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) - } - - if expected.Index != args.Index { - t.Errorf("Index shoud be %#v but is %#v", expected.Index, args.Index) - } -} - -func TestBlockNumIndexArgsWord(t *testing.T) { - input := `["latest", 67]` - expected := new(BlockNumIndexArgs) - expected.BlockNumber = -1 - expected.Index = 67 - - args := new(BlockNumIndexArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.BlockNumber != args.BlockNumber { - t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) - } - - if expected.Index != args.Index { - t.Errorf("Index shoud be %#v but is %#v", expected.Index, args.Index) - } -} - -func TestBlockNumIndexArgsEmpty(t *testing.T) { - input := `[]` - - args := new(BlockNumIndexArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestBlockNumIndexArgsInvalid(t *testing.T) { - input := `"foo"` - - args := new(BlockNumIndexArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestBlockNumIndexArgsBlocknumInvalid(t *testing.T) { - input := `[{}, "0x1"]` - - args := new(BlockNumIndexArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestBlockNumIndexArgsIndexInvalid(t *testing.T) { - input := `["0x29a", true]` - - args := new(BlockNumIndexArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestHashIndexArgs(t *testing.T) { - input := `["0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b", "0x1"]` - expected := new(HashIndexArgs) - expected.Hash = "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b" - expected.Index = 1 - - args := new(HashIndexArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.Hash != args.Hash { - t.Errorf("Hash shoud be %#v but is %#v", expected.Hash, args.Hash) - } - - if expected.Index != args.Index { - t.Errorf("Index shoud be %#v but is %#v", expected.Index, args.Index) - } -} - -func TestHashIndexArgsEmpty(t *testing.T) { - input := `[]` - - args := new(HashIndexArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestHashIndexArgsInvalid(t *testing.T) { - input := `{}` - - args := new(HashIndexArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestHashIndexArgsInvalidHash(t *testing.T) { - input := `[7, "0x1"]` - - args := new(HashIndexArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestHashIndexArgsInvalidIndex(t *testing.T) { - input := `["0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b", false]` - - args := new(HashIndexArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestHashArgs(t *testing.T) { - input := `["0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b"]` - expected := new(HashIndexArgs) - expected.Hash = "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b" - - args := new(HashArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.Hash != args.Hash { - t.Errorf("Hash shoud be %#v but is %#v", expected.Hash, args.Hash) - } -} - -func TestHashArgsEmpty(t *testing.T) { - input := `[]` - - args := new(HashArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestHashArgsInvalid(t *testing.T) { - input := `{}` - - args := new(HashArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestHashArgsInvalidHash(t *testing.T) { - input := `[7]` - - args := new(HashArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestSubmitWorkArgs(t *testing.T) { - input := `["0x0000000000000001", "0x1234567890abcdef1234567890abcdef", "0xD1GE5700000000000000000000000000"]` - expected := new(SubmitWorkArgs) - expected.Nonce = 1 - expected.Header = "0x1234567890abcdef1234567890abcdef" - expected.Digest = "0xD1GE5700000000000000000000000000" - - args := new(SubmitWorkArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.Nonce != args.Nonce { - t.Errorf("Nonce shoud be %d but is %d", expected.Nonce, args.Nonce) - } - - if expected.Header != args.Header { - t.Errorf("Header shoud be %#v but is %#v", expected.Header, args.Header) - } - - if expected.Digest != args.Digest { - t.Errorf("Digest shoud be %#v but is %#v", expected.Digest, args.Digest) - } -} - -func TestSubmitWorkArgsInvalid(t *testing.T) { - input := `{}` - - args := new(SubmitWorkArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestSubmitWorkArgsEmpty(t *testing.T) { - input := `[]` - - args := new(SubmitWorkArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestSubmitWorkArgsNonceInt(t *testing.T) { - input := `[1, "0x1234567890abcdef1234567890abcdef", "0xD1GE5700000000000000000000000000"]` - - args := new(SubmitWorkArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} -func TestSubmitWorkArgsHeaderInt(t *testing.T) { - input := `["0x0000000000000001", 1, "0xD1GE5700000000000000000000000000"]` - - args := new(SubmitWorkArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} -func TestSubmitWorkArgsDigestInt(t *testing.T) { - input := `["0x0000000000000001", "0x1234567890abcdef1234567890abcdef", 1]` - - args := new(SubmitWorkArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestBlockHeightFromJsonInvalid(t *testing.T) { - var num int64 - var msg json.RawMessage = []byte(`}{`) - str := ExpectDecodeParamError(blockHeightFromJson(msg, &num)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestSourceArgsEmpty(t *testing.T) { - input := `[]` - - args := new(SourceArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), &args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestSigArgs(t *testing.T) { - input := `["0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", "0x0"]` - expected := new(NewSigArgs) - expected.From = "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" - expected.Data = "0x0" - - args := new(NewSigArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.From != args.From { - t.Errorf("From should be %v but is %v", expected.From, args.From) - } - - if expected.Data != args.Data { - t.Errorf("Data should be %v but is %v", expected.Data, args.Data) - } -} - -func TestSigArgsEmptyData(t *testing.T) { - input := `["0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", ""]` - - args := new(NewSigArgs) - str := ExpectValidationError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestSigArgsDataType(t *testing.T) { - input := `["0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", 13]` - - args := new(NewSigArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestSigArgsEmptyFrom(t *testing.T) { - input := `["", "0x0"]` - - args := new(NewSigArgs) - str := ExpectValidationError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestSigArgsFromType(t *testing.T) { - input := `[false, "0x0"]` - - args := new(NewSigArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestSigArgsEmpty(t *testing.T) { - input := `[]` - args := new(NewSigArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestDataArgs(t *testing.T) { - input := `["0x0123"]` - expected := new(NewDataArgs) - expected.Data = "0x0123" - - args := new(NewDataArgs) - if err := json.Unmarshal([]byte(input), &args); err != nil { - t.Error(err) - } - - if expected.Data != args.Data { - t.Errorf("Data should be %v but is %v", expected.Data, args.Data) - } -} - -func TestDataArgsEmptyData(t *testing.T) { - input := `[""]` - - args := new(NewDataArgs) - str := ExpectValidationError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestDataArgsDataType(t *testing.T) { - input := `[13]` - - args := new(NewDataArgs) - str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestDataArgsEmpty(t *testing.T) { - input := `[]` - args := new(NewDataArgs) - str := ExpectInsufficientParamsError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} - -func TestDataArgsInvalid(t *testing.T) { - input := `{}` - - args := new(NewDataArgs) - str := ExpectDecodeParamError(json.Unmarshal([]byte(input), args)) - if len(str) > 0 { - t.Error(str) - } -} diff --git a/rpc/api/db.go b/rpc/api/db.go deleted file mode 100644 index 0eddc410e..000000000 --- a/rpc/api/db.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/shared" - "github.com/ethereum/go-ethereum/xeth" -) - -const ( - DbApiversion = "1.0" -) - -var ( - // mapping between methods and handlers - DbMapping = map[string]dbhandler{ - "db_getString": (*dbApi).GetString, - "db_putString": (*dbApi).PutString, - "db_getHex": (*dbApi).GetHex, - "db_putHex": (*dbApi).PutHex, - } -) - -// db callback handler -type dbhandler func(*dbApi, *shared.Request) (interface{}, error) - -// db api provider -type dbApi struct { - xeth *xeth.XEth - ethereum *eth.Ethereum - methods map[string]dbhandler - codec codec.ApiCoder -} - -// create a new db api instance -func NewDbApi(xeth *xeth.XEth, ethereum *eth.Ethereum, coder codec.Codec) *dbApi { - return &dbApi{ - xeth: xeth, - ethereum: ethereum, - methods: DbMapping, - codec: coder.New(nil), - } -} - -// collection with supported methods -func (self *dbApi) Methods() []string { - methods := make([]string, len(self.methods)) - i := 0 - for k := range self.methods { - methods[i] = k - i++ - } - return methods -} - -// Execute given request -func (self *dbApi) Execute(req *shared.Request) (interface{}, error) { - if callback, ok := self.methods[req.Method]; ok { - return callback(self, req) - } - - return nil, &shared.NotImplementedError{req.Method} -} - -func (self *dbApi) Name() string { - return shared.DbApiName -} - -func (self *dbApi) ApiVersion() string { - return DbApiversion -} - -func (self *dbApi) GetString(req *shared.Request) (interface{}, error) { - args := new(DbArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - if err := args.requirements(); err != nil { - return nil, err - } - - ret, err := self.xeth.DbGet([]byte(args.Database + args.Key)) - return string(ret), err -} - -func (self *dbApi) PutString(req *shared.Request) (interface{}, error) { - args := new(DbArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - if err := args.requirements(); err != nil { - return nil, err - } - - return self.xeth.DbPut([]byte(args.Database+args.Key), args.Value), nil -} - -func (self *dbApi) GetHex(req *shared.Request) (interface{}, error) { - args := new(DbHexArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - if err := args.requirements(); err != nil { - return nil, err - } - - if res, err := self.xeth.DbGet([]byte(args.Database + args.Key)); err == nil { - return newHexData(res), nil - } else { - return nil, err - } -} - -func (self *dbApi) PutHex(req *shared.Request) (interface{}, error) { - args := new(DbHexArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - if err := args.requirements(); err != nil { - return nil, err - } - - return self.xeth.DbPut([]byte(args.Database+args.Key), args.Value), nil -} diff --git a/rpc/api/db_args.go b/rpc/api/db_args.go deleted file mode 100644 index d61ea77ee..000000000 --- a/rpc/api/db_args.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "encoding/json" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/rpc/shared" -) - -type DbArgs struct { - Database string - Key string - Value []byte -} - -func (args *DbArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 2 { - return shared.NewInsufficientParamsError(len(obj), 2) - } - - var objstr string - var ok bool - - if objstr, ok = obj[0].(string); !ok { - return shared.NewInvalidTypeError("database", "not a string") - } - args.Database = objstr - - if objstr, ok = obj[1].(string); !ok { - return shared.NewInvalidTypeError("key", "not a string") - } - args.Key = objstr - - if len(obj) > 2 { - objstr, ok = obj[2].(string) - if !ok { - return shared.NewInvalidTypeError("value", "not a string") - } - - args.Value = []byte(objstr) - } - - return nil -} - -func (a *DbArgs) requirements() error { - if len(a.Database) == 0 { - return shared.NewValidationError("Database", "cannot be blank") - } - if len(a.Key) == 0 { - return shared.NewValidationError("Key", "cannot be blank") - } - return nil -} - -type DbHexArgs struct { - Database string - Key string - Value []byte -} - -func (args *DbHexArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 2 { - return shared.NewInsufficientParamsError(len(obj), 2) - } - - var objstr string - var ok bool - - if objstr, ok = obj[0].(string); !ok { - return shared.NewInvalidTypeError("database", "not a string") - } - args.Database = objstr - - if objstr, ok = obj[1].(string); !ok { - return shared.NewInvalidTypeError("key", "not a string") - } - args.Key = objstr - - if len(obj) > 2 { - objstr, ok = obj[2].(string) - if !ok { - return shared.NewInvalidTypeError("value", "not a string") - } - - args.Value = common.FromHex(objstr) - } - - return nil -} - -func (a *DbHexArgs) requirements() error { - if len(a.Database) == 0 { - return shared.NewValidationError("Database", "cannot be blank") - } - if len(a.Key) == 0 { - return shared.NewValidationError("Key", "cannot be blank") - } - return nil -} diff --git a/rpc/api/db_js.go b/rpc/api/db_js.go deleted file mode 100644 index 899f8abd9..000000000 --- a/rpc/api/db_js.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -const Db_JS = ` -web3._extend({ - property: 'db', - methods: - [ - ], - properties: - [ - ] -}); -` diff --git a/rpc/api/debug.go b/rpc/api/debug.go deleted file mode 100644 index a6faa335e..000000000 --- a/rpc/api/debug.go +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "fmt" - "strings" - "time" - - "github.com/ethereum/ethash" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/shared" - "github.com/ethereum/go-ethereum/xeth" - "github.com/rcrowley/go-metrics" -) - -const ( - DebugApiVersion = "1.0" -) - -var ( - // mapping between methods and handlers - DebugMapping = map[string]debughandler{ - "debug_dumpBlock": (*debugApi).DumpBlock, - "debug_getBlockRlp": (*debugApi).GetBlockRlp, - "debug_printBlock": (*debugApi).PrintBlock, - "debug_processBlock": (*debugApi).ProcessBlock, - "debug_seedHash": (*debugApi).SeedHash, - "debug_setHead": (*debugApi).SetHead, - "debug_metrics": (*debugApi).Metrics, - } -) - -// debug callback handler -type debughandler func(*debugApi, *shared.Request) (interface{}, error) - -// admin api provider -type debugApi struct { - xeth *xeth.XEth - ethereum *eth.Ethereum - methods map[string]debughandler - codec codec.ApiCoder -} - -// create a new debug api instance -func NewDebugApi(xeth *xeth.XEth, ethereum *eth.Ethereum, coder codec.Codec) *debugApi { - return &debugApi{ - xeth: xeth, - ethereum: ethereum, - methods: DebugMapping, - codec: coder.New(nil), - } -} - -// collection with supported methods -func (self *debugApi) Methods() []string { - methods := make([]string, len(self.methods)) - i := 0 - for k := range self.methods { - methods[i] = k - i++ - } - return methods -} - -// Execute given request -func (self *debugApi) Execute(req *shared.Request) (interface{}, error) { - if callback, ok := self.methods[req.Method]; ok { - return callback(self, req) - } - - return nil, &shared.NotImplementedError{req.Method} -} - -func (self *debugApi) Name() string { - return shared.DebugApiName -} - -func (self *debugApi) ApiVersion() string { - return DebugApiVersion -} - -func (self *debugApi) PrintBlock(req *shared.Request) (interface{}, error) { - args := new(BlockNumArg) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - block := self.xeth.EthBlockByNumber(args.BlockNumber) - return fmt.Sprintf("%s", block), nil -} - -func (self *debugApi) DumpBlock(req *shared.Request) (interface{}, error) { - args := new(BlockNumArg) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - block := self.xeth.EthBlockByNumber(args.BlockNumber) - if block == nil { - return nil, fmt.Errorf("block #%d not found", args.BlockNumber) - } - - stateDb, err := state.New(block.Root(), self.ethereum.ChainDb()) - if err != nil { - return nil, err - } - - return stateDb.RawDump(), nil -} - -func (self *debugApi) GetBlockRlp(req *shared.Request) (interface{}, error) { - args := new(BlockNumArg) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - block := self.xeth.EthBlockByNumber(args.BlockNumber) - if block == nil { - return nil, fmt.Errorf("block #%d not found", args.BlockNumber) - } - encoded, err := rlp.EncodeToBytes(block) - return fmt.Sprintf("%x", encoded), err -} - -func (self *debugApi) SetHead(req *shared.Request) (interface{}, error) { - args := new(BlockNumArg) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - self.ethereum.BlockChain().SetHead(uint64(args.BlockNumber)) - - return nil, nil -} - -func (self *debugApi) ProcessBlock(req *shared.Request) (interface{}, error) { - args := new(BlockNumArg) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - block := self.xeth.EthBlockByNumber(args.BlockNumber) - if block == nil { - return nil, fmt.Errorf("block #%d not found", args.BlockNumber) - } - - old := vm.Debug - defer func() { vm.Debug = old }() - vm.Debug = true - - var ( - blockchain = self.ethereum.BlockChain() - validator = blockchain.Validator() - processor = blockchain.Processor() - ) - - err := core.ValidateHeader(blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash()), true, false) - if err != nil { - return false, err - } - statedb, err := state.New(blockchain.GetBlock(block.ParentHash()).Root(), self.ethereum.ChainDb()) - if err != nil { - return false, err - } - receipts, _, usedGas, err := processor.Process(block, statedb) - if err != nil { - return false, err - } - err = validator.ValidateState(block, blockchain.GetBlock(block.ParentHash()), statedb, receipts, usedGas) - if err != nil { - return false, err - } - - return true, nil -} - -func (self *debugApi) SeedHash(req *shared.Request) (interface{}, error) { - args := new(BlockNumArg) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - if hash, err := ethash.GetSeedHash(uint64(args.BlockNumber)); err == nil { - return fmt.Sprintf("0x%x", hash), nil - } else { - return nil, err - } -} - -func (self *debugApi) Metrics(req *shared.Request) (interface{}, error) { - args := new(MetricsArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - // Create a rate formatter - units := []string{"", "K", "M", "G", "T", "E", "P"} - round := func(value float64, prec int) string { - unit := 0 - for value >= 1000 { - unit, value, prec = unit+1, value/1000, 2 - } - return fmt.Sprintf(fmt.Sprintf("%%.%df%s", prec, units[unit]), value) - } - format := func(total float64, rate float64) string { - return fmt.Sprintf("%s (%s/s)", round(total, 0), round(rate, 2)) - } - // Iterate over all the metrics, and just dump for now - counters := make(map[string]interface{}) - metrics.DefaultRegistry.Each(func(name string, metric interface{}) { - // Create or retrieve the counter hierarchy for this metric - root, parts := counters, strings.Split(name, "/") - for _, part := range parts[:len(parts)-1] { - if _, ok := root[part]; !ok { - root[part] = make(map[string]interface{}) - } - root = root[part].(map[string]interface{}) - } - name = parts[len(parts)-1] - - // Fill the counter with the metric details, formatting if requested - if args.Raw { - switch metric := metric.(type) { - case metrics.Meter: - root[name] = map[string]interface{}{ - "AvgRate01Min": metric.Rate1(), - "AvgRate05Min": metric.Rate5(), - "AvgRate15Min": metric.Rate15(), - "MeanRate": metric.RateMean(), - "Overall": float64(metric.Count()), - } - - case metrics.Timer: - root[name] = map[string]interface{}{ - "AvgRate01Min": metric.Rate1(), - "AvgRate05Min": metric.Rate5(), - "AvgRate15Min": metric.Rate15(), - "MeanRate": metric.RateMean(), - "Overall": float64(metric.Count()), - "Percentiles": map[string]interface{}{ - "5": metric.Percentile(0.05), - "20": metric.Percentile(0.2), - "50": metric.Percentile(0.5), - "80": metric.Percentile(0.8), - "95": metric.Percentile(0.95), - }, - } - - default: - root[name] = "Unknown metric type" - } - } else { - switch metric := metric.(type) { - case metrics.Meter: - root[name] = map[string]interface{}{ - "Avg01Min": format(metric.Rate1()*60, metric.Rate1()), - "Avg05Min": format(metric.Rate5()*300, metric.Rate5()), - "Avg15Min": format(metric.Rate15()*900, metric.Rate15()), - "Overall": format(float64(metric.Count()), metric.RateMean()), - } - - case metrics.Timer: - root[name] = map[string]interface{}{ - "Avg01Min": format(metric.Rate1()*60, metric.Rate1()), - "Avg05Min": format(metric.Rate5()*300, metric.Rate5()), - "Avg15Min": format(metric.Rate15()*900, metric.Rate15()), - "Overall": format(float64(metric.Count()), metric.RateMean()), - "Maximum": time.Duration(metric.Max()).String(), - "Minimum": time.Duration(metric.Min()).String(), - "Percentiles": map[string]interface{}{ - "5": time.Duration(metric.Percentile(0.05)).String(), - "20": time.Duration(metric.Percentile(0.2)).String(), - "50": time.Duration(metric.Percentile(0.5)).String(), - "80": time.Duration(metric.Percentile(0.8)).String(), - "95": time.Duration(metric.Percentile(0.95)).String(), - }, - } - - default: - root[name] = "Unknown metric type" - } - } - }) - return counters, nil -} diff --git a/rpc/api/debug_args.go b/rpc/api/debug_args.go deleted file mode 100644 index 041ad6b6a..000000000 --- a/rpc/api/debug_args.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "encoding/json" - "fmt" - "math/big" - "reflect" - - "github.com/ethereum/go-ethereum/rpc/shared" -) - -type WaitForBlockArgs struct { - MinHeight int - Timeout int // in seconds -} - -func (args *WaitForBlockArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) > 2 { - return fmt.Errorf("waitForArgs needs 0, 1, 2 arguments") - } - - // default values when not provided - args.MinHeight = -1 - args.Timeout = -1 - - if len(obj) >= 1 { - var minHeight *big.Int - if minHeight, err = numString(obj[0]); err != nil { - return err - } - args.MinHeight = int(minHeight.Int64()) - } - - if len(obj) >= 2 { - timeout, err := numString(obj[1]) - if err != nil { - return err - } - args.Timeout = int(timeout.Int64()) - } - - return nil -} - -type MetricsArgs struct { - Raw bool -} - -func (args *MetricsArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - if len(obj) > 1 { - return fmt.Errorf("metricsArgs needs 0, 1 arguments") - } - // default values when not provided - if len(obj) >= 1 && obj[0] != nil { - if value, ok := obj[0].(bool); !ok { - return fmt.Errorf("invalid argument %v", reflect.TypeOf(obj[0])) - } else { - args.Raw = value - } - } - return nil -} diff --git a/rpc/api/debug_js.go b/rpc/api/debug_js.go deleted file mode 100644 index 03755ada0..000000000 --- a/rpc/api/debug_js.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -const Debug_JS = ` -web3._extend({ - property: 'debug', - methods: - [ - new web3._extend.Method({ - name: 'printBlock', - call: 'debug_printBlock', - params: 1, - inputFormatter: [null] - }), - new web3._extend.Method({ - name: 'getBlockRlp', - call: 'debug_getBlockRlp', - params: 1, - inputFormatter: [null] - }), - new web3._extend.Method({ - name: 'setHead', - call: 'debug_setHead', - params: 1, - inputFormatter: [null] - }), - new web3._extend.Method({ - name: 'processBlock', - call: 'debug_processBlock', - params: 1, - inputFormatter: [null] - }), - new web3._extend.Method({ - name: 'seedHash', - call: 'debug_seedHash', - params: 1, - inputFormatter: [null] - }), - new web3._extend.Method({ - name: 'dumpBlock', - call: 'debug_dumpBlock', - params: 1, - inputFormatter: [null] - }), - new web3._extend.Method({ - name: 'metrics', - call: 'debug_metrics', - params: 1, - inputFormatter: [null] - }) - ], - properties: - [ - ] -}); -` diff --git a/rpc/api/eth.go b/rpc/api/eth.go deleted file mode 100644 index db7a643d8..000000000 --- a/rpc/api/eth.go +++ /dev/null @@ -1,721 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "bytes" - "encoding/json" - "math/big" - - "fmt" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/natspec" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/shared" - "github.com/ethereum/go-ethereum/xeth" - "gopkg.in/fatih/set.v0" -) - -const ( - EthApiVersion = "1.0" -) - -// eth api provider -// See https://github.com/ethereum/wiki/wiki/JSON-RPC -type ethApi struct { - xeth *xeth.XEth - ethereum *eth.Ethereum - methods map[string]ethhandler - codec codec.ApiCoder -} - -// eth callback handler -type ethhandler func(*ethApi, *shared.Request) (interface{}, error) - -var ( - ethMapping = map[string]ethhandler{ - "eth_accounts": (*ethApi).Accounts, - "eth_blockNumber": (*ethApi).BlockNumber, - "eth_getBalance": (*ethApi).GetBalance, - "eth_protocolVersion": (*ethApi).ProtocolVersion, - "eth_coinbase": (*ethApi).Coinbase, - "eth_mining": (*ethApi).IsMining, - "eth_syncing": (*ethApi).IsSyncing, - "eth_gasPrice": (*ethApi).GasPrice, - "eth_getStorage": (*ethApi).GetStorage, - "eth_storageAt": (*ethApi).GetStorage, - "eth_getStorageAt": (*ethApi).GetStorageAt, - "eth_getTransactionCount": (*ethApi).GetTransactionCount, - "eth_getBlockTransactionCountByHash": (*ethApi).GetBlockTransactionCountByHash, - "eth_getBlockTransactionCountByNumber": (*ethApi).GetBlockTransactionCountByNumber, - "eth_getUncleCountByBlockHash": (*ethApi).GetUncleCountByBlockHash, - "eth_getUncleCountByBlockNumber": (*ethApi).GetUncleCountByBlockNumber, - "eth_getData": (*ethApi).GetData, - "eth_getCode": (*ethApi).GetData, - "eth_getNatSpec": (*ethApi).GetNatSpec, - "eth_sign": (*ethApi).Sign, - "eth_sendRawTransaction": (*ethApi).SubmitTransaction, - "eth_submitTransaction": (*ethApi).SubmitTransaction, - "eth_sendTransaction": (*ethApi).SendTransaction, - "eth_signTransaction": (*ethApi).SignTransaction, - "eth_transact": (*ethApi).SendTransaction, - "eth_estimateGas": (*ethApi).EstimateGas, - "eth_call": (*ethApi).Call, - "eth_flush": (*ethApi).Flush, - "eth_getBlockByHash": (*ethApi).GetBlockByHash, - "eth_getBlockByNumber": (*ethApi).GetBlockByNumber, - "eth_getTransactionByHash": (*ethApi).GetTransactionByHash, - "eth_getTransactionByBlockNumberAndIndex": (*ethApi).GetTransactionByBlockNumberAndIndex, - "eth_getTransactionByBlockHashAndIndex": (*ethApi).GetTransactionByBlockHashAndIndex, - "eth_getUncleByBlockHashAndIndex": (*ethApi).GetUncleByBlockHashAndIndex, - "eth_getUncleByBlockNumberAndIndex": (*ethApi).GetUncleByBlockNumberAndIndex, - "eth_getCompilers": (*ethApi).GetCompilers, - "eth_compileSolidity": (*ethApi).CompileSolidity, - "eth_newFilter": (*ethApi).NewFilter, - "eth_newBlockFilter": (*ethApi).NewBlockFilter, - "eth_newPendingTransactionFilter": (*ethApi).NewPendingTransactionFilter, - "eth_uninstallFilter": (*ethApi).UninstallFilter, - "eth_getFilterChanges": (*ethApi).GetFilterChanges, - "eth_getFilterLogs": (*ethApi).GetFilterLogs, - "eth_getLogs": (*ethApi).GetLogs, - "eth_hashrate": (*ethApi).Hashrate, - "eth_getWork": (*ethApi).GetWork, - "eth_submitWork": (*ethApi).SubmitWork, - "eth_submitHashrate": (*ethApi).SubmitHashrate, - "eth_resend": (*ethApi).Resend, - "eth_pendingTransactions": (*ethApi).PendingTransactions, - "eth_getTransactionReceipt": (*ethApi).GetTransactionReceipt, - } -) - -// create new ethApi instance -func NewEthApi(xeth *xeth.XEth, eth *eth.Ethereum, codec codec.Codec) *ethApi { - return ðApi{xeth, eth, ethMapping, codec.New(nil)} -} - -// collection with supported methods -func (self *ethApi) Methods() []string { - methods := make([]string, len(self.methods)) - i := 0 - for k := range self.methods { - methods[i] = k - i++ - } - return methods -} - -// Execute given request -func (self *ethApi) Execute(req *shared.Request) (interface{}, error) { - if callback, ok := self.methods[req.Method]; ok { - return callback(self, req) - } - - return nil, shared.NewNotImplementedError(req.Method) -} - -func (self *ethApi) Name() string { - return shared.EthApiName -} - -func (self *ethApi) ApiVersion() string { - return EthApiVersion -} - -func (self *ethApi) Accounts(req *shared.Request) (interface{}, error) { - return self.xeth.Accounts(), nil -} - -func (self *ethApi) Hashrate(req *shared.Request) (interface{}, error) { - return newHexNum(self.xeth.HashRate()), nil -} - -func (self *ethApi) BlockNumber(req *shared.Request) (interface{}, error) { - num := self.xeth.CurrentBlock().Number() - return newHexNum(num.Bytes()), nil -} - -func (self *ethApi) GetBalance(req *shared.Request) (interface{}, error) { - args := new(GetBalanceArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - return self.xeth.AtStateNum(args.BlockNumber).BalanceAt(args.Address), nil -} - -func (self *ethApi) ProtocolVersion(req *shared.Request) (interface{}, error) { - return self.xeth.EthVersion(), nil -} - -func (self *ethApi) Coinbase(req *shared.Request) (interface{}, error) { - return newHexData(self.xeth.Coinbase()), nil -} - -func (self *ethApi) IsMining(req *shared.Request) (interface{}, error) { - return self.xeth.IsMining(), nil -} - -func (self *ethApi) IsSyncing(req *shared.Request) (interface{}, error) { - origin, current, height := self.ethereum.Downloader().Progress() - if current < height { - return map[string]interface{}{ - "startingBlock": newHexNum(big.NewInt(int64(origin)).Bytes()), - "currentBlock": newHexNum(big.NewInt(int64(current)).Bytes()), - "highestBlock": newHexNum(big.NewInt(int64(height)).Bytes()), - }, nil - } - return false, nil -} - -func (self *ethApi) GasPrice(req *shared.Request) (interface{}, error) { - return newHexNum(self.xeth.DefaultGasPrice().Bytes()), nil -} - -func (self *ethApi) GetStorage(req *shared.Request) (interface{}, error) { - args := new(GetStorageArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - return self.xeth.AtStateNum(args.BlockNumber).State().SafeGet(args.Address).Storage(), nil -} - -func (self *ethApi) GetStorageAt(req *shared.Request) (interface{}, error) { - args := new(GetStorageAtArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - return self.xeth.AtStateNum(args.BlockNumber).StorageAt(args.Address, args.Key), nil -} - -func (self *ethApi) GetTransactionCount(req *shared.Request) (interface{}, error) { - args := new(GetTxCountArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - count := self.xeth.AtStateNum(args.BlockNumber).TxCountAt(args.Address) - return fmt.Sprintf("%#x", count), nil -} - -func (self *ethApi) GetBlockTransactionCountByHash(req *shared.Request) (interface{}, error) { - args := new(HashArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - block := self.xeth.EthBlockByHash(args.Hash) - if block == nil { - return nil, nil - } - return fmt.Sprintf("%#x", len(block.Transactions())), nil -} - -func (self *ethApi) GetBlockTransactionCountByNumber(req *shared.Request) (interface{}, error) { - args := new(BlockNumArg) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - block := self.xeth.EthBlockByNumber(args.BlockNumber) - if block == nil { - return nil, nil - } - return fmt.Sprintf("%#x", len(block.Transactions())), nil -} - -func (self *ethApi) GetUncleCountByBlockHash(req *shared.Request) (interface{}, error) { - args := new(HashArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - block := self.xeth.EthBlockByHash(args.Hash) - if block == nil { - return nil, nil - } - return fmt.Sprintf("%#x", len(block.Uncles())), nil -} - -func (self *ethApi) GetUncleCountByBlockNumber(req *shared.Request) (interface{}, error) { - args := new(BlockNumArg) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - block := self.xeth.EthBlockByNumber(args.BlockNumber) - if block == nil { - return nil, nil - } - return fmt.Sprintf("%#x", len(block.Uncles())), nil -} - -func (self *ethApi) GetData(req *shared.Request) (interface{}, error) { - args := new(GetDataArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - v := self.xeth.AtStateNum(args.BlockNumber).CodeAtBytes(args.Address) - return newHexData(v), nil -} - -func (self *ethApi) Sign(req *shared.Request) (interface{}, error) { - args := new(NewSigArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - v, err := self.xeth.Sign(args.From, args.Data, false) - if err != nil { - return nil, err - } - return v, nil -} - -func (self *ethApi) SubmitTransaction(req *shared.Request) (interface{}, error) { - args := new(NewDataArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - v, err := self.xeth.PushTx(args.Data) - if err != nil { - return nil, err - } - return v, nil -} - -// JsonTransaction is returned as response by the JSON RPC. It contains the -// signed RLP encoded transaction as Raw and the signed transaction object as Tx. -type JsonTransaction struct { - Raw string `json:"raw"` - Tx *tx `json:"tx"` -} - -func (self *ethApi) SignTransaction(req *shared.Request) (interface{}, error) { - args := new(NewTxArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - // nonce may be nil ("guess" mode) - var nonce string - if args.Nonce != nil { - nonce = args.Nonce.String() - } - - var gas, price string - if args.Gas != nil { - gas = args.Gas.String() - } - if args.GasPrice != nil { - price = args.GasPrice.String() - } - tx, err := self.xeth.SignTransaction(args.From, args.To, nonce, args.Value.String(), gas, price, args.Data) - if err != nil { - return nil, err - } - - data, err := rlp.EncodeToBytes(tx) - if err != nil { - return nil, err - } - - return JsonTransaction{"0x" + common.Bytes2Hex(data), newTx(tx)}, nil -} - -func (self *ethApi) SendTransaction(req *shared.Request) (interface{}, error) { - args := new(NewTxArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - // nonce may be nil ("guess" mode) - var nonce string - if args.Nonce != nil { - nonce = args.Nonce.String() - } - - var gas, price string - if args.Gas != nil { - gas = args.Gas.String() - } - if args.GasPrice != nil { - price = args.GasPrice.String() - } - v, err := self.xeth.Transact(args.From, args.To, nonce, args.Value.String(), gas, price, args.Data) - if err != nil { - return nil, err - } - return v, nil -} - -func (self *ethApi) GetNatSpec(req *shared.Request) (interface{}, error) { - args := new(NewTxArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - var jsontx = fmt.Sprintf(`{"params":[{"to":"%s","data": "%s"}]}`, args.To, args.Data) - notice := natspec.GetNotice(self.xeth, jsontx, self.ethereum.HTTPClient()) - - return notice, nil -} - -func (self *ethApi) EstimateGas(req *shared.Request) (interface{}, error) { - _, gas, err := self.doCall(req.Params) - if err != nil { - return nil, err - } - - // TODO unwrap the parent method's ToHex call - if len(gas) == 0 { - return newHexNum(0), nil - } else { - return newHexNum(common.String2Big(gas)), err - } -} - -func (self *ethApi) Call(req *shared.Request) (interface{}, error) { - v, _, err := self.doCall(req.Params) - if err != nil { - return nil, err - } - - // TODO unwrap the parent method's ToHex call - if v == "0x0" { - return newHexData([]byte{}), nil - } else { - return newHexData(common.FromHex(v)), nil - } -} - -func (self *ethApi) Flush(req *shared.Request) (interface{}, error) { - return nil, shared.NewNotImplementedError(req.Method) -} - -func (self *ethApi) doCall(params json.RawMessage) (string, string, error) { - args := new(CallArgs) - if err := self.codec.Decode(params, &args); err != nil { - return "", "", err - } - - return self.xeth.AtStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data) -} - -func (self *ethApi) GetBlockByHash(req *shared.Request) (interface{}, error) { - args := new(GetBlockByHashArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - block := self.xeth.EthBlockByHash(args.BlockHash) - if block == nil { - return nil, nil - } - return NewBlockRes(block, self.xeth.Td(block.Hash()), args.IncludeTxs), nil -} - -func (self *ethApi) GetBlockByNumber(req *shared.Request) (interface{}, error) { - args := new(GetBlockByNumberArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - block := self.xeth.EthBlockByNumber(args.BlockNumber) - if block == nil { - return nil, nil - } - return NewBlockRes(block, self.xeth.Td(block.Hash()), args.IncludeTxs), nil -} - -func (self *ethApi) GetTransactionByHash(req *shared.Request) (interface{}, error) { - args := new(HashArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - tx, bhash, bnum, txi := self.xeth.EthTransactionByHash(args.Hash) - if tx != nil { - v := NewTransactionRes(tx) - // if the blockhash is 0, assume this is a pending transaction - if bytes.Compare(bhash.Bytes(), bytes.Repeat([]byte{0}, 32)) != 0 { - v.BlockHash = newHexData(bhash) - v.BlockNumber = newHexNum(bnum) - v.TxIndex = newHexNum(txi) - } - return v, nil - } - return nil, nil -} - -func (self *ethApi) GetTransactionByBlockHashAndIndex(req *shared.Request) (interface{}, error) { - args := new(HashIndexArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - raw := self.xeth.EthBlockByHash(args.Hash) - if raw == nil { - return nil, nil - } - block := NewBlockRes(raw, self.xeth.Td(raw.Hash()), true) - if args.Index >= int64(len(block.Transactions)) || args.Index < 0 { - return nil, nil - } else { - return block.Transactions[args.Index], nil - } -} - -func (self *ethApi) GetTransactionByBlockNumberAndIndex(req *shared.Request) (interface{}, error) { - args := new(BlockNumIndexArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - raw := self.xeth.EthBlockByNumber(args.BlockNumber) - if raw == nil { - return nil, nil - } - block := NewBlockRes(raw, self.xeth.Td(raw.Hash()), true) - if args.Index >= int64(len(block.Transactions)) || args.Index < 0 { - // return NewValidationError("Index", "does not exist") - return nil, nil - } - return block.Transactions[args.Index], nil -} - -func (self *ethApi) GetUncleByBlockHashAndIndex(req *shared.Request) (interface{}, error) { - args := new(HashIndexArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - raw := self.xeth.EthBlockByHash(args.Hash) - if raw == nil { - return nil, nil - } - block := NewBlockRes(raw, self.xeth.Td(raw.Hash()), false) - if args.Index >= int64(len(block.Uncles)) || args.Index < 0 { - // return NewValidationError("Index", "does not exist") - return nil, nil - } - return block.Uncles[args.Index], nil -} - -func (self *ethApi) GetUncleByBlockNumberAndIndex(req *shared.Request) (interface{}, error) { - args := new(BlockNumIndexArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - raw := self.xeth.EthBlockByNumber(args.BlockNumber) - if raw == nil { - return nil, nil - } - block := NewBlockRes(raw, self.xeth.Td(raw.Hash()), true) - if args.Index >= int64(len(block.Uncles)) || args.Index < 0 { - return nil, nil - } else { - return block.Uncles[args.Index], nil - } -} - -func (self *ethApi) GetCompilers(req *shared.Request) (interface{}, error) { - var lang string - if solc, _ := self.xeth.Solc(); solc != nil { - lang = "Solidity" - } - c := []string{lang} - return c, nil -} - -func (self *ethApi) CompileSolidity(req *shared.Request) (interface{}, error) { - solc, _ := self.xeth.Solc() - if solc == nil { - return nil, shared.NewNotAvailableError(req.Method, "solc (solidity compiler) not found") - } - - args := new(SourceArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - contracts, err := solc.Compile(args.Source) - if err != nil { - return nil, err - } - return contracts, nil -} - -func (self *ethApi) NewFilter(req *shared.Request) (interface{}, error) { - args := new(BlockFilterArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - id := self.xeth.NewLogFilter(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics) - return newHexNum(big.NewInt(int64(id)).Bytes()), nil -} - -func (self *ethApi) NewBlockFilter(req *shared.Request) (interface{}, error) { - return newHexNum(self.xeth.NewBlockFilter()), nil -} - -func (self *ethApi) NewPendingTransactionFilter(req *shared.Request) (interface{}, error) { - return newHexNum(self.xeth.NewTransactionFilter()), nil -} - -func (self *ethApi) UninstallFilter(req *shared.Request) (interface{}, error) { - args := new(FilterIdArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - return self.xeth.UninstallFilter(args.Id), nil -} - -func (self *ethApi) GetFilterChanges(req *shared.Request) (interface{}, error) { - args := new(FilterIdArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - switch self.xeth.GetFilterType(args.Id) { - case xeth.BlockFilterTy: - return NewHashesRes(self.xeth.BlockFilterChanged(args.Id)), nil - case xeth.TransactionFilterTy: - return NewHashesRes(self.xeth.TransactionFilterChanged(args.Id)), nil - case xeth.LogFilterTy: - return NewLogsRes(self.xeth.LogFilterChanged(args.Id)), nil - default: - return []string{}, nil // reply empty string slice - } -} - -func (self *ethApi) GetFilterLogs(req *shared.Request) (interface{}, error) { - args := new(FilterIdArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - return NewLogsRes(self.xeth.Logs(args.Id)), nil -} - -func (self *ethApi) GetLogs(req *shared.Request) (interface{}, error) { - args := new(BlockFilterArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - return NewLogsRes(self.xeth.AllLogs(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics)), nil -} - -func (self *ethApi) GetWork(req *shared.Request) (interface{}, error) { - self.xeth.SetMining(true, 0) - ret, err := self.xeth.RemoteMining().GetWork() - if err != nil { - return nil, shared.NewNotReadyError("mining work") - } else { - return ret, nil - } -} - -func (self *ethApi) SubmitWork(req *shared.Request) (interface{}, error) { - args := new(SubmitWorkArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - return self.xeth.RemoteMining().SubmitWork(args.Nonce, common.HexToHash(args.Digest), common.HexToHash(args.Header)), nil -} - -func (self *ethApi) SubmitHashrate(req *shared.Request) (interface{}, error) { - args := new(SubmitHashRateArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return false, shared.NewDecodeParamError(err.Error()) - } - self.xeth.RemoteMining().SubmitHashrate(common.HexToHash(args.Id), args.Rate) - return true, nil -} - -func (self *ethApi) Resend(req *shared.Request) (interface{}, error) { - args := new(ResendArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - from := common.HexToAddress(args.Tx.From) - - pending := self.ethereum.TxPool().GetTransactions() - for _, p := range pending { - if pFrom, err := p.From(); err == nil && pFrom == from && p.SigHash() == args.Tx.tx.SigHash() { - self.ethereum.TxPool().RemoveTx(common.HexToHash(args.Tx.Hash)) - return self.xeth.Transact(args.Tx.From, args.Tx.To, args.Tx.Nonce, args.Tx.Value, args.GasLimit, args.GasPrice, args.Tx.Data) - } - } - - return nil, fmt.Errorf("Transaction %s not found", args.Tx.Hash) -} - -func (self *ethApi) PendingTransactions(req *shared.Request) (interface{}, error) { - txs := self.ethereum.TxPool().GetTransactions() - - // grab the accounts from the account manager. This will help with determining which - // transactions should be returned. - accounts, err := self.ethereum.AccountManager().Accounts() - if err != nil { - return nil, err - } - - // Add the accouns to a new set - accountSet := set.New() - for _, account := range accounts { - accountSet.Add(account.Address) - } - - var ltxs []*tx - for _, tx := range txs { - if from, _ := tx.From(); accountSet.Has(from) { - ltxs = append(ltxs, newTx(tx)) - } - } - - return ltxs, nil -} - -func (self *ethApi) GetTransactionReceipt(req *shared.Request) (interface{}, error) { - args := new(HashArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - txhash := common.BytesToHash(common.FromHex(args.Hash)) - tx, bhash, bnum, txi := self.xeth.EthTransactionByHash(args.Hash) - rec := self.xeth.GetTxReceipt(txhash) - // We could have an error of "not found". Should disambiguate - // if err != nil { - // return err, nil - // } - if rec != nil && tx != nil { - v := NewReceiptRes(rec) - v.BlockHash = newHexData(bhash) - v.BlockNumber = newHexNum(bnum) - v.TransactionIndex = newHexNum(txi) - return v, nil - } - - return nil, nil -} diff --git a/rpc/api/eth_args.go b/rpc/api/eth_args.go deleted file mode 100644 index ed3d761f1..000000000 --- a/rpc/api/eth_args.go +++ /dev/null @@ -1,1104 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "encoding/json" - "fmt" - "math/big" - "strconv" - "strings" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/rpc/shared" -) - -const ( - defaultLogLimit = 100 - defaultLogOffset = 0 -) - -type GetBalanceArgs struct { - Address string - BlockNumber int64 -} - -func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - addstr, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("address", "not a string") - } - args.Address = addstr - - if len(obj) > 1 { - if err := blockHeight(obj[1], &args.BlockNumber); err != nil { - return err - } - } else { - args.BlockNumber = -1 - } - - return nil -} - -type GetStorageArgs struct { - Address string - BlockNumber int64 -} - -func (args *GetStorageArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - addstr, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("address", "not a string") - } - args.Address = addstr - - if len(obj) > 1 { - if err := blockHeight(obj[1], &args.BlockNumber); err != nil { - return err - } - } else { - args.BlockNumber = -1 - } - - return nil -} - -type GetStorageAtArgs struct { - Address string - BlockNumber int64 - Key string -} - -func (args *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 2 { - return shared.NewInsufficientParamsError(len(obj), 2) - } - - addstr, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("address", "not a string") - } - args.Address = addstr - - keystr, ok := obj[1].(string) - if !ok { - return shared.NewInvalidTypeError("key", "not a string") - } - args.Key = keystr - - if len(obj) > 2 { - if err := blockHeight(obj[2], &args.BlockNumber); err != nil { - return err - } - } else { - args.BlockNumber = -1 - } - - return nil -} - -type GetTxCountArgs struct { - Address string - BlockNumber int64 -} - -func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - addstr, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("address", "not a string") - } - args.Address = addstr - - if len(obj) > 1 { - if err := blockHeight(obj[1], &args.BlockNumber); err != nil { - return err - } - } else { - args.BlockNumber = -1 - } - - return nil -} - -type SubmitHashRateArgs struct { - Id string - Rate uint64 -} - -func (args *SubmitHashRateArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 2 { - return shared.NewInsufficientParamsError(len(obj), 2) - } - - arg0, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("hash", "not a string") - } - args.Id = arg0 - - arg1, ok := obj[1].(string) - if !ok { - return shared.NewInvalidTypeError("rate", "not a string") - } - - args.Rate = common.String2Big(arg1).Uint64() - - return nil -} - -type HashArgs struct { - Hash string -} - -func (args *HashArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - arg0, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("hash", "not a string") - } - args.Hash = arg0 - - return nil -} - -type BlockNumArg struct { - BlockNumber int64 -} - -func (args *BlockNumArg) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - if err := blockHeight(obj[0], &args.BlockNumber); err != nil { - return err - } - - return nil -} - -type GetDataArgs struct { - Address string - BlockNumber int64 -} - -func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - addstr, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("address", "not a string") - } - args.Address = addstr - - if len(obj) > 1 { - if err := blockHeight(obj[1], &args.BlockNumber); err != nil { - return err - } - } else { - args.BlockNumber = -1 - } - - return nil -} - -type NewDataArgs struct { - Data string -} - -func (args *NewDataArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - // Check for sufficient params - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - data, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("data", "not a string") - } - args.Data = data - - if len(args.Data) == 0 { - return shared.NewValidationError("data", "is required") - } - - return nil -} - -type NewSigArgs struct { - From string - Data string -} - -func (args *NewSigArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - // Check for sufficient params - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - from, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("from", "not a string") - } - args.From = from - - if len(args.From) == 0 { - return shared.NewValidationError("from", "is required") - } - - data, ok := obj[1].(string) - if !ok { - return shared.NewInvalidTypeError("data", "not a string") - } - args.Data = data - - if len(args.Data) == 0 { - return shared.NewValidationError("data", "is required") - } - - return nil -} - -type NewTxArgs struct { - From string - To string - Nonce *big.Int - Value *big.Int - Gas *big.Int - GasPrice *big.Int - Data string - - BlockNumber int64 -} - -func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { - var obj []json.RawMessage - var ext struct { - From string - To string - Nonce interface{} - Value interface{} - Gas interface{} - GasPrice interface{} - Data string - } - - // Decode byte slice to array of RawMessages - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - // Check for sufficient params - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - // Decode 0th RawMessage to temporary struct - if err := json.Unmarshal(obj[0], &ext); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(ext.From) == 0 { - return shared.NewValidationError("from", "is required") - } - - args.From = ext.From - args.To = ext.To - args.Data = ext.Data - - var num *big.Int - if ext.Nonce != nil { - num, err = numString(ext.Nonce) - if err != nil { - return err - } - } - args.Nonce = num - - if ext.Value == nil { - num = big.NewInt(0) - } else { - num, err = numString(ext.Value) - if err != nil { - return err - } - } - args.Value = num - - num = nil - if ext.Gas != nil { - if num, err = numString(ext.Gas); err != nil { - return err - } - } - args.Gas = num - - num = nil - if ext.GasPrice != nil { - if num, err = numString(ext.GasPrice); err != nil { - return err - } - } - args.GasPrice = num - - // Check for optional BlockNumber param - if len(obj) > 1 { - if err := blockHeightFromJson(obj[1], &args.BlockNumber); err != nil { - return err - } - } else { - args.BlockNumber = -1 - } - - return nil -} - -type SourceArgs struct { - Source string -} - -func (args *SourceArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - arg0, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("source code", "not a string") - } - args.Source = arg0 - - return nil -} - -type CallArgs struct { - From string - To string - Value *big.Int - Gas *big.Int - GasPrice *big.Int - Data string - - BlockNumber int64 -} - -func (args *CallArgs) UnmarshalJSON(b []byte) (err error) { - var obj []json.RawMessage - var ext struct { - From string - To string - Value interface{} - Gas interface{} - GasPrice interface{} - Data string - } - - // Decode byte slice to array of RawMessages - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - // Check for sufficient params - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - // Decode 0th RawMessage to temporary struct - if err := json.Unmarshal(obj[0], &ext); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - args.From = ext.From - args.To = ext.To - - var num *big.Int - if ext.Value == nil { - num = big.NewInt(0) - } else { - if num, err = numString(ext.Value); err != nil { - return err - } - } - args.Value = num - - if ext.Gas != nil { - if num, err = numString(ext.Gas); err != nil { - return err - } - } else { - num = nil - } - args.Gas = num - - if ext.GasPrice != nil { - if num, err = numString(ext.GasPrice); err != nil { - return err - } - } else { - num = nil - } - args.GasPrice = num - - args.Data = ext.Data - - // Check for optional BlockNumber param - if len(obj) > 1 { - if err := blockHeightFromJson(obj[1], &args.BlockNumber); err != nil { - return err - } - } else { - args.BlockNumber = -1 - } - - return nil -} - -type HashIndexArgs struct { - Hash string - Index int64 -} - -func (args *HashIndexArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 2 { - return shared.NewInsufficientParamsError(len(obj), 2) - } - - arg0, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("hash", "not a string") - } - args.Hash = arg0 - - arg1, ok := obj[1].(string) - if !ok { - return shared.NewInvalidTypeError("index", "not a string") - } - args.Index = common.Big(arg1).Int64() - - return nil -} - -type BlockNumIndexArgs struct { - BlockNumber int64 - Index int64 -} - -func (args *BlockNumIndexArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 2 { - return shared.NewInsufficientParamsError(len(obj), 2) - } - - if err := blockHeight(obj[0], &args.BlockNumber); err != nil { - return err - } - - var arg1 *big.Int - if arg1, err = numString(obj[1]); err != nil { - return err - } - args.Index = arg1.Int64() - - return nil -} - -type GetBlockByHashArgs struct { - BlockHash string - IncludeTxs bool -} - -func (args *GetBlockByHashArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 2 { - return shared.NewInsufficientParamsError(len(obj), 2) - } - - argstr, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("blockHash", "not a string") - } - args.BlockHash = argstr - - args.IncludeTxs = obj[1].(bool) - - if inclTx, ok := obj[1].(bool); ok { - args.IncludeTxs = inclTx - return nil - } - - return shared.NewInvalidTypeError("includeTxs", "not a bool") -} - -type GetBlockByNumberArgs struct { - BlockNumber int64 - IncludeTxs bool -} - -func (args *GetBlockByNumberArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 2 { - return shared.NewInsufficientParamsError(len(obj), 2) - } - - if err := blockHeight(obj[0], &args.BlockNumber); err != nil { - return err - } - - if inclTx, ok := obj[1].(bool); ok { - args.IncludeTxs = inclTx - return nil - } - - return shared.NewInvalidTypeError("includeTxs", "not a bool") -} - -type BlockFilterArgs struct { - Earliest int64 - Latest int64 - Address []string - Topics [][]string - Skip int - Max int -} - -func (args *BlockFilterArgs) UnmarshalJSON(b []byte) (err error) { - var obj []struct { - FromBlock interface{} `json:"fromBlock"` - ToBlock interface{} `json:"toBlock"` - Limit interface{} `json:"limit"` - Offset interface{} `json:"offset"` - Address interface{} `json:"address"` - Topics interface{} `json:"topics"` - } - - if err = json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - // args.Earliest, err = toNumber(obj[0].ToBlock) - // if err != nil { - // return shared.NewDecodeParamError(fmt.Sprintf("FromBlock %v", err)) - // } - // args.Latest, err = toNumber(obj[0].FromBlock) - // if err != nil { - // return shared.NewDecodeParamError(fmt.Sprintf("ToBlock %v", err)) - - var num int64 - var numBig *big.Int - - // if blank then latest - if obj[0].FromBlock == nil { - num = -1 - } else { - if err := blockHeight(obj[0].FromBlock, &num); err != nil { - return err - } - } - // if -2 or other "silly" number, use latest - if num < 0 { - args.Earliest = -1 //latest block - } else { - args.Earliest = num - } - - // if blank than latest - if obj[0].ToBlock == nil { - num = -1 - } else { - if err := blockHeight(obj[0].ToBlock, &num); err != nil { - return err - } - } - - if num == -2 { - return fmt.Errorf("\"pending\" is unsupported") - } else if num < -2 { - return fmt.Errorf("Invalid to block number") - } - - args.Latest = num - - if obj[0].Limit == nil { - numBig = big.NewInt(defaultLogLimit) - } else { - if numBig, err = numString(obj[0].Limit); err != nil { - return err - } - } - args.Max = int(numBig.Int64()) - - if obj[0].Offset == nil { - numBig = big.NewInt(defaultLogOffset) - } else { - if numBig, err = numString(obj[0].Offset); err != nil { - return err - } - } - args.Skip = int(numBig.Int64()) - - if obj[0].Address != nil { - marg, ok := obj[0].Address.([]interface{}) - if ok { - v := make([]string, len(marg)) - for i, arg := range marg { - argstr, ok := arg.(string) - if !ok { - return shared.NewInvalidTypeError(fmt.Sprintf("address[%d]", i), "is not a string") - } - v[i] = argstr - } - args.Address = v - } else { - argstr, ok := obj[0].Address.(string) - if ok { - v := make([]string, 1) - v[0] = argstr - args.Address = v - } else { - return shared.NewInvalidTypeError("address", "is not a string or array") - } - } - } - - if obj[0].Topics != nil { - other, ok := obj[0].Topics.([]interface{}) - if ok { - topicdbl := make([][]string, len(other)) - for i, iv := range other { - if argstr, ok := iv.(string); ok { - // Found a string, push into first element of array - topicsgl := make([]string, 1) - topicsgl[0] = argstr - topicdbl[i] = topicsgl - } else if argarray, ok := iv.([]interface{}); ok { - // Found an array of other - topicdbl[i] = make([]string, len(argarray)) - for j, jv := range argarray { - if v, ok := jv.(string); ok { - topicdbl[i][j] = v - } else if jv == nil { - topicdbl[i][j] = "" - } else { - return shared.NewInvalidTypeError(fmt.Sprintf("topic[%d][%d]", i, j), "is not a string") - } - } - } else if iv == nil { - topicdbl[i] = []string{""} - } else { - return shared.NewInvalidTypeError(fmt.Sprintf("topic[%d]", i), "not a string or array") - } - } - args.Topics = topicdbl - return nil - } else { - return shared.NewInvalidTypeError("topic", "is not a string or array") - } - } - - return nil -} - -type FilterIdArgs struct { - Id int -} - -func (args *FilterIdArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - var num *big.Int - if num, err = numString(obj[0]); err != nil { - return err - } - args.Id = int(num.Int64()) - - return nil -} - -type LogRes struct { - Address *hexdata `json:"address"` - Topics []*hexdata `json:"topics"` - Data *hexdata `json:"data"` - BlockNumber *hexnum `json:"blockNumber"` - LogIndex *hexnum `json:"logIndex"` - BlockHash *hexdata `json:"blockHash"` - TransactionHash *hexdata `json:"transactionHash"` - TransactionIndex *hexnum `json:"transactionIndex"` -} - -func NewLogRes(log *vm.Log) LogRes { - var l LogRes - l.Topics = make([]*hexdata, len(log.Topics)) - for j, topic := range log.Topics { - l.Topics[j] = newHexData(topic) - } - l.Address = newHexData(log.Address) - l.Data = newHexData(log.Data) - l.BlockNumber = newHexNum(log.BlockNumber) - l.LogIndex = newHexNum(log.Index) - l.TransactionHash = newHexData(log.TxHash) - l.TransactionIndex = newHexNum(log.TxIndex) - l.BlockHash = newHexData(log.BlockHash) - - return l -} - -func NewLogsRes(logs vm.Logs) (ls []LogRes) { - ls = make([]LogRes, len(logs)) - - for i, log := range logs { - ls[i] = NewLogRes(log) - } - - return -} - -func NewHashesRes(hs []common.Hash) []string { - hashes := make([]string, len(hs)) - - for i, hash := range hs { - hashes[i] = hash.Hex() - } - - return hashes -} - -type SubmitWorkArgs struct { - Nonce uint64 - Header string - Digest string -} - -func (args *SubmitWorkArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err = json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 3 { - return shared.NewInsufficientParamsError(len(obj), 3) - } - - var objstr string - var ok bool - if objstr, ok = obj[0].(string); !ok { - return shared.NewInvalidTypeError("nonce", "not a string") - } - - args.Nonce = common.String2Big(objstr).Uint64() - if objstr, ok = obj[1].(string); !ok { - return shared.NewInvalidTypeError("header", "not a string") - } - - args.Header = objstr - - if objstr, ok = obj[2].(string); !ok { - return shared.NewInvalidTypeError("digest", "not a string") - } - - args.Digest = objstr - - return nil -} - -type tx struct { - tx *types.Transaction - - To string `json:"to"` - From string `json:"from"` - Nonce string `json:"nonce"` - Value string `json:"value"` - Data string `json:"data"` - GasLimit string `json:"gas"` - GasPrice string `json:"gasPrice"` - Hash string `json:"hash"` -} - -func newTx(t *types.Transaction) *tx { - from, _ := t.From() - var to string - if t := t.To(); t != nil { - to = t.Hex() - } - - return &tx{ - tx: t, - To: to, - From: from.Hex(), - Value: t.Value().String(), - Nonce: strconv.Itoa(int(t.Nonce())), - Data: "0x" + common.Bytes2Hex(t.Data()), - GasLimit: t.Gas().String(), - GasPrice: t.GasPrice().String(), - Hash: t.Hash().Hex(), - } -} - -type ResendArgs struct { - Tx *tx - GasPrice string - GasLimit string -} - -func (tx *tx) UnmarshalJSON(b []byte) (err error) { - var fields map[string]interface{} - if err := json.Unmarshal(b, &fields); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - var ( - nonce uint64 - to common.Address - amount = new(big.Int).Set(common.Big0) - gasLimit = new(big.Int).Set(common.Big0) - gasPrice = new(big.Int).Set(common.Big0) - data []byte - contractCreation = true - ) - - if val, found := fields["Hash"]; found { - if hashVal, ok := val.(string); ok { - tx.Hash = hashVal - } - } - - if val, found := fields["To"]; found { - if strVal, ok := val.(string); ok && len(strVal) > 0 { - tx.To = strVal - to = common.HexToAddress(strVal) - contractCreation = false - } - } - - if val, found := fields["From"]; found { - if strVal, ok := val.(string); ok { - tx.From = strVal - } - } - - if val, found := fields["Nonce"]; found { - if strVal, ok := val.(string); ok { - tx.Nonce = strVal - if nonce, err = strconv.ParseUint(strVal, 10, 64); err != nil { - return shared.NewDecodeParamError(fmt.Sprintf("Unable to decode tx.Nonce - %v", err)) - } - } - } else { - return shared.NewDecodeParamError("tx.Nonce not found") - } - - var parseOk bool - if val, found := fields["Value"]; found { - if strVal, ok := val.(string); ok { - tx.Value = strVal - if _, parseOk = amount.SetString(strVal, 0); !parseOk { - return shared.NewDecodeParamError(fmt.Sprintf("Unable to decode tx.Amount - %v", err)) - } - } - } - - if val, found := fields["Data"]; found { - if strVal, ok := val.(string); ok { - tx.Data = strVal - if strings.HasPrefix(strVal, "0x") { - data = common.Hex2Bytes(strVal[2:]) - } else { - data = common.Hex2Bytes(strVal) - } - } - } - - if val, found := fields["GasLimit"]; found { - if strVal, ok := val.(string); ok { - tx.GasLimit = strVal - if _, parseOk = gasLimit.SetString(strVal, 0); !parseOk { - return shared.NewDecodeParamError(fmt.Sprintf("Unable to decode tx.GasLimit - %v", err)) - } - } - } - - if val, found := fields["GasPrice"]; found { - if strVal, ok := val.(string); ok { - tx.GasPrice = strVal - if _, parseOk = gasPrice.SetString(strVal, 0); !parseOk { - return shared.NewDecodeParamError(fmt.Sprintf("Unable to decode tx.GasPrice - %v", err)) - } - } - } - - if contractCreation { - tx.tx = types.NewContractCreation(nonce, amount, gasLimit, gasPrice, data) - } else { - tx.tx = types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data) - } - - return nil -} - -func (args *ResendArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err = json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - data, err := json.Marshal(obj[0]) - if err != nil { - return shared.NewDecodeParamError("Unable to parse transaction object") - } - - trans := new(tx) - err = json.Unmarshal(data, trans) - if err != nil { - return shared.NewDecodeParamError("Unable to parse transaction object") - } - - if trans == nil || trans.tx == nil { - return shared.NewDecodeParamError("Unable to parse transaction object") - } - - gasLimit, gasPrice := trans.GasLimit, trans.GasPrice - - if len(obj) > 1 && obj[1] != nil { - if gp, ok := obj[1].(string); ok { - gasPrice = gp - } else { - return shared.NewInvalidTypeError("gasPrice", "not a string") - } - } - if len(obj) > 2 && obj[2] != nil { - if gl, ok := obj[2].(string); ok { - gasLimit = gl - } else { - return shared.NewInvalidTypeError("gasLimit", "not a string") - } - } - - args.Tx = trans - args.GasPrice = gasPrice - args.GasLimit = gasLimit - - return nil -} diff --git a/rpc/api/eth_js.go b/rpc/api/eth_js.go deleted file mode 100644 index dfc104ad8..000000000 --- a/rpc/api/eth_js.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -// JS api provided by web3.js -// eth_sign not standard - -const Eth_JS = ` -web3._extend({ - property: 'eth', - methods: - [ - new web3._extend.Method({ - name: 'sign', - call: 'eth_sign', - params: 2, - inputFormatter: [web3._extend.utils.toAddress, null] - }), - new web3._extend.Method({ - name: 'resend', - call: 'eth_resend', - params: 3, - inputFormatter: [web3._extend.formatters.inputTransactionFormatter, web3._extend.utils.fromDecimal, web3._extend.utils.fromDecimal] - }), - new web3._extend.Method({ - name: 'getNatSpec', - call: 'eth_getNatSpec', - params: 1, - inputFormatter: [web3._extend.formatters.inputTransactionFormatter] - }), - new web3._extend.Method({ - name: 'signTransaction', - call: 'eth_signTransaction', - params: 1, - inputFormatter: [web3._extend.formatters.inputTransactionFormatter] - }), - new web3._extend.Method({ - name: 'submitTransaction', - call: 'eth_submitTransaction', - params: 1, - inputFormatter: [web3._extend.formatters.inputTransactionFormatter] - }) - ], - properties: - [ - new web3._extend.Property({ - name: 'pendingTransactions', - getter: 'eth_pendingTransactions' - }) - ] -}); -` diff --git a/rpc/api/mergedapi.go b/rpc/api/mergedapi.go deleted file mode 100644 index 92e1e2bb7..000000000 --- a/rpc/api/mergedapi.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/rpc/shared" -) - -const ( - MergedApiVersion = "1.0" -) - -// combines multiple API's -type MergedApi struct { - apis map[string]string - methods map[string]shared.EthereumApi -} - -// create new merged api instance -func newMergedApi(apis ...shared.EthereumApi) *MergedApi { - mergedApi := new(MergedApi) - mergedApi.apis = make(map[string]string, len(apis)) - mergedApi.methods = make(map[string]shared.EthereumApi) - - for _, api := range apis { - if api != nil { - mergedApi.apis[api.Name()] = api.ApiVersion() - for _, method := range api.Methods() { - mergedApi.methods[method] = api - } - } - } - return mergedApi -} - -// Supported RPC methods -func (self *MergedApi) Methods() []string { - all := make([]string, len(self.methods)) - for method, _ := range self.methods { - all = append(all, method) - } - return all -} - -// Call the correct API's Execute method for the given request -func (self *MergedApi) Execute(req *shared.Request) (interface{}, error) { - glog.V(logger.Detail).Infof("%s %s", req.Method, req.Params) - - if res, _ := self.handle(req); res != nil { - return res, nil - } - if api, found := self.methods[req.Method]; found { - return api.Execute(req) - } - return nil, shared.NewNotImplementedError(req.Method) -} - -func (self *MergedApi) Name() string { - return shared.MergedApiName -} - -func (self *MergedApi) ApiVersion() string { - return MergedApiVersion -} - -func (self *MergedApi) handle(req *shared.Request) (interface{}, error) { - if req.Method == "modules" { // provided API's - return self.apis, nil - } - - return nil, nil -} diff --git a/rpc/api/miner.go b/rpc/api/miner.go deleted file mode 100644 index e07855dd2..000000000 --- a/rpc/api/miner.go +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "github.com/ethereum/ethash" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/shared" -) - -const ( - MinerApiVersion = "1.0" -) - -var ( - // mapping between methods and handlers - MinerMapping = map[string]minerhandler{ - "miner_hashrate": (*minerApi).Hashrate, - "miner_makeDAG": (*minerApi).MakeDAG, - "miner_setExtra": (*minerApi).SetExtra, - "miner_setGasPrice": (*minerApi).SetGasPrice, - "miner_setEtherbase": (*minerApi).SetEtherbase, - "miner_startAutoDAG": (*minerApi).StartAutoDAG, - "miner_start": (*minerApi).StartMiner, - "miner_stopAutoDAG": (*minerApi).StopAutoDAG, - "miner_stop": (*minerApi).StopMiner, - } -) - -// miner callback handler -type minerhandler func(*minerApi, *shared.Request) (interface{}, error) - -// miner api provider -type minerApi struct { - ethereum *eth.Ethereum - methods map[string]minerhandler - codec codec.ApiCoder -} - -// create a new miner api instance -func NewMinerApi(ethereum *eth.Ethereum, coder codec.Codec) *minerApi { - return &minerApi{ - ethereum: ethereum, - methods: MinerMapping, - codec: coder.New(nil), - } -} - -// Execute given request -func (self *minerApi) Execute(req *shared.Request) (interface{}, error) { - if callback, ok := self.methods[req.Method]; ok { - return callback(self, req) - } - - return nil, &shared.NotImplementedError{req.Method} -} - -// collection with supported methods -func (self *minerApi) Methods() []string { - methods := make([]string, len(self.methods)) - i := 0 - for k := range self.methods { - methods[i] = k - i++ - } - return methods -} - -func (self *minerApi) Name() string { - return shared.MinerApiName -} - -func (self *minerApi) ApiVersion() string { - return MinerApiVersion -} - -func (self *minerApi) StartMiner(req *shared.Request) (interface{}, error) { - args := new(StartMinerArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, err - } - if args.Threads == -1 { // (not specified by user, use default) - args.Threads = self.ethereum.MinerThreads - } - - self.ethereum.StartAutoDAG() - err := self.ethereum.StartMining(args.Threads, "") - if err == nil { - return true, nil - } - - return false, err -} - -func (self *minerApi) StopMiner(req *shared.Request) (interface{}, error) { - self.ethereum.StopMining() - return true, nil -} - -func (self *minerApi) Hashrate(req *shared.Request) (interface{}, error) { - return self.ethereum.Miner().HashRate(), nil -} - -func (self *minerApi) SetExtra(req *shared.Request) (interface{}, error) { - args := new(SetExtraArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, err - } - - if err := self.ethereum.Miner().SetExtra([]byte(args.Data)); err != nil { - return false, err - } - - return true, nil -} - -func (self *minerApi) SetGasPrice(req *shared.Request) (interface{}, error) { - args := new(GasPriceArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return false, err - } - - self.ethereum.Miner().SetGasPrice(common.String2Big(args.Price)) - return true, nil -} - -func (self *minerApi) SetEtherbase(req *shared.Request) (interface{}, error) { - args := new(SetEtherbaseArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return false, err - } - self.ethereum.SetEtherbase(args.Etherbase) - return nil, nil -} - -func (self *minerApi) StartAutoDAG(req *shared.Request) (interface{}, error) { - self.ethereum.StartAutoDAG() - return true, nil -} - -func (self *minerApi) StopAutoDAG(req *shared.Request) (interface{}, error) { - self.ethereum.StopAutoDAG() - return true, nil -} - -func (self *minerApi) MakeDAG(req *shared.Request) (interface{}, error) { - args := new(MakeDAGArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, err - } - - if args.BlockNumber < 0 { - return false, shared.NewValidationError("BlockNumber", "BlockNumber must be positive") - } - - err := ethash.MakeDAG(uint64(args.BlockNumber), "") - if err == nil { - return true, nil - } - return false, err -} diff --git a/rpc/api/miner_args.go b/rpc/api/miner_args.go deleted file mode 100644 index 5ceb244fe..000000000 --- a/rpc/api/miner_args.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "encoding/json" - - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/rpc/shared" -) - -type StartMinerArgs struct { - Threads int -} - -func (args *StartMinerArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) == 0 || obj[0] == nil { - args.Threads = -1 - return nil - } - - var num *big.Int - if num, err = numString(obj[0]); err != nil { - return err - } - args.Threads = int(num.Int64()) - return nil -} - -type SetExtraArgs struct { - Data string -} - -func (args *SetExtraArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - extrastr, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("Price", "not a string") - } - args.Data = extrastr - - return nil -} - -type GasPriceArgs struct { - Price string -} - -func (args *GasPriceArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - if pricestr, ok := obj[0].(string); ok { - args.Price = pricestr - return nil - } - - return shared.NewInvalidTypeError("Price", "not a string") -} - -type SetEtherbaseArgs struct { - Etherbase common.Address -} - -func (args *SetEtherbaseArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - if addr, ok := obj[0].(string); ok { - args.Etherbase = common.HexToAddress(addr) - if (args.Etherbase == common.Address{}) { - return shared.NewInvalidTypeError("Etherbase", "not a valid address") - } - return nil - } - - return shared.NewInvalidTypeError("Etherbase", "not a string") -} - -type MakeDAGArgs struct { - BlockNumber int64 -} - -func (args *MakeDAGArgs) UnmarshalJSON(b []byte) (err error) { - args.BlockNumber = -1 - var obj []interface{} - - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - if err := blockHeight(obj[0], &args.BlockNumber); err != nil { - return err - } - - return nil -} diff --git a/rpc/api/miner_js.go b/rpc/api/miner_js.go deleted file mode 100644 index 0998a9f41..000000000 --- a/rpc/api/miner_js.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -const Miner_JS = ` -web3._extend({ - property: 'miner', - methods: - [ - new web3._extend.Method({ - name: 'start', - call: 'miner_start', - params: 1, - inputFormatter: [null] - }), - new web3._extend.Method({ - name: 'stop', - call: 'miner_stop', - params: 1, - inputFormatter: [null] - }), - new web3._extend.Method({ - name: 'setEtherbase', - call: 'miner_setEtherbase', - params: 1, - inputFormatter: [web3._extend.formatters.formatInputInt], - outputFormatter: web3._extend.formatters.formatOutputBool - }), - new web3._extend.Method({ - name: 'setExtra', - call: 'miner_setExtra', - params: 1, - inputFormatter: [null] - }), - new web3._extend.Method({ - name: 'setGasPrice', - call: 'miner_setGasPrice', - params: 1, - inputFormatter: [web3._extend.utils.fromDecial] - }), - new web3._extend.Method({ - name: 'startAutoDAG', - call: 'miner_startAutoDAG', - params: 0, - inputFormatter: [] - }), - new web3._extend.Method({ - name: 'stopAutoDAG', - call: 'miner_stopAutoDAG', - params: 0, - inputFormatter: [] - }), - new web3._extend.Method({ - name: 'makeDAG', - call: 'miner_makeDAG', - params: 1, - inputFormatter: [web3._extend.formatters.inputDefaultBlockNumberFormatter] - }) - ], - properties: - [ - new web3._extend.Property({ - name: 'hashrate', - getter: 'miner_hashrate', - outputFormatter: web3._extend.utils.toDecimal - }) - ] -}); -` diff --git a/rpc/api/net.go b/rpc/api/net.go deleted file mode 100644 index 9c6369615..000000000 --- a/rpc/api/net.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/shared" - "github.com/ethereum/go-ethereum/xeth" -) - -const ( - NetApiVersion = "1.0" -) - -var ( - // mapping between methods and handlers - netMapping = map[string]nethandler{ - "net_peerCount": (*netApi).PeerCount, - "net_listening": (*netApi).IsListening, - "net_version": (*netApi).Version, - } -) - -// net callback handler -type nethandler func(*netApi, *shared.Request) (interface{}, error) - -// net api provider -type netApi struct { - xeth *xeth.XEth - ethereum *eth.Ethereum - methods map[string]nethandler - codec codec.ApiCoder -} - -// create a new net api instance -func NewNetApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *netApi { - return &netApi{ - xeth: xeth, - ethereum: eth, - methods: netMapping, - codec: coder.New(nil), - } -} - -// collection with supported methods -func (self *netApi) Methods() []string { - methods := make([]string, len(self.methods)) - i := 0 - for k := range self.methods { - methods[i] = k - i++ - } - return methods -} - -// Execute given request -func (self *netApi) Execute(req *shared.Request) (interface{}, error) { - if callback, ok := self.methods[req.Method]; ok { - return callback(self, req) - } - - return nil, shared.NewNotImplementedError(req.Method) -} - -func (self *netApi) Name() string { - return shared.NetApiName -} - -func (self *netApi) ApiVersion() string { - return NetApiVersion -} - -// Number of connected peers -func (self *netApi) PeerCount(req *shared.Request) (interface{}, error) { - return newHexNum(self.xeth.PeerCount()), nil -} - -func (self *netApi) IsListening(req *shared.Request) (interface{}, error) { - return self.xeth.IsListening(), nil -} - -func (self *netApi) Version(req *shared.Request) (interface{}, error) { - return self.xeth.NetworkVersion(), nil -} diff --git a/rpc/api/net_js.go b/rpc/api/net_js.go deleted file mode 100644 index 2ee1f0041..000000000 --- a/rpc/api/net_js.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -const Net_JS = ` -web3._extend({ - property: 'net', - methods: - [ - new web3._extend.Method({ - name: 'addPeer', - call: 'net_addPeer', - params: 1, - inputFormatter: [null] - }) - ], - properties: - [ - new web3._extend.Property({ - name: 'version', - getter: 'net_version' - }) - ] -}); -` diff --git a/rpc/api/parsing.go b/rpc/api/parsing.go deleted file mode 100644 index 7667616ff..000000000 --- a/rpc/api/parsing.go +++ /dev/null @@ -1,522 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "bytes" - "encoding/binary" - "encoding/hex" - "encoding/json" - "math/big" - "strings" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/rpc/shared" -) - -type hexdata struct { - data []byte - isNil bool -} - -func (d *hexdata) String() string { - return "0x" + common.Bytes2Hex(d.data) -} - -func (d *hexdata) MarshalJSON() ([]byte, error) { - if d.isNil { - return json.Marshal(nil) - } - return json.Marshal(d.String()) -} - -func newHexData(input interface{}) *hexdata { - d := new(hexdata) - - if input == nil { - d.isNil = true - return d - } - switch input := input.(type) { - case []byte: - d.data = input - case common.Hash: - d.data = input.Bytes() - case *common.Hash: - if input == nil { - d.isNil = true - } else { - d.data = input.Bytes() - } - case common.Address: - d.data = input.Bytes() - case *common.Address: - if input == nil { - d.isNil = true - } else { - d.data = input.Bytes() - } - case types.Bloom: - d.data = input.Bytes() - case *types.Bloom: - if input == nil { - d.isNil = true - } else { - d.data = input.Bytes() - } - case *big.Int: - if input == nil { - d.isNil = true - } else { - d.data = input.Bytes() - } - case int64: - d.data = big.NewInt(input).Bytes() - case uint64: - buff := make([]byte, 8) - binary.BigEndian.PutUint64(buff, input) - d.data = buff - case int: - d.data = big.NewInt(int64(input)).Bytes() - case uint: - d.data = big.NewInt(int64(input)).Bytes() - case int8: - d.data = big.NewInt(int64(input)).Bytes() - case uint8: - d.data = big.NewInt(int64(input)).Bytes() - case int16: - d.data = big.NewInt(int64(input)).Bytes() - case uint16: - buff := make([]byte, 2) - binary.BigEndian.PutUint16(buff, input) - d.data = buff - case int32: - d.data = big.NewInt(int64(input)).Bytes() - case uint32: - buff := make([]byte, 4) - binary.BigEndian.PutUint32(buff, input) - d.data = buff - case string: // hexstring - // aaargh ffs TODO: avoid back-and-forth hex encodings where unneeded - bytes, err := hex.DecodeString(strings.TrimPrefix(input, "0x")) - if err != nil { - d.isNil = true - } else { - d.data = bytes - } - default: - d.isNil = true - } - - return d -} - -type hexnum struct { - data []byte - isNil bool -} - -func (d *hexnum) String() string { - // Get hex string from bytes - out := common.Bytes2Hex(d.data) - // Trim leading 0s - out = strings.TrimLeft(out, "0") - // Output "0x0" when value is 0 - if len(out) == 0 { - out = "0" - } - return "0x" + out -} - -func (d *hexnum) MarshalJSON() ([]byte, error) { - if d.isNil { - return json.Marshal(nil) - } - return json.Marshal(d.String()) -} - -func newHexNum(input interface{}) *hexnum { - d := new(hexnum) - - d.data = newHexData(input).data - - return d -} - -type BlockRes struct { - fullTx bool - - BlockNumber *hexnum `json:"number"` - BlockHash *hexdata `json:"hash"` - ParentHash *hexdata `json:"parentHash"` - Nonce *hexdata `json:"nonce"` - Sha3Uncles *hexdata `json:"sha3Uncles"` - LogsBloom *hexdata `json:"logsBloom"` - TransactionRoot *hexdata `json:"transactionsRoot"` - StateRoot *hexdata `json:"stateRoot"` - ReceiptRoot *hexdata `json:"receiptRoot"` - Miner *hexdata `json:"miner"` - Difficulty *hexnum `json:"difficulty"` - TotalDifficulty *hexnum `json:"totalDifficulty"` - Size *hexnum `json:"size"` - ExtraData *hexdata `json:"extraData"` - GasLimit *hexnum `json:"gasLimit"` - GasUsed *hexnum `json:"gasUsed"` - UnixTimestamp *hexnum `json:"timestamp"` - Transactions []*TransactionRes `json:"transactions"` - Uncles []*UncleRes `json:"uncles"` -} - -func (b *BlockRes) MarshalJSON() ([]byte, error) { - if b.fullTx { - var ext struct { - BlockNumber *hexnum `json:"number"` - BlockHash *hexdata `json:"hash"` - ParentHash *hexdata `json:"parentHash"` - Nonce *hexdata `json:"nonce"` - Sha3Uncles *hexdata `json:"sha3Uncles"` - LogsBloom *hexdata `json:"logsBloom"` - TransactionRoot *hexdata `json:"transactionsRoot"` - StateRoot *hexdata `json:"stateRoot"` - ReceiptRoot *hexdata `json:"receiptRoot"` - Miner *hexdata `json:"miner"` - Difficulty *hexnum `json:"difficulty"` - TotalDifficulty *hexnum `json:"totalDifficulty"` - Size *hexnum `json:"size"` - ExtraData *hexdata `json:"extraData"` - GasLimit *hexnum `json:"gasLimit"` - GasUsed *hexnum `json:"gasUsed"` - UnixTimestamp *hexnum `json:"timestamp"` - Transactions []*TransactionRes `json:"transactions"` - Uncles []*hexdata `json:"uncles"` - } - - ext.BlockNumber = b.BlockNumber - ext.BlockHash = b.BlockHash - ext.ParentHash = b.ParentHash - ext.Nonce = b.Nonce - ext.Sha3Uncles = b.Sha3Uncles - ext.LogsBloom = b.LogsBloom - ext.TransactionRoot = b.TransactionRoot - ext.StateRoot = b.StateRoot - ext.ReceiptRoot = b.ReceiptRoot - ext.Miner = b.Miner - ext.Difficulty = b.Difficulty - ext.TotalDifficulty = b.TotalDifficulty - ext.Size = b.Size - ext.ExtraData = b.ExtraData - ext.GasLimit = b.GasLimit - ext.GasUsed = b.GasUsed - ext.UnixTimestamp = b.UnixTimestamp - ext.Transactions = b.Transactions - ext.Uncles = make([]*hexdata, len(b.Uncles)) - for i, u := range b.Uncles { - ext.Uncles[i] = u.BlockHash - } - return json.Marshal(ext) - } else { - var ext struct { - BlockNumber *hexnum `json:"number"` - BlockHash *hexdata `json:"hash"` - ParentHash *hexdata `json:"parentHash"` - Nonce *hexdata `json:"nonce"` - Sha3Uncles *hexdata `json:"sha3Uncles"` - LogsBloom *hexdata `json:"logsBloom"` - TransactionRoot *hexdata `json:"transactionsRoot"` - StateRoot *hexdata `json:"stateRoot"` - ReceiptRoot *hexdata `json:"receiptRoot"` - Miner *hexdata `json:"miner"` - Difficulty *hexnum `json:"difficulty"` - TotalDifficulty *hexnum `json:"totalDifficulty"` - Size *hexnum `json:"size"` - ExtraData *hexdata `json:"extraData"` - GasLimit *hexnum `json:"gasLimit"` - GasUsed *hexnum `json:"gasUsed"` - UnixTimestamp *hexnum `json:"timestamp"` - Transactions []*hexdata `json:"transactions"` - Uncles []*hexdata `json:"uncles"` - } - - ext.BlockNumber = b.BlockNumber - ext.BlockHash = b.BlockHash - ext.ParentHash = b.ParentHash - ext.Nonce = b.Nonce - ext.Sha3Uncles = b.Sha3Uncles - ext.LogsBloom = b.LogsBloom - ext.TransactionRoot = b.TransactionRoot - ext.StateRoot = b.StateRoot - ext.ReceiptRoot = b.ReceiptRoot - ext.Miner = b.Miner - ext.Difficulty = b.Difficulty - ext.TotalDifficulty = b.TotalDifficulty - ext.Size = b.Size - ext.ExtraData = b.ExtraData - ext.GasLimit = b.GasLimit - ext.GasUsed = b.GasUsed - ext.UnixTimestamp = b.UnixTimestamp - ext.Transactions = make([]*hexdata, len(b.Transactions)) - for i, tx := range b.Transactions { - ext.Transactions[i] = tx.Hash - } - ext.Uncles = make([]*hexdata, len(b.Uncles)) - for i, u := range b.Uncles { - ext.Uncles[i] = u.BlockHash - } - return json.Marshal(ext) - } -} - -func NewBlockRes(block *types.Block, td *big.Int, fullTx bool) *BlockRes { - if block == nil { - return nil - } - - res := new(BlockRes) - res.fullTx = fullTx - res.BlockNumber = newHexNum(block.Number()) - res.BlockHash = newHexData(block.Hash()) - res.ParentHash = newHexData(block.ParentHash()) - res.Nonce = newHexData(block.Nonce()) - res.Sha3Uncles = newHexData(block.UncleHash()) - res.LogsBloom = newHexData(block.Bloom()) - res.TransactionRoot = newHexData(block.TxHash()) - res.StateRoot = newHexData(block.Root()) - res.ReceiptRoot = newHexData(block.ReceiptHash()) - res.Miner = newHexData(block.Coinbase()) - res.Difficulty = newHexNum(block.Difficulty()) - res.TotalDifficulty = newHexNum(td) - res.Size = newHexNum(block.Size().Int64()) - res.ExtraData = newHexData(block.Extra()) - res.GasLimit = newHexNum(block.GasLimit()) - res.GasUsed = newHexNum(block.GasUsed()) - res.UnixTimestamp = newHexNum(block.Time()) - - txs := block.Transactions() - res.Transactions = make([]*TransactionRes, len(txs)) - for i, tx := range txs { - res.Transactions[i] = NewTransactionRes(tx) - res.Transactions[i].BlockHash = res.BlockHash - res.Transactions[i].BlockNumber = res.BlockNumber - res.Transactions[i].TxIndex = newHexNum(i) - } - - uncles := block.Uncles() - res.Uncles = make([]*UncleRes, len(uncles)) - for i, uncle := range uncles { - res.Uncles[i] = NewUncleRes(uncle) - } - - return res -} - -type TransactionRes struct { - Hash *hexdata `json:"hash"` - Nonce *hexnum `json:"nonce"` - BlockHash *hexdata `json:"blockHash"` - BlockNumber *hexnum `json:"blockNumber"` - TxIndex *hexnum `json:"transactionIndex"` - From *hexdata `json:"from"` - To *hexdata `json:"to"` - Value *hexnum `json:"value"` - Gas *hexnum `json:"gas"` - GasPrice *hexnum `json:"gasPrice"` - Input *hexdata `json:"input"` -} - -func NewTransactionRes(tx *types.Transaction) *TransactionRes { - if tx == nil { - return nil - } - - var v = new(TransactionRes) - v.Hash = newHexData(tx.Hash()) - v.Nonce = newHexNum(tx.Nonce()) - // v.BlockHash = - // v.BlockNumber = - // v.TxIndex = - from, _ := tx.From() - v.From = newHexData(from) - v.To = newHexData(tx.To()) - v.Value = newHexNum(tx.Value()) - v.Gas = newHexNum(tx.Gas()) - v.GasPrice = newHexNum(tx.GasPrice()) - v.Input = newHexData(tx.Data()) - return v -} - -type UncleRes struct { - BlockNumber *hexnum `json:"number"` - BlockHash *hexdata `json:"hash"` - ParentHash *hexdata `json:"parentHash"` - Nonce *hexdata `json:"nonce"` - Sha3Uncles *hexdata `json:"sha3Uncles"` - ReceiptHash *hexdata `json:"receiptHash"` - LogsBloom *hexdata `json:"logsBloom"` - TransactionRoot *hexdata `json:"transactionsRoot"` - StateRoot *hexdata `json:"stateRoot"` - Miner *hexdata `json:"miner"` - Difficulty *hexnum `json:"difficulty"` - ExtraData *hexdata `json:"extraData"` - GasLimit *hexnum `json:"gasLimit"` - GasUsed *hexnum `json:"gasUsed"` - UnixTimestamp *hexnum `json:"timestamp"` -} - -func NewUncleRes(h *types.Header) *UncleRes { - if h == nil { - return nil - } - - var v = new(UncleRes) - v.BlockNumber = newHexNum(h.Number) - v.BlockHash = newHexData(h.Hash()) - v.ParentHash = newHexData(h.ParentHash) - v.Sha3Uncles = newHexData(h.UncleHash) - v.Nonce = newHexData(h.Nonce[:]) - v.LogsBloom = newHexData(h.Bloom) - v.TransactionRoot = newHexData(h.TxHash) - v.StateRoot = newHexData(h.Root) - v.Miner = newHexData(h.Coinbase) - v.Difficulty = newHexNum(h.Difficulty) - v.ExtraData = newHexData(h.Extra) - v.GasLimit = newHexNum(h.GasLimit) - v.GasUsed = newHexNum(h.GasUsed) - v.UnixTimestamp = newHexNum(h.Time) - v.ReceiptHash = newHexData(h.ReceiptHash) - - return v -} - -// type FilterLogRes struct { -// Hash string `json:"hash"` -// Address string `json:"address"` -// Data string `json:"data"` -// BlockNumber string `json:"blockNumber"` -// TransactionHash string `json:"transactionHash"` -// BlockHash string `json:"blockHash"` -// TransactionIndex string `json:"transactionIndex"` -// LogIndex string `json:"logIndex"` -// } - -// type FilterWhisperRes struct { -// Hash string `json:"hash"` -// From string `json:"from"` -// To string `json:"to"` -// Expiry string `json:"expiry"` -// Sent string `json:"sent"` -// Ttl string `json:"ttl"` -// Topics string `json:"topics"` -// Payload string `json:"payload"` -// WorkProved string `json:"workProved"` -// } - -type ReceiptRes struct { - TransactionHash *hexdata `json:"transactionHash"` - TransactionIndex *hexnum `json:"transactionIndex"` - BlockNumber *hexnum `json:"blockNumber"` - BlockHash *hexdata `json:"blockHash"` - CumulativeGasUsed *hexnum `json:"cumulativeGasUsed"` - GasUsed *hexnum `json:"gasUsed"` - ContractAddress *hexdata `json:"contractAddress"` - Logs *[]interface{} `json:"logs"` -} - -func NewReceiptRes(rec *types.Receipt) *ReceiptRes { - if rec == nil { - return nil - } - - var v = new(ReceiptRes) - v.TransactionHash = newHexData(rec.TxHash) - if rec.GasUsed != nil { - v.GasUsed = newHexNum(rec.GasUsed.Bytes()) - } - v.CumulativeGasUsed = newHexNum(rec.CumulativeGasUsed) - - // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation - if bytes.Compare(rec.ContractAddress.Bytes(), bytes.Repeat([]byte{0}, 20)) != 0 { - v.ContractAddress = newHexData(rec.ContractAddress) - } - - logs := make([]interface{}, len(rec.Logs)) - for i, log := range rec.Logs { - logs[i] = NewLogRes(log) - } - v.Logs = &logs - - return v -} - -func numString(raw interface{}) (*big.Int, error) { - var number *big.Int - // Parse as integer - num, ok := raw.(float64) - if ok { - number = big.NewInt(int64(num)) - return number, nil - } - - // Parse as string/hexstring - str, ok := raw.(string) - if ok { - number = common.String2Big(str) - return number, nil - } - - return nil, shared.NewInvalidTypeError("", "not a number or string") -} - -func blockHeight(raw interface{}, number *int64) error { - // Parse as integer - num, ok := raw.(float64) - if ok { - *number = int64(num) - return nil - } - - // Parse as string/hexstring - str, ok := raw.(string) - if !ok { - return shared.NewInvalidTypeError("", "not a number or string") - } - - switch str { - case "earliest": - *number = 0 - case "latest": - *number = -1 - case "pending": - *number = -2 - default: - if common.HasHexPrefix(str) { - *number = common.String2Big(str).Int64() - } else { - return shared.NewInvalidTypeError("blockNumber", "is not a valid string") - } - } - - return nil -} - -func blockHeightFromJson(msg json.RawMessage, number *int64) error { - var raw interface{} - if err := json.Unmarshal(msg, &raw); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - return blockHeight(raw, number) -} diff --git a/rpc/api/personal.go b/rpc/api/personal.go deleted file mode 100644 index 4f347c610..000000000 --- a/rpc/api/personal.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "fmt" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/shared" - "github.com/ethereum/go-ethereum/xeth" -) - -const ( - PersonalApiVersion = "1.0" -) - -var ( - // mapping between methods and handlers - personalMapping = map[string]personalhandler{ - "personal_listAccounts": (*personalApi).ListAccounts, - "personal_newAccount": (*personalApi).NewAccount, - "personal_unlockAccount": (*personalApi).UnlockAccount, - } -) - -// net callback handler -type personalhandler func(*personalApi, *shared.Request) (interface{}, error) - -// net api provider -type personalApi struct { - xeth *xeth.XEth - ethereum *eth.Ethereum - methods map[string]personalhandler - codec codec.ApiCoder -} - -// create a new net api instance -func NewPersonalApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *personalApi { - return &personalApi{ - xeth: xeth, - ethereum: eth, - methods: personalMapping, - codec: coder.New(nil), - } -} - -// collection with supported methods -func (self *personalApi) Methods() []string { - methods := make([]string, len(self.methods)) - i := 0 - for k := range self.methods { - methods[i] = k - i++ - } - return methods -} - -// Execute given request -func (self *personalApi) Execute(req *shared.Request) (interface{}, error) { - if callback, ok := self.methods[req.Method]; ok { - return callback(self, req) - } - - return nil, shared.NewNotImplementedError(req.Method) -} - -func (self *personalApi) Name() string { - return shared.PersonalApiName -} - -func (self *personalApi) ApiVersion() string { - return PersonalApiVersion -} - -func (self *personalApi) ListAccounts(req *shared.Request) (interface{}, error) { - return self.xeth.Accounts(), nil -} - -func (self *personalApi) NewAccount(req *shared.Request) (interface{}, error) { - args := new(NewAccountArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - var passwd string - if args.Passphrase == nil { - fe := self.xeth.Frontend() - if fe == nil { - return false, fmt.Errorf("unable to create account: unable to interact with user") - } - var ok bool - passwd, ok = fe.AskPassword() - if !ok { - return false, fmt.Errorf("unable to create account: no password given") - } - } else { - passwd = *args.Passphrase - } - am := self.ethereum.AccountManager() - acc, err := am.NewAccount(passwd) - return acc.Address.Hex(), err -} - -func (self *personalApi) UnlockAccount(req *shared.Request) (interface{}, error) { - args := new(UnlockAccountArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, shared.NewDecodeParamError(err.Error()) - } - - if args.Passphrase == nil { - fe := self.xeth.Frontend() - if fe == nil { - return false, fmt.Errorf("No password provided") - } - return fe.UnlockAccount(common.HexToAddress(args.Address).Bytes()), nil - } - - am := self.ethereum.AccountManager() - addr := common.HexToAddress(args.Address) - - err := am.TimedUnlock(addr, *args.Passphrase, time.Duration(args.Duration)*time.Second) - return err == nil, err -} diff --git a/rpc/api/personal_args.go b/rpc/api/personal_args.go deleted file mode 100644 index 5d215c71d..000000000 --- a/rpc/api/personal_args.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "encoding/json" - - "github.com/ethereum/go-ethereum/rpc/shared" -) - -type NewAccountArgs struct { - Passphrase *string -} - -func (args *NewAccountArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) >= 1 && obj[0] != nil { - if passphrasestr, ok := obj[0].(string); ok { - args.Passphrase = &passphrasestr - } else { - return shared.NewInvalidTypeError("passphrase", "not a string") - } - } - - return nil -} - -type UnlockAccountArgs struct { - Address string - Passphrase *string - Duration int -} - -func (args *UnlockAccountArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - args.Duration = 0 - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - if addrstr, ok := obj[0].(string); ok { - args.Address = addrstr - } else { - return shared.NewInvalidTypeError("address", "not a string") - } - - if len(obj) >= 2 && obj[1] != nil { - if passphrasestr, ok := obj[1].(string); ok { - args.Passphrase = &passphrasestr - } else { - return shared.NewInvalidTypeError("passphrase", "not a string") - } - } - - if len(obj) >= 3 && obj[2] != nil { - if duration, ok := obj[2].(float64); ok { - args.Duration = int(duration) - } - } - - return nil -} diff --git a/rpc/api/personal_js.go b/rpc/api/personal_js.go deleted file mode 100644 index 84c669af7..000000000 --- a/rpc/api/personal_js.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -const Personal_JS = ` -web3._extend({ - property: 'personal', - methods: - [ - new web3._extend.Method({ - name: 'newAccount', - call: 'personal_newAccount', - params: 1, - inputFormatter: [null], - outputFormatter: web3._extend.utils.toAddress - }), - new web3._extend.Method({ - name: 'unlockAccount', - call: 'personal_unlockAccount', - params: 3, - inputFormatter: [null, null, null] - }), - new web3._extend.Method({ - name: 'lockAccount', - call: 'personal_lockAccount', - params: 1 - }) - ], - properties: - [ - new web3._extend.Property({ - name: 'listAccounts', - getter: 'personal_listAccounts' - }) - ] -}); -` diff --git a/rpc/api/shh.go b/rpc/api/shh.go deleted file mode 100644 index 60e805605..000000000 --- a/rpc/api/shh.go +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/shared" - "github.com/ethereum/go-ethereum/xeth" -) - -const ( - ShhApiVersion = "1.0" -) - -var ( - // mapping between methods and handlers - shhMapping = map[string]shhhandler{ - "shh_version": (*shhApi).Version, - "shh_post": (*shhApi).Post, - "shh_hasIdentity": (*shhApi).HasIdentity, - "shh_newIdentity": (*shhApi).NewIdentity, - "shh_newFilter": (*shhApi).NewFilter, - "shh_uninstallFilter": (*shhApi).UninstallFilter, - "shh_getMessages": (*shhApi).GetMessages, - "shh_getFilterChanges": (*shhApi).GetFilterChanges, - } -) - -func newWhisperOfflineError(method string) error { - return shared.NewNotAvailableError(method, "whisper offline") -} - -// net callback handler -type shhhandler func(*shhApi, *shared.Request) (interface{}, error) - -// shh api provider -type shhApi struct { - xeth *xeth.XEth - ethereum *eth.Ethereum - methods map[string]shhhandler - codec codec.ApiCoder -} - -// create a new whisper api instance -func NewShhApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *shhApi { - return &shhApi{ - xeth: xeth, - ethereum: eth, - methods: shhMapping, - codec: coder.New(nil), - } -} - -// collection with supported methods -func (self *shhApi) Methods() []string { - methods := make([]string, len(self.methods)) - i := 0 - for k := range self.methods { - methods[i] = k - i++ - } - return methods -} - -// Execute given request -func (self *shhApi) Execute(req *shared.Request) (interface{}, error) { - if callback, ok := self.methods[req.Method]; ok { - return callback(self, req) - } - - return nil, shared.NewNotImplementedError(req.Method) -} - -func (self *shhApi) Name() string { - return shared.ShhApiName -} - -func (self *shhApi) ApiVersion() string { - return ShhApiVersion -} - -func (self *shhApi) Version(req *shared.Request) (interface{}, error) { - w := self.xeth.Whisper() - if w == nil { - return nil, newWhisperOfflineError(req.Method) - } - - return w.Version(), nil -} - -func (self *shhApi) Post(req *shared.Request) (interface{}, error) { - w := self.xeth.Whisper() - if w == nil { - return nil, newWhisperOfflineError(req.Method) - } - - args := new(WhisperMessageArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, err - } - - err := w.Post(args.Payload, args.To, args.From, args.Topics, args.Priority, args.Ttl) - if err != nil { - return false, err - } - - return true, nil -} - -func (self *shhApi) HasIdentity(req *shared.Request) (interface{}, error) { - w := self.xeth.Whisper() - if w == nil { - return nil, newWhisperOfflineError(req.Method) - } - - args := new(WhisperIdentityArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, err - } - - return w.HasIdentity(args.Identity), nil -} - -func (self *shhApi) NewIdentity(req *shared.Request) (interface{}, error) { - w := self.xeth.Whisper() - if w == nil { - return nil, newWhisperOfflineError(req.Method) - } - - return w.NewIdentity(), nil -} - -func (self *shhApi) NewFilter(req *shared.Request) (interface{}, error) { - args := new(WhisperFilterArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, err - } - - id := self.xeth.NewWhisperFilter(args.To, args.From, args.Topics) - return newHexNum(big.NewInt(int64(id)).Bytes()), nil -} - -func (self *shhApi) UninstallFilter(req *shared.Request) (interface{}, error) { - args := new(FilterIdArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, err - } - return self.xeth.UninstallWhisperFilter(args.Id), nil -} - -func (self *shhApi) GetFilterChanges(req *shared.Request) (interface{}, error) { - w := self.xeth.Whisper() - if w == nil { - return nil, newWhisperOfflineError(req.Method) - } - - // Retrieve all the new messages arrived since the last request - args := new(FilterIdArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, err - } - - return self.xeth.WhisperMessagesChanged(args.Id), nil -} - -func (self *shhApi) GetMessages(req *shared.Request) (interface{}, error) { - w := self.xeth.Whisper() - if w == nil { - return nil, newWhisperOfflineError(req.Method) - } - - // Retrieve all the cached messages matching a specific, existing filter - args := new(FilterIdArgs) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, err - } - - return self.xeth.WhisperMessages(args.Id), nil -} diff --git a/rpc/api/shh_args.go b/rpc/api/shh_args.go deleted file mode 100644 index 468a0b98f..000000000 --- a/rpc/api/shh_args.go +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "encoding/json" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/rpc/shared" -) - -type WhisperMessageArgs struct { - Payload string - To string - From string - Topics []string - Priority uint32 - Ttl uint32 -} - -func (args *WhisperMessageArgs) UnmarshalJSON(b []byte) (err error) { - var obj []struct { - Payload string - To string - From string - Topics []string - Priority interface{} - Ttl interface{} - } - - if err = json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - args.Payload = obj[0].Payload - args.To = obj[0].To - args.From = obj[0].From - args.Topics = obj[0].Topics - - var num *big.Int - if num, err = numString(obj[0].Priority); err != nil { - return err - } - args.Priority = uint32(num.Int64()) - - if num, err = numString(obj[0].Ttl); err != nil { - return err - } - args.Ttl = uint32(num.Int64()) - - return nil -} - -type WhisperIdentityArgs struct { - Identity string -} - -func (args *WhisperIdentityArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - - argstr, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("arg0", "not a string") - } - - args.Identity = argstr - - return nil -} - -type WhisperFilterArgs struct { - To string - From string - Topics [][]string -} - -// UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a -// JSON message blob into a WhisperFilterArgs structure. -func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { - // Unmarshal the JSON message and sanity check - var obj []struct { - To interface{} `json:"to"` - From interface{} `json:"from"` - Topics interface{} `json:"topics"` - } - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) - } - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) - } - // Retrieve the simple data contents of the filter arguments - if obj[0].To == nil { - args.To = "" - } else { - argstr, ok := obj[0].To.(string) - if !ok { - return shared.NewInvalidTypeError("to", "is not a string") - } - args.To = argstr - } - if obj[0].From == nil { - args.From = "" - } else { - argstr, ok := obj[0].From.(string) - if !ok { - return shared.NewInvalidTypeError("from", "is not a string") - } - args.From = argstr - } - // Construct the nested topic array - if obj[0].Topics != nil { - // Make sure we have an actual topic array - list, ok := obj[0].Topics.([]interface{}) - if !ok { - return shared.NewInvalidTypeError("topics", "is not an array") - } - // Iterate over each topic and handle nil, string or array - topics := make([][]string, len(list)) - for idx, field := range list { - switch value := field.(type) { - case nil: - topics[idx] = []string{} - - case string: - topics[idx] = []string{value} - - case []interface{}: - topics[idx] = make([]string, len(value)) - for i, nested := range value { - switch value := nested.(type) { - case nil: - topics[idx][i] = "" - - case string: - topics[idx][i] = value - - default: - return shared.NewInvalidTypeError(fmt.Sprintf("topic[%d][%d]", idx, i), "is not a string") - } - } - default: - return shared.NewInvalidTypeError(fmt.Sprintf("topic[%d]", idx), "not a string or array") - } - } - args.Topics = topics - } - return nil -} diff --git a/rpc/api/shh_js.go b/rpc/api/shh_js.go deleted file mode 100644 index a92ad1644..000000000 --- a/rpc/api/shh_js.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -const Shh_JS = ` -web3._extend({ - property: 'shh', - methods: - [ - - ], - properties: - [ - new web3._extend.Property({ - name: 'version', - getter: 'shh_version' - }) - ] -}); -` diff --git a/rpc/api/txpool.go b/rpc/api/txpool.go deleted file mode 100644 index 27e40cae5..000000000 --- a/rpc/api/txpool.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/shared" - "github.com/ethereum/go-ethereum/xeth" -) - -const ( - TxPoolApiVersion = "1.0" -) - -var ( - // mapping between methods and handlers - txpoolMapping = map[string]txpoolhandler{ - "txpool_status": (*txPoolApi).Status, - } -) - -// net callback handler -type txpoolhandler func(*txPoolApi, *shared.Request) (interface{}, error) - -// txpool api provider -type txPoolApi struct { - xeth *xeth.XEth - ethereum *eth.Ethereum - methods map[string]txpoolhandler - codec codec.ApiCoder -} - -// create a new txpool api instance -func NewTxPoolApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *txPoolApi { - return &txPoolApi{ - xeth: xeth, - ethereum: eth, - methods: txpoolMapping, - codec: coder.New(nil), - } -} - -// collection with supported methods -func (self *txPoolApi) Methods() []string { - methods := make([]string, len(self.methods)) - i := 0 - for k := range self.methods { - methods[i] = k - i++ - } - return methods -} - -// Execute given request -func (self *txPoolApi) Execute(req *shared.Request) (interface{}, error) { - if callback, ok := self.methods[req.Method]; ok { - return callback(self, req) - } - - return nil, shared.NewNotImplementedError(req.Method) -} - -func (self *txPoolApi) Name() string { - return shared.TxPoolApiName -} - -func (self *txPoolApi) ApiVersion() string { - return TxPoolApiVersion -} - -func (self *txPoolApi) Status(req *shared.Request) (interface{}, error) { - pending, queue := self.ethereum.TxPool().Stats() - return map[string]int{ - "pending": pending, - "queued": queue, - }, nil -} diff --git a/rpc/api/txpool_js.go b/rpc/api/txpool_js.go deleted file mode 100644 index b6c29871a..000000000 --- a/rpc/api/txpool_js.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -const TxPool_JS = ` -web3._extend({ - property: 'txpool', - methods: - [ - ], - properties: - [ - new web3._extend.Property({ - name: 'status', - getter: 'txpool_status' - }) - ] -}); -` diff --git a/rpc/api/utils.go b/rpc/api/utils.go deleted file mode 100644 index 794b6abee..000000000 --- a/rpc/api/utils.go +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "strings" - - "fmt" - - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/shared" - "github.com/ethereum/go-ethereum/xeth" -) - -var ( - // Mapping between the different methods each api supports - AutoCompletion = map[string][]string{ - "admin": []string{ - "addPeer", - "datadir", - "enableUserAgent", - "exportChain", - "getContractInfo", - "httpGet", - "importChain", - "nodeInfo", - "peers", - "register", - "registerUrl", - "saveInfo", - "setGlobalRegistrar", - "setHashReg", - "setUrlHint", - "setSolc", - "sleep", - "sleepBlocks", - "startNatSpec", - "startRPC", - "stopNatSpec", - "stopRPC", - "verbosity", - }, - "db": []string{ - "getString", - "putString", - "getHex", - "putHex", - }, - "debug": []string{ - "dumpBlock", - "getBlockRlp", - "metrics", - "printBlock", - "processBlock", - "seedHash", - "setHead", - }, - "eth": []string{ - "accounts", - "blockNumber", - "call", - "contract", - "coinbase", - "compile.lll", - "compile.serpent", - "compile.solidity", - "contract", - "defaultAccount", - "defaultBlock", - "estimateGas", - "filter", - "getBalance", - "getBlock", - "getBlockTransactionCount", - "getBlockUncleCount", - "getCode", - "getNatSpec", - "getCompilers", - "gasPrice", - "getStorageAt", - "getTransaction", - "getTransactionCount", - "getTransactionFromBlock", - "getTransactionReceipt", - "getUncle", - "hashrate", - "mining", - "namereg", - "pendingTransactions", - "resend", - "sendRawTransaction", - "sendTransaction", - "sign", - "syncing", - }, - "miner": []string{ - "hashrate", - "makeDAG", - "setEtherbase", - "setExtra", - "setGasPrice", - "startAutoDAG", - "start", - "stopAutoDAG", - "stop", - }, - "net": []string{ - "peerCount", - "listening", - }, - "personal": []string{ - "listAccounts", - "newAccount", - "unlockAccount", - }, - "shh": []string{ - "post", - "newIdentity", - "hasIdentity", - "newGroup", - "addToGroup", - "filter", - }, - "txpool": []string{ - "status", - }, - "web3": []string{ - "sha3", - "version", - "fromWei", - "toWei", - "toHex", - "toAscii", - "fromAscii", - "toBigNumber", - "isAddress", - }, - } -) - -// Parse a comma separated API string to individual api's -func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, stack *node.Node) ([]shared.EthereumApi, error) { - if len(strings.TrimSpace(apistr)) == 0 { - return nil, fmt.Errorf("Empty apistr provided") - } - - names := strings.Split(apistr, ",") - apis := make([]shared.EthereumApi, len(names)) - - var eth *eth.Ethereum - if stack != nil { - if err := stack.Service(ð); err != nil { - return nil, err - } - } - for i, name := range names { - switch strings.ToLower(strings.TrimSpace(name)) { - case shared.AdminApiName: - apis[i] = NewAdminApi(xeth, stack, codec) - case shared.DebugApiName: - apis[i] = NewDebugApi(xeth, eth, codec) - case shared.DbApiName: - apis[i] = NewDbApi(xeth, eth, codec) - case shared.EthApiName: - apis[i] = NewEthApi(xeth, eth, codec) - case shared.MinerApiName: - apis[i] = NewMinerApi(eth, codec) - case shared.NetApiName: - apis[i] = NewNetApi(xeth, eth, codec) - case shared.ShhApiName: - apis[i] = NewShhApi(xeth, eth, codec) - case shared.TxPoolApiName: - apis[i] = NewTxPoolApi(xeth, eth, codec) - case shared.PersonalApiName: - apis[i] = NewPersonalApi(xeth, eth, codec) - case shared.Web3ApiName: - apis[i] = NewWeb3Api(xeth, codec) - case "rpc": // gives information about the RPC interface - continue - default: - return nil, fmt.Errorf("Unknown API '%s'", name) - } - } - return apis, nil -} - -func Javascript(name string) string { - switch strings.ToLower(strings.TrimSpace(name)) { - case shared.AdminApiName: - return Admin_JS - case shared.DebugApiName: - return Debug_JS - case shared.DbApiName: - return Db_JS - case shared.EthApiName: - return Eth_JS - case shared.MinerApiName: - return Miner_JS - case shared.NetApiName: - return Net_JS - case shared.ShhApiName: - return Shh_JS - case shared.TxPoolApiName: - return TxPool_JS - case shared.PersonalApiName: - return Personal_JS - } - - return "" -} diff --git a/rpc/api/web3.go b/rpc/api/web3.go deleted file mode 100644 index e2d8543d3..000000000 --- a/rpc/api/web3.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package api - -import ( - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/shared" - "github.com/ethereum/go-ethereum/xeth" -) - -const ( - Web3ApiVersion = "1.0" -) - -var ( - // mapping between methods and handlers - Web3Mapping = map[string]web3handler{ - "web3_sha3": (*web3Api).Sha3, - "web3_clientVersion": (*web3Api).ClientVersion, - } -) - -// web3 callback handler -type web3handler func(*web3Api, *shared.Request) (interface{}, error) - -// web3 api provider -type web3Api struct { - xeth *xeth.XEth - methods map[string]web3handler - codec codec.ApiCoder -} - -// create a new web3 api instance -func NewWeb3Api(xeth *xeth.XEth, coder codec.Codec) *web3Api { - return &web3Api{ - xeth: xeth, - methods: Web3Mapping, - codec: coder.New(nil), - } -} - -// collection with supported methods -func (self *web3Api) Methods() []string { - methods := make([]string, len(self.methods)) - i := 0 - for k := range self.methods { - methods[i] = k - i++ - } - return methods -} - -// Execute given request -func (self *web3Api) Execute(req *shared.Request) (interface{}, error) { - if callback, ok := self.methods[req.Method]; ok { - return callback(self, req) - } - - return nil, &shared.NotImplementedError{req.Method} -} - -func (self *web3Api) Name() string { - return shared.Web3ApiName -} - -func (self *web3Api) ApiVersion() string { - return Web3ApiVersion -} - -// Calculates the sha3 over req.Params.Data -func (self *web3Api) Sha3(req *shared.Request) (interface{}, error) { - args := new(Sha3Args) - if err := self.codec.Decode(req.Params, &args); err != nil { - return nil, err - } - - return common.ToHex(crypto.Sha3(common.FromHex(args.Data))), nil -} - -// returns the xeth client vrsion -func (self *web3Api) ClientVersion(req *shared.Request) (interface{}, error) { - return self.xeth.ClientVersion(), nil -} diff --git a/rpc/codec/codec.go b/rpc/codec/codec.go deleted file mode 100644 index 786080b44..000000000 --- a/rpc/codec/codec.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package codec - -import ( - "net" - "strconv" - - "github.com/ethereum/go-ethereum/rpc/shared" -) - -type Codec int - -// (de)serialization support for rpc interface -type ApiCoder interface { - // Parse message to request from underlying stream - ReadRequest() ([]*shared.Request, bool, error) - // Parse response message from underlying stream - ReadResponse() (interface{}, error) - // Read raw message from underlying stream - Recv() (interface{}, error) - // Encode response to encoded form in underlying stream - WriteResponse(interface{}) error - // Decode single message from data - Decode([]byte, interface{}) error - // Encode msg to encoded form - Encode(msg interface{}) ([]byte, error) - // close the underlying stream - Close() -} - -// supported codecs -const ( - JSON Codec = iota - nCodecs -) - -var ( - // collection with supported coders - coders = make([]func(net.Conn) ApiCoder, nCodecs) -) - -// create a new coder instance -func (c Codec) New(conn net.Conn) ApiCoder { - switch c { - case JSON: - return NewJsonCoder(conn) - } - - panic("codec: request for codec #" + strconv.Itoa(int(c)) + " is unavailable") -} diff --git a/rpc/codec/json.go b/rpc/codec/json.go deleted file mode 100644 index cfc449143..000000000 --- a/rpc/codec/json.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package codec - -import ( - "encoding/json" - "fmt" - "net" - "time" - "strings" - - "github.com/ethereum/go-ethereum/rpc/shared" -) - -const ( - READ_TIMEOUT = 60 // in seconds - MAX_REQUEST_SIZE = 1024 * 1024 - MAX_RESPONSE_SIZE = 1024 * 1024 -) - -// Json serialization support -type JsonCodec struct { - c net.Conn - d *json.Decoder -} - -// Create new JSON coder instance -func NewJsonCoder(conn net.Conn) ApiCoder { - return &JsonCodec{ - c: conn, - d: json.NewDecoder(conn), - } -} - -// Read incoming request and parse it to RPC request -func (self *JsonCodec) ReadRequest() (requests []*shared.Request, isBatch bool, err error) { - deadline := time.Now().Add(READ_TIMEOUT * time.Second) - if err := self.c.SetDeadline(deadline); err != nil { - return nil, false, err - } - - var incoming json.RawMessage - err = self.d.Decode(&incoming) - if err == nil { - isBatch = incoming[0] == '[' - if isBatch { - requests = make([]*shared.Request, 0) - err = json.Unmarshal(incoming, &requests) - } else { - requests = make([]*shared.Request, 1) - var singleRequest shared.Request - if err = json.Unmarshal(incoming, &singleRequest); err == nil { - requests[0] = &singleRequest - } - } - return - } - - self.c.Close() - return nil, false, err -} - -func (self *JsonCodec) Recv() (interface{}, error) { - var msg json.RawMessage - err := self.d.Decode(&msg) - if err != nil { - self.c.Close() - return nil, err - } - - return msg, err -} - -func (self *JsonCodec) ReadResponse() (interface{}, error) { - in, err := self.Recv() - if err != nil { - return nil, err - } - - if msg, ok := in.(json.RawMessage); ok { - var req *shared.Request - if err = json.Unmarshal(msg, &req); err == nil && strings.HasPrefix(req.Method, "agent_") { - return req, nil - } - - var failure *shared.ErrorResponse - if err = json.Unmarshal(msg, &failure); err == nil && failure.Error != nil { - return failure, fmt.Errorf(failure.Error.Message) - } - - var success *shared.SuccessResponse - if err = json.Unmarshal(msg, &success); err == nil { - return success, nil - } - } - - return in, err -} - -// Decode data -func (self *JsonCodec) Decode(data []byte, msg interface{}) error { - return json.Unmarshal(data, msg) -} - -// Encode message -func (self *JsonCodec) Encode(msg interface{}) ([]byte, error) { - return json.Marshal(msg) -} - -// Parse JSON data from conn to obj -func (self *JsonCodec) WriteResponse(res interface{}) error { - data, err := json.Marshal(res) - if err != nil { - self.c.Close() - return err - } - - bytesWritten := 0 - - for bytesWritten < len(data) { - n, err := self.c.Write(data[bytesWritten:]) - if err != nil { - self.c.Close() - return err - } - bytesWritten += n - } - - return nil -} - -// Close decoder and encoder -func (self *JsonCodec) Close() { - self.c.Close() -} diff --git a/rpc/codec/json_test.go b/rpc/codec/json_test.go deleted file mode 100644 index 01ef77e57..000000000 --- a/rpc/codec/json_test.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package codec - -import ( - "bytes" - "io" - "net" - "testing" - "time" -) - -type jsonTestConn struct { - buffer *bytes.Buffer -} - -func newJsonTestConn(data []byte) *jsonTestConn { - return &jsonTestConn{ - buffer: bytes.NewBuffer(data), - } -} - -func (self *jsonTestConn) Read(p []byte) (n int, err error) { - return self.buffer.Read(p) -} - -func (self *jsonTestConn) Write(p []byte) (n int, err error) { - return self.buffer.Write(p) -} - -func (self *jsonTestConn) Close() error { - // not implemented - return nil -} - -func (self *jsonTestConn) LocalAddr() net.Addr { - // not implemented - return nil -} - -func (self *jsonTestConn) RemoteAddr() net.Addr { - // not implemented - return nil -} - -func (self *jsonTestConn) SetDeadline(t time.Time) error { - return nil -} - -func (self *jsonTestConn) SetReadDeadline(t time.Time) error { - return nil -} - -func (self *jsonTestConn) SetWriteDeadline(t time.Time) error { - return nil -} - -func TestJsonDecoderWithValidRequest(t *testing.T) { - reqdata := []byte(`{"jsonrpc":"2.0","method":"modules","params":[],"id":64}`) - decoder := newJsonTestConn(reqdata) - - jsonDecoder := NewJsonCoder(decoder) - requests, batch, err := jsonDecoder.ReadRequest() - - if err != nil { - t.Errorf("Read valid request failed - %v", err) - } - - if len(requests) != 1 { - t.Errorf("Expected to get a single request but got %d", len(requests)) - } - - if batch { - t.Errorf("Got batch indication while expecting single request") - } - - if requests[0].Id != float64(64) { - t.Errorf("Expected req.Id == 64 but got %v", requests[0].Id) - } - - if requests[0].Method != "modules" { - t.Errorf("Expected req.Method == 'modules' got '%s'", requests[0].Method) - } -} - -func TestJsonDecoderWithValidBatchRequest(t *testing.T) { - reqdata := []byte(`[{"jsonrpc":"2.0","method":"modules","params":[],"id":64}, - {"jsonrpc":"2.0","method":"modules","params":[],"id":64}]`) - decoder := newJsonTestConn(reqdata) - - jsonDecoder := NewJsonCoder(decoder) - requests, batch, err := jsonDecoder.ReadRequest() - - if err != nil { - t.Errorf("Read valid batch request failed - %v", err) - } - - if len(requests) != 2 { - t.Errorf("Expected to get two requests but got %d", len(requests)) - } - - if !batch { - t.Errorf("Got no batch indication while expecting batch request") - } - - for i := 0; i < len(requests); i++ { - if requests[i].Id != float64(64) { - t.Errorf("Expected req.Id == 64 but got %v", requests[i].Id) - } - - if requests[i].Method != "modules" { - t.Errorf("Expected req.Method == 'modules' got '%s'", requests[i].Method) - } - } -} - -func TestJsonDecoderWithInvalidIncompleteMessage(t *testing.T) { - reqdata := []byte(`{"jsonrpc":"2.0","method":"modules","pa`) - decoder := newJsonTestConn(reqdata) - - jsonDecoder := NewJsonCoder(decoder) - requests, batch, err := jsonDecoder.ReadRequest() - - if err != io.ErrUnexpectedEOF { - t.Errorf("Expected to read an incomplete request err but got %v", err) - } - - // remaining message - decoder.Write([]byte(`rams":[],"id:64"}`)) - requests, batch, err = jsonDecoder.ReadRequest() - - if err == nil { - t.Errorf("Expected an error but got nil") - } - - if len(requests) != 0 { - t.Errorf("Expected to get no requests but got %d", len(requests)) - } - - if batch { - t.Errorf("Got batch indication while expecting non batch") - } -} diff --git a/rpc/comms/comms.go b/rpc/comms/comms.go deleted file mode 100644 index 61fba5722..000000000 --- a/rpc/comms/comms.go +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package comms - -import ( - "io" - "net" - - "fmt" - "strings" - - "strconv" - - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/shared" -) - -const ( - maxHttpSizeReqLength = 1024 * 1024 // 1MB -) - -var ( - // List with all API's which are offered over the in proc interface by default - DefaultInProcApis = shared.AllApis - - // List with all API's which are offered over the IPC interface by default - DefaultIpcApis = shared.AllApis - - // List with API's which are offered over thr HTTP/RPC interface by default - DefaultHttpRpcApis = strings.Join([]string{ - shared.DbApiName, shared.EthApiName, shared.NetApiName, shared.Web3ApiName, - }, ",") -) - -type EthereumClient interface { - // Close underlying connection - Close() - // Send request - Send(interface{}) error - // Receive response - Recv() (interface{}, error) - // List with modules this client supports - SupportedModules() (map[string]string, error) -} - -func handle(id int, conn net.Conn, api shared.EthereumApi, c codec.Codec) { - codec := c.New(conn) - - defer func() { - if r := recover(); r != nil { - glog.Errorf("panic: %v\n", r) - } - codec.Close() - }() - - for { - requests, isBatch, err := codec.ReadRequest() - if err == io.EOF { - return - } else if err != nil { - glog.V(logger.Debug).Infof("Closed IPC Conn %06d recv err - %v\n", id, err) - return - } - - if isBatch { - responses := make([]*interface{}, len(requests)) - responseCount := 0 - for _, req := range requests { - res, err := api.Execute(req) - if req.Id != nil { - rpcResponse := shared.NewRpcResponse(req.Id, req.Jsonrpc, res, err) - responses[responseCount] = rpcResponse - responseCount += 1 - } - } - - err = codec.WriteResponse(responses[:responseCount]) - if err != nil { - glog.V(logger.Debug).Infof("Closed IPC Conn %06d send err - %v\n", id, err) - return - } - } else { - var rpcResponse interface{} - res, err := api.Execute(requests[0]) - - rpcResponse = shared.NewRpcResponse(requests[0].Id, requests[0].Jsonrpc, res, err) - err = codec.WriteResponse(rpcResponse) - if err != nil { - glog.V(logger.Debug).Infof("Closed IPC Conn %06d send err - %v\n", id, err) - return - } - } - } -} - -// Endpoint must be in the form of: -// ${protocol}:${path} -// e.g. ipc:/tmp/geth.ipc -// rpc:localhost:8545 -func ClientFromEndpoint(endpoint string, c codec.Codec) (EthereumClient, error) { - if strings.HasPrefix(endpoint, "ipc:") { - cfg := IpcConfig{ - Endpoint: endpoint[4:], - } - return NewIpcClient(cfg, codec.JSON) - } - - if strings.HasPrefix(endpoint, "rpc:") { - parts := strings.Split(endpoint, ":") - addr := "http://localhost" - port := uint(8545) - if len(parts) >= 3 { - addr = parts[1] + ":" + parts[2] - } - - if len(parts) >= 4 { - p, err := strconv.Atoi(parts[3]) - - if err != nil { - return nil, err - } - port = uint(p) - } - - cfg := HttpConfig{ - ListenAddress: addr, - ListenPort: port, - } - - return NewHttpClient(cfg, codec.JSON), nil - } - - return nil, fmt.Errorf("Invalid endpoint") -} diff --git a/rpc/comms/http.go b/rpc/comms/http.go deleted file mode 100644 index f4a930d0e..000000000 --- a/rpc/comms/http.go +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package comms - -import ( - "encoding/json" - "fmt" - "net" - "net/http" - "strings" - "sync" - "time" - - "bytes" - "io" - "io/ioutil" - - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/shared" - "github.com/rs/cors" -) - -const ( - serverIdleTimeout = 10 * time.Second // idle keep-alive connections - serverReadTimeout = 15 * time.Second // per-request read timeout - serverWriteTimeout = 15 * time.Second // per-request read timeout -) - -var ( - httpServerMu sync.Mutex - httpServer *stopServer -) - -type HttpConfig struct { - ListenAddress string - ListenPort uint - CorsDomain string -} - -// stopServer augments http.Server with idle connection tracking. -// Idle keep-alive connections are shut down when Close is called. -type stopServer struct { - *http.Server - l net.Listener - // connection tracking state - mu sync.Mutex - shutdown bool // true when Stop has returned - idle map[net.Conn]struct{} -} - -type handler struct { - codec codec.Codec - api shared.EthereumApi -} - -// StartHTTP starts listening for RPC requests sent via HTTP. -func StartHttp(cfg HttpConfig, codec codec.Codec, api shared.EthereumApi) error { - httpServerMu.Lock() - defer httpServerMu.Unlock() - - addr := fmt.Sprintf("%s:%d", cfg.ListenAddress, cfg.ListenPort) - if httpServer != nil { - if addr != httpServer.Addr { - return fmt.Errorf("RPC service already running on %s ", httpServer.Addr) - } - return nil // RPC service already running on given host/port - } - // Set up the request handler, wrapping it with CORS headers if configured. - handler := http.Handler(&handler{codec, api}) - if len(cfg.CorsDomain) > 0 { - opts := cors.Options{ - AllowedMethods: []string{"POST"}, - AllowedOrigins: strings.Split(cfg.CorsDomain, " "), - } - handler = cors.New(opts).Handler(handler) - } - // Start the server. - s, err := listenHTTP(addr, handler) - if err != nil { - glog.V(logger.Error).Infof("Can't listen on %s:%d: %v", cfg.ListenAddress, cfg.ListenPort, err) - return err - } - httpServer = s - return nil -} - -func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - w.Header().Set("Content-Type", "application/json") - - // Limit request size to resist DoS - if req.ContentLength > maxHttpSizeReqLength { - err := fmt.Errorf("Request too large") - response := shared.NewRpcErrorResponse(-1, shared.JsonRpcVersion, -32700, err) - sendJSON(w, &response) - return - } - - defer req.Body.Close() - payload, err := ioutil.ReadAll(req.Body) - if err != nil { - err := fmt.Errorf("Could not read request body") - response := shared.NewRpcErrorResponse(-1, shared.JsonRpcVersion, -32700, err) - sendJSON(w, &response) - return - } - - c := h.codec.New(nil) - var rpcReq shared.Request - if err = c.Decode(payload, &rpcReq); err == nil { - reply, err := h.api.Execute(&rpcReq) - res := shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err) - sendJSON(w, &res) - return - } - - var reqBatch []shared.Request - if err = c.Decode(payload, &reqBatch); err == nil { - resBatch := make([]*interface{}, len(reqBatch)) - resCount := 0 - for i, rpcReq := range reqBatch { - reply, err := h.api.Execute(&rpcReq) - if rpcReq.Id != nil { // this leaves nil entries in the response batch for later removal - resBatch[i] = shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err) - resCount += 1 - } - } - // make response omitting nil entries - sendJSON(w, resBatch[:resCount]) - return - } - - // invalid request - err = fmt.Errorf("Could not decode request") - res := shared.NewRpcErrorResponse(-1, shared.JsonRpcVersion, -32600, err) - sendJSON(w, res) -} - -func sendJSON(w io.Writer, v interface{}) { - if glog.V(logger.Detail) { - if payload, err := json.MarshalIndent(v, "", "\t"); err == nil { - glog.Infof("Sending payload: %s", payload) - } - } - if err := json.NewEncoder(w).Encode(v); err != nil { - glog.V(logger.Error).Infoln("Error sending JSON:", err) - } -} - -// Stop closes all active HTTP connections and shuts down the server. -func StopHttp() { - httpServerMu.Lock() - defer httpServerMu.Unlock() - if httpServer != nil { - httpServer.Close() - httpServer = nil - } -} - -func listenHTTP(addr string, h http.Handler) (*stopServer, error) { - l, err := net.Listen("tcp", addr) - if err != nil { - return nil, err - } - s := &stopServer{l: l, idle: make(map[net.Conn]struct{})} - s.Server = &http.Server{ - Addr: addr, - Handler: h, - ReadTimeout: serverReadTimeout, - WriteTimeout: serverWriteTimeout, - ConnState: s.connState, - } - go s.Serve(l) - return s, nil -} - -func (s *stopServer) connState(c net.Conn, state http.ConnState) { - s.mu.Lock() - defer s.mu.Unlock() - // Close c immediately if we're past shutdown. - if s.shutdown { - if state != http.StateClosed { - c.Close() - } - return - } - if state == http.StateIdle { - s.idle[c] = struct{}{} - } else { - delete(s.idle, c) - } -} - -func (s *stopServer) Close() { - s.mu.Lock() - defer s.mu.Unlock() - // Shut down the acceptor. No new connections can be created. - s.l.Close() - // Drop all idle connections. Non-idle connections will be - // closed by connState as soon as they become idle. - s.shutdown = true - for c := range s.idle { - glog.V(logger.Detail).Infof("closing idle connection %v", c.RemoteAddr()) - c.Close() - delete(s.idle, c) - } -} - -type httpClient struct { - address string - port uint - codec codec.ApiCoder - lastRes interface{} - lastErr error -} - -// Create a new in process client -func NewHttpClient(cfg HttpConfig, c codec.Codec) *httpClient { - return &httpClient{ - address: cfg.ListenAddress, - port: cfg.ListenPort, - codec: c.New(nil), - } -} - -func (self *httpClient) Close() { - // do nothing -} - -func (self *httpClient) Send(req interface{}) error { - var body []byte - var err error - - self.lastRes = nil - self.lastErr = nil - - if body, err = self.codec.Encode(req); err != nil { - return err - } - - httpReq, err := http.NewRequest("POST", fmt.Sprintf("%s:%d", self.address, self.port), bytes.NewBuffer(body)) - if err != nil { - return err - } - httpReq.Header.Set("Content-Type", "application/json") - - client := http.Client{} - resp, err := client.Do(httpReq) - if err != nil { - return err - } - - defer resp.Body.Close() - - if resp.Status == "200 OK" { - reply, _ := ioutil.ReadAll(resp.Body) - var rpcSuccessResponse shared.SuccessResponse - if err = self.codec.Decode(reply, &rpcSuccessResponse); err == nil { - self.lastRes = &rpcSuccessResponse - self.lastErr = err - return nil - } else { - var rpcErrorResponse shared.ErrorResponse - if err = self.codec.Decode(reply, &rpcErrorResponse); err == nil { - self.lastRes = &rpcErrorResponse - self.lastErr = err - return nil - } else { - return err - } - } - } - - return fmt.Errorf("Not implemented") -} - -func (self *httpClient) Recv() (interface{}, error) { - return self.lastRes, self.lastErr -} - -func (self *httpClient) SupportedModules() (map[string]string, error) { - var body []byte - var err error - - payload := shared.Request{ - Id: 1, - Jsonrpc: "2.0", - Method: "modules", - } - - if body, err = self.codec.Encode(payload); err != nil { - return nil, err - } - - req, err := http.NewRequest("POST", fmt.Sprintf("%s:%d", self.address, self.port), bytes.NewBuffer(body)) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", "application/json") - - client := http.Client{} - resp, err := client.Do(req) - if err != nil { - return nil, err - } - - defer resp.Body.Close() - - if resp.Status == "200 OK" { - reply, _ := ioutil.ReadAll(resp.Body) - var rpcRes shared.SuccessResponse - if err = self.codec.Decode(reply, &rpcRes); err != nil { - return nil, err - } - - result := make(map[string]string) - if modules, ok := rpcRes.Result.(map[string]interface{}); ok { - for a, v := range modules { - result[a] = fmt.Sprintf("%s", v) - } - return result, nil - } - err = fmt.Errorf("Unable to parse module response - %v", rpcRes.Result) - } else { - fmt.Printf("resp.Status = %s\n", resp.Status) - fmt.Printf("err = %v\n", err) - } - - return nil, err -} diff --git a/rpc/comms/inproc.go b/rpc/comms/inproc.go deleted file mode 100644 index e8058e32b..000000000 --- a/rpc/comms/inproc.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package comms - -import ( - "fmt" - - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/shared" -) - -type InProcClient struct { - api shared.EthereumApi - codec codec.Codec - lastId interface{} - lastJsonrpc string - lastErr error - lastRes interface{} -} - -// Create a new in process client -func NewInProcClient(codec codec.Codec) *InProcClient { - return &InProcClient{ - codec: codec, - } -} - -func (self *InProcClient) Close() { - // do nothing -} - -// Need to setup api support -func (self *InProcClient) Initialize(offeredApi shared.EthereumApi) { - self.api = offeredApi -} - -func (self *InProcClient) Send(req interface{}) error { - if r, ok := req.(*shared.Request); ok { - self.lastId = r.Id - self.lastJsonrpc = r.Jsonrpc - self.lastRes, self.lastErr = self.api.Execute(r) - return self.lastErr - } - - return fmt.Errorf("Invalid request (%T)", req) -} - -func (self *InProcClient) Recv() (interface{}, error) { - return *shared.NewRpcResponse(self.lastId, self.lastJsonrpc, self.lastRes, self.lastErr), nil -} - -func (self *InProcClient) SupportedModules() (map[string]string, error) { - req := shared.Request{ - Id: 1, - Jsonrpc: "2.0", - Method: "modules", - } - - if res, err := self.api.Execute(&req); err == nil { - if result, ok := res.(map[string]string); ok { - return result, nil - } - } else { - return nil, err - } - - return nil, fmt.Errorf("Invalid response") -} diff --git a/rpc/comms/ipc.go b/rpc/comms/ipc.go deleted file mode 100644 index 3ba747b1d..000000000 --- a/rpc/comms/ipc.go +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package comms - -import ( - "fmt" - "math/rand" - "net" - "os" - - "encoding/json" - - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/shared" -) - -type Stopper interface { - Stop() -} - -type InitFunc func(conn net.Conn) (Stopper, shared.EthereumApi, error) - -type IpcConfig struct { - Endpoint string -} - -type ipcClient struct { - endpoint string - c net.Conn - codec codec.Codec - coder codec.ApiCoder -} - -func (self *ipcClient) Close() { - self.coder.Close() -} - -func (self *ipcClient) Send(msg interface{}) error { - var err error - if err = self.coder.WriteResponse(msg); err != nil { - if err = self.reconnect(); err == nil { - err = self.coder.WriteResponse(msg) - } - } - return err -} - -func (self *ipcClient) Recv() (interface{}, error) { - return self.coder.ReadResponse() -} - -func (self *ipcClient) SupportedModules() (map[string]string, error) { - req := shared.Request{ - Id: 1, - Jsonrpc: "2.0", - Method: "rpc_modules", - } - - if err := self.coder.WriteResponse(req); err != nil { - return nil, err - } - - res, _ := self.coder.ReadResponse() - if sucRes, ok := res.(*shared.SuccessResponse); ok { - data, _ := json.Marshal(sucRes.Result) - modules := make(map[string]string) - if err := json.Unmarshal(data, &modules); err == nil { - return modules, nil - } - } - - // old version uses modules instead of rpc_modules, this can be removed after full migration - req.Method = "modules" - if err := self.coder.WriteResponse(req); err != nil { - return nil, err - } - - res, err := self.coder.ReadResponse() - if err != nil { - return nil, err - } - - if sucRes, ok := res.(*shared.SuccessResponse); ok { - data, _ := json.Marshal(sucRes.Result) - modules := make(map[string]string) - err = json.Unmarshal(data, &modules) - if err == nil { - return modules, nil - } - } - - return nil, fmt.Errorf("Invalid response") -} - -// Create a new IPC client, UNIX domain socket on posix, named pipe on Windows -func NewIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) { - return newIpcClient(cfg, codec) -} - -// Start IPC server -func StartIpc(cfg IpcConfig, codec codec.Codec, initializer InitFunc) error { - l, err := ipcListen(cfg) - if err != nil { - return err - } - go ipcLoop(cfg, codec, initializer, l) - return nil -} - -// CreateListener creates an listener, on Unix platforms this is a unix socket, on Windows this is a named pipe -func CreateListener(cfg IpcConfig) (net.Listener, error) { - return ipcListen(cfg) -} - -func ipcLoop(cfg IpcConfig, codec codec.Codec, initializer InitFunc, l net.Listener) { - glog.V(logger.Info).Infof("IPC service started (%s)\n", cfg.Endpoint) - defer os.Remove(cfg.Endpoint) - defer l.Close() - for { - conn, err := l.Accept() - if err != nil { - glog.V(logger.Debug).Infof("accept: %v", err) - return - } - id := newIpcConnId() - go func() { - defer conn.Close() - glog.V(logger.Debug).Infof("new connection with id %06d started", id) - stopper, api, err := initializer(conn) - if err != nil { - glog.V(logger.Error).Infof("Unable to initialize IPC connection: %v", err) - return - } - defer stopper.Stop() - handle(id, conn, api, codec) - }() - } -} - -func newIpcConnId() int { - return rand.Int() % 1000000 -} diff --git a/rpc/comms/ipc_unix.go b/rpc/comms/ipc_unix.go deleted file mode 100644 index 4b839572a..000000000 --- a/rpc/comms/ipc_unix.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris - -package comms - -import ( - "net" - "os" - "path/filepath" - - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/shared" - "github.com/ethereum/go-ethereum/rpc/useragent" -) - -func newIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) { - c, err := net.DialUnix("unix", nil, &net.UnixAddr{cfg.Endpoint, "unix"}) - if err != nil { - return nil, err - } - - coder := codec.New(c) - msg := shared.Request{ - Id: 0, - Method: useragent.EnableUserAgentMethod, - Jsonrpc: shared.JsonRpcVersion, - Params: []byte("[]"), - } - - coder.WriteResponse(msg) - coder.Recv() - - return &ipcClient{cfg.Endpoint, c, codec, coder}, nil -} - -func (self *ipcClient) reconnect() error { - self.coder.Close() - c, err := net.DialUnix("unix", nil, &net.UnixAddr{self.endpoint, "unix"}) - if err == nil { - self.coder = self.codec.New(c) - - msg := shared.Request{ - Id: 0, - Method: useragent.EnableUserAgentMethod, - Jsonrpc: shared.JsonRpcVersion, - Params: []byte("[]"), - } - self.coder.WriteResponse(msg) - self.coder.Recv() - } - - return err -} - -func ipcListen(cfg IpcConfig) (net.Listener, error) { - // Ensure the IPC path exists and remove any previous leftover - if err := os.MkdirAll(filepath.Dir(cfg.Endpoint), 0751); err != nil { - return nil, err - } - os.Remove(cfg.Endpoint) - l, err := net.Listen("unix", cfg.Endpoint) - if err != nil { - return nil, err - } - os.Chmod(cfg.Endpoint, 0600) - return l, nil -} diff --git a/rpc/v2/doc.go b/rpc/doc.go index e51494adb..e8f8f977b 100644 --- a/rpc/v2/doc.go +++ b/rpc/doc.go @@ -99,4 +99,130 @@ Subscriptions are deleted when: - the user sends an unsubscribe request - the connection which was used to create the subscription is closed */ -package v2 +package rpc + +var ( + // Mapping between the different methods each api supports + AutoCompletion = map[string][]string{ + "admin": []string{ + "addPeer", + "datadir", + "enableUserAgent", + "exportChain", + "getContractInfo", + "httpGet", + "importChain", + "nodeInfo", + "peers", + "register", + "registerUrl", + "saveInfo", + "setGlobalRegistrar", + "setHashReg", + "setUrlHint", + "setSolc", + "sleep", + "sleepBlocks", + "startNatSpec", + "startRPC", + "stopNatSpec", + "stopRPC", + "verbosity", + }, + "db": []string{ + "getString", + "putString", + "getHex", + "putHex", + }, + "debug": []string{ + "dumpBlock", + "getBlockRlp", + "metrics", + "printBlock", + "processBlock", + "seedHash", + "setHead", + }, + "eth": []string{ + "accounts", + "blockNumber", + "call", + "contract", + "coinbase", + "compile.lll", + "compile.serpent", + "compile.solidity", + "contract", + "defaultAccount", + "defaultBlock", + "estimateGas", + "filter", + "getBalance", + "getBlock", + "getBlockTransactionCount", + "getBlockUncleCount", + "getCode", + "getNatSpec", + "getCompilers", + "gasPrice", + "getStorageAt", + "getTransaction", + "getTransactionCount", + "getTransactionFromBlock", + "getTransactionReceipt", + "getUncle", + "hashrate", + "mining", + "namereg", + "pendingTransactions", + "resend", + "sendRawTransaction", + "sendTransaction", + "sign", + "syncing", + }, + "miner": []string{ + "hashrate", + "makeDAG", + "setEtherbase", + "setExtra", + "setGasPrice", + "startAutoDAG", + "start", + "stopAutoDAG", + "stop", + }, + "net": []string{ + "peerCount", + "listening", + }, + "personal": []string{ + "listAccounts", + "newAccount", + "unlockAccount", + }, + "shh": []string{ + "post", + "newIdentity", + "hasIdentity", + "newGroup", + "addToGroup", + "filter", + }, + "txpool": []string{ + "status", + }, + "web3": []string{ + "sha3", + "version", + "fromWei", + "toWei", + "toHex", + "toAscii", + "fromAscii", + "toBigNumber", + "isAddress", + }, + } +) diff --git a/rpc/v2/errors.go b/rpc/errors.go index a06d19d84..bc352fc45 100644 --- a/rpc/v2/errors.go +++ b/rpc/errors.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/>. -package v2 +package rpc import "fmt" @@ -83,3 +83,15 @@ func (e *callbackError) Code() int { func (e *callbackError) Error() string { return e.message } + +// issued when a request is received after the server is issued to stop. +type shutdownError struct { +} + +func (e *shutdownError) Code() int { + return -32000 +} + +func (e *shutdownError) Error() string { + return "server is shutting down" +} diff --git a/rpc/http.go b/rpc/http.go new file mode 100644 index 000000000..e4b25bed8 --- /dev/null +++ b/rpc/http.go @@ -0,0 +1,368 @@ +// Copyright 2015 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 <http://www.gnu.org/licenses/>. + +package rpc + +import ( + "bufio" + "fmt" + "io" + "net" + "net/http" + "strconv" + "strings" + "time" + + "errors" + "sync" + + "bytes" + "encoding/json" + "io/ioutil" + "net/url" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "gopkg.in/fatih/set.v0" +) + +const ( + httpReadDeadLine = 60 * time.Second // wait max httpReadDeadeline for next request +) + +var ( + httpServerMu sync.Mutex // prevent concurrent access to the httpListener and httpServer + httpListener net.Listener // listener for the http server + httpRPCServer *Server // the node can only start 1 HTTP RPC server instance +) + +// httpMessageStream is the glue between a HTTP connection which is message based +// and the RPC codecs that expect json requests to be read from a stream. It will +// parse HTTP messages and offer the bodies of these requests as a stream through +// the Read method. This will require full control of the connection and thus need +// a "hijacked" HTTP connection. +type httpMessageStream struct { + conn net.Conn // TCP connection + rw *bufio.ReadWriter // buffered where HTTP requests/responses are read/written from/to + currentReq *http.Request // pending request, codec can pass in a too small buffer for a single read + // we need to keep track of the current requests if it was not read at once + payloadBytesRead int64 // number of bytes which are read from the current request + allowedOrigins *set.Set // allowed CORS domains + origin string // origin of this connection/request +} + +// NewHttpMessageStream will create a new http message stream parser that can be +// used by the codes in the RPC package. It will take full control of the given +// connection and thus needs to be hijacked. It will read and write HTTP messages +// from the passed rwbuf. The allowed origins are the RPC CORS domains the user has supplied. +func NewHTTPMessageStream(c net.Conn, rwbuf *bufio.ReadWriter, initialReq *http.Request, allowdOrigins []string) *httpMessageStream { + r := &httpMessageStream{conn: c, rw: rwbuf, currentReq: initialReq, allowedOrigins: set.New()} + for _, origin := range allowdOrigins { + r.allowedOrigins.Add(origin) + } + return r +} + +// handleOptionsRequest handles the HTTP preflight requests (OPTIONS) that browsers +// make to enforce CORS rules. Only the POST method is allowed and the origin must +// be on the rpccorsdomain list the user has specified. +func (h *httpMessageStream) handleOptionsRequest(req *http.Request) error { + headers := req.Header + + if !strings.EqualFold(req.Method, "OPTIONS") { + return fmt.Errorf("preflight aborted: %s!=OPTIONS", req.Method) + } + + origin := headers.Get("Origin") + if origin == "" { + return fmt.Errorf("preflight aborted: empty origin") + } + + responseHeaders := make(http.Header) + responseHeaders.Set("Access-Control-Allow-Methods", "POST") + if h.allowedOrigins.Has(origin) || h.allowedOrigins.Has("*") { + responseHeaders.Set("Access-Control-Allow-Origin", origin) + } else { + glog.V(logger.Info).Infof("origin '%s' not allowed", origin) + } + responseHeaders.Set("Access-Control-Allow-Headers", "Content-Type") + responseHeaders.Set("Date", string(httpTimestamp(time.Now()))) + responseHeaders.Set("Content-Type", "text/plain; charset=utf-8") + responseHeaders.Set("Content-Length", "0") + responseHeaders.Set("Vary", "Origin") + + defer h.rw.Flush() + + if _, err := h.rw.WriteString("HTTP/1.1 200 OK\r\n"); err != nil { + glog.V(logger.Error).Infof("unable to write OPTIONS response: %v\n", err) + return err + } + if err := responseHeaders.Write(h.rw); err != nil { + glog.V(logger.Error).Infof("unable to write OPTIONS headers: %v\n", err) + } + if _, err := h.rw.WriteString("\r\n"); err != nil { + glog.V(logger.Error).Infof("unable to write OPTIONS response: %v\n", err) + } + + return nil +} + +// Read will read incoming HTTP requests and reads the body data from these requests +// as an endless stream of data. +func (h *httpMessageStream) Read(buf []byte) (n int, err error) { + h.conn.SetReadDeadline(time.Now().Add(httpReadDeadLine)) + for { + // if the last request was read completely try to read the next request + if h.currentReq == nil { + if h.currentReq, err = http.ReadRequest(bufio.NewReader(h.rw)); err != nil { + return 0, err + } + } + + // The "options" method is http specific and not interested for the RPC server. + // Handle it internally and wait for the next request. + if strings.EqualFold(h.currentReq.Method, "OPTIONS") { + if err = h.handleOptionsRequest(h.currentReq); err != nil { + glog.V(logger.Info).Infof("RPC/HTTP OPTIONS error: %v\n", err) + h.currentReq = nil + return 0, err + } + + // processed valid request -> reset deadline + h.conn.SetReadDeadline(time.Now().Add(httpReadDeadLine)) + h.currentReq = nil + continue + } + + if strings.EqualFold(h.currentReq.Method, "POST") { + n, err := h.currentReq.Body.Read(buf) + h.payloadBytesRead += int64(n) + + // entire payload read, read new request next time + if err == io.EOF || h.payloadBytesRead >= h.currentReq.ContentLength { + h.origin = h.currentReq.Header.Get("origin") + h.payloadBytesRead = 0 + h.currentReq.Body.Close() + h.currentReq = nil + err = nil // io.EOF is not an error + } else if err != nil { + // unable to read body + h.currentReq.Body.Close() + h.currentReq = nil + h.payloadBytesRead = 0 + } + + // partial read of body + return n, err + } + + h.currentReq = nil + return 0, fmt.Errorf("unsupported HTTP method '%s'", h.currentReq.Method) + } +} + +// Write will create a HTTP response with the given payload and send it to the peer. +func (h *httpMessageStream) Write(payload []byte) (int, error) { + defer h.rw.Flush() + + responseHeaders := make(http.Header) + responseHeaders.Set("Content-Type", "application/json") + responseHeaders.Set("Content-Length", strconv.Itoa(len(payload))) + if h.origin != "" { + responseHeaders.Set("Access-Control-Allow-Origin", h.origin) + } + + h.rw.WriteString("HTTP/1.1 200 OK\r\n") + responseHeaders.Write(h.rw) + h.rw.WriteString("\r\n") + + return h.rw.Write(payload) +} + +// Close will close the underlying TCP connection this instance has taken ownership over. +func (h *httpMessageStream) Close() error { + h.rw.Flush() + return h.conn.Close() +} + +// TimeFormat is the time format to use with time.Parse and time.Time.Format when +// parsing or generating times in HTTP headers. It is like time.RFC1123 but hard +// codes GMT as the time zone. +const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT" + +// httpTimestamp formats the given t as specified in RFC1123. +func httpTimestamp(t time.Time) []byte { + const days = "SunMonTueWedThuFriSat" + const months = "JanFebMarAprMayJunJulAugSepOctNovDec" + + b := make([]byte, 0) + t = t.UTC() + yy, mm, dd := t.Date() + hh, mn, ss := t.Clock() + day := days[3 * t.Weekday():] + mon := months[3 * (mm - 1):] + + return append(b, + day[0], day[1], day[2], ',', ' ', + byte('0' + dd / 10), byte('0' + dd % 10), ' ', + mon[0], mon[1], mon[2], ' ', + byte('0' + yy / 1000), byte('0' + (yy / 100) % 10), byte('0' + (yy / 10) % 10), byte('0' + yy % 10), ' ', + byte('0' + hh / 10), byte('0' + hh % 10), ':', + byte('0' + mn / 10), byte('0' + mn % 10), ':', + byte('0' + ss / 10), byte('0' + ss % 10), ' ', + 'G', 'M', 'T') +} + +// httpConnHijacker is a http.Handler implementation that will hijack the HTTP +// connection, wraps it in a HttpMessageStream that is then wrapped in a JSON +// codec which will be served on the rpcServer. +type httpConnHijacker struct { + corsdomains []string + rpcServer *Server +} + +// ServeHTTP will hijack the connection, wraps the captured connection in a +// HttpMessageStream which is then used as codec. +func (h *httpConnHijacker) ServeHTTP(w http.ResponseWriter, req *http.Request) { + hj, ok := w.(http.Hijacker) + if !ok { + http.Error(w, "webserver doesn't support hijacking", http.StatusInternalServerError) + return + } + + conn, rwbuf, err := hj.Hijack() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + httpRequestStream := NewHTTPMessageStream(conn, rwbuf, req, h.corsdomains) + + codec := NewJSONCodec(httpRequestStream) + go h.rpcServer.ServeCodec(codec) +} + +// StartHTTP will start the JSONRPC HTTP RPC interface when its not yet running. +func StartHTTP(address string, port int, corsdomains []string, apis []API) error { + httpServerMu.Lock() + defer httpServerMu.Unlock() + + if httpRPCServer != nil { + return fmt.Errorf("HTTP RPC interface already started on %s", httpListener.Addr()) + } + + rpcServer := NewServer() + + for _, api := range apis { + if err := rpcServer.RegisterName(api.Namespace, api.Service); err != nil { + return err + } + } + + listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", address, port)) + if err != nil { + return err + } + + httpServer := http.Server{Handler: &httpConnHijacker{corsdomains, rpcServer}} + go httpServer.Serve(listener) + + httpListener = listener + httpRPCServer = rpcServer + + return nil +} + +// StopHTTP will stop the running HTTP interface. If it is not running an error will be returned. +func StopHTTP() error { + httpServerMu.Lock() + defer httpServerMu.Unlock() + + if httpRPCServer == nil { + return errors.New("HTTP RPC interface not started") + } + + httpListener.Close() + httpRPCServer.Stop() + + httpRPCServer = nil + httpListener = nil + + return nil +} + +// httpClient connects to a geth RPC server over HTTP. +type httpClient struct { + endpoint *url.URL // HTTP-RPC server endpoint + lastRes []byte // HTTP requests are synchronous, store last response +} + +// NewHTTPClient create a new RPC clients that connection to a geth RPC server +// over HTTP. +func NewHTTPClient(endpoint string) (*httpClient, error) { + url, err := url.Parse(endpoint) + if err != nil { + return nil, err + } + return &httpClient{endpoint: url}, nil +} + +// Send will serialize the given msg to JSON and sends it to the RPC server. +// Since HTTP is synchronous the response is stored until Recv is called. +func (client *httpClient) Send(msg interface{}) error { + var body []byte + var err error + + client.lastRes = nil + + if body, err = json.Marshal(msg); err != nil { + return err + } + + httpReq, err := http.NewRequest("POST", client.endpoint.String(), bytes.NewBuffer(body)) + if err != nil { + return err + } + httpReq.Header.Set("Content-Type", "application/json") + + httpClient := http.Client{} + resp, err := httpClient.Do(httpReq) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusOK { + client.lastRes, err = ioutil.ReadAll(resp.Body) + return err + } + + return fmt.Errorf("unable to handle request") +} + +// Recv will try to deserialize the last received response into the given msg. +func (client *httpClient) Recv(msg interface{}) error { + return json.Unmarshal(client.lastRes, &msg) +} + +// Close is not necessary for httpClient +func (client *httpClient) Close() { +} + +// SupportedModules will return the collection of offered RPC modules. +func (client *httpClient) SupportedModules() (map[string]string, error) { + return SupportedModules(client) +} diff --git a/rpc/ipc.go b/rpc/ipc.go new file mode 100644 index 000000000..b87bfcbd7 --- /dev/null +++ b/rpc/ipc.go @@ -0,0 +1,84 @@ +// Copyright 2015 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 <http://www.gnu.org/licenses/>. + +package rpc + +import ( + "encoding/json" + "net" +) + +// CreateIPCListener creates an listener, on Unix platforms this is a unix socket, on Windows this is a named pipe +func CreateIPCListener(endpoint string) (net.Listener, error) { + return ipcListen(endpoint) +} + +// ipcClient represent an IPC RPC client. It will connect to a given endpoint and tries to communicate with a node using +// JSON serialization. +type ipcClient struct { + endpoint string + conn net.Conn + out *json.Encoder + in *json.Decoder +} + +// NewIPCClient create a new IPC client that will connect on the given endpoint. Messages are JSON encoded and encoded. +// On Unix it assumes the endpoint is the full path to a unix socket, and Windows the endpoint is an identifier for a +// named pipe. +func NewIPCClient(endpoint string) (*ipcClient, error) { + conn, err := newIPCConnection(endpoint) + if err != nil { + return nil, err + } + return &ipcClient{endpoint: endpoint, conn: conn, in: json.NewDecoder(conn), out: json.NewEncoder(conn)}, nil +} + +// Send will serialize the given message and send it to the server. +// When sending the message fails it will try to reconnect once and send the message again. +func (client *ipcClient) Send(msg interface{}) error { + if err := client.out.Encode(msg); err == nil { + return nil + } + + // retry once + client.conn.Close() + + conn, err := newIPCConnection(client.endpoint) + if err != nil { + return err + } + + client.conn = conn + client.in = json.NewDecoder(conn) + client.out = json.NewEncoder(conn) + + return client.out.Encode(msg) +} + +// Recv will read a message from the connection and tries to parse it. It assumes the received message is JSON encoded. +func (client *ipcClient) Recv(msg interface{}) error { + return client.in.Decode(&msg) +} + +// Close will close the underlying IPC connection +func (client *ipcClient) Close() { + client.conn.Close() +} + +// SupportedModules will return the collection of offered RPC modules. +func (client *ipcClient) SupportedModules() (map[string]string, error) { + return SupportedModules(client) +} diff --git a/rpc/api/web3_args.go b/rpc/ipc_unix.go index 9e39f7130..310286e96 100644 --- a/rpc/api/web3_args.go +++ b/rpc/ipc_unix.go @@ -14,32 +14,32 @@ // 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/>. -package api +// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris -import ( - "encoding/json" +package rpc - "github.com/ethereum/go-ethereum/rpc/shared" +import ( + "net" + "os" + "path/filepath" ) -type Sha3Args struct { - Data string -} - -func (args *Sha3Args) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - if err := json.Unmarshal(b, &obj); err != nil { - return shared.NewDecodeParamError(err.Error()) +// ipcListen will create a Unix socket on the given endpoint. +func ipcListen(endpoint string) (net.Listener, error) { + // Ensure the IPC path exists and remove any previous leftover + if err := os.MkdirAll(filepath.Dir(endpoint), 0751); err != nil { + return nil, err } - - if len(obj) < 1 { - return shared.NewInsufficientParamsError(len(obj), 1) + os.Remove(endpoint) + l, err := net.Listen("unix", endpoint) + if err != nil { + return nil, err } + os.Chmod(endpoint, 0600) + return l, nil +} - argstr, ok := obj[0].(string) - if !ok { - return shared.NewInvalidTypeError("data", "is not a string") - } - args.Data = argstr - return nil +// newIPCConnection will connect to a Unix socket on the given endpoint. +func newIPCConnection(endpoint string) (net.Conn, error) { + return net.DialUnix("unix", nil, &net.UnixAddr{endpoint, "unix"}) } diff --git a/rpc/comms/ipc_windows.go b/rpc/ipc_windows.go index e25fba253..1d4672ad2 100644 --- a/rpc/comms/ipc_windows.go +++ b/rpc/ipc_windows.go @@ -16,21 +16,16 @@ // +build windows -package comms +package rpc import ( "fmt" "io" "net" - "os" "sync" "syscall" "time" "unsafe" - - "github.com/ethereum/go-ethereum/rpc/codec" - "github.com/ethereum/go-ethereum/rpc/shared" - "github.com/ethereum/go-ethereum/rpc/useragent" ) var ( @@ -649,49 +644,12 @@ func timeout(addr string) PipeError { return PipeError{fmt.Sprintf("Pipe IO timed out waiting for '%s'", addr), true} } -func newIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) { - c, err := Dial(cfg.Endpoint) - if err != nil { - return nil, err - } - - coder := codec.New(c) - msg := shared.Request{ - Id: 0, - Method: useragent.EnableUserAgentMethod, - Jsonrpc: shared.JsonRpcVersion, - Params: []byte("[]"), - } - - coder.WriteResponse(msg) - coder.Recv() - - return &ipcClient{cfg.Endpoint, c, codec, coder}, nil -} - -func (self *ipcClient) reconnect() error { - c, err := Dial(self.endpoint) - if err == nil { - self.coder = self.codec.New(c) - - req := shared.Request{ - Id: 0, - Method: useragent.EnableUserAgentMethod, - Jsonrpc: shared.JsonRpcVersion, - Params: []byte("[]"), - } - self.coder.WriteResponse(req) - self.coder.Recv() - } - return err +// ipcListen will create a named pipe on the given endpoint. +func ipcListen(endpoint string) (net.Listener, error) { + return Listen(endpoint) } -func ipcListen(cfg IpcConfig) (net.Listener, error) { - os.Remove(cfg.Endpoint) // in case it still exists from a previous run - l, err := Listen(cfg.Endpoint) - if err != nil { - return nil, err - } - os.Chmod(cfg.Endpoint, 0600) - return l, nil +// newIPCConnection will connect to a named pipe with the given endpoint as name. +func newIPCConnection(endpoint string) (net.Conn, error) { + return Dial(endpoint) } diff --git a/rpc/javascript.go b/rpc/javascript.go new file mode 100644 index 000000000..d147aa045 --- /dev/null +++ b/rpc/javascript.go @@ -0,0 +1,414 @@ +// Copyright 2015 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 <http://www.gnu.org/licenses/>. + +package rpc + +var ( + // Holds geth specific RPC extends which can be used to extend web3 + WEB3Extensions = map[string]string{ + "personal": Personal_JS, + "txpool": TxPool_JS, + "admin": Admin_JS, + "db": Db_JS, + "eth": Eth_JS, + "miner": Miner_JS, + "debug": Debug_JS, + "net": Net_JS, + } +) + +const Personal_JS = ` +web3._extend({ + property: 'personal', + methods: + [ + new web3._extend.Method({ + name: 'newAccount', + call: 'personal_newAccount', + params: 1, + outputFormatter: web3._extend.utils.toAddress + }), + new web3._extend.Method({ + name: 'unlockAccount', + call: 'personal_unlockAccount', + params: 3, + }), + new web3._extend.Method({ + name: 'lockAccount', + call: 'personal_lockAccount', + params: 1 + }) + ], + properties: + [ + new web3._extend.Property({ + name: 'listAccounts', + getter: 'personal_listAccounts' + }) + ] +}); +` + +const TxPool_JS = ` +web3._extend({ + property: 'txpool', + methods: + [ + ], + properties: + [ + new web3._extend.Property({ + name: 'status', + getter: 'txpool_status' + outputFormatter: function(status) { + status.pending = web3._extend.utils.toDecimal(status.pending); + status.queued = web3._extend.utils.toDecimal(status.queued); + return status; + } + }) + ] +}); +` + +const Admin_JS = ` +web3._extend({ + property: 'admin', + methods: + [ + new web3._extend.Method({ + name: 'addPeer', + call: 'admin_addPeer', + params: 1 + }), + new web3._extend.Method({ + name: 'exportChain', + call: 'admin_exportChain', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'importChain', + call: 'admin_importChain', + params: 1 + }), + new web3._extend.Method({ + name: 'sleepBlocks', + call: 'admin_sleepBlocks', + params: 2 + }), + new web3._extend.Method({ + name: 'verbosity', + call: 'admin_verbosity', + params: 1, + inputFormatter: [web3._extend.utils.fromDecimal] + }), + new web3._extend.Method({ + name: 'setSolc', + call: 'admin_setSolc', + params: 1 + }), + new web3._extend.Method({ + name: 'startRPC', + call: 'admin_startRPC', + params: 4 + }), + new web3._extend.Method({ + name: 'stopRPC', + call: 'admin_stopRPC', + params: 0 + }), + new web3._extend.Method({ + name: 'startWS', + call: 'admin_startWS', + params: 4 + }), + new web3._extend.Method({ + name: 'stopWS', + call: 'admin_stopWS', + params: 0 + }), + new web3._extend.Method({ + name: 'setGlobalRegistrar', + call: 'admin_setGlobalRegistrar', + params: 2 + }), + new web3._extend.Method({ + name: 'setHashReg', + call: 'admin_setHashReg', + params: 2 + }), + new web3._extend.Method({ + name: 'setUrlHint', + call: 'admin_setUrlHint', + params: 2 + }), + new web3._extend.Method({ + name: 'saveInfo', + call: 'admin_saveInfo', + params: 2 + }), + new web3._extend.Method({ + name: 'register', + call: 'admin_register', + params: 3 + }), + new web3._extend.Method({ + name: 'registerUrl', + call: 'admin_registerUrl', + params: 3 + }), + new web3._extend.Method({ + name: 'startNatSpec', + call: 'admin_startNatSpec', + params: 0 + }), + new web3._extend.Method({ + name: 'stopNatSpec', + call: 'admin_stopNatSpec', + params: 0 + }), + new web3._extend.Method({ + name: 'getContractInfo', + call: 'admin_getContractInfo', + params: 1 + }), + new web3._extend.Method({ + name: 'httpGet', + call: 'admin_httpGet', + params: 2 + }) + ], + properties: + [ + new web3._extend.Property({ + name: 'nodeInfo', + getter: 'admin_nodeInfo' + }), + new web3._extend.Property({ + name: 'peers', + getter: 'admin_peers' + }), + new web3._extend.Property({ + name: 'datadir', + getter: 'admin_datadir' + }) + ] +}); +` + +const Db_JS = ` +web3._extend({ + property: 'db', + methods: + [ + ], + properties: + [ + ] +}); +` + +const Eth_JS = ` +web3._extend({ + property: 'eth', + methods: + [ + new web3._extend.Method({ + name: 'sign', + call: 'eth_sign', + params: 2, + inputFormatter: [web3._extend.utils.toAddress, null] + }), + new web3._extend.Method({ + name: 'resend', + call: 'eth_resend', + params: 3, + inputFormatter: [web3._extend.formatters.inputTransactionFormatter, web3._extend.utils.fromDecimal, web3._extend.utils.fromDecimal] + }), + new web3._extend.Method({ + name: 'getNatSpec', + call: 'eth_getNatSpec', + params: 1, + inputFormatter: [web3._extend.formatters.inputTransactionFormatter] + }), + new web3._extend.Method({ + name: 'signTransaction', + call: 'eth_signTransaction', + params: 1, + inputFormatter: [web3._extend.formatters.inputTransactionFormatter] + }), + new web3._extend.Method({ + name: 'submitTransaction', + call: 'eth_submitTransaction', + params: 1, + inputFormatter: [web3._extend.formatters.inputTransactionFormatter] + }) + ], + properties: + [ + new web3._extend.Property({ + name: 'pendingTransactions', + getter: 'eth_pendingTransactions' + }) + ] +}); +` + +const Net_JS = ` +web3._extend({ + property: 'net', + methods: + [ + new web3._extend.Method({ + name: 'addPeer', + call: 'net_addPeer', + params: 1 + }) + ], + properties: + [ + new web3._extend.Property({ + name: 'version', + getter: 'net_version' + }) + ] +}); +` + +const Debug_JS = ` +web3._extend({ + property: 'debug', + methods: + [ + new web3._extend.Method({ + name: 'printBlock', + call: 'debug_printBlock', + params: 1 + }), + new web3._extend.Method({ + name: 'getBlockRlp', + call: 'debug_getBlockRlp', + params: 1 + }), + new web3._extend.Method({ + name: 'setHead', + call: 'debug_setHead', + params: 1 + }), + new web3._extend.Method({ + name: 'processBlock', + call: 'debug_processBlock', + params: 1 + }), + new web3._extend.Method({ + name: 'seedHash', + call: 'debug_seedHash', + params: 1 + }), + new web3._extend.Method({ + name: 'dumpBlock', + call: 'debug_dumpBlock', + params: 1 + }), + new web3._extend.Method({ + name: 'metrics', + call: 'debug_metrics', + params: 1 + }) + ], + properties: + [ + ] +}); +` + +const Miner_JS = ` +web3._extend({ + property: 'miner', + methods: + [ + new web3._extend.Method({ + name: 'start', + call: 'miner_start', + params: 1 + }), + new web3._extend.Method({ + name: 'stop', + call: 'miner_stop', + params: 1 + }), + new web3._extend.Method({ + name: 'setEtherbase', + call: 'miner_setEtherbase', + params: 1, + inputFormatter: [web3._extend.formatters.formatInputInt], + outputFormatter: web3._extend.formatters.formatOutputBool + }), + new web3._extend.Method({ + name: 'setExtra', + call: 'miner_setExtra', + params: 1 + }), + new web3._extend.Method({ + name: 'setGasPrice', + call: 'miner_setGasPrice', + params: 1, + inputFormatter: [web3._extend.utils.fromDecial] + }), + new web3._extend.Method({ + name: 'startAutoDAG', + call: 'miner_startAutoDAG', + params: 0 + }), + new web3._extend.Method({ + name: 'stopAutoDAG', + call: 'miner_stopAutoDAG', + params: 0 + }), + new web3._extend.Method({ + name: 'makeDAG', + call: 'miner_makeDAG', + params: 1, + inputFormatter: [web3._extend.formatters.inputDefaultBlockNumberFormatter] + }) + ], + properties: + [ + new web3._extend.Property({ + name: 'hashrate', + getter: 'miner_hashrate', + outputFormatter: web3._extend.utils.toDecimal + }) + ] +}); +` + +const Shh_JS = ` +web3._extend({ + property: 'shh', + methods: + [ + + ], + properties: + [ + new web3._extend.Property({ + name: 'version', + getter: 'shh_version' + }) + ] +}); +` diff --git a/rpc/v2/json.go b/rpc/json.go index 9208e2d37..8bdb4665d 100644 --- a/rpc/v2/json.go +++ b/rpc/json.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/>. -package v2 +package rpc import ( "encoding/json" @@ -37,7 +37,7 @@ const ( ) // JSON-RPC request -type jsonRequest struct { +type JSONRequest struct { Method string `json:"method"` Version string `json:"jsonrpc"` Id *int64 `json:"id,omitempty"` @@ -45,24 +45,24 @@ type jsonRequest struct { } // JSON-RPC response -type jsonSuccessResponse struct { +type JSONSuccessResponse struct { Version string `json:"jsonrpc"` Id int64 `json:"id"` Result interface{} `json:"result,omitempty"` } // JSON-RPC error object -type jsonError struct { +type JSONError struct { Code int `json:"code"` Message string `json:"message"` Data interface{} `json:"data,omitempty"` } // JSON-RPC error response -type jsonErrResponse struct { +type JSONErrResponse struct { Version string `json:"jsonrpc"` Id *int64 `json:"id,omitempty"` - Error jsonError `json:"error"` + Error JSONError `json:"error"` } // JSON-RPC notification payload @@ -85,7 +85,7 @@ type jsonCodec struct { isClosed int32 d *json.Decoder e *json.Encoder - req jsonRequest + req JSONRequest rw io.ReadWriteCloser } @@ -126,7 +126,7 @@ func (c *jsonCodec) ReadRequestHeaders() ([]rpcRequest, bool, RPCError) { // parseRequest will parse a single request from the given RawMessage. It will return the parsed request, an indication // if the request was a batch or an error when the request could not be parsed. func parseRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, RPCError) { - var in jsonRequest + var in JSONRequest if err := json.Unmarshal(incomingMsg, &in); err != nil { return nil, false, &invalidMessageError{err.Error()} } @@ -175,7 +175,7 @@ func parseRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, RPCError) { // parseBatchRequest will parse a batch request into a collection of requests from the given RawMessage, an indication // if the request was a batch or an error when the request could not be read. func parseBatchRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, RPCError) { - var in []jsonRequest + var in []JSONRequest if err := json.Unmarshal(incomingMsg, &in); err != nil { return nil, false, &invalidMessageError{err.Error()} } @@ -296,21 +296,21 @@ func parsePositionalArguments(args json.RawMessage, argTypes []reflect.Type) ([] // CreateResponse will create a JSON-RPC success response with the given id and reply as result. func (c *jsonCodec) CreateResponse(id int64, reply interface{}) interface{} { if isHexNum(reflect.TypeOf(reply)) { - return &jsonSuccessResponse{Version: jsonRPCVersion, Id: id, Result: fmt.Sprintf(`%#x`, reply)} + return &JSONSuccessResponse{Version: jsonRPCVersion, Id: id, Result: fmt.Sprintf(`%#x`, reply)} } - return &jsonSuccessResponse{Version: jsonRPCVersion, Id: id, Result: reply} + return &JSONSuccessResponse{Version: jsonRPCVersion, Id: id, Result: reply} } // CreateErrorResponse will create a JSON-RPC error response with the given id and error. func (c *jsonCodec) CreateErrorResponse(id *int64, err RPCError) interface{} { - return &jsonErrResponse{Version: jsonRPCVersion, Id: id, Error: jsonError{Code: err.Code(), Message: err.Error()}} + return &JSONErrResponse{Version: jsonRPCVersion, Id: id, Error: JSONError{Code: err.Code(), Message: err.Error()}} } // CreateErrorResponseWithInfo will create a JSON-RPC error response with the given id and error. // info is optional and contains additional information about the error. When an empty string is passed it is ignored. func (c *jsonCodec) CreateErrorResponseWithInfo(id *int64, err RPCError, info interface{}) interface{} { - return &jsonErrResponse{Version: jsonRPCVersion, Id: id, - Error: jsonError{Code: err.Code(), Message: err.Error(), Data: info}} + return &JSONErrResponse{Version: jsonRPCVersion, Id: id, + Error: JSONError{Code: err.Code(), Message: err.Error(), Data: info}} } // CreateNotification will create a JSON-RPC notification with the given subscription id and event as params. diff --git a/rpc/v2/json_test.go b/rpc/json_test.go index dc8a345d7..39aae1f54 100644 --- a/rpc/v2/json_test.go +++ b/rpc/json_test.go @@ -1,4 +1,4 @@ -package v2 +package rpc import ( "bufio" diff --git a/rpc/v2/server.go b/rpc/server.go index 4c04f04d2..0b93a4e64 100644 --- a/rpc/v2/server.go +++ b/rpc/server.go @@ -14,23 +14,37 @@ // 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/>. -package v2 +package rpc import ( "fmt" "reflect" - "runtime" + "sync/atomic" + "time" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "golang.org/x/net/context" + "gopkg.in/fatih/set.v0" +) + +const ( + stopPendingRequestTimeout = 3 * time.Second // give pending requests stopPendingRequestTimeout the time to finish when the server is stopped + + DefaultIpcApis = "admin,eth,debug,miner,net,shh,txpool,personal,web3" + DefaultHttpRpcApis = "eth,net,web3" ) // NewServer will create a new server instance with no registered handlers. func NewServer() *Server { - server := &Server{services: make(serviceRegistry), subscriptions: make(subscriptionRegistry)} + server := &Server{ + services: make(serviceRegistry), + subscriptions: make(subscriptionRegistry), + codecs: set.New(), + run: 1, + } // register a default service which will provide meta information about the RPC service such as the services and // methods it offers. @@ -124,14 +138,37 @@ func (s *Server) ServeCodec(codec ServerCodec) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - for { + s.codecsMu.Lock() + if atomic.LoadInt32(&s.run) != 1 { // server stopped + s.codecsMu.Unlock() + return + } + s.codecs.Add(codec) + s.codecsMu.Unlock() + + for atomic.LoadInt32(&s.run) == 1 { reqs, batch, err := s.readRequest(codec) + if err != nil { glog.V(logger.Debug).Infof("%v\n", err) codec.Write(codec.CreateErrorResponse(nil, err)) break } + if atomic.LoadInt32(&s.run) != 1 { + err = &shutdownError{} + if batch { + resps := make([]interface{}, len(reqs)) + for i, r := range reqs { + resps[i] = codec.CreateErrorResponse(&r.id, err) + } + codec.Write(resps) + } else { + codec.Write(codec.CreateErrorResponse(&reqs[0].id, err)) + } + break + } + if batch { go s.execBatch(ctx, codec, reqs) } else { @@ -140,6 +177,22 @@ func (s *Server) ServeCodec(codec ServerCodec) { } } +// Stop will stop reading new requests, wait for stopPendingRequestTimeout to allow pending requests to finish, +// close all codecs which will cancels pending requests/subscriptions. +func (s *Server) Stop() { + if atomic.CompareAndSwapInt32(&s.run, 1, 0) { + glog.V(logger.Debug).Infoln("RPC Server shutdown initiatied") + time.AfterFunc(stopPendingRequestTimeout, func() { + s.codecsMu.Lock() + defer s.codecsMu.Unlock() + s.codecs.Each(func(c interface{}) bool { + c.(ServerCodec).Close() + return true + }) + }) + } +} + // sendNotification will create a notification from the given event by serializing member fields of the event. // It will then send the notification to the client, when it fails the codec is closed. When the event has multiple // fields an array of values is returned. diff --git a/rpc/v2/server_test.go b/rpc/server_test.go index f250c184f..5b91fe42a 100644 --- a/rpc/v2/server_test.go +++ b/rpc/server_test.go @@ -1,4 +1,20 @@ -package v2 +// Copyright 2015 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 <http://www.gnu.org/licenses/>. + +package rpc import ( "encoding/json" @@ -91,7 +107,7 @@ func (c *ServerTestCodec) ReadRequestHeaders() ([]rpcRequest, bool, RPCError) { c.counter += 1 if c.counter == 1 { - var req jsonRequest + var req JSONRequest json.Unmarshal(c.input, &req) return []rpcRequest{rpcRequest{id: *req.Id, isPubSub: false, service: "test", method: req.Method, params: req.Payload}}, false, nil } @@ -157,16 +173,16 @@ func (c *ServerTestCodec) ParseRequestArguments(argTypes []reflect.Type, payload } func (c *ServerTestCodec) CreateResponse(id int64, reply interface{}) interface{} { - return &jsonSuccessResponse{Version: jsonRPCVersion, Id: id, Result: reply} + return &JSONSuccessResponse{Version: jsonRPCVersion, Id: id, Result: reply} } func (c *ServerTestCodec) CreateErrorResponse(id *int64, err RPCError) interface{} { - return &jsonErrResponse{Version: jsonRPCVersion, Id: id, Error: jsonError{Code: err.Code(), Message: err.Error()}} + return &JSONErrResponse{Version: jsonRPCVersion, Id: id, Error: JSONError{Code: err.Code(), Message: err.Error()}} } func (c *ServerTestCodec) CreateErrorResponseWithInfo(id *int64, err RPCError, info interface{}) interface{} { - return &jsonErrResponse{Version: jsonRPCVersion, Id: id, - Error: jsonError{Code: err.Code(), Message: err.Error(), Data: info}} + return &JSONErrResponse{Version: jsonRPCVersion, Id: id, + Error: JSONError{Code: err.Code(), Message: err.Error(), Data: info}} } func (c *ServerTestCodec) CreateNotification(subid string, event interface{}) interface{} { @@ -203,7 +219,7 @@ func TestServerMethodExecution(t *testing.T) { } id := int64(12345) - req := jsonRequest{ + req := JSONRequest{ Method: "echo", Version: "2.0", Id: &id, @@ -233,7 +249,7 @@ func TestServerMethodWithCtx(t *testing.T) { } id := int64(12345) - req := jsonRequest{ + req := JSONRequest{ Method: "echoWithCtx", Version: "2.0", Id: &id, diff --git a/rpc/shared/errors.go b/rpc/shared/errors.go deleted file mode 100644 index d5a7011f9..000000000 --- a/rpc/shared/errors.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package shared - -import "fmt" - -type InvalidTypeError struct { - method string - msg string -} - -func (e *InvalidTypeError) Error() string { - return fmt.Sprintf("invalid type on field %s: %s", e.method, e.msg) -} - -func NewInvalidTypeError(method, msg string) *InvalidTypeError { - return &InvalidTypeError{ - method: method, - msg: msg, - } -} - -type InsufficientParamsError struct { - have int - want int -} - -func (e *InsufficientParamsError) Error() string { - return fmt.Sprintf("insufficient params, want %d have %d", e.want, e.have) -} - -func NewInsufficientParamsError(have int, want int) *InsufficientParamsError { - return &InsufficientParamsError{ - have: have, - want: want, - } -} - -type NotImplementedError struct { - Method string -} - -func (e *NotImplementedError) Error() string { - return fmt.Sprintf("%s method not implemented", e.Method) -} - -func NewNotImplementedError(method string) *NotImplementedError { - return &NotImplementedError{ - Method: method, - } -} - -type NotReadyError struct { - Resource string -} - -func (e *NotReadyError) Error() string { - return fmt.Sprintf("%s not ready", e.Resource) -} - -func NewNotReadyError(resource string) *NotReadyError { - return &NotReadyError{ - Resource: resource, - } -} - -type DecodeParamError struct { - err string -} - -func (e *DecodeParamError) Error() string { - return fmt.Sprintf("could not decode, %s", e.err) - -} - -func NewDecodeParamError(errstr string) error { - return &DecodeParamError{ - err: errstr, - } -} - -type ValidationError struct { - ParamName string - msg string -} - -func (e *ValidationError) Error() string { - return fmt.Sprintf("%s not valid, %s", e.ParamName, e.msg) -} - -func NewValidationError(param string, msg string) error { - return &ValidationError{ - ParamName: param, - msg: msg, - } -} - -type NotAvailableError struct { - Method string - Reason string -} - -func (e *NotAvailableError) Error() string { - return fmt.Sprintf("%s method not available: %s", e.Method, e.Reason) -} - -func NewNotAvailableError(method string, reason string) *NotAvailableError { - return &NotAvailableError{ - Method: method, - Reason: reason, - } -} diff --git a/rpc/shared/types.go b/rpc/shared/types.go deleted file mode 100644 index db328234d..000000000 --- a/rpc/shared/types.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package shared - -import ( - "encoding/json" - - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" -) - -// Ethereum RPC API interface -type EthereumApi interface { - // API identifier - Name() string - - // API version - ApiVersion() string - - // Execute the given request and returns the response or an error - Execute(*Request) (interface{}, error) - - // List of supported RCP methods this API provides - Methods() []string -} - -// RPC request -type Request struct { - Id interface{} `json:"id"` - Jsonrpc string `json:"jsonrpc"` - Method string `json:"method"` - Params json.RawMessage `json:"params"` -} - -// RPC response -type Response struct { - Id interface{} `json:"id"` - Jsonrpc string `json:"jsonrpc"` -} - -// RPC success response -type SuccessResponse struct { - Id interface{} `json:"id"` - Jsonrpc string `json:"jsonrpc"` - Result interface{} `json:"result"` -} - -// RPC error response -type ErrorResponse struct { - Id interface{} `json:"id"` - Jsonrpc string `json:"jsonrpc"` - Error *ErrorObject `json:"error"` -} - -// RPC error response details -type ErrorObject struct { - Code int `json:"code"` - Message string `json:"message"` - // Data interface{} `json:"data"` -} - -// Create RPC error response, this allows for custom error codes -func NewRpcErrorResponse(id interface{}, jsonrpcver string, errCode int, err error) *ErrorResponse { - jsonerr := &ErrorObject{errCode, err.Error()} - response := ErrorResponse{Jsonrpc: jsonrpcver, Id: id, Error: jsonerr} - - glog.V(logger.Detail).Infof("Generated error response: %s", response) - return &response -} - -// Create RPC response -func NewRpcResponse(id interface{}, jsonrpcver string, reply interface{}, err error) *interface{} { - var response interface{} - - switch err.(type) { - case nil: - response = &SuccessResponse{Jsonrpc: jsonrpcver, Id: id, Result: reply} - case *NotImplementedError: - jsonerr := &ErrorObject{-32601, err.Error()} - response = &ErrorResponse{Jsonrpc: jsonrpcver, Id: id, Error: jsonerr} - case *NotReadyError: - jsonerr := &ErrorObject{-32000, err.Error()} - response = &ErrorResponse{Jsonrpc: jsonrpcver, Id: id, Error: jsonerr} - case *DecodeParamError, *InsufficientParamsError, *ValidationError, *InvalidTypeError: - jsonerr := &ErrorObject{-32602, err.Error()} - response = &ErrorResponse{Jsonrpc: jsonrpcver, Id: id, Error: jsonerr} - default: - jsonerr := &ErrorObject{-32603, err.Error()} - response = &ErrorResponse{Jsonrpc: jsonrpcver, Id: id, Error: jsonerr} - } - - glog.V(logger.Detail).Infof("Generated response: %T %s", response, response) - return &response -} diff --git a/rpc/shared/utils.go b/rpc/shared/utils.go deleted file mode 100644 index b13e9eb1b..000000000 --- a/rpc/shared/utils.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package shared - -import "strings" - -const ( - AdminApiName = "admin" - EthApiName = "eth" - DbApiName = "db" - DebugApiName = "debug" - MergedApiName = "merged" - MinerApiName = "miner" - NetApiName = "net" - ShhApiName = "shh" - TxPoolApiName = "txpool" - PersonalApiName = "personal" - Web3ApiName = "web3" - - JsonRpcVersion = "2.0" -) - -var ( - // All API's - AllApis = strings.Join([]string{ - AdminApiName, DbApiName, EthApiName, DebugApiName, MinerApiName, NetApiName, - ShhApiName, TxPoolApiName, PersonalApiName, Web3ApiName, - }, ",") -) diff --git a/rpc/v2/types.go b/rpc/types.go index 8e638726f..02295a022 100644 --- a/rpc/v2/types.go +++ b/rpc/types.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/>. -package v2 +package rpc import ( "fmt" @@ -25,6 +25,7 @@ import ( "sync" "github.com/ethereum/go-ethereum/event" + "gopkg.in/fatih/set.v0" ) // API describes the set of methods offered over the RPC interface @@ -75,6 +76,10 @@ type Server struct { services serviceRegistry muSubcriptions sync.Mutex // protects subscriptions subscriptions subscriptionRegistry + + run int32 + codecsMu sync.Mutex + codecs *set.Set } // rpcRequest represents a raw incoming RPC request @@ -350,3 +355,14 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error { func (bn *BlockNumber) Int64() int64 { return (int64)(*bn) } + +// Client defines the interface for go client that wants to connect to a geth RPC endpoint +type Client interface { + // SupportedModules returns the collection of API's the server offers + SupportedModules() (map[string]string, error) + + Send(req interface{}) error + Recv(msg interface{}) error + + Close() +} diff --git a/rpc/v2/types_test.go b/rpc/types_test.go index f73a2369e..c2c5c6db6 100644 --- a/rpc/v2/types_test.go +++ b/rpc/types_test.go @@ -1,4 +1,20 @@ -package v2 +// Copyright 2015 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 <http://www.gnu.org/licenses/>. + +package rpc import ( "bytes" diff --git a/rpc/useragent/agent.go b/rpc/useragent/agent.go deleted file mode 100644 index df0739e65..000000000 --- a/rpc/useragent/agent.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -// package user agent provides frontends and agents which can interact with the user -package useragent - -var ( - AskPasswordMethod = "agent_askPassword" - ConfirmTransactionMethod = "agent_confirmTransaction" - EnableUserAgentMethod = "admin_enableUserAgent" -) diff --git a/rpc/useragent/remote_frontend.go b/rpc/useragent/remote_frontend.go deleted file mode 100644 index 944ab287a..000000000 --- a/rpc/useragent/remote_frontend.go +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package useragent - -import ( - "encoding/json" - "fmt" - "net" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/rpc/shared" -) - -// remoteFrontend implements xeth.Frontend and will communicate with an external -// user agent over a connection -type RemoteFrontend struct { - enabled bool - mgr *accounts.Manager - d *json.Decoder - e *json.Encoder - n int -} - -// NewRemoteFrontend creates a new frontend which will interact with an user agent -// over the given connection -func NewRemoteFrontend(conn net.Conn, mgr *accounts.Manager) *RemoteFrontend { - return &RemoteFrontend{false, mgr, json.NewDecoder(conn), json.NewEncoder(conn), 0} -} - -// Enable will enable user interaction -func (fe *RemoteFrontend) Enable() { - fe.enabled = true -} - -func (fe *RemoteFrontend) AskPassword() (string, bool) { - if !fe.enabled { - return "", false - } - - err := fe.send(AskPasswordMethod) - if err != nil { - glog.V(logger.Error).Infof("Unable to send password request to agent - %v\n", err) - return "", false - } - - passwdRes, err := fe.recv() - if err != nil { - glog.V(logger.Error).Infof("Unable to recv password response from agent - %v\n", err) - return "", false - } - - if passwd, ok := passwdRes.Result.(string); ok { - return passwd, true - } - - return "", false - -} - -// UnlockAccount asks the user agent for the user password and tries to unlock the account. -// It will try 3 attempts before giving up. -func (fe *RemoteFrontend) UnlockAccount(address []byte) bool { - if !fe.enabled { - return false - } - - err := fe.send(AskPasswordMethod, common.Bytes2Hex(address)) - if err != nil { - glog.V(logger.Error).Infof("Unable to send password request to agent - %v\n", err) - return false - } - - passwdRes, err := fe.recv() - if err != nil { - glog.V(logger.Error).Infof("Unable to recv password response from agent - %v\n", err) - return false - } - - if passwd, ok := passwdRes.Result.(string); ok { - err = fe.mgr.Unlock(common.BytesToAddress(address), passwd) - } - - if err == nil { - return true - } - - glog.V(logger.Debug).Infoln("3 invalid account unlock attempts") - return false -} - -// ConfirmTransaction asks the user for approval -func (fe *RemoteFrontend) ConfirmTransaction(tx string) bool { - if !fe.enabled { - return true // backwards compatibility - } - - err := fe.send(ConfirmTransactionMethod, tx) - if err != nil { - glog.V(logger.Error).Infof("Unable to send tx confirmation request to agent - %v\n", err) - return false - } - - confirmResponse, err := fe.recv() - if err != nil { - glog.V(logger.Error).Infof("Unable to recv tx confirmation response from agent - %v\n", err) - return false - } - - if confirmed, ok := confirmResponse.Result.(bool); ok { - return confirmed - } - - return false -} - -// send request to the agent -func (fe *RemoteFrontend) send(method string, params ...interface{}) error { - fe.n += 1 - - p, err := json.Marshal(params) - if err != nil { - glog.V(logger.Info).Infof("Unable to send agent request %v\n", err) - return err - } - - req := shared.Request{ - Method: method, - Jsonrpc: shared.JsonRpcVersion, - Id: fe.n, - Params: p, - } - - return fe.e.Encode(&req) -} - -// recv user response from agent -func (fe *RemoteFrontend) recv() (*shared.SuccessResponse, error) { - var res json.RawMessage - if err := fe.d.Decode(&res); err != nil { - return nil, err - } - - var response shared.SuccessResponse - if err := json.Unmarshal(res, &response); err == nil { - return &response, nil - } - - return nil, fmt.Errorf("Invalid user agent response") -} diff --git a/rpc/v2/utils.go b/rpc/utils.go index ca37924a3..25321c543 100644 --- a/rpc/v2/utils.go +++ b/rpc/utils.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/>. -package v2 +package rpc import ( "crypto/rand" @@ -25,6 +25,8 @@ import ( "unicode" "unicode/utf8" + "fmt" + "golang.org/x/net/context" ) @@ -212,3 +214,33 @@ func newSubscriptionId() (string, error) { } return "0x" + hex.EncodeToString(subid[:]), nil } + +// SupportedModules returns the collection of API's that the RPC server offers +// on which the given client connects. +func SupportedModules(client Client) (map[string]string, error) { + req := map[string]interface{}{ + "id": 1, + "method": "rpc_modules", + } + + if err := client.Send(req); err != nil { + return nil, err + } + + var response map[string]interface{} + if err := client.Recv(&response); err != nil { + return nil, err + } + + if payload, ok := response["result"]; ok { + mods := make(map[string]string) + if modules, ok := payload.(map[string]interface{}); ok { + for m, v := range modules { + mods[m] = fmt.Sprintf("%s", v) + } + return mods, nil + } + } + + return nil, fmt.Errorf("unable to retrieve modules") +} diff --git a/rpc/websocket.go b/rpc/websocket.go new file mode 100644 index 000000000..b5bcbf4f6 --- /dev/null +++ b/rpc/websocket.go @@ -0,0 +1,235 @@ +// Copyright 2015 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 <http://www.gnu.org/licenses/>. + +package rpc + +import ( + "errors" + "fmt" + "net" + "net/http" + "sync" + + "os" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "golang.org/x/net/websocket" + "gopkg.in/fatih/set.v0" +) + +var ( + wsServerMu sync.Mutex + wsRPCServer *Server + wsListener net.Listener +) + +// wsReaderWriterCloser reads and write payloads from and to a websocket connection. +type wsReaderWriterCloser struct { + c *websocket.Conn +} + +// Read will read incoming payload data into p. +func (rw *wsReaderWriterCloser) Read(p []byte) (int, error) { + return rw.c.Read(p) +} + +// Write writes p to the websocket. +func (rw *wsReaderWriterCloser) Write(p []byte) (int, error) { + return rw.c.Write(p) +} + +// Close closes the websocket connection. +func (rw *wsReaderWriterCloser) Close() error { + return rw.c.Close() +} + +// wsHandler accepts a websocket connection and handles incoming RPC requests. +// Will return when the websocket connection is closed, either by the client or +// server. +func wsHandler(conn *websocket.Conn) { + rwc := &wsReaderWriterCloser{conn} + wsRPCServer.ServeCodec(NewJSONCodec(rwc)) +} + +// wsHandshakeValidator returns a handler that verifies the origin during the +// websocket upgrade process. When a '*' is specified as an allowed origins all +// connections are accepted. +func wsHandshakeValidator(allowedOrigins []string) func(*websocket.Config, *http.Request) error { + origins := set.New() + allowAllOrigins := false + + for _, origin := range allowedOrigins { + if origin == "*" { + allowAllOrigins = true + } + if origin != "" { + origins.Add(origin) + } + } + + // allow localhost if no allowedOrigins are specified + if len(origins.List()) == 0 { + origins.Add("http://localhost") + if hostname, err := os.Hostname(); err == nil { + origins.Add("http://" + hostname) + } + } + + glog.V(logger.Debug).Infof("Allowed origin(s) for WS RPC interface %v\n", origins.List()) + + f := func(cfg *websocket.Config, req *http.Request) error { + origin := req.Header.Get("Origin") + if allowAllOrigins || origins.Has(origin) { + return nil + } + glog.V(logger.Debug).Infof("origin '%s' not allowed on WS-RPC interface\n", origin) + return fmt.Errorf("origin %s not allowed", origin) + } + + return f +} + +// StartWS will start a websocket RPC server on the given address and port. +func StartWS(address string, port int, corsdomains []string, apis []API) error { + wsServerMu.Lock() + defer wsServerMu.Unlock() + + if wsRPCServer != nil { + return fmt.Errorf("WS RPC interface already started on %s", wsListener.Addr()) + } + + rpcServer := NewServer() + for _, api := range apis { + if err := rpcServer.RegisterName(api.Namespace, api.Service); err != nil { + return err + } + } + + listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", address, port)) + if err != nil { + return err + } + + wsServer := websocket.Server{Handshake: wsHandshakeValidator(corsdomains), Handler: wsHandler} + wsHTTPServer := http.Server{Handler: wsServer} + + go wsHTTPServer.Serve(listener) + + wsListener = listener + wsRPCServer = rpcServer + + return nil +} + +// StopWS stops the running websocket RPC server. +func StopWS() error { + wsServerMu.Lock() + defer wsServerMu.Unlock() + + if wsRPCServer == nil { + return errors.New("HTTP RPC interface not started") + } + + wsListener.Close() + wsRPCServer.Stop() + + wsRPCServer = nil + wsListener = nil + + return nil +} + +// wsClient represents a RPC client that communicates over websockets with a +// RPC server. +type wsClient struct { + endpoint string + connMu sync.Mutex + conn *websocket.Conn +} + +// NewWSClientj creates a new RPC client that communicates with a RPC server +// that is listening on the given endpoint using JSON encoding. +func NewWSClient(endpoint string) (*wsClient, error) { + return &wsClient{endpoint: endpoint}, nil +} + +// connection will return a websocket connection to the RPC server. It will +// (re)connect when necessary. +func (client *wsClient) connection() (*websocket.Conn, error) { + if client.conn != nil { + return client.conn, nil + } + + origin, err := os.Hostname() + if err != nil { + return nil, err + } + + origin = "http://" + origin + client.conn, err = websocket.Dial(client.endpoint, "", origin) + + return client.conn, err +} + +// SupportedModules is the collection of modules the RPC server offers. +func (client *wsClient) SupportedModules() (map[string]string, error) { + return SupportedModules(client) +} + +// Send writes the JSON serialized msg to the websocket. It will create a new +// websocket connection to the server if the client is currently not connected. +func (client *wsClient) Send(msg interface{}) (err error) { + client.connMu.Lock() + defer client.connMu.Unlock() + + var conn *websocket.Conn + if conn, err = client.connection(); err == nil { + if err = websocket.JSON.Send(conn, msg); err != nil { + client.conn.Close() + client.conn = nil + } + } + + return err +} + +// Recv reads a JSON message from the websocket and unmarshals it into msg. +func (client *wsClient) Recv(msg interface{}) (err error) { + client.connMu.Lock() + defer client.connMu.Unlock() + + var conn *websocket.Conn + if conn, err = client.connection(); err == nil { + if err = websocket.JSON.Receive(conn, msg); err != nil { + client.conn.Close() + client.conn = nil + } + } + return +} + +// Close closes the underlaying websocket connection. +func (client *wsClient) Close() { + client.connMu.Lock() + defer client.connMu.Unlock() + + if client.conn != nil { + client.conn.Close() + client.conn = nil + } + +} diff --git a/rpc/xeth.go b/rpc/xeth.go deleted file mode 100644 index 9527a96c0..000000000 --- a/rpc/xeth.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -// Package rpc implements the Ethereum JSON-RPC API. -package rpc - -import ( - "encoding/json" - "fmt" - "reflect" - "sync/atomic" - - "github.com/ethereum/go-ethereum/rpc/comms" - "github.com/ethereum/go-ethereum/rpc/shared" -) - -// Xeth is a native API interface to a remote node. -type Xeth struct { - client comms.EthereumClient - reqId uint32 -} - -// NewXeth constructs a new native API interface to a remote node. -func NewXeth(client comms.EthereumClient) *Xeth { - return &Xeth{ - client: client, - } -} - -// Call invokes a method with the given parameters are the remote node. -func (self *Xeth) Call(method string, params []interface{}) (map[string]interface{}, error) { - // Assemble the json RPC request - data, err := json.Marshal(params) - if err != nil { - return nil, err - } - req := &shared.Request{ - Id: atomic.AddUint32(&self.reqId, 1), - Jsonrpc: "2.0", - Method: method, - Params: data, - } - // Send the request over and retrieve the response - if err := self.client.Send(req); err != nil { - return nil, err - } - res, err := self.client.Recv() - if err != nil { - return nil, err - } - // Ensure the response is valid, and extract the results - success, isSuccessResponse := res.(*shared.SuccessResponse) - failure, isFailureResponse := res.(*shared.ErrorResponse) - switch { - case isFailureResponse: - return nil, fmt.Errorf("Method invocation failed: %v", failure.Error) - - case isSuccessResponse: - return success.Result.(map[string]interface{}), nil - - default: - return nil, fmt.Errorf("Invalid response type: %v", reflect.TypeOf(res)) - } -} diff --git a/whisper/api.go b/whisper/api.go index 16f8bd329..575b9bc89 100644 --- a/whisper/api.go +++ b/whisper/api.go @@ -24,7 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - rpc "github.com/ethereum/go-ethereum/rpc/v2" + "github.com/ethereum/go-ethereum/rpc" ) // PublicWhisperAPI provides the whisper RPC service. diff --git a/whisper/whisper.go b/whisper/whisper.go index e99950b76..e5f686e43 100644 --- a/whisper/whisper.go +++ b/whisper/whisper.go @@ -28,7 +28,7 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/p2p" - rpc "github.com/ethereum/go-ethereum/rpc/v2" + "github.com/ethereum/go-ethereum/rpc" "gopkg.in/fatih/set.v0" ) diff --git a/xeth/frontend.go b/xeth/frontend.go deleted file mode 100644 index 70d99ebc4..000000000 --- a/xeth/frontend.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package xeth - -// Frontend should be implemented by users of XEth. Its methods are -// called whenever XEth makes a decision that requires user input. -type Frontend interface { - // AskPassword is called when a new account is created or updated - AskPassword() (string, bool) - - // UnlockAccount is called when a transaction needs to be signed - // but the key corresponding to the transaction's sender is - // locked. - // - // It should unlock the account with the given address and return - // true if unlocking succeeded. - UnlockAccount(address []byte) bool - - // This is called for all transactions inititated through - // Transact. It should prompt the user to confirm the transaction - // and return true if the transaction was acknowledged. - // - // ConfirmTransaction is not used for Call transactions - // because they cannot change any state. - ConfirmTransaction(tx string) bool -} - -// dummyFrontend is a non-interactive frontend that allows all -// transactions but cannot not unlock any keys. -type dummyFrontend struct{} - -func (dummyFrontend) AskPassword() (string, bool) { return "", false } -func (dummyFrontend) UnlockAccount([]byte) bool { return false } -func (dummyFrontend) ConfirmTransaction(string) bool { return true } diff --git a/xeth/state.go b/xeth/state.go deleted file mode 100644 index 7daccb525..000000000 --- a/xeth/state.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package xeth - -import ( - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" -) - -type State struct { - xeth *XEth - state *state.StateDB -} - -func NewState(xeth *XEth, statedb *state.StateDB) *State { - return &State{xeth, statedb} -} - -func (self *State) State() *state.StateDB { - return self.state -} - -func (self *State) Get(addr string) *Object { - return &Object{self.state.GetStateObject(common.HexToAddress(addr))} -} - -func (self *State) SafeGet(addr string) *Object { - return &Object{self.safeGet(addr)} -} - -func (self *State) safeGet(addr string) *state.StateObject { - object := self.state.GetStateObject(common.HexToAddress(addr)) - if object == nil { - object = state.NewStateObject(common.HexToAddress(addr), self.xeth.EthereumService().ChainDb()) - } - return object -} diff --git a/xeth/types.go b/xeth/types.go deleted file mode 100644 index 090115b7e..000000000 --- a/xeth/types.go +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2014 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 <http://www.gnu.org/licenses/>. - -package xeth - -import ( - "bytes" - "fmt" - "math/big" - "strings" - - "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/crypto" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/rlp" -) - -type Object struct { - *state.StateObject -} - -func NewObject(state *state.StateObject) *Object { - return &Object{state} -} - -func (self *Object) StorageString(str string) []byte { - if common.IsHex(str) { - return self.storage(common.Hex2Bytes(str[2:])) - } else { - return self.storage(common.RightPadBytes([]byte(str), 32)) - } -} - -func (self *Object) storage(addr []byte) []byte { - return self.StateObject.GetState(common.BytesToHash(addr)).Bytes() -} - -func (self *Object) Storage() (storage map[string]string) { - storage = make(map[string]string) - - it := self.StateObject.Trie().Iterator() - for it.Next() { - var data []byte - rlp.Decode(bytes.NewReader(it.Value), &data) - storage[common.ToHex(self.Trie().GetKey(it.Key))] = common.ToHex(data) - } - - return -} - -// Block interface exposed to QML -type Block struct { - //Transactions string `json:"transactions"` - ref *types.Block - Size string `json:"size"` - Number int `json:"number"` - Hash string `json:"hash"` - Transactions *common.List `json:"transactions"` - Uncles *common.List `json:"uncles"` - Time *big.Int `json:"time"` - Coinbase string `json:"coinbase"` - Name string `json:"name"` - GasLimit string `json:"gasLimit"` - GasUsed string `json:"gasUsed"` - PrevHash string `json:"prevHash"` - Bloom string `json:"bloom"` - Raw string `json:"raw"` -} - -// Creates a new QML Block from a chain block -func NewBlock(block *types.Block) *Block { - if block == nil { - return &Block{} - } - - ptxs := make([]*Transaction, len(block.Transactions())) - /* - for i, tx := range block.Transactions() { - ptxs[i] = NewTx(tx) - } - */ - txlist := common.NewList(ptxs) - - puncles := make([]*Block, len(block.Uncles())) - /* - for i, uncle := range block.Uncles() { - puncles[i] = NewBlock(types.NewBlockWithHeader(uncle)) - } - */ - ulist := common.NewList(puncles) - - return &Block{ - ref: block, Size: block.Size().String(), - Number: int(block.NumberU64()), GasUsed: block.GasUsed().String(), - GasLimit: block.GasLimit().String(), Hash: block.Hash().Hex(), - Transactions: txlist, Uncles: ulist, - Time: block.Time(), - Coinbase: block.Coinbase().Hex(), - PrevHash: block.ParentHash().Hex(), - Bloom: common.ToHex(block.Bloom().Bytes()), - Raw: block.String(), - } -} - -func (self *Block) ToString() string { - if self.ref != nil { - return self.ref.String() - } - - return "" -} - -func (self *Block) GetTransaction(hash string) *Transaction { - tx := self.ref.Transaction(common.HexToHash(hash)) - if tx == nil { - return nil - } - - return NewTx(tx) -} - -type Transaction struct { - ref *types.Transaction - - Value string `json:"value"` - Gas string `json:"gas"` - GasPrice string `json:"gasPrice"` - Hash string `json:"hash"` - Address string `json:"address"` - Sender string `json:"sender"` - RawData string `json:"rawData"` - Data string `json:"data"` - Contract bool `json:"isContract"` - CreatesContract bool `json:"createsContract"` - Confirmations int `json:"confirmations"` -} - -func NewTx(tx *types.Transaction) *Transaction { - sender, err := tx.From() - if err != nil { - return nil - } - hash := tx.Hash().Hex() - - var receiver string - if to := tx.To(); to != nil { - receiver = to.Hex() - } else { - from, _ := tx.From() - receiver = crypto.CreateAddress(from, tx.Nonce()).Hex() - } - createsContract := core.MessageCreatesContract(tx) - - var data string - if createsContract { - data = strings.Join(core.Disassemble(tx.Data()), "\n") - } else { - data = common.ToHex(tx.Data()) - } - - return &Transaction{ref: tx, Hash: hash, Value: common.CurrencyToString(tx.Value()), Address: receiver, Contract: createsContract, Gas: tx.Gas().String(), GasPrice: tx.GasPrice().String(), Data: data, Sender: sender.Hex(), CreatesContract: createsContract, RawData: common.ToHex(tx.Data())} -} - -func (self *Transaction) ToString() string { - return self.ref.String() -} - -type PReceipt struct { - CreatedContract bool `json:"createdContract"` - Address string `json:"address"` - Hash string `json:"hash"` - Sender string `json:"sender"` -} - -func NewPReciept(contractCreation bool, creationAddress, hash, address []byte) *PReceipt { - return &PReceipt{ - contractCreation, - common.ToHex(creationAddress), - common.ToHex(hash), - common.ToHex(address), - } -} - -// Peer interface exposed to QML - -type Peer struct { - ref *p2p.Peer - Ip string `json:"ip"` - Version string `json:"version"` - Caps string `json:"caps"` -} - -func NewPeer(peer *p2p.Peer) *Peer { - var caps []string - for _, cap := range peer.Caps() { - caps = append(caps, fmt.Sprintf("%s/%d", cap.Name, cap.Version)) - } - - return &Peer{ - ref: peer, - Ip: fmt.Sprintf("%v", peer.RemoteAddr()), - Version: fmt.Sprintf("%v", peer.ID()), - Caps: fmt.Sprintf("%v", caps), - } -} - -type Receipt struct { - CreatedContract bool `json:"createdContract"` - Address string `json:"address"` - Hash string `json:"hash"` - Sender string `json:"sender"` -} - -func NewReciept(contractCreation bool, creationAddress, hash, address []byte) *Receipt { - return &Receipt{ - contractCreation, - common.ToHex(creationAddress), - common.ToHex(hash), - common.ToHex(address), - } -} diff --git a/xeth/whisper.go b/xeth/whisper.go deleted file mode 100644 index e7130978f..000000000 --- a/xeth/whisper.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -// Contains the external API to the whisper sub-protocol. - -package xeth - -import ( - "fmt" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/whisper" -) - -var qlogger = logger.NewLogger("XSHH") - -// Whisper represents the API wrapper around the internal whisper implementation. -type Whisper struct { - *whisper.Whisper -} - -// NewWhisper wraps an internal whisper client into an external API version. -func NewWhisper(w *whisper.Whisper) *Whisper { - return &Whisper{w} -} - -// NewIdentity generates a new cryptographic identity for the client, and injects -// it into the known identities for message decryption. -func (self *Whisper) NewIdentity() string { - identity := self.Whisper.NewIdentity() - return common.ToHex(crypto.FromECDSAPub(&identity.PublicKey)) -} - -// HasIdentity checks if the the whisper node is configured with the private key -// of the specified public pair. -func (self *Whisper) HasIdentity(key string) bool { - return self.Whisper.HasIdentity(crypto.ToECDSAPub(common.FromHex(key))) -} - -// Post injects a message into the whisper network for distribution. -func (self *Whisper) Post(payload string, to, from string, topics []string, priority, ttl uint32) error { - // Decode the topic strings - topicsDecoded := make([][]byte, len(topics)) - for i, topic := range topics { - topicsDecoded[i] = common.FromHex(topic) - } - // Construct the whisper message and transmission options - message := whisper.NewMessage(common.FromHex(payload)) - options := whisper.Options{ - To: crypto.ToECDSAPub(common.FromHex(to)), - TTL: time.Duration(ttl) * time.Second, - Topics: whisper.NewTopics(topicsDecoded...), - } - if len(from) != 0 { - if key := self.Whisper.GetIdentity(crypto.ToECDSAPub(common.FromHex(from))); key != nil { - options.From = key - } else { - return fmt.Errorf("unknown identity to send from: %s", from) - } - } - // Wrap and send the message - pow := time.Duration(priority) * time.Millisecond - envelope, err := message.Wrap(pow, options) - if err != nil { - return err - } - if err := self.Whisper.Send(envelope); err != nil { - return err - } - return nil -} - -// Watch installs a new message handler to run in case a matching packet arrives -// from the whisper network. -func (self *Whisper) Watch(to, from string, topics [][]string, fn func(WhisperMessage)) int { - // Decode the topic strings - topicsDecoded := make([][][]byte, len(topics)) - for i, condition := range topics { - topicsDecoded[i] = make([][]byte, len(condition)) - for j, topic := range condition { - topicsDecoded[i][j] = common.FromHex(topic) - } - } - // Assemble and inject the filter into the whisper client - filter := whisper.Filter{ - To: crypto.ToECDSAPub(common.FromHex(to)), - From: crypto.ToECDSAPub(common.FromHex(from)), - Topics: whisper.NewFilterTopics(topicsDecoded...), - } - filter.Fn = func(message *whisper.Message) { - fn(NewWhisperMessage(message)) - } - return self.Whisper.Watch(filter) -} - -// Messages retrieves all the currently pooled messages matching a filter id. -func (self *Whisper) Messages(id int) []WhisperMessage { - pool := self.Whisper.Messages(id) - - messages := make([]WhisperMessage, len(pool)) - for i, message := range pool { - messages[i] = NewWhisperMessage(message) - } - return messages -} diff --git a/xeth/whisper_filter.go b/xeth/whisper_filter.go deleted file mode 100644 index fdf5cebae..000000000 --- a/xeth/whisper_filter.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -// Contains the external API side message filter for watching, pooling and polling -// matched whisper messages, also serializing data access to avoid duplications. - -package xeth - -import ( - "sync" - "time" - - "github.com/ethereum/go-ethereum/common" -) - -// whisperFilter is the message cache matching a specific filter, accumulating -// inbound messages until the are requested by the client. -type whisperFilter struct { - id int // Filter identifier for old message retrieval - ref *Whisper // Whisper reference for old message retrieval - - cache []WhisperMessage // Cache of messages not yet polled - skip map[common.Hash]struct{} // List of retrieved messages to avoid duplication - update time.Time // Time of the last message query - - lock sync.RWMutex // Lock protecting the filter internals -} - -// newWhisperFilter creates a new serialized, poll based whisper topic filter. -func newWhisperFilter(id int, ref *Whisper) *whisperFilter { - return &whisperFilter{ - id: id, - ref: ref, - - update: time.Now(), - skip: make(map[common.Hash]struct{}), - } -} - -// messages retrieves all the cached messages from the entire pool matching the -// filter, resetting the filter's change buffer. -func (w *whisperFilter) messages() []WhisperMessage { - w.lock.Lock() - defer w.lock.Unlock() - - w.cache = nil - w.update = time.Now() - - w.skip = make(map[common.Hash]struct{}) - messages := w.ref.Messages(w.id) - for _, message := range messages { - w.skip[message.ref.Hash] = struct{}{} - } - return messages -} - -// insert injects a new batch of messages into the filter cache. -func (w *whisperFilter) insert(messages ...WhisperMessage) { - w.lock.Lock() - defer w.lock.Unlock() - - for _, message := range messages { - if _, ok := w.skip[message.ref.Hash]; !ok { - w.cache = append(w.cache, messages...) - } - } -} - -// retrieve fetches all the cached messages from the filter. -func (w *whisperFilter) retrieve() (messages []WhisperMessage) { - w.lock.Lock() - defer w.lock.Unlock() - - messages, w.cache = w.cache, nil - w.update = time.Now() - - return -} - -// activity returns the last time instance when client requests were executed on -// the filter. -func (w *whisperFilter) activity() time.Time { - w.lock.RLock() - defer w.lock.RUnlock() - - return w.update -} diff --git a/xeth/whisper_message.go b/xeth/whisper_message.go deleted file mode 100644 index b3014a697..000000000 --- a/xeth/whisper_message.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -// Contains the external API representation of a whisper message. - -package xeth - -import ( - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/whisper" -) - -// WhisperMessage is the external API representation of a whisper.Message. -type WhisperMessage struct { - ref *whisper.Message - - Payload string `json:"payload"` - To string `json:"to"` - From string `json:"from"` - Sent int64 `json:"sent"` - TTL int64 `json:"ttl"` - Hash string `json:"hash"` -} - -// NewWhisperMessage converts an internal message into an API version. -func NewWhisperMessage(message *whisper.Message) WhisperMessage { - return WhisperMessage{ - ref: message, - - Payload: common.ToHex(message.Payload), - From: common.ToHex(crypto.FromECDSAPub(message.Recover())), - To: common.ToHex(crypto.FromECDSAPub(message.To)), - Sent: message.Sent.Unix(), - TTL: int64(message.TTL / time.Second), - Hash: common.ToHex(message.Hash.Bytes()), - } -} diff --git a/xeth/xeth.go b/xeth/xeth.go deleted file mode 100644 index 5a5399a3e..000000000 --- a/xeth/xeth.go +++ /dev/null @@ -1,1137 +0,0 @@ -// Copyright 2014 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 <http://www.gnu.org/licenses/>. - -// Package xeth is the interface to all Ethereum functionality. -package xeth - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "math/big" - "regexp" - "sync" - "time" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/compiler" - "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" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/eth/filters" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/miner" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/whisper" -) - -var ( - filterTickerTime = 5 * time.Minute - defaultGasPrice = big.NewInt(10000000000000) //150000000000 - defaultGas = big.NewInt(90000) //500000 - dappStorePre = []byte("dapp-") - addrReg = regexp.MustCompile(`^(0x)?[a-fA-F0-9]{40}$`) -) - -// byte will be inferred -const ( - UnknownFilterTy = iota - BlockFilterTy - TransactionFilterTy - LogFilterTy -) - -type XEth struct { - quit chan struct{} - - logMu sync.RWMutex - logQueue map[int]*logQueue - - blockMu sync.RWMutex - blockQueue map[int]*hashQueue - - transactionMu sync.RWMutex - transactionQueue map[int]*hashQueue - - messagesMu sync.RWMutex - messages map[int]*whisperFilter - - transactMu sync.Mutex - - // read-only fields - backend *node.Node - frontend Frontend - agent *miner.RemoteAgent - gpo *eth.GasPriceOracle - state *State - whisper *Whisper - filterManager *filters.FilterSystem -} - -func NewTest(stack *node.Node, frontend Frontend) *XEth { - return &XEth{backend: stack, frontend: frontend} -} - -// New creates an XEth that uses the given frontend. -// If a nil Frontend is provided, a default frontend which -// confirms all transactions will be used. -func New(stack *node.Node, frontend Frontend) *XEth { - var ( - ethereum *eth.Ethereum - whisper *whisper.Whisper - ) - stack.Service(ðereum) - stack.Service(&whisper) - - xeth := &XEth{ - backend: stack, - frontend: frontend, - quit: make(chan struct{}), - filterManager: filters.NewFilterSystem(stack.EventMux()), - logQueue: make(map[int]*logQueue), - blockQueue: make(map[int]*hashQueue), - transactionQueue: make(map[int]*hashQueue), - messages: make(map[int]*whisperFilter), - agent: miner.NewRemoteAgent(), - gpo: eth.NewGasPriceOracle(ethereum), - } - if whisper != nil { - xeth.whisper = NewWhisper(whisper) - } - ethereum.Miner().Register(xeth.agent) - if frontend == nil { - xeth.frontend = dummyFrontend{} - } - state, _ := ethereum.BlockChain().State() - xeth.state = NewState(xeth, state) - go xeth.start() - return xeth -} - -func (self *XEth) EthereumService() *eth.Ethereum { - var ethereum *eth.Ethereum - if err := self.backend.Service(ðereum); err != nil { - return nil - } - return ethereum -} - -func (self *XEth) WhisperService() *whisper.Whisper { - var whisper *whisper.Whisper - if err := self.backend.Service(&whisper); err != nil { - return nil - } - return whisper -} - -func (self *XEth) start() { - timer := time.NewTicker(2 * time.Second) - defer timer.Stop() -done: - for { - select { - case <-timer.C: - self.logMu.Lock() - for id, filter := range self.logQueue { - if time.Since(filter.timeout) > filterTickerTime { - self.filterManager.Remove(id) - delete(self.logQueue, id) - } - } - self.logMu.Unlock() - - self.blockMu.Lock() - for id, filter := range self.blockQueue { - if time.Since(filter.timeout) > filterTickerTime { - self.filterManager.Remove(id) - delete(self.blockQueue, id) - } - } - self.blockMu.Unlock() - - self.transactionMu.Lock() - for id, filter := range self.transactionQueue { - if time.Since(filter.timeout) > filterTickerTime { - self.filterManager.Remove(id) - delete(self.transactionQueue, id) - } - } - self.transactionMu.Unlock() - - self.messagesMu.Lock() - for id, filter := range self.messages { - if time.Since(filter.activity()) > filterTickerTime { - self.Whisper().Unwatch(id) - delete(self.messages, id) - } - } - self.messagesMu.Unlock() - case <-self.quit: - break done - } - } -} - -// Stop releases any resources associated with self. -// It may not be called more than once. -func (self *XEth) Stop() { - close(self.quit) - self.filterManager.Stop() - self.EthereumService().Miner().Unregister(self.agent) -} - -func cAddress(a []string) []common.Address { - bslice := make([]common.Address, len(a)) - for i, addr := range a { - bslice[i] = common.HexToAddress(addr) - } - return bslice -} - -func cTopics(t [][]string) [][]common.Hash { - topics := make([][]common.Hash, len(t)) - for i, iv := range t { - topics[i] = make([]common.Hash, len(iv)) - for j, jv := range iv { - topics[i][j] = common.HexToHash(jv) - } - } - return topics -} - -func DefaultGas() *big.Int { return new(big.Int).Set(defaultGas) } - -func (self *XEth) DefaultGasPrice() *big.Int { - return self.gpo.SuggestPrice() -} - -func (self *XEth) RemoteMining() *miner.RemoteAgent { return self.agent } - -func (self *XEth) AtStateNum(num int64) *XEth { - var st *state.StateDB - var err error - switch num { - case -2: - st = self.EthereumService().Miner().PendingState().Copy() - default: - if block := self.getBlockByHeight(num); block != nil { - st, err = state.New(block.Root(), self.EthereumService().ChainDb()) - if err != nil { - return nil - } - } else { - st, err = state.New(self.EthereumService().BlockChain().GetBlockByNumber(0).Root(), self.EthereumService().ChainDb()) - if err != nil { - return nil - } - } - } - return self.WithState(st) -} - -func (self *XEth) WithState(statedb *state.StateDB) *XEth { - xeth := &XEth{ - backend: self.backend, - frontend: self.frontend, - gpo: self.gpo, - } - - xeth.state = NewState(xeth, statedb) - return xeth -} - -func (self *XEth) State() *State { return self.state } - -// subscribes to new head block events and -// waits until blockchain height is greater n at any time -// given the current head, waits for the next chain event -// sets the state to the current head -// loop is async and quit by closing the channel -// used in tests and JS console debug module to control advancing private chain manually -// Note: this is not threadsafe, only called in JS single process and tests -func (self *XEth) UpdateState() (wait chan *big.Int) { - wait = make(chan *big.Int) - go func() { - eventSub := self.backend.EventMux().Subscribe(core.ChainHeadEvent{}) - defer eventSub.Unsubscribe() - - var m, n *big.Int - var ok bool - - eventCh := eventSub.Chan() - for { - select { - case event, ok := <-eventCh: - if !ok { - // Event subscription closed, set the channel to nil to stop spinning - eventCh = nil - continue - } - // A real event arrived, process if new head block assignment - if event, ok := event.Data.(core.ChainHeadEvent); ok { - m = event.Block.Number() - if n != nil && n.Cmp(m) < 0 { - wait <- n - n = nil - } - statedb, err := state.New(event.Block.Root(), self.EthereumService().ChainDb()) - if err != nil { - glog.V(logger.Error).Infoln("Could not create new state: %v", err) - return - } - self.state = NewState(self, statedb) - } - case n, ok = <-wait: - if !ok { - return - } - } - } - }() - return -} - -func (self *XEth) Whisper() *Whisper { return self.whisper } - -func (self *XEth) getBlockByHeight(height int64) *types.Block { - var num uint64 - - switch height { - case -2: - return self.EthereumService().Miner().PendingBlock() - case -1: - return self.CurrentBlock() - default: - if height < 0 { - return nil - } - - num = uint64(height) - } - - return self.EthereumService().BlockChain().GetBlockByNumber(num) -} - -func (self *XEth) BlockByHash(strHash string) *Block { - hash := common.HexToHash(strHash) - block := self.EthereumService().BlockChain().GetBlock(hash) - - return NewBlock(block) -} - -func (self *XEth) EthBlockByHash(strHash string) *types.Block { - hash := common.HexToHash(strHash) - block := self.EthereumService().BlockChain().GetBlock(hash) - - return block -} - -func (self *XEth) EthTransactionByHash(hash string) (*types.Transaction, common.Hash, uint64, uint64) { - ethereum := self.EthereumService() - if tx, hash, number, index := core.GetTransaction(ethereum.ChainDb(), common.HexToHash(hash)); tx != nil { - return tx, hash, number, index - } - return ethereum.TxPool().GetTransaction(common.HexToHash(hash)), common.Hash{}, 0, 0 -} - -func (self *XEth) BlockByNumber(num int64) *Block { - return NewBlock(self.getBlockByHeight(num)) -} - -func (self *XEth) EthBlockByNumber(num int64) *types.Block { - return self.getBlockByHeight(num) -} - -func (self *XEth) Td(hash common.Hash) *big.Int { - return self.EthereumService().BlockChain().GetTd(hash) -} - -func (self *XEth) CurrentBlock() *types.Block { - return self.EthereumService().BlockChain().CurrentBlock() -} - -func (self *XEth) GetBlockReceipts(bhash common.Hash) types.Receipts { - return core.GetBlockReceipts(self.EthereumService().ChainDb(), bhash) -} - -func (self *XEth) GetTxReceipt(txhash common.Hash) *types.Receipt { - return core.GetReceipt(self.EthereumService().ChainDb(), txhash) -} - -func (self *XEth) GasLimit() *big.Int { - return self.EthereumService().BlockChain().GasLimit() -} - -func (self *XEth) Block(v interface{}) *Block { - if n, ok := v.(int32); ok { - return self.BlockByNumber(int64(n)) - } else if str, ok := v.(string); ok { - return self.BlockByHash(str) - } else if f, ok := v.(float64); ok { // JSON numbers are represented as float64 - return self.BlockByNumber(int64(f)) - } - - return nil -} - -func (self *XEth) Accounts() []string { - // TODO: check err? - accounts, _ := self.EthereumService().AccountManager().Accounts() - accountAddresses := make([]string, len(accounts)) - for i, ac := range accounts { - accountAddresses[i] = ac.Address.Hex() - } - return accountAddresses -} - -// accessor for solidity compiler. -// memoized if available, retried on-demand if not -func (self *XEth) Solc() (*compiler.Solidity, error) { - return self.EthereumService().Solc() -} - -// set in js console via admin interface or wrapper from cli flags -func (self *XEth) SetSolc(solcPath string) (*compiler.Solidity, error) { - self.EthereumService().SetSolc(solcPath) - return self.Solc() -} - -// store DApp value in extra database -func (self *XEth) DbPut(key, val []byte) bool { - self.EthereumService().DappDb().Put(append(dappStorePre, key...), val) - return true -} - -// retrieve DApp value from extra database -func (self *XEth) DbGet(key []byte) ([]byte, error) { - val, err := self.EthereumService().DappDb().Get(append(dappStorePre, key...)) - return val, err -} - -func (self *XEth) PeerCount() int { - return self.backend.Server().PeerCount() -} - -func (self *XEth) IsMining() bool { - return self.EthereumService().IsMining() -} - -func (self *XEth) HashRate() int64 { - return self.EthereumService().Miner().HashRate() -} - -func (self *XEth) EthVersion() string { - return fmt.Sprintf("%d", self.EthereumService().EthVersion()) -} - -func (self *XEth) NetworkVersion() string { - return fmt.Sprintf("%d", self.EthereumService().NetVersion()) -} - -func (self *XEth) WhisperVersion() string { - return fmt.Sprintf("%d", self.WhisperService().Version()) -} - -func (self *XEth) ClientVersion() string { - return self.backend.Server().Name -} - -func (self *XEth) SetMining(shouldmine bool, threads int) bool { - ismining := self.EthereumService().IsMining() - if shouldmine && !ismining { - err := self.EthereumService().StartMining(threads, "") - return err == nil - } - if ismining && !shouldmine { - self.EthereumService().StopMining() - } - return self.EthereumService().IsMining() -} - -func (self *XEth) IsListening() bool { - return true -} - -func (self *XEth) Coinbase() string { - eb, err := self.EthereumService().Etherbase() - if err != nil { - return "0x0" - } - return eb.Hex() -} - -func (self *XEth) NumberToHuman(balance string) string { - b := common.Big(balance) - - return common.CurrencyToString(b) -} - -func (self *XEth) StorageAt(addr, storageAddr string) string { - return self.State().state.GetState(common.HexToAddress(addr), common.HexToHash(storageAddr)).Hex() -} - -func (self *XEth) BalanceAt(addr string) string { - return common.ToHex(self.State().state.GetBalance(common.HexToAddress(addr)).Bytes()) -} - -func (self *XEth) TxCountAt(address string) int { - return int(self.State().state.GetNonce(common.HexToAddress(address))) -} - -func (self *XEth) CodeAt(address string) string { - return common.ToHex(self.State().state.GetCode(common.HexToAddress(address))) -} - -func (self *XEth) CodeAtBytes(address string) []byte { - return self.State().SafeGet(address).Code() -} - -func (self *XEth) IsContract(address string) bool { - return len(self.State().SafeGet(address).Code()) > 0 -} - -func (self *XEth) UninstallFilter(id int) bool { - defer self.filterManager.Remove(id) - - if _, ok := self.logQueue[id]; ok { - self.logMu.Lock() - defer self.logMu.Unlock() - delete(self.logQueue, id) - return true - } - if _, ok := self.blockQueue[id]; ok { - self.blockMu.Lock() - defer self.blockMu.Unlock() - delete(self.blockQueue, id) - return true - } - if _, ok := self.transactionQueue[id]; ok { - self.transactionMu.Lock() - defer self.transactionMu.Unlock() - delete(self.transactionQueue, id) - return true - } - - return false -} - -func (self *XEth) NewLogFilter(earliest, latest int64, skip, max int, address []string, topics [][]string) int { - self.logMu.Lock() - defer self.logMu.Unlock() - - filter := filters.New(self.EthereumService().ChainDb()) - id := self.filterManager.Add(filter) - self.logQueue[id] = &logQueue{timeout: time.Now()} - - filter.SetBeginBlock(earliest) - filter.SetEndBlock(latest) - filter.SetAddresses(cAddress(address)) - filter.SetTopics(cTopics(topics)) - filter.LogsCallback = func(logs vm.Logs) { - self.logMu.Lock() - defer self.logMu.Unlock() - - if queue := self.logQueue[id]; queue != nil { - queue.add(logs...) - } - } - - return id -} - -func (self *XEth) NewTransactionFilter() int { - self.transactionMu.Lock() - defer self.transactionMu.Unlock() - - filter := filters.New(self.EthereumService().ChainDb()) - id := self.filterManager.Add(filter) - self.transactionQueue[id] = &hashQueue{timeout: time.Now()} - - filter.TransactionCallback = func(tx *types.Transaction) { - self.transactionMu.Lock() - defer self.transactionMu.Unlock() - - if queue := self.transactionQueue[id]; queue != nil { - queue.add(tx.Hash()) - } - } - return id -} - -func (self *XEth) NewBlockFilter() int { - self.blockMu.Lock() - defer self.blockMu.Unlock() - - filter := filters.New(self.EthereumService().ChainDb()) - id := self.filterManager.Add(filter) - self.blockQueue[id] = &hashQueue{timeout: time.Now()} - - filter.BlockCallback = func(block *types.Block, logs vm.Logs) { - self.blockMu.Lock() - defer self.blockMu.Unlock() - - if queue := self.blockQueue[id]; queue != nil { - queue.add(block.Hash()) - } - } - return id -} - -func (self *XEth) GetFilterType(id int) byte { - if _, ok := self.blockQueue[id]; ok { - return BlockFilterTy - } else if _, ok := self.transactionQueue[id]; ok { - return TransactionFilterTy - } else if _, ok := self.logQueue[id]; ok { - return LogFilterTy - } - - return UnknownFilterTy -} - -func (self *XEth) LogFilterChanged(id int) vm.Logs { - self.logMu.Lock() - defer self.logMu.Unlock() - - if self.logQueue[id] != nil { - return self.logQueue[id].get() - } - return nil -} - -func (self *XEth) BlockFilterChanged(id int) []common.Hash { - self.blockMu.Lock() - defer self.blockMu.Unlock() - - if self.blockQueue[id] != nil { - return self.blockQueue[id].get() - } - return nil -} - -func (self *XEth) TransactionFilterChanged(id int) []common.Hash { - self.blockMu.Lock() - defer self.blockMu.Unlock() - - if self.transactionQueue[id] != nil { - return self.transactionQueue[id].get() - } - return nil -} - -func (self *XEth) Logs(id int) vm.Logs { - filter := self.filterManager.Get(id) - if filter != nil { - return filter.Find() - } - - return nil -} - -func (self *XEth) AllLogs(earliest, latest int64, skip, max int, address []string, topics [][]string) vm.Logs { - filter := filters.New(self.EthereumService().ChainDb()) - filter.SetBeginBlock(earliest) - filter.SetEndBlock(latest) - filter.SetAddresses(cAddress(address)) - filter.SetTopics(cTopics(topics)) - - return filter.Find() -} - -// NewWhisperFilter creates and registers a new message filter to watch for -// inbound whisper messages. All parameters at this point are assumed to be -// HEX encoded. -func (p *XEth) NewWhisperFilter(to, from string, topics [][]string) int { - // Pre-define the id to be filled later - var id int - - // Callback to delegate core whisper messages to this xeth filter - callback := func(msg WhisperMessage) { - p.messagesMu.RLock() // Only read lock to the filter pool - defer p.messagesMu.RUnlock() - if p.messages[id] != nil { - p.messages[id].insert(msg) - } - } - // Initialize the core whisper filter and wrap into xeth - id = p.Whisper().Watch(to, from, topics, callback) - - p.messagesMu.Lock() - p.messages[id] = newWhisperFilter(id, p.Whisper()) - p.messagesMu.Unlock() - - return id -} - -// UninstallWhisperFilter disables and removes an existing filter. -func (p *XEth) UninstallWhisperFilter(id int) bool { - p.messagesMu.Lock() - defer p.messagesMu.Unlock() - - if _, ok := p.messages[id]; ok { - delete(p.messages, id) - return true - } - return false -} - -// WhisperMessages retrieves all the known messages that match a specific filter. -func (self *XEth) WhisperMessages(id int) []WhisperMessage { - self.messagesMu.RLock() - defer self.messagesMu.RUnlock() - - if self.messages[id] != nil { - return self.messages[id].messages() - } - return nil -} - -// WhisperMessagesChanged retrieves all the new messages matched by a filter -// since the last retrieval -func (self *XEth) WhisperMessagesChanged(id int) []WhisperMessage { - self.messagesMu.RLock() - defer self.messagesMu.RUnlock() - - if self.messages[id] != nil { - return self.messages[id].retrieve() - } - return nil -} - -// func (self *XEth) Register(args string) bool { -// self.regmut.Lock() -// defer self.regmut.Unlock() - -// if _, ok := self.register[args]; ok { -// self.register[args] = nil // register with empty -// } -// return true -// } - -// func (self *XEth) Unregister(args string) bool { -// self.regmut.Lock() -// defer self.regmut.Unlock() - -// if _, ok := self.register[args]; ok { -// delete(self.register, args) -// return true -// } - -// return false -// } - -// // TODO improve return type -// func (self *XEth) PullWatchTx(args string) []*interface{} { -// self.regmut.Lock() -// defer self.regmut.Unlock() - -// txs := self.register[args] -// self.register[args] = nil - -// return txs -// } - -type KeyVal struct { - Key string `json:"key"` - Value string `json:"value"` -} - -func (self *XEth) EachStorage(addr string) string { - var values []KeyVal - object := self.State().SafeGet(addr) - it := object.Trie().Iterator() - for it.Next() { - values = append(values, KeyVal{common.ToHex(object.Trie().GetKey(it.Key)), common.ToHex(it.Value)}) - } - - valuesJson, err := json.Marshal(values) - if err != nil { - return "" - } - - return string(valuesJson) -} - -func (self *XEth) ToAscii(str string) string { - padded := common.RightPadBytes([]byte(str), 32) - - return "0x" + common.ToHex(padded) -} - -func (self *XEth) FromAscii(str string) string { - if common.IsHex(str) { - str = str[2:] - } - - return string(bytes.Trim(common.FromHex(str), "\x00")) -} - -func (self *XEth) FromNumber(str string) string { - if common.IsHex(str) { - str = str[2:] - } - - return common.BigD(common.FromHex(str)).String() -} - -func (self *XEth) PushTx(encodedTx string) (string, error) { - tx := new(types.Transaction) - err := rlp.DecodeBytes(common.FromHex(encodedTx), tx) - if err != nil { - glog.V(logger.Error).Infoln(err) - return "", err - } - - err = self.EthereumService().TxPool().Add(tx) - if err != nil { - return "", err - } - - if tx.To() == nil { - from, err := tx.From() - if err != nil { - return "", err - } - - addr := crypto.CreateAddress(from, tx.Nonce()) - glog.V(logger.Info).Infof("Tx(%x) created: %x\n", tx.Hash(), addr) - } else { - glog.V(logger.Info).Infof("Tx(%x) to: %x\n", tx.Hash(), tx.To()) - } - - return tx.Hash().Hex(), nil -} - -func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, string, error) { - statedb := self.State().State().Copy() - var from *state.StateObject - if len(fromStr) == 0 { - accounts, err := self.EthereumService().AccountManager().Accounts() - if err != nil || len(accounts) == 0 { - from = statedb.GetOrNewStateObject(common.Address{}) - } else { - from = statedb.GetOrNewStateObject(accounts[0].Address) - } - } else { - from = statedb.GetOrNewStateObject(common.HexToAddress(fromStr)) - } - - from.SetBalance(common.MaxBig) - - msg := callmsg{ - from: from, - gas: common.Big(gasStr), - gasPrice: common.Big(gasPriceStr), - value: common.Big(valueStr), - data: common.FromHex(dataStr), - } - if len(toStr) > 0 { - addr := common.HexToAddress(toStr) - msg.to = &addr - } - - if msg.gas.Cmp(big.NewInt(0)) == 0 { - msg.gas = big.NewInt(50000000) - } - - if msg.gasPrice.Cmp(big.NewInt(0)) == 0 { - msg.gasPrice = self.DefaultGasPrice() - } - - header := self.CurrentBlock().Header() - vmenv := core.NewEnv(statedb, self.EthereumService().BlockChain(), msg, header) - gp := new(core.GasPool).AddGas(common.MaxBig) - res, gas, err := core.ApplyMessage(vmenv, msg, gp) - return common.ToHex(res), gas.String(), err -} - -func (self *XEth) ConfirmTransaction(tx string) bool { - return self.frontend.ConfirmTransaction(tx) -} - -func (self *XEth) doSign(from common.Address, hash common.Hash, didUnlock bool) ([]byte, error) { - sig, err := self.EthereumService().AccountManager().Sign(accounts.Account{Address: from}, hash.Bytes()) - if err == accounts.ErrLocked { - if didUnlock { - return nil, fmt.Errorf("signer account still locked after successful unlock") - } - if !self.frontend.UnlockAccount(from.Bytes()) { - return nil, fmt.Errorf("could not unlock signer account") - } - // retry signing, the account should now be unlocked. - return self.doSign(from, hash, true) - } else if err != nil { - return nil, err - } - return sig, nil -} - -func (self *XEth) Sign(fromStr, hashStr string, didUnlock bool) (string, error) { - var ( - from = common.HexToAddress(fromStr) - hash = common.HexToHash(hashStr) - ) - sig, err := self.doSign(from, hash, didUnlock) - if err != nil { - return "", err - } - return common.ToHex(sig), nil -} - -func isAddress(addr string) bool { - return addrReg.MatchString(addr) -} - -func (self *XEth) Frontend() Frontend { - return self.frontend -} - -func (self *XEth) SignTransaction(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (*types.Transaction, error) { - if len(toStr) > 0 && toStr != "0x" && !isAddress(toStr) { - return nil, errors.New("Invalid address") - } - - var ( - from = common.HexToAddress(fromStr) - to = common.HexToAddress(toStr) - value = common.Big(valueStr) - gas *big.Int - price *big.Int - data []byte - contractCreation bool - ) - - if len(gasStr) == 0 { - gas = DefaultGas() - } else { - gas = common.Big(gasStr) - } - - if len(gasPriceStr) == 0 { - price = self.DefaultGasPrice() - } else { - price = common.Big(gasPriceStr) - } - - data = common.FromHex(codeStr) - if len(toStr) == 0 { - contractCreation = true - } - - var nonce uint64 - if len(nonceStr) != 0 { - nonce = common.Big(nonceStr).Uint64() - } else { - state := self.EthereumService().TxPool().State() - nonce = state.GetNonce(from) - } - var tx *types.Transaction - if contractCreation { - tx = types.NewContractCreation(nonce, value, gas, price, data) - } else { - tx = types.NewTransaction(nonce, to, value, gas, price, data) - } - - signed, err := self.sign(tx, from, false) - if err != nil { - return nil, err - } - - return signed, nil -} - -func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { - - // this minimalistic recoding is enough (works for natspec.js) - var jsontx = fmt.Sprintf(`{"params":[{"to":"%s","data": "%s"}]}`, toStr, codeStr) - if !self.ConfirmTransaction(jsontx) { - err := fmt.Errorf("Transaction not confirmed") - return "", err - } - - if len(toStr) > 0 && toStr != "0x" && !isAddress(toStr) { - return "", errors.New("Invalid address") - } - - var ( - from = common.HexToAddress(fromStr) - to = common.HexToAddress(toStr) - value = common.Big(valueStr) - gas *big.Int - price *big.Int - data []byte - contractCreation bool - ) - - if len(gasStr) == 0 { - gas = DefaultGas() - } else { - gas = common.Big(gasStr) - } - - if len(gasPriceStr) == 0 { - price = self.DefaultGasPrice() - } else { - price = common.Big(gasPriceStr) - } - - data = common.FromHex(codeStr) - if len(toStr) == 0 { - contractCreation = true - } - - // 2015-05-18 Is this still needed? - // TODO if no_private_key then - //if _, exists := p.register[args.From]; exists { - // p.register[args.From] = append(p.register[args.From], args) - //} else { - /* - account := accounts.Get(common.FromHex(args.From)) - if account != nil { - if account.Unlocked() { - if !unlockAccount(account) { - return - } - } - - result, _ := account.Transact(common.FromHex(args.To), common.FromHex(args.Value), common.FromHex(args.Gas), common.FromHex(args.GasPrice), common.FromHex(args.Data)) - if len(result) > 0 { - *reply = common.ToHex(result) - } - } else if _, exists := p.register[args.From]; exists { - p.register[ags.From] = append(p.register[args.From], args) - } - */ - - self.transactMu.Lock() - defer self.transactMu.Unlock() - - var nonce uint64 - if len(nonceStr) != 0 { - nonce = common.Big(nonceStr).Uint64() - } else { - state := self.EthereumService().TxPool().State() - nonce = state.GetNonce(from) - } - var tx *types.Transaction - if contractCreation { - tx = types.NewContractCreation(nonce, value, gas, price, data) - } else { - tx = types.NewTransaction(nonce, to, value, gas, price, data) - } - - signed, err := self.sign(tx, from, false) - if err != nil { - return "", err - } - self.EthereumService().TxPool().SetLocal(signed) - if err = self.EthereumService().TxPool().Add(signed); err != nil { - return "", err - } - - if contractCreation { - addr := crypto.CreateAddress(from, nonce) - glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signed.Hash().Hex(), addr.Hex()) - } else { - glog.V(logger.Info).Infof("Tx(%s) to: %s\n", signed.Hash().Hex(), tx.To().Hex()) - } - - return signed.Hash().Hex(), nil -} - -func (self *XEth) sign(tx *types.Transaction, from common.Address, didUnlock bool) (*types.Transaction, error) { - hash := tx.SigHash() - sig, err := self.doSign(from, hash, didUnlock) - if err != nil { - return tx, err - } - return tx.WithSignature(sig) -} - -// callmsg is the message type used for call transations. -type callmsg struct { - from *state.StateObject - to *common.Address - gas, gasPrice *big.Int - value *big.Int - data []byte -} - -// accessor boilerplate to implement core.Message -func (m callmsg) From() (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.gas } -func (m callmsg) Value() *big.Int { return m.value } -func (m callmsg) Data() []byte { return m.data } - -type logQueue struct { - mu sync.Mutex - - logs vm.Logs - timeout time.Time - id int -} - -func (l *logQueue) add(logs ...*vm.Log) { - l.mu.Lock() - defer l.mu.Unlock() - - l.logs = append(l.logs, logs...) -} - -func (l *logQueue) get() vm.Logs { - l.mu.Lock() - defer l.mu.Unlock() - - l.timeout = time.Now() - tmp := l.logs - l.logs = nil - return tmp -} - -type hashQueue struct { - mu sync.Mutex - - hashes []common.Hash - timeout time.Time - id int -} - -func (l *hashQueue) add(hashes ...common.Hash) { - l.mu.Lock() - defer l.mu.Unlock() - - l.hashes = append(l.hashes, hashes...) -} - -func (l *hashQueue) get() []common.Hash { - l.mu.Lock() - defer l.mu.Unlock() - - l.timeout = time.Now() - tmp := l.hashes - l.hashes = nil - return tmp -} diff --git a/xeth/xeth_test.go b/xeth/xeth_test.go deleted file mode 100644 index e649d20ef..000000000 --- a/xeth/xeth_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package xeth - -import "testing" - -func TestIsAddress(t *testing.T) { - for _, invalid := range []string{ - "0x00", - "0xNN", - "0x00000000000000000000000000000000000000NN", - "0xAAar000000000000000000000000000000000000", - } { - if isAddress(invalid) { - t.Error("Expected", invalid, "to be invalid") - } - } - - for _, valid := range []string{ - "0x0000000000000000000000000000000000000000", - "0xAABBbbCCccff9900000000000000000000000000", - "AABBbbCCccff9900000000000000000000000000", - } { - if !isAddress(valid) { - t.Error("Expected", valid, "to be valid") - } - } -} |