rpc: migrated the RPC insterface to a new reflection based RPC layer

This commit is contained in:
Bas van Kervel 2015-12-16 10:58:01 +01:00 committed by Jeffrey Wilcke
parent f2ab351e8d
commit 19b2640e89
132 changed files with 4711 additions and 14320 deletions

14
Godeps/Godeps.json generated
View File

@ -1,6 +1,6 @@
{ {
"ImportPath": "github.com/ethereum/go-ethereum", "ImportPath": "github.com/ethereum/go-ethereum",
"GoVersion": "go1.4", "GoVersion": "go1.5.2",
"Packages": [ "Packages": [
"./..." "./..."
], ],
@ -40,10 +40,6 @@
"ImportPath": "github.com/jackpal/go-nat-pmp", "ImportPath": "github.com/jackpal/go-nat-pmp",
"Rev": "a45aa3d54aef73b504e15eb71bea0e5565b5e6e1" "Rev": "a45aa3d54aef73b504e15eb71bea0e5565b5e6e1"
}, },
{
"ImportPath": "github.com/kardianos/osext",
"Rev": "ccfcd0245381f0c94c68f50626665eed3c6b726a"
},
{ {
"ImportPath": "github.com/mattn/go-isatty", "ImportPath": "github.com/mattn/go-isatty",
"Rev": "7fcbc72f853b92b5720db4a6b8482be612daef24" "Rev": "7fcbc72f853b92b5720db4a6b8482be612daef24"
@ -73,10 +69,6 @@
"ImportPath": "github.com/robertkrimen/otto", "ImportPath": "github.com/robertkrimen/otto",
"Rev": "dea31a3d392779af358ec41f77a07fcc7e9d04ba" "Rev": "dea31a3d392779af358ec41f77a07fcc7e9d04ba"
}, },
{
"ImportPath": "github.com/rs/cors",
"Rev": "6e0c3cb65fc0fdb064c743d176a620e3ca446dfb"
},
{ {
"ImportPath": "github.com/shiena/ansicolor", "ImportPath": "github.com/shiena/ansicolor",
"Rev": "a5e2b567a4dd6cc74545b8a4f27c9d63b9e7735b" "Rev": "a5e2b567a4dd6cc74545b8a4f27c9d63b9e7735b"
@ -109,6 +101,10 @@
"ImportPath": "golang.org/x/net/html", "ImportPath": "golang.org/x/net/html",
"Rev": "e0403b4e005737430c05a57aac078479844f919c" "Rev": "e0403b4e005737430c05a57aac078479844f919c"
}, },
{
"ImportPath": "golang.org/x/net/websocket",
"Rev": "e0403b4e005737430c05a57aac078479844f919c"
},
{ {
"ImportPath": "golang.org/x/text/encoding", "ImportPath": "golang.org/x/text/encoding",
"Rev": "c93e7c9fff19fb9139b5ab04ce041833add0134e" "Rev": "c93e7c9fff19fb9139b5ab04ce041833add0134e"

View File

@ -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.

View File

@ -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.

View File

@ -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
}

View File

@ -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()))
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -1,4 +0,0 @@
language: go
go:
- 1.3
- 1.4

View File

@ -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.

View File

@ -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).

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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": "",
})
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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()
}

View File

@ -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()
}

View File

@ -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")
}

View File

@ -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))
}

View File

@ -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))
}

View File

@ -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
}

View File

@ -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")
}
}

View File

@ -0,0 +1,113 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
import (
"bufio"
"crypto/tls"
"io"
"net"
"net/http"
"net/url"
)
// DialError is an error that occurs while dialling a websocket server.
type DialError struct {
*Config
Err error
}
func (e *DialError) Error() string {
return "websocket.Dial " + e.Config.Location.String() + ": " + e.Err.Error()
}
// NewConfig creates a new WebSocket config for client connection.
func NewConfig(server, origin string) (config *Config, err error) {
config = new(Config)
config.Version = ProtocolVersionHybi13
config.Location, err = url.ParseRequestURI(server)
if err != nil {
return
}
config.Origin, err = url.ParseRequestURI(origin)
if err != nil {
return
}
config.Header = http.Header(make(map[string][]string))
return
}
// NewClient creates a new WebSocket client connection over rwc.
func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err error) {
br := bufio.NewReader(rwc)
bw := bufio.NewWriter(rwc)
err = hybiClientHandshake(config, br, bw)
if err != nil {
return
}
buf := bufio.NewReadWriter(br, bw)
ws = newHybiClientConn(config, buf, rwc)
return
}
// Dial opens a new client connection to a WebSocket.
func Dial(url_, protocol, origin string) (ws *Conn, err error) {
config, err := NewConfig(url_, origin)
if err != nil {
return nil, err
}
if protocol != "" {
config.Protocol = []string{protocol}
}
return DialConfig(config)
}
var portMap = map[string]string{
"ws": "80",
"wss": "443",
}
func parseAuthority(location *url.URL) string {
if _, ok := portMap[location.Scheme]; ok {
if _, _, err := net.SplitHostPort(location.Host); err != nil {
return net.JoinHostPort(location.Host, portMap[location.Scheme])
}
}
return location.Host
}
// DialConfig opens a new client connection to a WebSocket with a config.
func DialConfig(config *Config) (ws *Conn, err error) {
var client net.Conn
if config.Location == nil {
return nil, &DialError{config, ErrBadWebSocketLocation}
}
if config.Origin == nil {
return nil, &DialError{config, ErrBadWebSocketOrigin}
}
switch config.Location.Scheme {
case "ws":
client, err = net.Dial("tcp", parseAuthority(config.Location))
case "wss":
client, err = tls.Dial("tcp", parseAuthority(config.Location), config.TlsConfig)
default:
err = ErrBadScheme
}
if err != nil {
goto Error
}
ws, err = NewClient(config, client)
if err != nil {
client.Close()
goto Error
}
return
Error:
return nil, &DialError{config, err}
}

View 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])
}

View 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())
}
}

View File

@ -0,0 +1,564 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
// This file implements a protocol of hybi draft.
// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17
import (
"bufio"
"bytes"
"crypto/rand"
"crypto/sha1"
"encoding/base64"
"encoding/binary"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
)
const (
websocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
closeStatusNormal = 1000
closeStatusGoingAway = 1001
closeStatusProtocolError = 1002
closeStatusUnsupportedData = 1003
closeStatusFrameTooLarge = 1004
closeStatusNoStatusRcvd = 1005
closeStatusAbnormalClosure = 1006
closeStatusBadMessageData = 1007
closeStatusPolicyViolation = 1008
closeStatusTooBigData = 1009
closeStatusExtensionMismatch = 1010
maxControlFramePayloadLength = 125
)
var (
ErrBadMaskingKey = &ProtocolError{"bad masking key"}
ErrBadPongMessage = &ProtocolError{"bad pong message"}
ErrBadClosingStatus = &ProtocolError{"bad closing status"}
ErrUnsupportedExtensions = &ProtocolError{"unsupported extensions"}
ErrNotImplemented = &ProtocolError{"not implemented"}
handshakeHeader = map[string]bool{
"Host": true,
"Upgrade": true,
"Connection": true,
"Sec-Websocket-Key": true,
"Sec-Websocket-Origin": true,
"Sec-Websocket-Version": true,
"Sec-Websocket-Protocol": true,
"Sec-Websocket-Accept": true,
}
)
// A hybiFrameHeader is a frame header as defined in hybi draft.
type hybiFrameHeader struct {
Fin bool
Rsv [3]bool
OpCode byte
Length int64
MaskingKey []byte
data *bytes.Buffer
}
// A hybiFrameReader is a reader for hybi frame.
type hybiFrameReader struct {
reader io.Reader
header hybiFrameHeader
pos int64
length int
}
func (frame *hybiFrameReader) Read(msg []byte) (n int, err error) {
n, err = frame.reader.Read(msg)
if err != nil {
return 0, err
}
if frame.header.MaskingKey != nil {
for i := 0; i < n; i++ {
msg[i] = msg[i] ^ frame.header.MaskingKey[frame.pos%4]
frame.pos++
}
}
return n, err
}
func (frame *hybiFrameReader) PayloadType() byte { return frame.header.OpCode }
func (frame *hybiFrameReader) HeaderReader() io.Reader {
if frame.header.data == nil {
return nil
}
if frame.header.data.Len() == 0 {
return nil
}
return frame.header.data
}
func (frame *hybiFrameReader) TrailerReader() io.Reader { return nil }
func (frame *hybiFrameReader) Len() (n int) { return frame.length }
// A hybiFrameReaderFactory creates new frame reader based on its frame type.
type hybiFrameReaderFactory struct {
*bufio.Reader
}
// NewFrameReader reads a frame header from the connection, and creates new reader for the frame.
// See Section 5.2 Base Framing protocol for detail.
// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5.2
func (buf hybiFrameReaderFactory) NewFrameReader() (frame frameReader, err error) {
hybiFrame := new(hybiFrameReader)
frame = hybiFrame
var header []byte
var b byte
// First byte. FIN/RSV1/RSV2/RSV3/OpCode(4bits)
b, err = buf.ReadByte()
if err != nil {
return
}
header = append(header, b)
hybiFrame.header.Fin = ((header[0] >> 7) & 1) != 0
for i := 0; i < 3; i++ {
j := uint(6 - i)
hybiFrame.header.Rsv[i] = ((header[0] >> j) & 1) != 0
}
hybiFrame.header.OpCode = header[0] & 0x0f
// Second byte. Mask/Payload len(7bits)
b, err = buf.ReadByte()
if err != nil {
return
}
header = append(header, b)
mask := (b & 0x80) != 0
b &= 0x7f
lengthFields := 0
switch {
case b <= 125: // Payload length 7bits.
hybiFrame.header.Length = int64(b)
case b == 126: // Payload length 7+16bits
lengthFields = 2
case b == 127: // Payload length 7+64bits
lengthFields = 8
}
for i := 0; i < lengthFields; i++ {
b, err = buf.ReadByte()
if err != nil {
return
}
header = append(header, b)
hybiFrame.header.Length = hybiFrame.header.Length*256 + int64(b)
}
if mask {
// Masking key. 4 bytes.
for i := 0; i < 4; i++ {
b, err = buf.ReadByte()
if err != nil {
return
}
header = append(header, b)
hybiFrame.header.MaskingKey = append(hybiFrame.header.MaskingKey, b)
}
}
hybiFrame.reader = io.LimitReader(buf.Reader, hybiFrame.header.Length)
hybiFrame.header.data = bytes.NewBuffer(header)
hybiFrame.length = len(header) + int(hybiFrame.header.Length)
return
}
// A HybiFrameWriter is a writer for hybi frame.
type hybiFrameWriter struct {
writer *bufio.Writer
header *hybiFrameHeader
}
func (frame *hybiFrameWriter) Write(msg []byte) (n int, err error) {
var header []byte
var b byte
if frame.header.Fin {
b |= 0x80
}
for i := 0; i < 3; i++ {
if frame.header.Rsv[i] {
j := uint(6 - i)
b |= 1 << j
}
}
b |= frame.header.OpCode
header = append(header, b)
if frame.header.MaskingKey != nil {
b = 0x80
} else {
b = 0
}
lengthFields := 0
length := len(msg)
switch {
case length <= 125:
b |= byte(length)
case length < 65536:
b |= 126
lengthFields = 2
default:
b |= 127
lengthFields = 8
}
header = append(header, b)
for i := 0; i < lengthFields; i++ {
j := uint((lengthFields - i - 1) * 8)
b = byte((length >> j) & 0xff)
header = append(header, b)
}
if frame.header.MaskingKey != nil {
if len(frame.header.MaskingKey) != 4 {
return 0, ErrBadMaskingKey
}
header = append(header, frame.header.MaskingKey...)
frame.writer.Write(header)
data := make([]byte, length)
for i := range data {
data[i] = msg[i] ^ frame.header.MaskingKey[i%4]
}
frame.writer.Write(data)
err = frame.writer.Flush()
return length, err
}
frame.writer.Write(header)
frame.writer.Write(msg)
err = frame.writer.Flush()
return length, err
}
func (frame *hybiFrameWriter) Close() error { return nil }
type hybiFrameWriterFactory struct {
*bufio.Writer
needMaskingKey bool
}
func (buf hybiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) {
frameHeader := &hybiFrameHeader{Fin: true, OpCode: payloadType}
if buf.needMaskingKey {
frameHeader.MaskingKey, err = generateMaskingKey()
if err != nil {
return nil, err
}
}
return &hybiFrameWriter{writer: buf.Writer, header: frameHeader}, nil
}
type hybiFrameHandler struct {
conn *Conn
payloadType byte
}
func (handler *hybiFrameHandler) HandleFrame(frame frameReader) (r frameReader, err error) {
if handler.conn.IsServerConn() {
// The client MUST mask all frames sent to the server.
if frame.(*hybiFrameReader).header.MaskingKey == nil {
handler.WriteClose(closeStatusProtocolError)
return nil, io.EOF
}
} else {
// The server MUST NOT mask all frames.
if frame.(*hybiFrameReader).header.MaskingKey != nil {
handler.WriteClose(closeStatusProtocolError)
return nil, io.EOF
}
}
if header := frame.HeaderReader(); header != nil {
io.Copy(ioutil.Discard, header)
}
switch frame.PayloadType() {
case ContinuationFrame:
frame.(*hybiFrameReader).header.OpCode = handler.payloadType
case TextFrame, BinaryFrame:
handler.payloadType = frame.PayloadType()
case CloseFrame:
return nil, io.EOF
case PingFrame:
pingMsg := make([]byte, maxControlFramePayloadLength)
n, err := io.ReadFull(frame, pingMsg)
if err != nil && err != io.ErrUnexpectedEOF {
return nil, err
}
io.Copy(ioutil.Discard, frame)
n, err = handler.WritePong(pingMsg[:n])
if err != nil {
return nil, err
}
return nil, nil
case PongFrame:
return nil, ErrNotImplemented
}
return frame, nil
}
func (handler *hybiFrameHandler) WriteClose(status int) (err error) {
handler.conn.wio.Lock()
defer handler.conn.wio.Unlock()
w, err := handler.conn.frameWriterFactory.NewFrameWriter(CloseFrame)
if err != nil {
return err
}
msg := make([]byte, 2)
binary.BigEndian.PutUint16(msg, uint16(status))
_, err = w.Write(msg)
w.Close()
return err
}
func (handler *hybiFrameHandler) WritePong(msg []byte) (n int, err error) {
handler.conn.wio.Lock()
defer handler.conn.wio.Unlock()
w, err := handler.conn.frameWriterFactory.NewFrameWriter(PongFrame)
if err != nil {
return 0, err
}
n, err = w.Write(msg)
w.Close()
return n, err
}
// newHybiConn creates a new WebSocket connection speaking hybi draft protocol.
func newHybiConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
if buf == nil {
br := bufio.NewReader(rwc)
bw := bufio.NewWriter(rwc)
buf = bufio.NewReadWriter(br, bw)
}
ws := &Conn{config: config, request: request, buf: buf, rwc: rwc,
frameReaderFactory: hybiFrameReaderFactory{buf.Reader},
frameWriterFactory: hybiFrameWriterFactory{
buf.Writer, request == nil},
PayloadType: TextFrame,
defaultCloseStatus: closeStatusNormal}
ws.frameHandler = &hybiFrameHandler{conn: ws}
return ws
}
// generateMaskingKey generates a masking key for a frame.
func generateMaskingKey() (maskingKey []byte, err error) {
maskingKey = make([]byte, 4)
if _, err = io.ReadFull(rand.Reader, maskingKey); err != nil {
return
}
return
}
// generateNonce generates a nonce consisting of a randomly selected 16-byte
// value that has been base64-encoded.
func generateNonce() (nonce []byte) {
key := make([]byte, 16)
if _, err := io.ReadFull(rand.Reader, key); err != nil {
panic(err)
}
nonce = make([]byte, 24)
base64.StdEncoding.Encode(nonce, key)
return
}
// getNonceAccept computes the base64-encoded SHA-1 of the concatenation of
// the nonce ("Sec-WebSocket-Key" value) with the websocket GUID string.
func getNonceAccept(nonce []byte) (expected []byte, err error) {
h := sha1.New()
if _, err = h.Write(nonce); err != nil {
return
}
if _, err = h.Write([]byte(websocketGUID)); err != nil {
return
}
expected = make([]byte, 28)
base64.StdEncoding.Encode(expected, h.Sum(nil))
return
}
// Client handshake described in draft-ietf-hybi-thewebsocket-protocol-17
func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) {
bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n")
bw.WriteString("Host: " + config.Location.Host + "\r\n")
bw.WriteString("Upgrade: websocket\r\n")
bw.WriteString("Connection: Upgrade\r\n")
nonce := generateNonce()
if config.handshakeData != nil {
nonce = []byte(config.handshakeData["key"])
}
bw.WriteString("Sec-WebSocket-Key: " + string(nonce) + "\r\n")
bw.WriteString("Origin: " + strings.ToLower(config.Origin.String()) + "\r\n")
if config.Version != ProtocolVersionHybi13 {
return ErrBadProtocolVersion
}
bw.WriteString("Sec-WebSocket-Version: " + fmt.Sprintf("%d", config.Version) + "\r\n")
if len(config.Protocol) > 0 {
bw.WriteString("Sec-WebSocket-Protocol: " + strings.Join(config.Protocol, ", ") + "\r\n")
}
// TODO(ukai): send Sec-WebSocket-Extensions.
err = config.Header.WriteSubset(bw, handshakeHeader)
if err != nil {
return err
}
bw.WriteString("\r\n")
if err = bw.Flush(); err != nil {
return err
}
resp, err := http.ReadResponse(br, &http.Request{Method: "GET"})
if err != nil {
return err
}
if resp.StatusCode != 101 {
return ErrBadStatus
}
if strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" ||
strings.ToLower(resp.Header.Get("Connection")) != "upgrade" {
return ErrBadUpgrade
}
expectedAccept, err := getNonceAccept(nonce)
if err != nil {
return err
}
if resp.Header.Get("Sec-WebSocket-Accept") != string(expectedAccept) {
return ErrChallengeResponse
}
if resp.Header.Get("Sec-WebSocket-Extensions") != "" {
return ErrUnsupportedExtensions
}
offeredProtocol := resp.Header.Get("Sec-WebSocket-Protocol")
if offeredProtocol != "" {
protocolMatched := false
for i := 0; i < len(config.Protocol); i++ {
if config.Protocol[i] == offeredProtocol {
protocolMatched = true
break
}
}
if !protocolMatched {
return ErrBadWebSocketProtocol
}
config.Protocol = []string{offeredProtocol}
}
return nil
}
// newHybiClientConn creates a client WebSocket connection after handshake.
func newHybiClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn {
return newHybiConn(config, buf, rwc, nil)
}
// A HybiServerHandshaker performs a server handshake using hybi draft protocol.
type hybiServerHandshaker struct {
*Config
accept []byte
}
func (c *hybiServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) {
c.Version = ProtocolVersionHybi13
if req.Method != "GET" {
return http.StatusMethodNotAllowed, ErrBadRequestMethod
}
// HTTP version can be safely ignored.
if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
!strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") {
return http.StatusBadRequest, ErrNotWebSocket
}
key := req.Header.Get("Sec-Websocket-Key")
if key == "" {
return http.StatusBadRequest, ErrChallengeResponse
}
version := req.Header.Get("Sec-Websocket-Version")
switch version {
case "13":
c.Version = ProtocolVersionHybi13
default:
return http.StatusBadRequest, ErrBadWebSocketVersion
}
var scheme string
if req.TLS != nil {
scheme = "wss"
} else {
scheme = "ws"
}
c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI())
if err != nil {
return http.StatusBadRequest, err
}
protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol"))
if protocol != "" {
protocols := strings.Split(protocol, ",")
for i := 0; i < len(protocols); i++ {
c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i]))
}
}
c.accept, err = getNonceAccept([]byte(key))
if err != nil {
return http.StatusInternalServerError, err
}
return http.StatusSwitchingProtocols, nil
}
// Origin parses Origin header in "req".
// If origin is "null", returns (nil, nil).
func Origin(config *Config, req *http.Request) (*url.URL, error) {
var origin string
switch config.Version {
case ProtocolVersionHybi13:
origin = req.Header.Get("Origin")
}
if origin == "null" {
return nil, nil
}
return url.ParseRequestURI(origin)
}
func (c *hybiServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) {
if len(c.Protocol) > 0 {
if len(c.Protocol) != 1 {
// You need choose a Protocol in Handshake func in Server.
return ErrBadWebSocketProtocol
}
}
buf.WriteString("HTTP/1.1 101 Switching Protocols\r\n")
buf.WriteString("Upgrade: websocket\r\n")
buf.WriteString("Connection: Upgrade\r\n")
buf.WriteString("Sec-WebSocket-Accept: " + string(c.accept) + "\r\n")
if len(c.Protocol) > 0 {
buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n")
}
// TODO(ukai): send Sec-WebSocket-Extensions.
if c.Header != nil {
err := c.Header.WriteSubset(buf, handshakeHeader)
if err != nil {
return err
}
}
buf.WriteString("\r\n")
return buf.Flush()
}
func (c *hybiServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
return newHybiServerConn(c.Config, buf, rwc, request)
}
// newHybiServerConn returns a new WebSocket connection speaking hybi draft protocol.
func newHybiServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
return newHybiConn(config, buf, rwc, request)
}

View File

@ -0,0 +1,590 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
import (
"bufio"
"bytes"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"testing"
)
// Test the getNonceAccept function with values in
// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17
func TestSecWebSocketAccept(t *testing.T) {
nonce := []byte("dGhlIHNhbXBsZSBub25jZQ==")
expected := []byte("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=")
accept, err := getNonceAccept(nonce)
if err != nil {
t.Errorf("getNonceAccept: returned error %v", err)
return
}
if !bytes.Equal(expected, accept) {
t.Errorf("getNonceAccept: expected %q got %q", expected, accept)
}
}
func TestHybiClientHandshake(t *testing.T) {
b := bytes.NewBuffer([]byte{})
bw := bufio.NewWriter(b)
br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
`))
var err error
config := new(Config)
config.Location, err = url.ParseRequestURI("ws://server.example.com/chat")
if err != nil {
t.Fatal("location url", err)
}
config.Origin, err = url.ParseRequestURI("http://example.com")
if err != nil {
t.Fatal("origin url", err)
}
config.Protocol = append(config.Protocol, "chat")
config.Protocol = append(config.Protocol, "superchat")
config.Version = ProtocolVersionHybi13
config.handshakeData = map[string]string{
"key": "dGhlIHNhbXBsZSBub25jZQ==",
}
err = hybiClientHandshake(config, br, bw)
if err != nil {
t.Errorf("handshake failed: %v", err)
}
req, err := http.ReadRequest(bufio.NewReader(b))
if err != nil {
t.Fatalf("read request: %v", err)
}
if req.Method != "GET" {
t.Errorf("request method expected GET, but got %q", req.Method)
}
if req.URL.Path != "/chat" {
t.Errorf("request path expected /chat, but got %q", req.URL.Path)
}
if req.Proto != "HTTP/1.1" {
t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto)
}
if req.Host != "server.example.com" {
t.Errorf("request Host expected server.example.com, but got %v", req.Host)
}
var expectedHeader = map[string]string{
"Connection": "Upgrade",
"Upgrade": "websocket",
"Sec-Websocket-Key": config.handshakeData["key"],
"Origin": config.Origin.String(),
"Sec-Websocket-Protocol": "chat, superchat",
"Sec-Websocket-Version": fmt.Sprintf("%d", ProtocolVersionHybi13),
}
for k, v := range expectedHeader {
if req.Header.Get(k) != v {
t.Errorf(fmt.Sprintf("%s expected %q but got %q", k, v, req.Header.Get(k)))
}
}
}
func TestHybiClientHandshakeWithHeader(t *testing.T) {
b := bytes.NewBuffer([]byte{})
bw := bufio.NewWriter(b)
br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
`))
var err error
config := new(Config)
config.Location, err = url.ParseRequestURI("ws://server.example.com/chat")
if err != nil {
t.Fatal("location url", err)
}
config.Origin, err = url.ParseRequestURI("http://example.com")
if err != nil {
t.Fatal("origin url", err)
}
config.Protocol = append(config.Protocol, "chat")
config.Protocol = append(config.Protocol, "superchat")
config.Version = ProtocolVersionHybi13
config.Header = http.Header(make(map[string][]string))
config.Header.Add("User-Agent", "test")
config.handshakeData = map[string]string{
"key": "dGhlIHNhbXBsZSBub25jZQ==",
}
err = hybiClientHandshake(config, br, bw)
if err != nil {
t.Errorf("handshake failed: %v", err)
}
req, err := http.ReadRequest(bufio.NewReader(b))
if err != nil {
t.Fatalf("read request: %v", err)
}
if req.Method != "GET" {
t.Errorf("request method expected GET, but got %q", req.Method)
}
if req.URL.Path != "/chat" {
t.Errorf("request path expected /chat, but got %q", req.URL.Path)
}
if req.Proto != "HTTP/1.1" {
t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto)
}
if req.Host != "server.example.com" {
t.Errorf("request Host expected server.example.com, but got %v", req.Host)
}
var expectedHeader = map[string]string{
"Connection": "Upgrade",
"Upgrade": "websocket",
"Sec-Websocket-Key": config.handshakeData["key"],
"Origin": config.Origin.String(),
"Sec-Websocket-Protocol": "chat, superchat",
"Sec-Websocket-Version": fmt.Sprintf("%d", ProtocolVersionHybi13),
"User-Agent": "test",
}
for k, v := range expectedHeader {
if req.Header.Get(k) != v {
t.Errorf(fmt.Sprintf("%s expected %q but got %q", k, v, req.Header.Get(k)))
}
}
}
func TestHybiServerHandshake(t *testing.T) {
config := new(Config)
handshaker := &hybiServerHandshaker{Config: config}
br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
`))
req, err := http.ReadRequest(br)
if err != nil {
t.Fatal("request", err)
}
code, err := handshaker.ReadHandshake(br, req)
if err != nil {
t.Errorf("handshake failed: %v", err)
}
if code != http.StatusSwitchingProtocols {
t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code)
}
expectedProtocols := []string{"chat", "superchat"}
if fmt.Sprintf("%v", config.Protocol) != fmt.Sprintf("%v", expectedProtocols) {
t.Errorf("protocol expected %q but got %q", expectedProtocols, config.Protocol)
}
b := bytes.NewBuffer([]byte{})
bw := bufio.NewWriter(b)
config.Protocol = config.Protocol[:1]
err = handshaker.AcceptHandshake(bw)
if err != nil {
t.Errorf("handshake response failed: %v", err)
}
expectedResponse := strings.Join([]string{
"HTTP/1.1 101 Switching Protocols",
"Upgrade: websocket",
"Connection: Upgrade",
"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",
"Sec-WebSocket-Protocol: chat",
"", ""}, "\r\n")
if b.String() != expectedResponse {
t.Errorf("handshake expected %q but got %q", expectedResponse, b.String())
}
}
func TestHybiServerHandshakeNoSubProtocol(t *testing.T) {
config := new(Config)
handshaker := &hybiServerHandshaker{Config: config}
br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Version: 13
`))
req, err := http.ReadRequest(br)
if err != nil {
t.Fatal("request", err)
}
code, err := handshaker.ReadHandshake(br, req)
if err != nil {
t.Errorf("handshake failed: %v", err)
}
if code != http.StatusSwitchingProtocols {
t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code)
}
if len(config.Protocol) != 0 {
t.Errorf("len(config.Protocol) expected 0, but got %q", len(config.Protocol))
}
b := bytes.NewBuffer([]byte{})
bw := bufio.NewWriter(b)
err = handshaker.AcceptHandshake(bw)
if err != nil {
t.Errorf("handshake response failed: %v", err)
}
expectedResponse := strings.Join([]string{
"HTTP/1.1 101 Switching Protocols",
"Upgrade: websocket",
"Connection: Upgrade",
"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",
"", ""}, "\r\n")
if b.String() != expectedResponse {
t.Errorf("handshake expected %q but got %q", expectedResponse, b.String())
}
}
func TestHybiServerHandshakeHybiBadVersion(t *testing.T) {
config := new(Config)
handshaker := &hybiServerHandshaker{Config: config}
br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 9
`))
req, err := http.ReadRequest(br)
if err != nil {
t.Fatal("request", err)
}
code, err := handshaker.ReadHandshake(br, req)
if err != ErrBadWebSocketVersion {
t.Errorf("handshake expected err %q but got %q", ErrBadWebSocketVersion, err)
}
if code != http.StatusBadRequest {
t.Errorf("status expected %q but got %q", http.StatusBadRequest, code)
}
}
func testHybiFrame(t *testing.T, testHeader, testPayload, testMaskedPayload []byte, frameHeader *hybiFrameHeader) {
b := bytes.NewBuffer([]byte{})
frameWriterFactory := &hybiFrameWriterFactory{bufio.NewWriter(b), false}
w, _ := frameWriterFactory.NewFrameWriter(TextFrame)
w.(*hybiFrameWriter).header = frameHeader
_, err := w.Write(testPayload)
w.Close()
if err != nil {
t.Errorf("Write error %q", err)
}
var expectedFrame []byte
expectedFrame = append(expectedFrame, testHeader...)
expectedFrame = append(expectedFrame, testMaskedPayload...)
if !bytes.Equal(expectedFrame, b.Bytes()) {
t.Errorf("frame expected %q got %q", expectedFrame, b.Bytes())
}
frameReaderFactory := &hybiFrameReaderFactory{bufio.NewReader(b)}
r, err := frameReaderFactory.NewFrameReader()
if err != nil {
t.Errorf("Read error %q", err)
}
if header := r.HeaderReader(); header == nil {
t.Errorf("no header")
} else {
actualHeader := make([]byte, r.Len())
n, err := header.Read(actualHeader)
if err != nil {
t.Errorf("Read header error %q", err)
} else {
if n < len(testHeader) {
t.Errorf("header too short %q got %q", testHeader, actualHeader[:n])
}
if !bytes.Equal(testHeader, actualHeader[:n]) {
t.Errorf("header expected %q got %q", testHeader, actualHeader[:n])
}
}
}
if trailer := r.TrailerReader(); trailer != nil {
t.Errorf("unexpected trailer %q", trailer)
}
frame := r.(*hybiFrameReader)
if frameHeader.Fin != frame.header.Fin ||
frameHeader.OpCode != frame.header.OpCode ||
len(testPayload) != int(frame.header.Length) {
t.Errorf("mismatch %v (%d) vs %v", frameHeader, len(testPayload), frame)
}
payload := make([]byte, len(testPayload))
_, err = r.Read(payload)
if err != nil {
t.Errorf("read %v", err)
}
if !bytes.Equal(testPayload, payload) {
t.Errorf("payload %q vs %q", testPayload, payload)
}
}
func TestHybiShortTextFrame(t *testing.T) {
frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame}
payload := []byte("hello")
testHybiFrame(t, []byte{0x81, 0x05}, payload, payload, frameHeader)
payload = make([]byte, 125)
testHybiFrame(t, []byte{0x81, 125}, payload, payload, frameHeader)
}
func TestHybiShortMaskedTextFrame(t *testing.T) {
frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame,
MaskingKey: []byte{0xcc, 0x55, 0x80, 0x20}}
payload := []byte("hello")
maskedPayload := []byte{0xa4, 0x30, 0xec, 0x4c, 0xa3}
header := []byte{0x81, 0x85}
header = append(header, frameHeader.MaskingKey...)
testHybiFrame(t, header, payload, maskedPayload, frameHeader)
}
func TestHybiShortBinaryFrame(t *testing.T) {
frameHeader := &hybiFrameHeader{Fin: true, OpCode: BinaryFrame}
payload := []byte("hello")
testHybiFrame(t, []byte{0x82, 0x05}, payload, payload, frameHeader)
payload = make([]byte, 125)
testHybiFrame(t, []byte{0x82, 125}, payload, payload, frameHeader)
}
func TestHybiControlFrame(t *testing.T) {
frameHeader := &hybiFrameHeader{Fin: true, OpCode: PingFrame}
payload := []byte("hello")
testHybiFrame(t, []byte{0x89, 0x05}, payload, payload, frameHeader)
frameHeader = &hybiFrameHeader{Fin: true, OpCode: PongFrame}
testHybiFrame(t, []byte{0x8A, 0x05}, payload, payload, frameHeader)
frameHeader = &hybiFrameHeader{Fin: true, OpCode: CloseFrame}
payload = []byte{0x03, 0xe8} // 1000
testHybiFrame(t, []byte{0x88, 0x02}, payload, payload, frameHeader)
}
func TestHybiLongFrame(t *testing.T) {
frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame}
payload := make([]byte, 126)
testHybiFrame(t, []byte{0x81, 126, 0x00, 126}, payload, payload, frameHeader)
payload = make([]byte, 65535)
testHybiFrame(t, []byte{0x81, 126, 0xff, 0xff}, payload, payload, frameHeader)
payload = make([]byte, 65536)
testHybiFrame(t, []byte{0x81, 127, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00}, payload, payload, frameHeader)
}
func TestHybiClientRead(t *testing.T) {
wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o',
0x89, 0x05, 'h', 'e', 'l', 'l', 'o', // ping
0x81, 0x05, 'w', 'o', 'r', 'l', 'd'}
br := bufio.NewReader(bytes.NewBuffer(wireData))
bw := bufio.NewWriter(bytes.NewBuffer([]byte{}))
conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil)
msg := make([]byte, 512)
n, err := conn.Read(msg)
if err != nil {
t.Errorf("read 1st frame, error %q", err)
}
if n != 5 {
t.Errorf("read 1st frame, expect 5, got %d", n)
}
if !bytes.Equal(wireData[2:7], msg[:n]) {
t.Errorf("read 1st frame %v, got %v", wireData[2:7], msg[:n])
}
n, err = conn.Read(msg)
if err != nil {
t.Errorf("read 2nd frame, error %q", err)
}
if n != 5 {
t.Errorf("read 2nd frame, expect 5, got %d", n)
}
if !bytes.Equal(wireData[16:21], msg[:n]) {
t.Errorf("read 2nd frame %v, got %v", wireData[16:21], msg[:n])
}
n, err = conn.Read(msg)
if err == nil {
t.Errorf("read not EOF")
}
if n != 0 {
t.Errorf("expect read 0, got %d", n)
}
}
func TestHybiShortRead(t *testing.T) {
wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o',
0x89, 0x05, 'h', 'e', 'l', 'l', 'o', // ping
0x81, 0x05, 'w', 'o', 'r', 'l', 'd'}
br := bufio.NewReader(bytes.NewBuffer(wireData))
bw := bufio.NewWriter(bytes.NewBuffer([]byte{}))
conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil)
step := 0
pos := 0
expectedPos := []int{2, 5, 16, 19}
expectedLen := []int{3, 2, 3, 2}
for {
msg := make([]byte, 3)
n, err := conn.Read(msg)
if step >= len(expectedPos) {
if err == nil {
t.Errorf("read not EOF")
}
if n != 0 {
t.Errorf("expect read 0, got %d", n)
}
return
}
pos = expectedPos[step]
endPos := pos + expectedLen[step]
if err != nil {
t.Errorf("read from %d, got error %q", pos, err)
return
}
if n != endPos-pos {
t.Errorf("read from %d, expect %d, got %d", pos, endPos-pos, n)
}
if !bytes.Equal(wireData[pos:endPos], msg[:n]) {
t.Errorf("read from %d, frame %v, got %v", pos, wireData[pos:endPos], msg[:n])
}
step++
}
}
func TestHybiServerRead(t *testing.T) {
wireData := []byte{0x81, 0x85, 0xcc, 0x55, 0x80, 0x20,
0xa4, 0x30, 0xec, 0x4c, 0xa3, // hello
0x89, 0x85, 0xcc, 0x55, 0x80, 0x20,
0xa4, 0x30, 0xec, 0x4c, 0xa3, // ping: hello
0x81, 0x85, 0xed, 0x83, 0xb4, 0x24,
0x9a, 0xec, 0xc6, 0x48, 0x89, // world
}
br := bufio.NewReader(bytes.NewBuffer(wireData))
bw := bufio.NewWriter(bytes.NewBuffer([]byte{}))
conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, new(http.Request))
expected := [][]byte{[]byte("hello"), []byte("world")}
msg := make([]byte, 512)
n, err := conn.Read(msg)
if err != nil {
t.Errorf("read 1st frame, error %q", err)
}
if n != 5 {
t.Errorf("read 1st frame, expect 5, got %d", n)
}
if !bytes.Equal(expected[0], msg[:n]) {
t.Errorf("read 1st frame %q, got %q", expected[0], msg[:n])
}
n, err = conn.Read(msg)
if err != nil {
t.Errorf("read 2nd frame, error %q", err)
}
if n != 5 {
t.Errorf("read 2nd frame, expect 5, got %d", n)
}
if !bytes.Equal(expected[1], msg[:n]) {
t.Errorf("read 2nd frame %q, got %q", expected[1], msg[:n])
}
n, err = conn.Read(msg)
if err == nil {
t.Errorf("read not EOF")
}
if n != 0 {
t.Errorf("expect read 0, got %d", n)
}
}
func TestHybiServerReadWithoutMasking(t *testing.T) {
wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o'}
br := bufio.NewReader(bytes.NewBuffer(wireData))
bw := bufio.NewWriter(bytes.NewBuffer([]byte{}))
conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, new(http.Request))
// server MUST close the connection upon receiving a non-masked frame.
msg := make([]byte, 512)
_, err := conn.Read(msg)
if err != io.EOF {
t.Errorf("read 1st frame, expect %q, but got %q", io.EOF, err)
}
}
func TestHybiClientReadWithMasking(t *testing.T) {
wireData := []byte{0x81, 0x85, 0xcc, 0x55, 0x80, 0x20,
0xa4, 0x30, 0xec, 0x4c, 0xa3, // hello
}
br := bufio.NewReader(bytes.NewBuffer(wireData))
bw := bufio.NewWriter(bytes.NewBuffer([]byte{}))
conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil)
// client MUST close the connection upon receiving a masked frame.
msg := make([]byte, 512)
_, err := conn.Read(msg)
if err != io.EOF {
t.Errorf("read 1st frame, expect %q, but got %q", io.EOF, err)
}
}
// Test the hybiServerHandshaker supports firefox implementation and
// checks Connection request header include (but it's not necessary
// equal to) "upgrade"
func TestHybiServerFirefoxHandshake(t *testing.T) {
config := new(Config)
handshaker := &hybiServerHandshaker{Config: config}
br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: keep-alive, upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
`))
req, err := http.ReadRequest(br)
if err != nil {
t.Fatal("request", err)
}
code, err := handshaker.ReadHandshake(br, req)
if err != nil {
t.Errorf("handshake failed: %v", err)
}
if code != http.StatusSwitchingProtocols {
t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code)
}
b := bytes.NewBuffer([]byte{})
bw := bufio.NewWriter(b)
config.Protocol = []string{"chat"}
err = handshaker.AcceptHandshake(bw)
if err != nil {
t.Errorf("handshake response failed: %v", err)
}
expectedResponse := strings.Join([]string{
"HTTP/1.1 101 Switching Protocols",
"Upgrade: websocket",
"Connection: Upgrade",
"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",
"Sec-WebSocket-Protocol: chat",
"", ""}, "\r\n")
if b.String() != expectedResponse {
t.Errorf("handshake expected %q but got %q", expectedResponse, b.String())
}
}

