mirror of https://github.com/poanetwork/quorum.git
commit
63e76482ac
|
@ -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"
|
||||
|
|
|
@ -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.
|
|
@ -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.
|
|
@ -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
|
||||
}
|
|
@ -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()))
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
language: go
|
||||
go:
|
||||
- 1.3
|
||||
- 1.4
|
|
@ -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.
|
|
@ -1,84 +0,0 @@
|
|||
# Go CORS handler [![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/rs/cors) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/rs/cors/master/LICENSE) [![build](https://img.shields.io/travis/rs/cors.svg?style=flat)](https://travis-ci.org/rs/cors)
|
||||
|
||||
CORS is a `net/http` handler implementing [Cross Origin Resource Sharing W3 specification](http://www.w3.org/TR/cors/) in Golang.
|
||||
|
||||
## Getting Started
|
||||
|
||||
After installing Go and setting up your [GOPATH](http://golang.org/doc/code.html#GOPATH), create your first `.go` file. We'll call it `server.go`.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/rs/cors"
|
||||
)
|
||||
|
||||
func main() {
|
||||
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte("{\"hello\": \"world\"}"))
|
||||
})
|
||||
|
||||
// cors.Default() setup the middleware with default options being
|
||||
// all origins accepted with simple methods (GET, POST). See
|
||||
// documentation below for more options.
|
||||
handler = cors.Default().Handler(h)
|
||||
http.ListenAndServe(":8080", handler)
|
||||
}
|
||||
```
|
||||
|
||||
Install `cors`:
|
||||
|
||||
go get github.com/rs/cors
|
||||
|
||||
Then run your server:
|
||||
|
||||
go run server.go
|
||||
|
||||
The server now runs on `localhost:8080`:
|
||||
|
||||
$ curl -D - -H 'Origin: http://foo.com' http://localhost:8080/
|
||||
HTTP/1.1 200 OK
|
||||
Access-Control-Allow-Origin: foo.com
|
||||
Content-Type: application/json
|
||||
Date: Sat, 25 Oct 2014 03:43:57 GMT
|
||||
Content-Length: 18
|
||||
|
||||
{"hello": "world"}
|
||||
|
||||
### More Examples
|
||||
|
||||
* `net/http`: [examples/nethttp/server.go](https://github.com/rs/cors/blob/master/examples/nethttp/server.go)
|
||||
* [Goji](https://goji.io): [examples/goji/server.go](https://github.com/rs/cors/blob/master/examples/goji/server.go)
|
||||
* [Martini](http://martini.codegangsta.io): [examples/martini/server.go](https://github.com/rs/cors/blob/master/examples/martini/server.go)
|
||||
* [Negroni](https://github.com/codegangsta/negroni): [examples/negroni/server.go](https://github.com/rs/cors/blob/master/examples/negroni/server.go)
|
||||
* [Alice](https://github.com/justinas/alice): [examples/alice/server.go](https://github.com/rs/cors/blob/master/examples/alice/server.go)
|
||||
|
||||
## Parameters
|
||||
|
||||
Parameters are passed to the middleware thru the `cors.New` method as follow:
|
||||
|
||||
```go
|
||||
c := cors.New(cors.Options{
|
||||
AllowedOrigins: []string{"http://foo.com"},
|
||||
AllowCredentials: true,
|
||||
})
|
||||
|
||||
// Insert the middleware
|
||||
handler = c.Handler(handler)
|
||||
```
|
||||
|
||||
* **AllowedOrigins** `[]string`: A list of origins a cross-domain request can be executed from. If the special `*` value is present in the list, all origins will be allowed. The default value is `*`.
|
||||
* **AllowedMethods** `[]string`: A list of methods the client is allowed to use with cross-domain requests.
|
||||
* **AllowedHeaders** `[]string`: A list of non simple headers the client is allowed to use with cross-domain requests. Default value is simple methods (`GET` and `POST`)
|
||||
* **ExposedHeaders** `[]string`: Indicates which headers are safe to expose to the API of a CORS API specification
|
||||
* **AllowCredentials** `bool`: Indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates. The default is `false`.
|
||||
* **MaxAge** `int`: Indicates how long (in seconds) the results of a preflight request can be cached. The default is `0` which stands for no max age.
|
||||
|
||||
See [API documentation](http://godoc.org/github.com/rs/cors) for more info.
|
||||
|
||||
## Licenses
|
||||
|
||||
All source code is licensed under the [MIT License](https://raw.github.com/rs/cors/master/LICENSE).
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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": "",
|
||||
})
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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))
|
||||
}
|
|
@ -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))
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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())
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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())
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
|
|
130
cmd/geth/js.go
130
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,60 +72,66 @@ 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
|
||||
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 {
|
||||
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)
|
||||
results = append(results, fmt.Sprintf("%s.%s", strings.Join(parts[:len(parts) - 1], "."), k))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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' {
|
||||
if i >= 3 && line[i] == '3' && line[i - 3] == 'w' && line[i - 2] == 'e' && line[i - 1] == 'b' {
|
||||
continue
|
||||
}
|
||||
i += 1
|
||||
|
@ -143,18 +142,20 @@ func apiWordCompleter(line string, pos int) (head string, completions []string,
|
|||
keyword := line[i:pos]
|
||||
end := line[pos:]
|
||||
|
||||
completionWords := keywordCompleter(keyword)
|
||||
completionWords := keywordCompleter(jsre, 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,16 +298,18 @@ 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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, err = js.re.Run(shortcuts)
|
||||
if err != nil {
|
||||
|
@ -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,15 +32,12 @@ 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 (
|
||||
|
@ -50,7 +47,7 @@ const (
|
|||
testKey = "e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674"
|
||||
testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
|
||||
testBalance = "10000000000000000000"
|
||||
// of empty string
|
||||
// of empty string
|
||||
testHash = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
|
||||
)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,7 +476,8 @@ 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:
|
||||
|
|
|
@ -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 {
|
||||
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)
|
||||
}
|
|
@ -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,17 +795,13 @@ 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)
|
||||
endpoint := IPCSocketPath(ctx)
|
||||
listener, err := rpc.CreateIPCListener(endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -804,13 +815,8 @@ func StartIPC(stack *node.Node, ctx *cli.Context) error {
|
|||
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)
|
||||
glog.V(logger.Info).Infof("Start IPC server on %s\n", endpoint)
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
|
@ -823,36 +829,49 @@ func StartIPC(stack *node.Node, ctx *cli.Context) error {
|
|||
}()
|
||||
|
||||
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
|
||||
}
|
||||
return xeth, api.Merge(apis...), nil
|
||||
}
|
||||
return comms.StartIpc(config, codec.JSON, initializer)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
xeth := xeth.New(stack, nil)
|
||||
codec := codec.JSON
|
||||
|
||||
apis, err := api.ParseApiString(ctx.GlobalString(RpcApiFlag.Name), codec, xeth, stack)
|
||||
if err != nil {
|
||||
_, err := adminApi.StartRPC(address, port, cors, apiStr)
|
||||
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")
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
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) };
|
||||
`)
|
||||
|
@ -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
|
||||
|
||||
|
|
|
@ -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 (
|
||||
|
@ -129,6 +130,7 @@ type Ethereum struct {
|
|||
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('.');
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
// Configure and start the HTTP RPC server
|
||||
config := comms.HttpConfig{
|
||||
ListenAddress: address,
|
||||
ListenPort: port,
|
||||
CorsDomain: cors,
|
||||
for _, api := range api.node.APIs() {
|
||||
if namespaces.Has(api.Namespace) {
|
||||
offeredAPIs = append(offeredAPIs, api)
|
||||
}
|
||||
if err := comms.StartHttp(config, self.codec, api.Merge(apis...)); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil*/
|
||||
return false, fmt.Errorf("needs new RPC implementation to resolve circular dependency")
|
||||
} 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 = 8545
|
||||
}
|
||||
|
||||
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
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
|
||||
|
||||
package comms
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
"github.com/ethereum/go-ethereum/rpc/useragent"
|
||||
)
|
||||
|
||||
func newIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) {
|
||||
c, err := net.DialUnix("unix", nil, &net.UnixAddr{cfg.Endpoint, "unix"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
coder := codec.New(c)
|
||||
msg := shared.Request{
|
||||
Id: 0,
|
||||
Method: useragent.EnableUserAgentMethod,
|
||||
Jsonrpc: shared.JsonRpcVersion,
|
||||
Params: []byte("[]"),
|
||||
}
|
||||
|
||||
coder.WriteResponse(msg)
|
||||
coder.Recv()
|
||||
|
||||
return &ipcClient{cfg.Endpoint, c, codec, coder}, nil
|
||||
}
|
||||
|
||||
func (self *ipcClient) reconnect() error {
|
||||
self.coder.Close()
|
||||
c, err := net.DialUnix("unix", nil, &net.UnixAddr{self.endpoint, "unix"})
|
||||
if err == nil {
|
||||
self.coder = self.codec.New(c)
|
||||
|
||||
msg := shared.Request{
|
||||
Id: 0,
|
||||
Method: useragent.EnableUserAgentMethod,
|
||||
Jsonrpc: shared.JsonRpcVersion,
|
||||
Params: []byte("[]"),
|
||||
}
|
||||
self.coder.WriteResponse(msg)
|
||||
self.coder.Recv()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func ipcListen(cfg IpcConfig) (net.Listener, error) {
|
||||
// Ensure the IPC path exists and remove any previous leftover
|
||||
if err := os.MkdirAll(filepath.Dir(cfg.Endpoint), 0751); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
os.Remove(cfg.Endpoint)
|
||||
l, err := net.Listen("unix", cfg.Endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
os.Chmod(cfg.Endpoint, 0600)
|
||||
return l, nil
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue