rpc: migrated the RPC insterface to a new reflection based RPC layer
This commit is contained in:
		
				
					committed by
					
						 Jeffrey Wilcke
						Jeffrey Wilcke
					
				
			
			
				
	
			
			
			
						parent
						
							f2ab351e8d
						
					
				
				
					commit
					19b2640e89
				
			
							
								
								
									
										14
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										14
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
| 	"ImportPath": "github.com/ethereum/go-ethereum", | ||||
| 	"GoVersion": "go1.4", | ||||
| 	"GoVersion": "go1.5.2", | ||||
| 	"Packages": [ | ||||
| 		"./..." | ||||
| 	], | ||||
| @@ -40,10 +40,6 @@ | ||||
| 			"ImportPath": "github.com/jackpal/go-nat-pmp", | ||||
| 			"Rev": "a45aa3d54aef73b504e15eb71bea0e5565b5e6e1" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"ImportPath": "github.com/kardianos/osext", | ||||
| 			"Rev": "ccfcd0245381f0c94c68f50626665eed3c6b726a" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"ImportPath": "github.com/mattn/go-isatty", | ||||
| 			"Rev": "7fcbc72f853b92b5720db4a6b8482be612daef24" | ||||
| @@ -73,10 +69,6 @@ | ||||
| 			"ImportPath": "github.com/robertkrimen/otto", | ||||
| 			"Rev": "dea31a3d392779af358ec41f77a07fcc7e9d04ba" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"ImportPath": "github.com/rs/cors", | ||||
| 			"Rev": "6e0c3cb65fc0fdb064c743d176a620e3ca446dfb" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"ImportPath": "github.com/shiena/ansicolor", | ||||
| 			"Rev": "a5e2b567a4dd6cc74545b8a4f27c9d63b9e7735b" | ||||
| @@ -109,6 +101,10 @@ | ||||
| 			"ImportPath": "golang.org/x/net/html", | ||||
| 			"Rev": "e0403b4e005737430c05a57aac078479844f919c" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"ImportPath": "golang.org/x/net/websocket", | ||||
| 			"Rev": "e0403b4e005737430c05a57aac078479844f919c" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"ImportPath": "golang.org/x/text/encoding", | ||||
| 			"Rev": "c93e7c9fff19fb9139b5ab04ce041833add0134e" | ||||
|   | ||||
							
								
								
									
										27
									
								
								Godeps/_workspace/src/github.com/kardianos/osext/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								Godeps/_workspace/src/github.com/kardianos/osext/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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. | ||||
							
								
								
									
										14
									
								
								Godeps/_workspace/src/github.com/kardianos/osext/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								Godeps/_workspace/src/github.com/kardianos/osext/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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. | ||||
							
								
								
									
										27
									
								
								Godeps/_workspace/src/github.com/kardianos/osext/osext.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								Godeps/_workspace/src/github.com/kardianos/osext/osext.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										20
									
								
								Godeps/_workspace/src/github.com/kardianos/osext/osext_plan9.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								Godeps/_workspace/src/github.com/kardianos/osext/osext_plan9.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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())) | ||||
| } | ||||
							
								
								
									
										28
									
								
								Godeps/_workspace/src/github.com/kardianos/osext/osext_procfs.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								Godeps/_workspace/src/github.com/kardianos/osext/osext_procfs.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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) | ||||
| } | ||||
							
								
								
									
										79
									
								
								Godeps/_workspace/src/github.com/kardianos/osext/osext_sysctl.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										79
									
								
								Godeps/_workspace/src/github.com/kardianos/osext/osext_sysctl.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										79
									
								
								Godeps/_workspace/src/github.com/kardianos/osext/osext_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										79
									
								
								Godeps/_workspace/src/github.com/kardianos/osext/osext_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										34
									
								
								Godeps/_workspace/src/github.com/kardianos/osext/osext_windows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										34
									
								
								Godeps/_workspace/src/github.com/kardianos/osext/osext_windows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										4
									
								
								Godeps/_workspace/src/github.com/rs/cors/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								Godeps/_workspace/src/github.com/rs/cors/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,4 +0,0 @@ | ||||
| language: go | ||||
| go: | ||||
| - 1.3 | ||||
| - 1.4 | ||||
							
								
								
									
										19
									
								
								Godeps/_workspace/src/github.com/rs/cors/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								Godeps/_workspace/src/github.com/rs/cors/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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. | ||||
							
								
								
									
										84
									
								
								Godeps/_workspace/src/github.com/rs/cors/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										84
									
								
								Godeps/_workspace/src/github.com/rs/cors/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,84 +0,0 @@ | ||||
| # Go CORS handler [](https://godoc.org/github.com/rs/cors) [](https://raw.githubusercontent.com/rs/cors/master/LICENSE) [](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). | ||||
							
								
								
									
										37
									
								
								Godeps/_workspace/src/github.com/rs/cors/bench_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										37
									
								
								Godeps/_workspace/src/github.com/rs/cors/bench_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										308
									
								
								Godeps/_workspace/src/github.com/rs/cors/cors.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										308
									
								
								Godeps/_workspace/src/github.com/rs/cors/cors.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										288
									
								
								Godeps/_workspace/src/github.com/rs/cors/cors_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										288
									
								
								Godeps/_workspace/src/github.com/rs/cors/cors_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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":    "", | ||||
| 	}) | ||||
| } | ||||
							
								
								
									
										24
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/alice/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/alice/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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) | ||||
| } | ||||
							
								
								
									
										18
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/default/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/default/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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) | ||||
| } | ||||
							
								
								
									
										22
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/goji/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/goji/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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() | ||||
| } | ||||
							
								
								
									
										23
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/martini/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										23
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/martini/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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() | ||||
| } | ||||
							
								
								
									
										26
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/negroni/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/negroni/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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") | ||||
| } | ||||
							
								
								
									
										20
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/nethttp/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/nethttp/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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)) | ||||
| } | ||||
							
								
								
									
										22
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/openbar/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/openbar/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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)) | ||||
| } | ||||
							
								
								
									
										27
									
								
								Godeps/_workspace/src/github.com/rs/cors/utils.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								Godeps/_workspace/src/github.com/rs/cors/utils.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										28
									
								
								Godeps/_workspace/src/github.com/rs/cors/utils_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								Godeps/_workspace/src/github.com/rs/cors/utils_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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") | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										113
									
								
								Godeps/_workspace/src/golang.org/x/net/websocket/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								Godeps/_workspace/src/golang.org/x/net/websocket/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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} | ||||
| } | ||||
							
								
								
									
										31
									
								
								Godeps/_workspace/src/golang.org/x/net/websocket/exampledial_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								Godeps/_workspace/src/golang.org/x/net/websocket/exampledial_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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]) | ||||
| } | ||||
							
								
								
									
										26
									
								
								Godeps/_workspace/src/golang.org/x/net/websocket/examplehandler_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								Godeps/_workspace/src/golang.org/x/net/websocket/examplehandler_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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()) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										564
									
								
								Godeps/_workspace/src/golang.org/x/net/websocket/hybi.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										564
									
								
								Godeps/_workspace/src/golang.org/x/net/websocket/hybi.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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) | ||||
| } | ||||
							
								
								
									
										590
									
								
								Godeps/_workspace/src/golang.org/x/net/websocket/hybi_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										590
									
								
								Godeps/_workspace/src/golang.org/x/net/websocket/hybi_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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()) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										114
									
								
								Godeps/_workspace/src/golang.org/x/net/websocket/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								Godeps/_workspace/src/golang.org/x/net/websocket/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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) | ||||
| } | ||||
							
								
								
									
										411
									
								
								Godeps/_workspace/src/golang.org/x/net/websocket/websocket.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										411
									
								
								Godeps/_workspace/src/golang.org/x/net/websocket/websocket.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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} | ||||
							
								
								
									
										414
									
								
								Godeps/_workspace/src/golang.org/x/net/websocket/websocket_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										414
									
								
								Godeps/_workspace/src/golang.org/x/net/websocket/websocket_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -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() | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										174
									
								
								cmd/geth/js.go
									
									
									
									
									
								
							
							
						
						
									
										174
									
								
								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) | ||||