View File

@ -0,0 +1,114 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
import (
"bufio"
"fmt"
"io"
"net/http"
)
func newServerConn(rwc io.ReadWriteCloser, buf *bufio.ReadWriter, req *http.Request, config *Config, handshake func(*Config, *http.Request) error) (conn *Conn, err error) {
var hs serverHandshaker = &hybiServerHandshaker{Config: config}
code, err := hs.ReadHandshake(buf.Reader, req)
if err == ErrBadWebSocketVersion {
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
fmt.Fprintf(buf, "Sec-WebSocket-Version: %s\r\n", SupportedProtocolVersion)
buf.WriteString("\r\n")
buf.WriteString(err.Error())
buf.Flush()
return
}
if err != nil {
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
buf.WriteString("\r\n")
buf.WriteString(err.Error())
buf.Flush()
return
}
if handshake != nil {
err = handshake(config, req)
if err != nil {
code = http.StatusForbidden
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
buf.WriteString("\r\n")
buf.Flush()
return
}
}
err = hs.AcceptHandshake(buf.Writer)
if err != nil {
code = http.StatusBadRequest
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
buf.WriteString("\r\n")
buf.Flush()
return
}
conn = hs.NewServerConn(buf, rwc, req)
return
}
// Server represents a server of a WebSocket.
type Server struct {
// Config is a WebSocket configuration for new WebSocket connection.
Config
// Handshake is an optional function in WebSocket handshake.
// For example, you can check, or don't check Origin header.
// Another example, you can select config.Protocol.
Handshake func(*Config, *http.Request) error
// Handler handles a WebSocket connection.
Handler
}
// ServeHTTP implements the http.Handler interface for a WebSocket
func (s Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
s.serveWebSocket(w, req)
}
func (s Server) serveWebSocket(w http.ResponseWriter, req *http.Request) {
rwc, buf, err := w.(http.Hijacker).Hijack()
if err != nil {
panic("Hijack failed: " + err.Error())
return
}
// The server should abort the WebSocket connection if it finds
// the client did not send a handshake that matches with protocol
// specification.
defer rwc.Close()
conn, err := newServerConn(rwc, buf, req, &s.Config, s.Handshake)
if err != nil {
return
}
if conn == nil {
panic("unexpected nil conn")
}
s.Handler(conn)
}
// Handler is a simple interface to a WebSocket browser client.
// It checks if Origin header is valid URL by default.
// You might want to verify websocket.Conn.Config().Origin in the func.
// If you use Server instead of Handler, you could call websocket.Origin and
// check the origin in your Handshake func. So, if you want to accept
// non-browser client, which doesn't send Origin header, you could use Server
//. that doesn't check origin in its Handshake.
type Handler func(*Conn)
func checkOrigin(config *Config, req *http.Request) (err error) {
config.Origin, err = Origin(config, req)
if err == nil && config.Origin == nil {
return fmt.Errorf("null origin")
}
return err
}
// ServeHTTP implements the http.Handler interface for a WebSocket
func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
s := Server{Handler: h, Handshake: checkOrigin}
s.serveWebSocket(w, req)
}

View File

@ -0,0 +1,411 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package websocket implements a client and server for the WebSocket protocol
// as specified in RFC 6455.
package websocket
import (
"bufio"
"crypto/tls"
"encoding/json"
"errors"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"sync"
"time"
)
const (
ProtocolVersionHybi13 = 13
ProtocolVersionHybi = ProtocolVersionHybi13
SupportedProtocolVersion = "13"
ContinuationFrame = 0
TextFrame = 1
BinaryFrame = 2
CloseFrame = 8
PingFrame = 9
PongFrame = 10
UnknownFrame = 255
)
// ProtocolError represents WebSocket protocol errors.
type ProtocolError struct {
ErrorString string
}
func (err *ProtocolError) Error() string { return err.ErrorString }
var (
ErrBadProtocolVersion = &ProtocolError{"bad protocol version"}
ErrBadScheme = &ProtocolError{"bad scheme"}
ErrBadStatus = &ProtocolError{"bad status"}
ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"}
ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"}
ErrBadWebSocketLocation = &ProtocolError{"missing or bad WebSocket-Location"}
ErrBadWebSocketProtocol = &ProtocolError{"missing or bad WebSocket-Protocol"}
ErrBadWebSocketVersion = &ProtocolError{"missing or bad WebSocket Version"}
ErrChallengeResponse = &ProtocolError{"mismatch challenge/response"}
ErrBadFrame = &ProtocolError{"bad frame"}
ErrBadFrameBoundary = &ProtocolError{"not on frame boundary"}
ErrNotWebSocket = &ProtocolError{"not websocket protocol"}
ErrBadRequestMethod = &ProtocolError{"bad method"}
ErrNotSupported = &ProtocolError{"not supported"}
)
// Addr is an implementation of net.Addr for WebSocket.
type Addr struct {
*url.URL
}
// Network returns the network type for a WebSocket, "websocket".
func (addr *Addr) Network() string { return "websocket" }
// Config is a WebSocket configuration
type Config struct {
// A WebSocket server address.
Location *url.URL
// A Websocket client origin.
Origin *url.URL
// WebSocket subprotocols.
Protocol []string
// WebSocket protocol version.
Version int
// TLS config for secure WebSocket (wss).
TlsConfig *tls.Config
// Additional header fields to be sent in WebSocket opening handshake.
Header http.Header
handshakeData map[string]string
}
// serverHandshaker is an interface to handle WebSocket server side handshake.
type serverHandshaker interface {
// ReadHandshake reads handshake request message from client.
// Returns http response code and error if any.
ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error)
// AcceptHandshake accepts the client handshake request and sends
// handshake response back to client.
AcceptHandshake(buf *bufio.Writer) (err error)
// NewServerConn creates a new WebSocket connection.
NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn)
}
// frameReader is an interface to read a WebSocket frame.
type frameReader interface {
// Reader is to read payload of the frame.
io.Reader
// PayloadType returns payload type.
PayloadType() byte
// HeaderReader returns a reader to read header of the frame.
HeaderReader() io.Reader
// TrailerReader returns a reader to read trailer of the frame.
// If it returns nil, there is no trailer in the frame.
TrailerReader() io.Reader
// Len returns total length of the frame, including header and trailer.
Len() int
}
// frameReaderFactory is an interface to creates new frame reader.
type frameReaderFactory interface {
NewFrameReader() (r frameReader, err error)
}
// frameWriter is an interface to write a WebSocket frame.
type frameWriter interface {
// Writer is to write payload of the frame.
io.WriteCloser
}
// frameWriterFactory is an interface to create new frame writer.
type frameWriterFactory interface {
NewFrameWriter(payloadType byte) (w frameWriter, err error)
}
type frameHandler interface {
HandleFrame(frame frameReader) (r frameReader, err error)
WriteClose(status int) (err error)
}
// Conn represents a WebSocket connection.
type Conn struct {
config *Config
request *http.Request
buf *bufio.ReadWriter
rwc io.ReadWriteCloser
rio sync.Mutex
frameReaderFactory
frameReader
wio sync.Mutex
frameWriterFactory
frameHandler
PayloadType byte
defaultCloseStatus int
}
// Read implements the io.Reader interface:
// it reads data of a frame from the WebSocket connection.
// if msg is not large enough for the frame data, it fills the msg and next Read
// will read the rest of the frame data.
// it reads Text frame or Binary frame.
func (ws *Conn) Read(msg []byte) (n int, err error) {
ws.rio.Lock()
defer ws.rio.Unlock()
again:
if ws.frameReader == nil {
frame, err := ws.frameReaderFactory.NewFrameReader()
if err != nil {
return 0, err
}
ws.frameReader, err = ws.frameHandler.HandleFrame(frame)
if err != nil {
return 0, err
}
if ws.frameReader == nil {
goto again
}
}
n, err = ws.frameReader.Read(msg)
if err == io.EOF {
if trailer := ws.frameReader.TrailerReader(); trailer != nil {
io.Copy(ioutil.Discard, trailer)
}
ws.frameReader = nil
goto again
}
return n, err
}
// Write implements the io.Writer interface:
// it writes data as a frame to the WebSocket connection.
func (ws *Conn) Write(msg []byte) (n int, err error) {
ws.wio.Lock()
defer ws.wio.Unlock()
w, err := ws.frameWriterFactory.NewFrameWriter(ws.PayloadType)
if err != nil {
return 0, err
}
n, err = w.Write(msg)
w.Close()
if err != nil {
return n, err
}
return n, err
}
// Close implements the io.Closer interface.
func (ws *Conn) Close() error {
err := ws.frameHandler.WriteClose(ws.defaultCloseStatus)
if err != nil {
return err
}
return ws.rwc.Close()
}
func (ws *Conn) IsClientConn() bool { return ws.request == nil }
func (ws *Conn) IsServerConn() bool { return ws.request != nil }
// LocalAddr returns the WebSocket Origin for the connection for client, or
// the WebSocket location for server.
func (ws *Conn) LocalAddr() net.Addr {
if ws.IsClientConn() {
return &Addr{ws.config.Origin}
}
return &Addr{ws.config.Location}
}
// RemoteAddr returns the WebSocket location for the connection for client, or
// the Websocket Origin for server.
func (ws *Conn) RemoteAddr() net.Addr {
if ws.IsClientConn() {
return &Addr{ws.config.Location}
}
return &Addr{ws.config.Origin}
}
var errSetDeadline = errors.New("websocket: cannot set deadline: not using a net.Conn")
// SetDeadline sets the connection's network read & write deadlines.
func (ws *Conn) SetDeadline(t time.Time) error {
if conn, ok := ws.rwc.(net.Conn); ok {
return conn.SetDeadline(t)
}
return errSetDeadline
}
// SetReadDeadline sets the connection's network read deadline.
func (ws *Conn) SetReadDeadline(t time.Time) error {
if conn, ok := ws.rwc.(net.Conn); ok {
return conn.SetReadDeadline(t)
}
return errSetDeadline
}
// SetWriteDeadline sets the connection's network write deadline.
func (ws *Conn) SetWriteDeadline(t time.Time) error {
if conn, ok := ws.rwc.(net.Conn); ok {
return conn.SetWriteDeadline(t)
}
return errSetDeadline
}
// Config returns the WebSocket config.
func (ws *Conn) Config() *Config { return ws.config }
// Request returns the http request upgraded to the WebSocket.
// It is nil for client side.
func (ws *Conn) Request() *http.Request { return ws.request }
// Codec represents a symmetric pair of functions that implement a codec.
type Codec struct {
Marshal func(v interface{}) (data []byte, payloadType byte, err error)
Unmarshal func(data []byte, payloadType byte, v interface{}) (err error)
}
// Send sends v marshaled by cd.Marshal as single frame to ws.
func (cd Codec) Send(ws *Conn, v interface{}) (err error) {
data, payloadType, err := cd.Marshal(v)
if err != nil {
return err
}
ws.wio.Lock()
defer ws.wio.Unlock()
w, err := ws.frameWriterFactory.NewFrameWriter(payloadType)
if err != nil {
return err
}
_, err = w.Write(data)
w.Close()
return err
}
// Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores in v.
func (cd Codec) Receive(ws *Conn, v interface{}) (err error) {
ws.rio.Lock()
defer ws.rio.Unlock()
if ws.frameReader != nil {
_, err = io.Copy(ioutil.Discard, ws.frameReader)
if err != nil {
return err
}
ws.frameReader = nil
}
again:
frame, err := ws.frameReaderFactory.NewFrameReader()
if err != nil {
return err
}
frame, err = ws.frameHandler.HandleFrame(frame)
if err != nil {
return err
}
if frame == nil {
goto again
}
payloadType := frame.PayloadType()
data, err := ioutil.ReadAll(frame)
if err != nil {
return err
}
return cd.Unmarshal(data, payloadType, v)
}
func marshal(v interface{}) (msg []byte, payloadType byte, err error) {
switch data := v.(type) {
case string:
return []byte(data), TextFrame, nil
case []byte:
return data, BinaryFrame, nil
}
return nil, UnknownFrame, ErrNotSupported
}
func unmarshal(msg []byte, payloadType byte, v interface{}) (err error) {
switch data := v.(type) {
case *string:
*data = string(msg)
return nil
case *[]byte:
*data = msg
return nil
}
return ErrNotSupported
}
/*
Message is a codec to send/receive text/binary data in a frame on WebSocket connection.
To send/receive text frame, use string type.
To send/receive binary frame, use []byte type.
Trivial usage:
import "websocket"
// receive text frame
var message string
websocket.Message.Receive(ws, &message)
// send text frame
message = "hello"
websocket.Message.Send(ws, message)
// receive binary frame
var data []byte
websocket.Message.Receive(ws, &data)
// send binary frame
data = []byte{0, 1, 2}
websocket.Message.Send(ws, data)
*/
var Message = Codec{marshal, unmarshal}
func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err error) {
msg, err = json.Marshal(v)
return msg, TextFrame, err
}
func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err error) {
return json.Unmarshal(msg, v)
}
/*
JSON is a codec to send/receive JSON data in a frame from a WebSocket connection.
Trivial usage:
import "websocket"
type T struct {
Msg string
Count int
}
// receive JSON type T
var data T
websocket.JSON.Receive(ws, &data)
// send JSON type T
websocket.JSON.Send(ws, data)
*/
var JSON = Codec{jsonMarshal, jsonUnmarshal}

View 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)
}
}
}

View File

@ -24,23 +24,16 @@ import (
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings"
"sort" "sort"
"strings"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common" "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/common/registrar"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
re "github.com/ethereum/go-ethereum/jsre" re "github.com/ethereum/go-ethereum/jsre"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc" "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/peterh/liner"
"github.com/robertkrimen/otto" "github.com/robertkrimen/otto"
) )
@ -79,54 +72,60 @@ func (r dumbterm) AppendHistory(string) {}
type jsre struct { type jsre struct {
re *re.JSRE re *re.JSRE
stack *node.Node stack *node.Node
xeth *xeth.XEth
wait chan *big.Int wait chan *big.Int
ps1 string ps1 string
atexit func() atexit func()
corsDomain string corsDomain string
client comms.EthereumClient client rpc.Client
prompter prompter
} }
var ( var (
loadedModulesMethods map[string][]string loadedModulesMethods map[string][]string
autoCompleteStatement = "function _autocomplete(obj) {var results = []; for (var e in obj) { results.push(e); }; return results; }; _autocomplete(%s)"
) )
func keywordCompleter(line string) []string { func keywordCompleter(jsre *jsre, line string) []string {
results := make([]string, 0) 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, ".") { result, _ := jsre.re.Run(fmt.Sprintf(autoCompleteStatement, objRef))
elements := strings.Split(line, ".") raw, _ := result.Export()
if len(elements) == 2 { if keys, ok := raw.([]interface{}); ok {
module := elements[0] for _, k := range keys {
partialMethod := elements[1] if strings.HasPrefix(fmt.Sprintf("%s", k), prefix) {
if methods, found := loadedModulesMethods[module]; found { if objRef == "this" {
for _, method := range methods { results = append(results, fmt.Sprintf("%s", k))
if strings.HasPrefix(method, partialMethod) { // e.g. debug.se
results = append(results, module+"."+method)
}
}
}
}
} else { } else {
for module, methods := range loadedModulesMethods { results = append(results, fmt.Sprintf("%s.%s", strings.Join(parts[:len(parts) - 1], "."), k))
if line == module { // user typed in full module name, show all methods
for _, method := range methods {
results = append(results, module+"."+method)
}
} else if strings.HasPrefix(module, line) { // partial method name, e.g. admi
results = append(results, module)
} }
} }
} }
}
// e.g. web3<tab><tab> append dot since its an object
isObj, _ := jsre.re.Run(fmt.Sprintf("typeof(%s) === 'object'", line))
if isObject, _ := isObj.ToBoolean(); isObject {
results = append(results, line + ".")
}
sort.Strings(results)
return results 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 { if len(line) == 0 || pos == 0 {
return "", nil, "" return "", nil, ""
} }
// chuck data to relevant part for autocompletion, e.g. in case of nested lines eth.getBalance(eth.coinb<tab><tab>
i := 0 i := 0
for i = pos - 1; i > 0; i-- { for i = pos - 1; i > 0; i-- {
if line[i] == '.' || (line[i] >= 'a' && line[i] <= 'z') || (line[i] >= 'A' && line[i] <= 'Z') { if line[i] == '.' || (line[i] >= 'a' && line[i] <= 'z') || (line[i] >= 'A' && line[i] <= 'Z') {
@ -143,18 +142,20 @@ func apiWordCompleter(line string, pos int) (head string, completions []string,
keyword := line[i:pos] keyword := line[i:pos]
end := line[pos:] end := line[pos:]
completionWords := keywordCompleter(keyword) completionWords := keywordCompleter(jsre, keyword)
return begin, completionWords, end return begin, completionWords, end
} }
func newLightweightJSRE(docRoot string, client comms.EthereumClient, datadir string, interactive bool) *jsre { return completer
}
func newLightweightJSRE(docRoot string, client rpc.Client, datadir string, interactive bool) *jsre {
js := &jsre{ps1: "> "} js := &jsre{ps1: "> "}
js.wait = make(chan *big.Int) js.wait = make(chan *big.Int)
js.client = client js.client = client
// update state in separare forever blocks
js.re = re.New(docRoot) 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) 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) }) js.withHistory(datadir, func(hist *os.File) { lr.ReadHistory(hist) })
lr.SetCtrlCAborts(true) lr.SetCtrlCAborts(true)
js.loadAutoCompletion() js.loadAutoCompletion()
lr.SetWordCompleter(apiWordCompleter) lr.SetWordCompleter(apiWordCompleterWithContext(js))
lr.SetTabCompletionStyle(liner.TabPrints) lr.SetTabCompletionStyle(liner.TabPrints)
js.prompter = lr js.prompter = lr
js.atexit = func() { js.atexit = func() {
@ -177,25 +178,15 @@ func newLightweightJSRE(docRoot string, client comms.EthereumClient, datadir str
return js 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: "> "} js := &jsre{stack: stack, ps1: "> "}
// set default cors domain used by startRpc from CLI flag // set default cors domain used by startRpc from CLI flag
js.corsDomain = corsDomain js.corsDomain = corsDomain
if f == nil { js.wait = make(chan *big.Int)
f = js
}
js.xeth = xeth.New(stack, f)
js.wait = js.xeth.UpdateState()
js.client = client 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) 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) 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) }) js.withHistory(stack.DataDir(), func(hist *os.File) { lr.ReadHistory(hist) })
lr.SetCtrlCAborts(true) lr.SetCtrlCAborts(true)
js.loadAutoCompletion() js.loadAutoCompletion()
lr.SetWordCompleter(apiWordCompleter) lr.SetWordCompleter(apiWordCompleterWithContext(js))
lr.SetTabCompletionStyle(liner.TabPrints) lr.SetTabCompletionStyle(liner.TabPrints)
js.prompter = lr js.prompter = lr
js.atexit = func() { js.atexit = func() {
@ -222,7 +213,7 @@ func (self *jsre) loadAutoCompletion() {
if modules, err := self.supportedApis(); err == nil { if modules, err := self.supportedApis(); err == nil {
loadedModulesMethods = make(map[string][]string) loadedModulesMethods = make(map[string][]string)
for module, _ := range modules { 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)) loadedModules = append(loadedModules, fmt.Sprintf("%s:%s", api, version))
} }
sort.Strings(loadedModules) sort.Strings(loadedModules)
} }
} }
@ -266,7 +256,7 @@ func (self *jsre) supportedApis() (map[string]string, error) {
return self.client.SupportedModules() return self.client.SupportedModules()
} }
func (js *jsre) apiBindings(f xeth.Frontend) error { func (js *jsre) apiBindings() error {
apis, err := js.supportedApis() apis, err := js.supportedApis()
if err != nil { if err != nil {
return err return err
@ -277,12 +267,7 @@ func (js *jsre) apiBindings(f xeth.Frontend) error {
apiNames = append(apiNames, a) apiNames = append(apiNames, a)
} }
apiImpl, err := api.ParseApiString(strings.Join(apiNames, ","), codec.JSON, js.xeth, js.stack) jeth := utils.NewJeth(js.re, js.client)
if err != nil {
utils.Fatalf("Unable to determine supported api's: %v", err)
}
jeth := rpc.NewJeth(api.Merge(apiImpl...), js.re, js.client, f)
js.re.Set("jeth", struct{}{}) js.re.Set("jeth", struct{}{})
t, _ := js.re.Get("jeth") t, _ := js.re.Get("jeth")
jethObj := t.Object() jethObj := t.Object()
@ -313,16 +298,18 @@ func (js *jsre) apiBindings(f xeth.Frontend) error {
// load only supported API's in javascript runtime // load only supported API's in javascript runtime
shortcuts := "var eth = web3.eth; " shortcuts := "var eth = web3.eth; "
for _, apiName := range apiNames { for _, apiName := range apiNames {
if apiName == shared.Web3ApiName { if apiName == "web3" || apiName == "rpc" {
continue // manually mapped 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) shortcuts += fmt.Sprintf("var %s = web3.%s; ", apiName, apiName)
} else { } else {
utils.Fatalf("Error loading %s.js: %v", apiName, err) utils.Fatalf("Error loading %s.js: %v", apiName, err)
} }
} }
}
_, err = js.re.Run(shortcuts) _, err = js.re.Run(shortcuts)
if err != nil { if err != nil {
@ -375,14 +362,13 @@ func (self *jsre) ConfirmTransaction(tx string) bool {
return false return false
} }
// If natspec is enabled, ask for permission // If natspec is enabled, ask for permission
if ethereum.NatSpec { if ethereum.NatSpec && false /* disabled for now */ {
notice := natspec.GetNotice(self.xeth, tx, ethereum.HTTPClient()) // notice := natspec.GetNotice(self.xeth, tx, ethereum.HTTPClient())
fmt.Println(notice) // fmt.Println(notice)
answer, _ := self.Prompt("Confirm Transaction [y/n]") // answer, _ := self.Prompt("Confirm Transaction [y/n]")
return strings.HasPrefix(strings.Trim(answer, " "), "y") // return strings.HasPrefix(strings.Trim(answer, " "), "y")
} else {
return true
} }
return true
} }
func (self *jsre) UnlockAccount(addr []byte) bool { func (self *jsre) UnlockAccount(addr []byte) bool {

View File

@ -32,15 +32,12 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/compiler" "github.com/ethereum/go-ethereum/common/compiler"
"github.com/ethereum/go-ethereum/common/httpclient" "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/core"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/rpc/comms"
) )
const ( const (
@ -77,15 +74,16 @@ func (self *testjethre) UnlockAccount(acc []byte) bool {
return true return true
} }
func (self *testjethre) ConfirmTransaction(tx string) bool { // Temporary disabled while natspec hasn't been migrated
var ethereum *eth.Ethereum //func (self *testjethre) ConfirmTransaction(tx string) bool {
self.stack.Service(&ethereum) // var ethereum *eth.Ethereum
// self.stack.Service(&ethereum)
if ethereum.NatSpec { //
self.lastConfirm = natspec.GetNotice(self.xeth, tx, self.client) // if ethereum.NatSpec {
} // self.lastConfirm = natspec.GetNotice(self.xeth, tx, self.client)
return true // }
} // return true
//}
func testJEthRE(t *testing.T) (string, *testjethre, *node.Node) { func testJEthRE(t *testing.T) (string, *testjethre, *node.Node) {
return testREPL(t, nil) return testREPL(t, nil)
@ -118,7 +116,9 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod
if config != nil { if config != nil {
config(ethConf) 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) t.Fatalf("failed to register ethereum protocol: %v", err)
} }
// Initialize all the keys for testing // Initialize all the keys for testing
@ -141,9 +141,10 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod
stack.Service(&ethereum) stack.Service(&ethereum)
assetPath := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext") 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()} tf := &testjethre{client: ethereum.HTTPClient()}
repl := newJSRE(stack, assetPath, "", client, false, tf) repl := newJSRE(stack, assetPath, "", client, false)
tf.jsre = repl tf.jsre = repl
return tmp, tf, stack return tmp, tf, stack
} }
@ -278,19 +279,20 @@ func TestContract(t *testing.T) {
defer ethereum.Stop() defer ethereum.Stop()
defer os.RemoveAll(tmp) defer os.RemoveAll(tmp)
reg := registrar.New(repl.xeth) // Temporary disabled while registrar isn't migrated
_, err := reg.SetGlobalRegistrar("", coinbase) //reg := registrar.New(repl.xeth)
if err != nil { //_, err := reg.SetGlobalRegistrar("", coinbase)
t.Errorf("error setting HashReg: %v", err) //if err != nil {
} // t.Errorf("error setting HashReg: %v", err)
_, err = reg.SetHashReg("", coinbase) //}
if err != nil { //_, err = reg.SetHashReg("", coinbase)
t.Errorf("error setting HashReg: %v", err) //if err != nil {
} // t.Errorf("error setting HashReg: %v", err)
_, err = reg.SetUrlHint("", coinbase) //}
if err != nil { //_, err = reg.SetUrlHint("", coinbase)
t.Errorf("error setting HashReg: %v", err) //if err != nil {
} // t.Errorf("error setting HashReg: %v", err)
//}
/* TODO: /* TODO:
* lookup receipt and contract addresses by tx hash * lookup receipt and contract addresses by tx hash
* name registration for HashReg and UrlHint addresses * name registration for HashReg and UrlHint addresses
@ -474,7 +476,8 @@ func processTxs(repl *testjethre, t *testing.T, expTxc int) bool {
defer ethereum.StopMining() defer ethereum.StopMining()
timer := time.NewTimer(100 * time.Second) 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 repl.wait <- height
select { select {
case <-timer.C: case <-timer.C:

View File

@ -40,8 +40,6 @@ import (
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/comms"
) )
const ( const (
@ -309,11 +307,15 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
utils.RPCEnabledFlag, utils.RPCEnabledFlag,
utils.RPCListenAddrFlag, utils.RPCListenAddrFlag,
utils.RPCPortFlag, utils.RPCPortFlag,
utils.RpcApiFlag, utils.RPCApiFlag,
utils.WSEnabledFlag,
utils.WSListenAddrFlag,
utils.WSPortFlag,
utils.WSApiFlag,
utils.WSAllowedDomainsFlag,
utils.IPCDisabledFlag, utils.IPCDisabledFlag,
utils.IPCApiFlag, utils.IPCApiFlag,
utils.IPCPathFlag, utils.IPCPathFlag,
utils.IPCExperimental,
utils.ExecFlag, utils.ExecFlag,
utils.WhisperEnabledFlag, utils.WhisperEnabledFlag,
utils.DevModeFlag, utils.DevModeFlag,
@ -392,20 +394,12 @@ func geth(ctx *cli.Context) {
node.Wait() node.Wait()
} }
// attach will connect to a running geth instance attaching a JavaScript console and to it.
func attach(ctx *cli.Context) { func attach(ctx *cli.Context) {
var client comms.EthereumClient // attach to a running geth instance
var err error client, err := utils.NewRemoteRPCClient(ctx)
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)
}
if err != nil { if err != nil {
utils.Fatalf("Unable to attach to geth node - %v", err) utils.Fatalf("Unable to attach to geth - %v", err)
} }
repl := newLightweightJSRE( repl := newLightweightJSRE(
@ -431,11 +425,12 @@ func console(ctx *cli.Context) {
startNode(ctx, node) startNode(ctx, node)
// Attach to the newly started node, and either execute script or become interactive // 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, repl := newJSRE(node,
ctx.GlobalString(utils.JSpathFlag.Name), ctx.GlobalString(utils.JSpathFlag.Name),
ctx.GlobalString(utils.RPCCORSDomainFlag.Name), ctx.GlobalString(utils.RPCCORSDomainFlag.Name),
client, true, nil) client, true)
if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" { if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" {
repl.batch(script) repl.batch(script)
@ -454,11 +449,12 @@ func execScripts(ctx *cli.Context) {
startNode(ctx, node) startNode(ctx, node)
// Attach to the newly started node and execute the given scripts // Attach to the newly started node and execute the given scripts
client := comms.NewInProcClient(codec.JSON) client := utils.NewInProcRPCClient(node)
repl := newJSRE(node, repl := newJSRE(node,
ctx.GlobalString(utils.JSpathFlag.Name), ctx.GlobalString(utils.JSpathFlag.Name),
ctx.GlobalString(utils.RPCCORSDomainFlag.Name), ctx.GlobalString(utils.RPCCORSDomainFlag.Name),
client, false, nil) client, false)
for _, file := range ctx.Args() { for _, file := range ctx.Args() {
repl.exec(file) repl.exec(file)
@ -517,6 +513,11 @@ func startNode(ctx *cli.Context, stack *node.Node) {
utils.Fatalf("Failed to start RPC: %v", err) 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 ctx.GlobalBool(utils.MiningEnabledFlag.Name) {
if err := ethereum.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name), ctx.GlobalString(utils.MiningGPUFlag.Name)); err != nil { if err := ethereum.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name), ctx.GlobalString(utils.MiningGPUFlag.Name)); err != nil {
utils.Fatalf("Failed to start mining: %v", err) utils.Fatalf("Failed to start mining: %v", err)

View File

@ -21,16 +21,15 @@ import (
"math" "math"
"reflect" "reflect"
"runtime" "runtime"
"sort"
"strings" "strings"
"time" "time"
"sort"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/comms"
"github.com/gizak/termui" "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. // monitor starts a terminal UI based monitoring tool for the requested metrics.
func monitor(ctx *cli.Context) { func monitor(ctx *cli.Context) {
var ( var (
client comms.EthereumClient client rpc.Client
err error err error
) )
// Attach to an Ethereum node over IPC or RPC // Attach to an Ethereum node over IPC or RPC
endpoint := ctx.String(monitorCommandAttachFlag.Name) 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) utils.Fatalf("Unable to attach to geth node: %v", err)
} }
defer client.Close() defer client.Close()
xeth := rpc.NewXeth(client)
// Retrieve all the available metrics and resolve the user pattens // Retrieve all the available metrics and resolve the user pattens
metrics, err := retrieveMetrics(xeth) metrics, err := retrieveMetrics(client)
if err != nil { if err != nil {
utils.Fatalf("Failed to retrieve system metrics: %v", err) 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))) 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.Body.Align()
termui.Render(termui.Body) termui.Render(termui.Body)
@ -154,7 +151,7 @@ func monitor(ctx *cli.Context) {
termui.Render(termui.Body) termui.Render(termui.Body)
} }
case <-refresh: case <-refresh:
if refreshCharts(xeth, monitored, data, units, charts, ctx, footer) { if refreshCharts(client, monitored, data, units, charts, ctx, footer) {
termui.Body.Align() termui.Body.Align()
} }
termui.Render(termui.Body) termui.Render(termui.Body)
@ -164,8 +161,30 @@ func monitor(ctx *cli.Context) {
// retrieveMetrics contacts the attached geth node and retrieves the entire set // retrieveMetrics contacts the attached geth node and retrieves the entire set
// of collected system metrics. // of collected system metrics.
func retrieveMetrics(xeth *rpc.Xeth) (map[string]interface{}, error) { func retrieveMetrics(client rpc.Client) (map[string]interface{}, error) {
return xeth.Call("debug_metrics", []interface{}{true}) 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 // 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 // refreshCharts retrieves a next batch of metrics, and inserts all the new
// values into the active datasets and charts // 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) { 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(xeth) values, err := retrieveMetrics(client)
for i, metric := range metrics { for i, metric := range metrics {
if len(data) < 512 { if len(data) < 512 {
data[i] = append([]float64{fetchMetric(values, metric)}, data[i]...) data[i] = append([]float64{fetchMetric(values, metric)}, data[i]...)

View File

@ -87,7 +87,12 @@ var AppHelpFlagGroups = []flagGroup{
utils.RPCEnabledFlag, utils.RPCEnabledFlag,
utils.RPCListenAddrFlag, utils.RPCListenAddrFlag,
utils.RPCPortFlag, utils.RPCPortFlag,
utils.RpcApiFlag, utils.RPCApiFlag,
utils.WSEnabledFlag,
utils.WSListenAddrFlag,
utils.WSPortFlag,
utils.WSApiFlag,
utils.WSAllowedDomainsFlag,
utils.IPCDisabledFlag, utils.IPCDisabledFlag,
utils.IPCApiFlag, utils.IPCApiFlag,
utils.IPCPathFlag, utils.IPCPathFlag,
@ -158,7 +163,6 @@ var AppHelpFlagGroups = []flagGroup{
Flags: []cli.Flag{ Flags: []cli.Flag{
utils.WhisperEnabledFlag, utils.WhisperEnabledFlag,
utils.NatspecEnabledFlag, utils.NatspecEnabledFlag,
utils.IPCExperimental,
}, },
}, },
{ {

View File

@ -26,8 +26,9 @@ import (
"path/filepath" "path/filepath"
"runtime" "runtime"
"errors"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
@ -35,13 +36,9 @@ import (
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc/api" "github.com/ethereum/go-ethereum/rpc"
"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/tests" "github.com/ethereum/go-ethereum/tests"
"github.com/ethereum/go-ethereum/whisper" "github.com/ethereum/go-ethereum/whisper"
"github.com/ethereum/go-ethereum/xeth"
) )
const defaultTestKey = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291" 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. // StartRPC initializes an RPC interface to the given protocol stack.
func StartRPC(stack *node.Node) error { func StartRPC(stack *node.Node) error {
config := comms.HttpConfig{ /*
ListenAddress: "127.0.0.1", web3 := NewPublicWeb3API(stack)
ListenPort: 8545, server.RegisterName("web3", web3)
} net := NewPublicNetAPI(stack.Server(), ethereum.NetVersion())
xeth := xeth.New(stack, nil) server.RegisterName("net", net)
codec := codec.JSON */
apis, err := api.ParseApiString(comms.DefaultHttpRpcApis, codec, xeth, stack) for _, api := range stack.APIs() {
if err != nil { 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 err
} }
return comms.StartHttp(config, codec, api.Merge(apis...))
} }
// StartRPC initializes an IPC interface to the given protocol stack. 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")
}
// StartIPC initializes an IPC interface to the given protocol stack.
func StartIPC(stack *node.Node) error { func StartIPC(stack *node.Node) error {
var ethereum *eth.Ethereum var ethereum *eth.Ethereum
if err := stack.Service(&ethereum); err != nil { if err := stack.Service(&ethereum); err != nil {
@ -202,11 +203,7 @@ func StartIPC(stack *node.Node) error {
endpoint = filepath.Join(common.DefaultDataDir(), "geth.ipc") endpoint = filepath.Join(common.DefaultDataDir(), "geth.ipc")
} }
config := comms.IpcConfig{ listener, err := rpc.CreateIPCListener(endpoint)
Endpoint: endpoint,
}
listener, err := comms.CreateListener(config)
if err != nil { if err != nil {
return err return err
} }
@ -217,16 +214,16 @@ func StartIPC(stack *node.Node) error {
offered := stack.APIs() offered := stack.APIs()
for _, api := range offered { for _, api := range offered {
server.RegisterName(api.Namespace, api.Service) 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) //var ethereum *eth.Ethereum
server.RegisterName("web3", web3) //if err := stack.Service(&ethereum); err != nil {
net := utils.NewPublicNetAPI(stack.Server(), ethereum.NetVersion()) // return err
server.RegisterName("net", net) //}
go func() { 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 { for {
conn, err := listener.Accept() conn, err := listener.Accept()
if err != nil { if err != nil {

View File

@ -1,74 +0,0 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package utils
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
rpc "github.com/ethereum/go-ethereum/rpc/v2"
)
// PublicWeb3API offers helper utils
type PublicWeb3API struct {
stack *node.Node
}
// NewPublicWeb3API creates a new Web3Service instance
func NewPublicWeb3API(stack *node.Node) *PublicWeb3API {
return &PublicWeb3API{stack}
}
// ClientVersion returns the node name
func (s *PublicWeb3API) ClientVersion() string {
return s.stack.Server().Name
}
// Sha3 applies the ethereum sha3 implementation on the input.
// It assumes the input is hex encoded.
func (s *PublicWeb3API) Sha3(input string) string {
return common.ToHex(crypto.Sha3(common.FromHex(input)))
}
// PublicNetAPI offers network related RPC methods
type PublicNetAPI struct {
net *p2p.Server
networkVersion int
}
// NewPublicNetAPI creates a new net api instance.
func NewPublicNetAPI(net *p2p.Server, networkVersion int) *PublicNetAPI {
return &PublicNetAPI{net, networkVersion}
}
// Listening returns an indication if the node is listening for network connections.
func (s *PublicNetAPI) Listening() bool {
return true // always listening
}
// Peercount returns the number of connected peers
func (s *PublicNetAPI) PeerCount() *rpc.HexNumber {
return rpc.NewHexNumber(s.net.PeerCount())
}
// ProtocolVersion returns the current ethereum protocol version.
func (s *PublicNetAPI) Version() string {
return fmt.Sprintf("%d", s.networkVersion)
}

176
cmd/utils/client.go Normal file
View File

@ -0,0 +1,176 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package utils
import (
"encoding/json"
"fmt"
"strings"
"github.com/codegangsta/cli"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc"
)
// NewInProcRPCClient will start a new RPC server for the given node and returns a client to interact with it.
func NewInProcRPCClient(stack *node.Node) *inProcClient {
server := rpc.NewServer()
offered := stack.APIs()
for _, api := range offered {
server.RegisterName(api.Namespace, api.Service)
}
web3 := node.NewPublicWeb3API(stack)
server.RegisterName("web3", web3)
var ethereum *eth.Ethereum
if err := stack.Service(&ethereum); 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")
}

View File

@ -23,7 +23,6 @@ import (
"log" "log"
"math" "math"
"math/big" "math/big"
"net"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -31,6 +30,8 @@ import (
"strconv" "strconv"
"strings" "strings"
"errors"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/ethereum/ethash" "github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
@ -49,14 +50,8 @@ import (
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc/api" "github.com/ethereum/go-ethereum/rpc"
"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/whisper" "github.com/ethereum/go-ethereum/whisper"
"github.com/ethereum/go-ethereum/xeth"
) )
func init() { func init() {
@ -282,10 +277,10 @@ var (
Usage: "Domains from which to accept cross origin requests (browser enforced)", Usage: "Domains from which to accept cross origin requests (browser enforced)",
Value: "", Value: "",
} }
RpcApiFlag = cli.StringFlag{ RPCApiFlag = cli.StringFlag{
Name: "rpcapi", Name: "rpcapi",
Usage: "API's offered over the HTTP-RPC interface", Usage: "API's offered over the HTTP-RPC interface",
Value: comms.DefaultHttpRpcApis, Value: rpc.DefaultHttpRpcApis,
} }
IPCDisabledFlag = cli.BoolFlag{ IPCDisabledFlag = cli.BoolFlag{
Name: "ipcdisable", Name: "ipcdisable",
@ -294,16 +289,36 @@ var (
IPCApiFlag = cli.StringFlag{ IPCApiFlag = cli.StringFlag{
Name: "ipcapi", Name: "ipcapi",
Usage: "API's offered over the IPC-RPC interface", Usage: "API's offered over the IPC-RPC interface",
Value: comms.DefaultIpcApis, Value: rpc.DefaultIpcApis,
} }
IPCPathFlag = DirectoryFlag{ IPCPathFlag = DirectoryFlag{
Name: "ipcpath", Name: "ipcpath",
Usage: "Filename for IPC socket/pipe", Usage: "Filename for IPC socket/pipe",
Value: DirectoryString{common.DefaultIpcPath()}, Value: DirectoryString{common.DefaultIpcPath()},
} }
IPCExperimental = cli.BoolFlag{ WSEnabledFlag = cli.BoolFlag{
Name: "ipcexp", Name: "ws",
Usage: "Enable the new RPC implementation", 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{ ExecFlag = cli.StringFlag{
Name: "exec", Name: "exec",
@ -760,7 +775,7 @@ func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database
return chain, chainDb return chain, chainDb
} }
func IpcSocketPath(ctx *cli.Context) (ipcpath string) { func IPCSocketPath(ctx *cli.Context) (ipcpath string) {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
ipcpath = common.DefaultIpcPath() ipcpath = common.DefaultIpcPath()
if ctx.GlobalIsSet(IPCPathFlag.Name) { 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 { func StartIPC(stack *node.Node, ctx *cli.Context) error {
config := comms.IpcConfig{
Endpoint: IpcSocketPath(ctx),
}
var ethereum *eth.Ethereum var ethereum *eth.Ethereum
if err := stack.Service(&ethereum); err != nil { if err := stack.Service(&ethereum); err != nil {
return err return err
} }
if ctx.GlobalIsSet(IPCExperimental.Name) { endpoint := IPCSocketPath(ctx)
listener, err := comms.CreateListener(config) listener, err := rpc.CreateIPCListener(endpoint)
if err != nil { if err != nil {
return err 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) 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() { 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 { for {
conn, err := listener.Accept() conn, err := listener.Accept()
if err != nil { if err != nil {
@ -823,36 +829,49 @@ func StartIPC(stack *node.Node, ctx *cli.Context) error {
}() }()
return nil 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. // StartRPC starts a HTTP JSON-RPC API server.
func StartRPC(stack *node.Node, ctx *cli.Context) error { func StartRPC(stack *node.Node, ctx *cli.Context) error {
config := comms.HttpConfig{ for _, api := range stack.APIs() {
ListenAddress: ctx.GlobalString(RPCListenAddrFlag.Name), if adminApi, ok := api.Service.(*node.PrivateAdminAPI); ok {
ListenPort: uint(ctx.GlobalInt(RPCPortFlag.Name)), address := ctx.GlobalString(RPCListenAddrFlag.Name)
CorsDomain: ctx.GlobalString(RPCCORSDomainFlag.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) _, err := adminApi.StartRPC(address, port, cors, apiStr)
codec := codec.JSON
apis, err := api.ParseApiString(ctx.GlobalString(RpcApiFlag.Name), codec, xeth, stack)
if err != nil {
return err 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) { func StartPProf(ctx *cli.Context) {

View File

@ -14,38 +14,39 @@
// You should have received a copy of the GNU Lesser General Public License // 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/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package rpc package utils
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"time" "time"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/jsre" "github.com/ethereum/go-ethereum/jsre"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/rpc"
"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/robertkrimen/otto" "github.com/robertkrimen/otto"
) )
type Jeth struct { type Jeth struct {
ethApi shared.EthereumApi
re *jsre.JSRE re *jsre.JSRE
client comms.EthereumClient client rpc.Client
fe xeth.Frontend
} }
func NewJeth(ethApi shared.EthereumApi, re *jsre.JSRE, client comms.EthereumClient, fe xeth.Frontend) *Jeth { // NewJeth create a new backend for the JSRE console
return &Jeth{ethApi, re, client, fe} 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 *int64) (response otto.Value) {
m := rpc.JSONErrResponse{
Version: "2.0",
Id: id,
Error: rpc.JSONError{
Code: code,
Message: msg,
},
} }
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))
errObj, _ := json.Marshal(m.Error) errObj, _ := json.Marshal(m.Error)
errRes, _ := json.Marshal(m) 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 account, ok = accountExport.(string); ok {
if len(call.ArgumentList) == 1 { if len(call.ArgumentList) == 1 {
fmt.Printf("Unlock account %s\n", account) fmt.Printf("Unlock account %s\n", account)
passwd, err = utils.PromptPassword("Passphrase: ", true) passwd, err = PromptPassword("Passphrase: ", true)
if err != nil { if err != nil {
return otto.FalseValue() 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 // 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) { func (self *Jeth) NewAccount(call otto.FunctionCall) (response otto.Value) {
if len(call.ArgumentList) == 0 { if len(call.ArgumentList) == 0 {
passwd, err := utils.PromptPassword("Passphrase: ", true) passwd, err := PromptPassword("Passphrase: ", true)
if err != nil { if err != nil {
return otto.FalseValue() return otto.FalseValue()
} }
passwd2, err := utils.PromptPassword("Repeat passphrase: ", true) passwd2, err := PromptPassword("Repeat passphrase: ", true)
if err != nil { if err != nil {
return otto.FalseValue() return otto.FalseValue()
} }
@ -134,11 +135,11 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) {
} }
jsonreq, err := json.Marshal(reqif) jsonreq, err := json.Marshal(reqif)
var reqs []shared.Request var reqs []rpc.JSONRequest
batch := true batch := true
err = json.Unmarshal(jsonreq, &reqs) err = json.Unmarshal(jsonreq, &reqs)
if err != nil { if err != nil {
reqs = make([]shared.Request, 1) reqs = make([]rpc.JSONRequest, 1)
err = json.Unmarshal(jsonreq, &reqs[0]) err = json.Unmarshal(jsonreq, &reqs[0])
batch = false 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);") call.Otto.Run("var ret_response = new Array(response_len);")
for i, req := range reqs { for i, req := range reqs {
var respif interface{}
err := self.client.Send(&req) err := self.client.Send(&req)
if err != nil { if err != nil {
return self.err(call, -32603, err.Error(), req.Id) return self.err(call, -32603, err.Error(), req.Id)
} }
recv: result := make(map[string]interface{})
respif, err = self.client.Recv() err = self.client.Recv(&result)
if err != nil { if err != nil {
return self.err(call, -32603, err.Error(), req.Id) return self.err(call, -32603, err.Error(), req.Id)
} }
agentreq, isRequest := respif.(*shared.Request) _, isSuccessResponse := result["result"]
if isRequest { _, isErrorResponse := result["error"]
self.handleRequest(agentreq)
goto recv // receive response after agent interaction
}
sucres, isSuccessResponse := respif.(*shared.SuccessResponse)
errres, isErrorResponse := respif.(*shared.ErrorResponse)
if !isSuccessResponse && !isErrorResponse { 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) id, _ := result["id"]
call.Otto.Set("ret_id", req.Id) call.Otto.Set("ret_id", id)
var res []byte jsonver, _ := result["jsonrpc"]
call.Otto.Set("ret_jsonrpc", jsonver)
var payload []byte
if isSuccessResponse { if isSuccessResponse {
res, err = json.Marshal(sucres.Result) payload, _ = json.Marshal(result["result"])
} else if isErrorResponse { } else if isErrorResponse {
res, err = json.Marshal(errres.Error) payload, _ = json.Marshal(result["error"])
} }
call.Otto.Set("ret_result", string(payload))
call.Otto.Set("ret_result", string(res))
call.Otto.Set("response_idx", i) call.Otto.Set("response_idx", i)
response, err = call.Otto.Run(` response, err = call.Otto.Run(`
ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, result: JSON.parse(ret_result) }; 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 return
} }
/*
// handleRequest will handle user agent requests by interacting with the user and sending // handleRequest will handle user agent requests by interacting with the user and sending
// the user response back to the geth service // the user response back to the geth service
func (self *Jeth) handleRequest(req *shared.Request) bool { func (self *Jeth) handleRequest(req *shared.Request) bool {
@ -235,7 +233,7 @@ func (self *Jeth) askPassword(id interface{}, jsonrpc string, args []interface{}
return false 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 { 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) 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 // Accept all tx which are send from this console
return self.client.Send(shared.NewRpcResponse(id, jsonrpc, true, nil)) == nil 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. // throwJSExeception panics on an otto value, the Otto VM will then throw msg as a javascript error.
func throwJSExeception(msg interface{}) otto.Value { func throwJSExeception(msg interface{}) otto.Value {

View File

@ -13,6 +13,8 @@
// //
// You should have received a copy of the GNU Lesser General Public License // 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/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
//
// +build ignore
package natspec package natspec

View File

@ -13,6 +13,8 @@
// //
// You should have received a copy of the GNU Lesser General Public License // 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/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
//
// +build ignore
package natspec package natspec

View File

@ -13,6 +13,8 @@
// //
// You should have received a copy of the GNU Lesser General Public License // 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/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
//
// +build ignore
package natspec package natspec

View File

@ -0,0 +1,265 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package ethreg
import (
"errors"
"math/big"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/compiler"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/common/registrar"
)
// registryAPIBackend is a backend for an Ethereum Registry.
type registryAPIBackend struct {
bc *core.BlockChain
chainDb ethdb.Database
txPool *core.TxPool
am *accounts.Manager
}
// PrivateRegistarAPI offers various functions to access the Ethereum registry.
type PrivateRegistarAPI struct {
be *registryAPIBackend
}
// NewPrivateRegistarAPI creates a new PrivateRegistarAPI instance.
func NewPrivateRegistarAPI(bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI {
return &PrivateRegistarAPI{&registryAPIBackend{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
}

View File

@ -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)
}

View File

@ -42,8 +42,10 @@ import (
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "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" "github.com/ethereum/go-ethereum/rlp"
rpc "github.com/ethereum/go-ethereum/rpc/v2" "github.com/ethereum/go-ethereum/rpc"
) )
const ( const (
@ -51,6 +53,19 @@ const (
defaultGas = uint64(90000) 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. // PublicEthereumAPI provides an API to access Ethereum related information.
// It offers only methods that operate on public data that is freely available to anyone. // It offers only methods that operate on public data that is freely available to anyone.
type PublicEthereumAPI struct { type PublicEthereumAPI struct {
@ -293,11 +308,12 @@ type PublicBlockChainAPI struct {
chainDb ethdb.Database chainDb ethdb.Database
eventMux *event.TypeMux eventMux *event.TypeMux
am *accounts.Manager am *accounts.Manager
miner *miner.Miner
} }
// NewPublicBlockChainAPI creates a new Etheruem blockchain API. // NewPublicBlockChainAPI creates a new Etheruem blockchain API.
func NewPublicBlockChainAPI(bc *core.BlockChain, chainDb ethdb.Database, eventMux *event.TypeMux, am *accounts.Manager) *PublicBlockChainAPI { func NewPublicBlockChainAPI(bc *core.BlockChain, m *miner.Miner, chainDb ethdb.Database, eventMux *event.TypeMux, am *accounts.Manager) *PublicBlockChainAPI {
return &PublicBlockChainAPI{bc: bc, chainDb: chainDb, eventMux: eventMux, am: am} return &PublicBlockChainAPI{bc: bc, miner: m, chainDb: chainDb, eventMux: eventMux, am: am}
} }
// BlockNumber returns the block number of the chain head. // 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. // 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. // When block number equals rpc.LatestBlockNumber the current block is used.
func (s *PublicBlockChainAPI) GetBalance(address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) { 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 { if block == nil {
return nil, nil return nil, nil
} }
@ -320,20 +336,10 @@ func (s *PublicBlockChainAPI) GetBalance(address common.Address, blockNr rpc.Blo
return state.GetBalance(address), nil 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 // 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. // 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) { 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 s.rpcOutputBlock(block, true, fullTx)
} }
return nil, nil return nil, nil
@ -355,7 +361,7 @@ func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(blockNr rpc.BlockNum
return nil, nil return nil, nil
} }
if block := blockByNumber(s.bc, blockNr); block != nil { if block := blockByNumber(s.miner, s.bc, blockNr); block != nil {
uncles := block.Uncles() uncles := block.Uncles()
if index.Int() < 0 || index.Int() >= len(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) 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) 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 rpc.NewHexNumber(len(block.Uncles()))
} }
return nil 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. // 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) { 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) state, err := state.New(block.Root(), s.chainDb)
if err != nil { if err != nil {
return "", err 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. // 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) { 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) state, err := state.New(block.Root(), s.chainDb)
if err != nil { if err != nil {
return "", err return "", err
@ -490,7 +496,7 @@ type CallArgs struct {
} }
func (s *PublicBlockChainAPI) doCall(args CallArgs, blockNr rpc.BlockNumber) (string, *big.Int, error) { 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) stateDb, err := state.New(block.Root(), s.chainDb)
if err != nil { if err != nil {
return "0x", nil, err return "0x", nil, err
@ -684,19 +690,21 @@ type PublicTransactionPoolAPI struct {
eventMux *event.TypeMux eventMux *event.TypeMux
chainDb ethdb.Database chainDb ethdb.Database
bc *core.BlockChain bc *core.BlockChain
miner *miner.Miner
am *accounts.Manager am *accounts.Manager
txPool *core.TxPool txPool *core.TxPool
txMu sync.Mutex txMu sync.Mutex
} }
// NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool. // 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{ return &PublicTransactionPoolAPI{
eventMux: eventMux, eventMux: eventMux,
chainDb: chainDb, chainDb: chainDb,
bc: bc, bc: bc,
am: am, am: am,
txPool: txPool, txPool: txPool,
miner: m,
} }
} }
@ -724,7 +732,7 @@ func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(blockNr rpc.
return rpc.NewHexNumber(0) 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())) 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. // GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index.
func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(blockNr rpc.BlockNumber, index rpc.HexNumber) (*RPCTransaction, error) { 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 newRPCTransactionFromBlockIndex(block, index.Int())
} }
return nil, nil 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 // 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) { 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 { if block == nil {
return nil, nil return nil, nil
} }
@ -1256,6 +1264,16 @@ func (api *PrivateAdminAPI) ExportChain(file string) (bool, error) {
return true, nil 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. // ImportChain imports a blockchain from a local file.
func (api *PrivateAdminAPI) ImportChain(file string) (bool, error) { func (api *PrivateAdminAPI) ImportChain(file string) (bool, error) {
// Make sure the can access the file to import // 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 { if len(blocks) == 0 {
break break
} }
if hasAllBlocks(api.eth.BlockChain(), blocks) {
blocks = blocks[:0]
continue
}
// Import the batch and reset the buffer // Import the batch and reset the buffer
if _, err := api.eth.BlockChain().InsertChain(blocks); err != nil { if _, err := api.eth.BlockChain().InsertChain(blocks); err != nil {
return false, fmt.Errorf("batch %d: failed to insert: %v", batch, err) 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) { func (api *PrivateDebugAPI) SetHead(number uint64) {
api.eth.BlockChain().SetHead(number) 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)
}

View File

@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/compiler" "github.com/ethereum/go-ethereum/common/compiler"
"github.com/ethereum/go-ethereum/common/httpclient" "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"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/downloader"
@ -44,7 +45,7 @@ import (
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
rpc "github.com/ethereum/go-ethereum/rpc/v2" "github.com/ethereum/go-ethereum/rpc"
) )
const ( const (
@ -129,6 +130,7 @@ type Ethereum struct {
autodagquit chan bool autodagquit chan bool
etherbase common.Address etherbase common.Address
netVersionId int netVersionId int
netRPCService *PublicNetAPI
} }
func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
@ -262,12 +264,12 @@ func (s *Ethereum) APIs() []rpc.API {
}, { }, {
Namespace: "eth", Namespace: "eth",
Version: "1.0", 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, Public: true,
}, { }, {
Namespace: "eth", Namespace: "eth",
Version: "1.0", 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, Public: true,
}, { }, {
Namespace: "eth", Namespace: "eth",
@ -307,6 +309,15 @@ func (s *Ethereum) APIs() []rpc.API {
Namespace: "debug", Namespace: "debug",
Version: "1.0", Version: "1.0",
Service: NewPrivateDebugAPI(s), 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 // Start implements node.Service, starting all internal goroutines needed by the
// Ethereum protocol implementation. // Ethereum protocol implementation.
func (s *Ethereum) Start(*p2p.Server) error { func (s *Ethereum) Start(srvr *p2p.Server) error {
if s.AutoDAG { if s.AutoDAG {
s.StartAutoDAG() s.StartAutoDAG()
} }
s.protocolManager.Start() s.protocolManager.Start()
s.netRPCService = NewPublicNetAPI(srvr, s.NetVersion())
return nil return nil
} }

View File

@ -17,7 +17,7 @@
package downloader package downloader
import ( 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. // PublicDownloaderAPI provides an API which gives informatoin about the current synchronisation status.

View File

@ -32,7 +32,7 @@ import (
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
rpc "github.com/ethereum/go-ethereum/rpc/v2" "github.com/ethereum/go-ethereum/rpc"
) )
var ( var (

View File

@ -5740,7 +5740,9 @@ Property.prototype.extractCallback = function (args) {
*/ */
Property.prototype.attachToObject = function (obj) { Property.prototype.attachToObject = function (obj) {
var proto = { var proto = {
get: this.buildGet() //get: this.buildGet()
get: this.buildGet(),
enumerable: true
}; };
var names = this.name.split('.'); var names = this.name.split('.');

View File

@ -21,7 +21,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/logger/glog" "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. // PublicMinerAPI provides an API to control the miner.

View File

@ -21,11 +21,15 @@ import (
"strings" "strings"
"time" "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/logger/glog"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover" "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" "github.com/rcrowley/go-metrics"
"gopkg.in/fatih/set.v0"
) )
// PrivateAdminAPI is the collection of administrative API methods exposed only // 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. // StartRPC starts the HTTP RPC API server.
func (api *PrivateAdminAPI) StartRPC(address string, port int, cors string, apis string) (bool, error) { func (api *PrivateAdminAPI) StartRPC(address string, port int, cors string, apis string) (bool, error) {
/*// Parse the list of API modules to make available var offeredAPIs []rpc.API
apis, err := api.ParseApiString(apis, codec.JSON, xeth.New(api.node, nil), api.node) if len(apis) > 0 {
if err != nil { namespaces := set.New()
return false, err for _, a := range strings.Split(apis, ",") {
namespaces.Add(strings.TrimSpace(a))
} }
// Configure and start the HTTP RPC server for _, api := range api.node.APIs() {
config := comms.HttpConfig{ if namespaces.Has(api.Namespace) {
ListenAddress: address, offeredAPIs = append(offeredAPIs, api)
ListenPort: port,
CorsDomain: cors,
} }
if err := comms.StartHttp(config, self.codec, api.Merge(apis...)); err != nil {
return false, err
} }
return true, nil*/ } else { // use by default all public API's
return false, fmt.Errorf("needs new RPC implementation to resolve circular dependency") 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. // StopRPC terminates an already running HTTP RPC API endpoint.
func (api *PrivateAdminAPI) StopRPC() { func (api *PrivateAdminAPI) StopRPC() (bool, error) {
comms.StopHttp() 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 // 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 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)))
}

View File

@ -27,7 +27,7 @@ import (
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
rpc "github.com/ethereum/go-ethereum/rpc/v2" "github.com/ethereum/go-ethereum/rpc"
) )
var ( var (
@ -290,6 +290,11 @@ func (n *Node) APIs() []rpc.API {
Version: "1.0", Version: "1.0",
Service: NewPublicDebugAPI(n), Service: NewPublicDebugAPI(n),
Public: true, Public: true,
}, {
Namespace: "web3",
Version: "1.0",
Service: NewPublicWeb3API(n),
Public: true,
}, },
} }
// Inject all the APIs owned by various services // Inject all the APIs owned by various services

View File

@ -23,7 +23,7 @@ import (
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover" "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 // SampleService is a trivial network service that can be attached to a node for

View File

@ -23,7 +23,7 @@ import (
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/p2p" "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 // ServiceContext is a collection of service independent options inherited from

View File

@ -23,7 +23,7 @@ import (
"reflect" "reflect"
"github.com/ethereum/go-ethereum/p2p" "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. // NoopService is a trivial implementation of the Service interface.

View File

@ -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
}

View File

@ -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
}

View File

@ -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'
})
]
});
`

View File

@ -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...)
}

View File

@ -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 := &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))
}
}

View File

@ -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
}

File diff suppressed because it is too large Load Diff

View File

@ -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
}

View File

@ -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
}

View File

@ -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:
[
]
});
`

View File

@ -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
}

View File

@ -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
}

View File

@ -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:
[
]
});
`

View File

@ -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 &ethApi{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
}

File diff suppressed because it is too large Load Diff

View File

@ -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'
})
]
});
`

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
})
]
});
`

View File

@ -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
}

View File

@ -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'
})
]
});
`

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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'
})
]
});
`

View File

@ -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
}

View File

@ -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
}

View File

@ -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'
})
]
});
`

View File

@ -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
}

View File

@ -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'
})
]
});
`

View File

@ -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(&eth); 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 ""
}

View File

@ -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
}

View File

@ -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")
}

View File

@ -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()
}

View File

@ -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")
}
}

View File

@ -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")
}

View File

@ -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
}

View File

@ -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")
}

View File

@ -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
}

View File

@ -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
}

View File

@ -99,4 +99,130 @@ Subscriptions are deleted when:
- the user sends an unsubscribe request - the user sends an unsubscribe request
- the connection which was used to create the subscription is closed - the connection which was used to create the subscription is closed
*/ */
package v2 package rpc
var (
// Mapping between the different methods each api supports
AutoCompletion = map[string][]string{
"admin": []string{
"addPeer",
"datadir",
"enableUserAgent",
"exportChain",
"getContractInfo",
"httpGet",
"importChain",
"nodeInfo",
"peers",
"register",
"registerUrl",
"saveInfo",
"setGlobalRegistrar",
"setHashReg",
"setUrlHint",
"setSolc",
"sleep",
"sleepBlocks",
"startNatSpec",
"startRPC",
"stopNatSpec",
"stopRPC",
"verbosity",
},
"db": []string{
"getString",
"putString",
"getHex",
"putHex",
},
"debug": []string{
"dumpBlock",
"getBlockRlp",
"metrics",
"printBlock",
"processBlock",
"seedHash",
"setHead",
},
"eth": []string{
"accounts",
"blockNumber",
"call",
"contract",
"coinbase",
"compile.lll",
"compile.serpent",
"compile.solidity",
"contract",
"defaultAccount",
"defaultBlock",
"estimateGas",
"filter",
"getBalance",
"getBlock",
"getBlockTransactionCount",
"getBlockUncleCount",
"getCode",
"getNatSpec",
"getCompilers",
"gasPrice",
"getStorageAt",
"getTransaction",
"getTransactionCount",
"getTransactionFromBlock",
"getTransactionReceipt",
"getUncle",
"hashrate",
"mining",
"namereg",
"pendingTransactions",
"resend",
"sendRawTransaction",
"sendTransaction",
"sign",
"syncing",
},
"miner": []string{
"hashrate",
"makeDAG",
"setEtherbase",
"setExtra",
"setGasPrice",
"startAutoDAG",
"start",
"stopAutoDAG",
"stop",
},
"net": []string{
"peerCount",
"listening",
},
"personal": []string{
"listAccounts",
"newAccount",
"unlockAccount",
},
"shh": []string{
"post",
"newIdentity",
"hasIdentity",
"newGroup",
"addToGroup",
"filter",
},
"txpool": []string{
"status",
},
"web3": []string{
"sha3",
"version",
"fromWei",
"toWei",
"toHex",
"toAscii",
"fromAscii",
"toBigNumber",
"isAddress",
},
}
)

Some files were not shown because too many files have changed in this diff Show More