| 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] | ||||
| 	} | ||||
|  | ||||
| 	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) | ||||
| 					} | ||||
| 	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 { | ||||
| 		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) | ||||
| 				} | ||||
| 			} else if strings.HasPrefix(module, line) { // partial method name, e.g. admi | ||||
| 				results = append(results, module) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// 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 + ".") | ||||
| 	} | ||||
|  | ||||
| 	sort.Strings(results) | ||||
| 	return results | ||||
| } | ||||
|  | ||||
| func apiWordCompleter(line string, pos int) (head string, completions []string, tail string) { | ||||
| 	if len(line) == 0 || pos == 0 { | ||||
| 		return "", nil, "" | ||||
| 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, "" | ||||
| 		} | ||||
|  | ||||
| 		// 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 | ||||
| 		} | ||||
|  | ||||
| 		begin := line[:i] | ||||
| 		keyword := line[i:pos] | ||||
| 		end := line[pos:] | ||||
|  | ||||
| 		completionWords := keywordCompleter(jsre, keyword) | ||||
| 		return begin, completionWords, end | ||||
| 	} | ||||
|  | ||||
| 	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 | ||||
| 	} | ||||
|  | ||||
| 	begin := line[:i] | ||||
| 	keyword := line[i:pos] | ||||
| 	end := line[pos:] | ||||
|  | ||||
| 	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 { | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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]...) | ||||
|   | ||||
| @@ -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, | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
|   | ||||
| @@ -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, | ||||
| 	} | ||||
| 	xeth := xeth.New(stack, nil) | ||||
| 	codec := codec.JSON | ||||
| 	/* | ||||
| 		web3 := NewPublicWeb3API(stack) | ||||
| 		server.RegisterName("web3", web3) | ||||
| 		net := NewPublicNetAPI(stack.Server(), ethereum.NetVersion()) | ||||
| 		server.RegisterName("net", net) | ||||
| 	*/ | ||||
|  | ||||
| 	apis, err := api.ParseApiString(comms.DefaultHttpRpcApis, codec, xeth, stack) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	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 | ||||
| 		} | ||||
| 	} | ||||
| 	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 { | ||||
|   | ||||
| @@ -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) | ||||
| } | ||||
							
								
								
									
										176
									
								
								cmd/utils/client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								cmd/utils/client.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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") | ||||
| } | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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 { | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
							
								
								
									
										265
									
								
								common/registrar/ethreg/api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										265
									
								
								common/registrar/ethreg/api.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
| @@ -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) | ||||
| } | ||||
							
								
								
									
										97
									
								
								eth/api.go
									
									
									
									
									
								
							
							
						
						
									
										97
									
								
								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) | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
| @@ -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 ( | ||||
|   | ||||
| @@ -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('.'); | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
							
								
								
									
										113
									
								
								node/api.go
									
									
									
									
									
								
							
							
						
						
									
										113
									
								
								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 | ||||
| @@ -247,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))) | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
							
								
								
									
										465
									
								
								rpc/api/admin.go
									
									
									
									
									
								
							
							
						
						
									
										465
									
								
								rpc/api/admin.go
									
									
									
									
									
								
							| @@ -1,465 +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/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_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) 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 | ||||
| } | ||||
| @@ -1,468 +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 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 | ||||
| } | ||||
| @@ -1,143 +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: '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' | ||||
| 		}) | ||||
| 	] | ||||
| }); | ||||
| ` | ||||
| @@ -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...) | ||||
| } | ||||
| @@ -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)) | ||||
| 	} | ||||
| } | ||||
| @@ -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 | ||||
| } | ||||
							
								
								
									
										2649
									
								
								rpc/api/args_test.go
									
									
									
									
									
								
							
							
						
						
									
										2649
									
								
								rpc/api/args_test.go
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										144
									
								
								rpc/api/db.go
									
									
									
									
									
								
							
							
						
						
									
										144
									
								
								rpc/api/db.go
									
									
									
									
									
								
							| @@ -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 | ||||
| } | ||||
| @@ -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 | ||||
| } | ||||
| @@ -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: | ||||
| 	[ | ||||
| 	] | ||||
| }); | ||||
| ` | ||||
							
								
								
									
										303
									
								
								rpc/api/debug.go
									
									
									
									
									
								
							
							
						
						
									
										303
									
								
								rpc/api/debug.go
									
									
									
									
									
								
							| @@ -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 | ||||
| } | ||||
| @@ -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 | ||||
| } | ||||
| @@ -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 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] | ||||
| 		}), | ||||
| 		new web3._extend.Method({ | ||||
| 			name: 'verbosity', | ||||
| 			call: 'debug_verbosity', | ||||
| 			params: 1, | ||||
| 			inputFormatter: [web3._extend.utils.fromDecimal] | ||||
| 		}), | ||||
| 		new web3._extend.Method({ | ||||
| 			name: 'vmodule', | ||||
| 			call: 'debug_vmodule', | ||||
| 			params: 1, | ||||
| 			inputFormatter: [null] | ||||
| 		}), | ||||
| 	], | ||||
| 	properties: | ||||
| 	[ | ||||
| 	] | ||||
| }); | ||||
| ` | ||||
							
								
								
									
										721
									
								
								rpc/api/eth.go
									
									
									
									
									
								
							
							
						
						
									
										721
									
								
								rpc/api/eth.go
									
									
									
									
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										1104
									
								
								rpc/api/eth_args.go
									
									
									
									
									
								
							
							
						
						
									
										1104
									
								
								rpc/api/eth_args.go
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -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' | ||||
| 		}) | ||||
| 	] | ||||
| }); | ||||
| ` | ||||
| @@ -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 | ||||
| } | ||||
							
								
								
									
										177
									
								
								rpc/api/miner.go
									
									
									
									
									
								
							
							
						
						
									
										177
									
								
								rpc/api/miner.go
									
									
									
									
									
								
							| @@ -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 | ||||
| } | ||||
| @@ -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 | ||||
| } | ||||
| @@ -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 | ||||
| 		}) | ||||
| 	] | ||||
| }); | ||||
| ` | ||||
| @@ -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 | ||||
| } | ||||
| @@ -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' | ||||
| 		}) | ||||
| 	] | ||||
| }); | ||||
| ` | ||||
| @@ -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) | ||||
| } | ||||
| @@ -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 | ||||
| } | ||||
| @@ -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 | ||||
| } | ||||
| @@ -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' | ||||
| 		}) | ||||
| 	] | ||||
| }); | ||||
| ` | ||||
							
								
								
									
										196
									
								
								rpc/api/shh.go
									
									
									
									
									
								
							
							
						
						
									
										196
									
								
								rpc/api/shh.go
									
									
									
									
									
								
							| @@ -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 | ||||
| } | ||||
| @@ -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 | ||||
| } | ||||
| @@ -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' | ||||
| 		}) | ||||
| 	] | ||||
| }); | ||||
| ` | ||||
| @@ -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 | ||||
| } | ||||
| @@ -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' | ||||
| 		}) | ||||
| 	] | ||||
| }); | ||||
| ` | ||||
							
								
								
									
										226
									
								
								rpc/api/utils.go
									
									
									
									
									
								
							
							
						
						
									
										226
									
								
								rpc/api/utils.go
									
									
									
									
									
								
							| @@ -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 "" | ||||
| } | ||||
| @@ -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 | ||||
| } | ||||
| @@ -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") | ||||
| } | ||||
| @@ -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() | ||||
| } | ||||
| @@ -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") | ||||
| 	} | ||||
| } | ||||
| @@ -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") | ||||
| } | ||||
| @@ -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 | ||||
| } | ||||
| @@ -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") | ||||
| } | ||||
							
								
								
									
										158
									
								
								rpc/comms/ipc.go
									
									
									
									
									
								
							
							
						
						
									
										158
									
								
								rpc/comms/ipc.go
									
									
									
									
									
								
							| @@ -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 | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user